Generic Data

Previous chapters Molecule Properties, Atom Properties, Bond Properties have described how common global properties of molecule, atoms, and bonds can be modified and accessed. There are applications, however, when associating arbitrary data with objects such as molecules, atoms and bonds is necessary. OEChem TK provides a framework to solve this problem by allowing to attach generic data to an object by association either with an integer or character string, called tag identifier.

The following two snippets demonstrate how generic data (for example molecule weight) can be attached to a molecule:

tag = oechem.OEGetTag("MolWeight")
mol.SetData(tag, oechem.OECalculateMolecularWeight(mol))
mol.SetData("MolWeight", oechem.OECalculateMolecularWeight(mol))

After annotation, the data can be accessed with the same integer or character string identifier:

tag = oechem.OEGetTag("MolWeight")
print(mol.GetData(tag))
print(mol.GetData("MolWeight"))

Warning

The integer tag of a generic data should always be allocated using the OEGetTag function.

The following table shows the basic methods of the OEBase class that allow the manipulation of generic data.

Methods to manipulate generic data

Method

Description

OEBase.SetData

sets a generic data associating it with the given tag

OEBase.AddData

adds a generic data associating it with the given tag

OEBase.HasData

determines whether a molecule has any generic data with a given tag

OEBase.GetData

returns the generic data associated with the given tag

OEBase.DeleteData

deletes all generic data with the given tag

OEBase.Clear

clears all stored generic data

The main difference between the OEBase.SetData method and the OEBase.AddData method is that if a data with the same identifier is already attached to an object then:

Furthermore, OEBase.SetData does not allow replacing an existing tag with a different data type:

tag = oechem.OEGetTag("MolWeight")
weight = oechem.OECalculateMolecularWeight(mol)
mol.SetData(tag, float(weight))
mol.SetData(tag, int(weight))

The above code will throw the following warning:

Warning: data type mismatch found when using generic data

Attaching plain old data

The following simple code demonstrate how data calculated and attached to a molecule in one function can be accessed later on through the tag identifier.

Listing 1: Example of using generic data

from openeye import oechem


def CalculateMoleculeWeight(mol):
    mol.SetData("MolWeight", oechem.OECalculateMolecularWeight(mol))


def PrintMoleculeWeight(mol):
    tag = oechem.OEGetTag("MolWeight")
    if mol.HasData(tag):
        print("molecule weight =", mol.GetData(tag))
    else:
        print("molecule weight is not calculated!")


mol = oechem.OEGraphMol()
oechem.OESmilesToMol(mol, "C1CCCC(C(=O)O)C1")

CalculateMoleculeWeight(mol)
PrintMoleculeWeight(mol)

Note

It is a good programming practice to call OEBase.HasData in order to check whether or not a data exists before trying to access it by the OEBase.GetData method.

The OEBase.GetData method returns only the first instance of data stored previously with the associated data tag. Data can also be accessed by using the OEBase.GetDataIter method that returns an iterator over all data stored.

mol = oechem.OEGraphMol()
oechem.OEParseSmiles(mol, "C1CCCC(C(=O)O)C1")
activitytag = oechem.OEGetTag("activity")
mol.AddData(activitytag, "antiarthritic")
mol.AddData(activitytag, "antiinflammatory")
mol.SetData("weight", oechem.OECalculateMolecularWeight(mol))

for gdata in mol.GetDataIter():
    print(oechem.OEGetTag(gdata.GetTag()), gdata.GetData())

The output of code snippet above is the following:

activity antiarthritic
activity antiinflammatory
weight 128.16898

The OEBase.GetDataIter method can also take a tag identifier. In this case it iterates over only data associated with the given tag. For example, the following code will only prints out the two piece of ‘activity’ data.

for gdata in mol.GetDataIter(oechem.OEGetTag("activity")):
    print(oechem.OEGetTag(gdata.GetTag()), gdata.GetData())

Attaching data to atoms

Generic data can be attached to any object that derives from the OEBase class. The following program shows an example where hydrogen bonding donor property is attached as a bool value to the corresponding OEAtomBase object.

Listing 2: Example of attaching generic data to atoms

from openeye import oechem

IsDonorAtom = oechem.OEMatchAtom("[!H0;#7,#8]")

mol = oechem.OEGraphMol()
oechem.OESmilesToMol(mol, "c1c(Cl)cncc1C(=O)O")

for atom in mol.GetAtoms():
    atom.SetData("isdonor", IsDonorAtom(atom))


class IsDonorAtomPred(oechem.OEUnaryAtomPred):
    def __call__(self, atom):
        return atom.GetData("isdonor")


print("Donor atoms:", end=" ")
for atom in mol.GetAtoms(IsDonorAtomPred()):
    print(atom.GetIdx(), oechem.OEGetAtomicSymbol(atom.GetAtomicNum()), end=" ")
print()

See also

Attaching other objects

The type of the generic data is not restricted to fundamental data types of the programming language. High-level OEChem TK objects such as OEMolBase, OEAtomBase, OEBondBase OEScalarGrid, OESkewGrid and OESurface can also be stored through this mechanism. The following program demonstrates how to attach a subset of a molecule to the original molecule as generic data.

Listing 3: Example of attaching a molecule as generic data

from openeye import oechem
mol = oechem.OEGraphMol()
oechem.OESmilesToMol(mol, "c1ccccc1O")

frag = oechem.OEGraphMol()
oechem.OESubsetMol(frag, mol, oechem.OEIsCarbon())

mol.SetData("just_carbon", frag)
justCarbon = mol.GetData("just_carbon")

Sequences of objects can be stored as well. The following example shows how to attach a sequence of atoms into an OEAtomBase object.

Listing 4: Example of attaching list of atoms as generic data

from openeye import oechem


def CollectIncorrectStereo(mol):
    for atom in mol.GetAtoms():
        if atom.IsAromatic() and atom.HasStereoSpecified(oechem.OEAtomStereo_Tetrahedral):
            neighs = []
            for n in atom.GetAtoms():
                neighs.append(n.GetIdx())
            atom.SetData("incorrect_stereo_neighs", neighs)


def RemoveIncorrectStereo(mol):
    for atom in mol.GetAtoms():
        if atom.HasData("incorrect_stereo_neighs"):
            neighs = [mol.GetAtom(oechem.OEHasAtomIdx(a))
                      for a in atom.GetData("incorrect_stereo_neighs")]
            atom.SetStereo(neighs, oechem.OEAtomStereo_Tetrahedral, oechem.OEAtomStereo_Undefined)


mol = oechem.OEGraphMol()
oechem.OESmilesToMol(mol, "c1c[n@@H]cc1")
print(oechem.OEMolToSmiles(mol))

CollectIncorrectStereo(mol)
RemoveIncorrectStereo(mol)

print(oechem.OEMolToSmiles(mol))

See also

Note

Generic data attached to a molecule or any of its atoms or bonds is automatically saved when the molecule is written into an .oeb file.