Feature Representation Methods in ChemML
To build a machine learning model, raw chemical data is first converted into a numerical representation. The representation contains spatial or topological information that defines a molecule. The resulting features may either be in continuous (molecular descriptors) or discrete (molecular fingerprints) form.
[1]:
from chemml.chem import Molecule
from chemml.datasets import load_organic_density
import numpy as np
import warnings
warnings.filterwarnings('ignore')
Creating chemml.chem.Molecule object from molecule SMILES
All feature representation methods available in ChemML require chemml.chem.Molecule as inputs
[2]:
# Importing an existing dataset from ChemML
molecules, target, dragon_subset = load_organic_density()
mol_objs_list = []
for smi in molecules['smiles']:
mol = Molecule(smi, 'smiles')
mol.hydrogens('add')
mol.to_xyz('MMFF', maxIters=10000, mmffVariant='MMFF94s')
mol_objs_list.append(mol)
Coulomb Matrix
Simple molecular descriptor which mimics the electro-static interaction between nuclei.
[3]:
from chemml.chem import CoulombMatrix
#The coulomb matrix type can be sorted (SC), unsorted(UM), unsorted triangular(UT), eigen spectrum(E), or random (RC)
CM = CoulombMatrix(cm_type='SC',n_jobs=-1)
features = CM.represent(mol_objs_list)
display(features[:5])
featurizing molecules in batches of 31 ...
500/500 [==================================================] - 11s 21ms/step
Merging batch features ... [DONE]
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | ... | 1643 | 1644 | 1645 | 1646 | 1647 | 1648 | 1649 | 1650 | 1651 | 1652 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 388.023441 | 72.409291 | 388.023441 | 49.814751 | 70.881016 | 388.023441 | 43.493371 | 29.659901 | 22.280919 | 53.358707 | ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 1 | 73.516695 | 14.960493 | 73.516695 | 12.181409 | 15.302646 | 53.358707 | 20.835127 | 8.040502 | 7.380458 | 53.358707 | ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 2 | 388.023441 | 10.095779 | 73.516695 | 40.531118 | 5.583446 | 53.358707 | 21.559163 | 7.172857 | 20.141293 | 53.358707 | ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 3 | 388.023441 | 72.492835 | 388.023441 | 48.345064 | 39.988956 | 73.516695 | 28.398112 | 20.462810 | 13.932927 | 73.516695 | ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 4 | 388.023441 | 32.939514 | 388.023441 | 20.776038 | 19.839362 | 73.516695 | 18.287313 | 28.898187 | 12.782574 | 53.358707 | ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
5 rows × 1653 columns
Fingerprints from RDKit
Molecular fingerprints are a way of encoding the structure of a molecule. The most common type of fingerprint is a series of binary digits (bits) that represent the presence or absence of particular substructures in the molecule. Comparing fingerprints allows you to determine the similarity between two molecules, to find matches to a query substructure, etc.
[4]:
from chemml.chem import RDKitFingerprint
# RDKit fingerprint types: 'morgan', 'hashed_topological_torsion' or 'htt' , 'MACCS' or 'maccs', 'hashed_atom_pair' or 'hap'
morgan_fp = RDKitFingerprint(fingerprint_type='morgan', vector='bit', n_bits=1024, radius=3)
features = morgan_fp.represent(mol_objs_list)
display(features[:5])
Generating Morgan Fingerprints: 100%|██████████| 500/500 [00:00<00:00, 5901.04molecule/s]
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | ... | 1014 | 1015 | 1016 | 1017 | 1018 | 1019 | 1020 | 1021 | 1022 | 1023 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 |
| 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 2 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 3 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 4 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 0 |
5 rows × 1024 columns
Molecule tensors from chemml.chem.Molecule objects
Molecule tensors can be used to create neural graph fingerprints using chemml.models
[5]:
from chemml.chem import tensorise_molecules
atoms,bonds,edges = tensorise_molecules(molecules=mol_objs_list, max_degree=5, max_atoms=None, n_jobs=-1, batch_size=100, verbose=True)
Tensorising molecules in batches of 100 ...
500/500 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 12s 24ms/step
Merging batch tensors ... [DONE]
[6]:
print("Matrix for atom features (num_molecules, max_atoms, num_atom_features):\n", atoms.shape)
print("Matrix for connectivity between atoms (num_molecules, max_atoms, max_degree):\n", edges.shape)
print("Matrix for bond features (num_molecules, max_atoms, max_degree, num_bond_features):\n", bonds.shape)
Matrix for atom features (num_molecules, max_atoms, num_atom_features):
(500, 57, 62)
Matrix for connectivity between atoms (num_molecules, max_atoms, max_degree):
(500, 57, 5)
Matrix for bond features (num_molecules, max_atoms, max_degree, num_bond_features):
(500, 57, 5, 6)
Descriptors from RDKit
Comprehensive set of molecular descriptors calculated using RDKit. Includes topological, geometrical, electronic, and constitutional properties. Efficient calculation for large datasets. Flexible selection of specific or all descriptors via the RDKDesc class declaration. Integrates with other RDKit functions and Python workflows.
[7]:
from chemml.chem import RDKDesc
rdd = RDKDesc()
features = rdd.represent(mol_objs_list, n_jobs=-1)
display(features[:5])
Calculating RDKit descriptors: 100%|██████████| 500/500 [00:02<00:00, 248.34it/s]
| MaxAbsEStateIndex | MaxEStateIndex | MinAbsEStateIndex | MinEStateIndex | qed | SPS | MolWt | HeavyAtomMolWt | ExactMolWt | NumValenceElectrons | ... | fr_sulfonamd | fr_sulfone | fr_term_acetylene | fr_tetrazole | fr_thiazole | fr_thiocyan | fr_thiophene | fr_unbrch_alkane | fr_urea | SMILES | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 8.638741 | 8.638741 | 0.039513 | -3.852793 | 0.816913 | 70.705882 | 285.503 | 266.351 | 285.067963 | 94 | ... | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | c1nc(C2CSCCS2)sc1CC1CCCC1 |
| 1 | 8.193508 | 8.193508 | 0.286175 | -0.599619 | 0.735869 | 16.444444 | 240.222 | 232.158 | 240.064725 | 88 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | Oc1nccnc1-c1coc(-c2cnccn2)c1 |
| 2 | 8.666998 | 8.666998 | 0.049406 | -3.394321 | 0.801905 | 32.818182 | 313.386 | 298.266 | 313.099731 | 112 | ... | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | c1cc(-c2ccc(N3CNCN(c4cncs4)C3)nc2)co1 |
| 3 | 8.032986 | 8.032986 | 0.013750 | -2.742153 | 0.749729 | 51.384615 | 218.299 | 208.219 | 218.007136 | 72 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | Oc1occc1C1(O)CSCCS1 |
| 4 | 9.189644 | 9.189644 | 0.166504 | -3.306171 | 0.772983 | 38.095238 | 319.415 | 306.311 | 319.056152 | 108 | ... | 0 | 0 | 0 | 0 | 2 | 0 | 0 | 0 | 0 | c1nc(-c2cocc2C2NCNCN2c2cscn2)cs1 |
5 rows × 218 columns
Descriptors from Mordred
Note: This function requires Mordred to be installed from the link.
Mordred molecular descriptors are an open-source alternative to Dragon/RDKit descriptors. This library can generate up to 1800+ descriptors, in comparison to Dragon’s 5200+ and RDKit’s 200.
[8]:
from chemml.chem import Mordred
mord = Mordred()
features = mord.represent(mol_objs_list, quiet=False)
display(features[:5])
Normalizing Molecule input: 100%|██████████| 500/500 [00:00<00:00, 283437.22it/s]
100%|██████████| 500/500 [00:15<00:00, 32.49it/s]
| ABC | ABCGG | nAcid | nBase | SpAbs_A | SpMax_A | SpDiam_A | SpAD_A | SpMAD_A | LogEE_A | ... | TSRW10 | MW | AMW | WPath | WPol | Zagreb1 | Zagreb2 | mZagreb1 | mZagreb2 | SMILES | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 13.394589 | 11.392017 | 0 | 0 | 22.998278 | 2.356990 | 4.586123 | 22.998278 | 1.352840 | 3.780704 | ... | 64.739856 | 285.067963 | 7.918555 | 564 | 19 | 88.0 | 101.0 | 3.694444 | 3.777778 | c1nc(C2CSCCS2)sc1CC1CCCC1 |
| 1 | 14.130205 | 11.712802 | 0 | 0 | 24.114905 | 2.401132 | 4.700060 | 24.114905 | 1.339717 | 3.834684 | ... | 64.604946 | 240.064725 | 9.233259 | 624 | 25 | 94.0 | 110.0 | 4.555556 | 4.000000 | Oc1nccnc1-c1coc(-c2cnccn2)c1 |
| 2 | 17.556349 | 13.858237 | 0 | 1 | 30.133660 | 2.388743 | 4.753098 | 30.133660 | 1.369712 | 4.046753 | ... | 71.264318 | 313.099731 | 8.462155 | 1142 | 30 | 118.0 | 139.0 | 4.666667 | 4.833333 | c1cc(-c2ccc(N3CNCN(c4cncs4)C3)nc2)co1 |
| 3 | 10.065754 | 9.496809 | 0 | 0 | 16.550756 | 2.429396 | 4.799667 | 16.550756 | 1.273135 | 3.507942 | ... | 58.294869 | 218.007136 | 9.478571 | 226 | 18 | 68.0 | 80.0 | 4.284722 | 2.861111 | Oc1occc1C1(O)CSCCS1 |
| 4 | 16.768362 | 14.962063 | 0 | 2 | 28.597221 | 2.464180 | 4.847996 | 28.597221 | 1.361772 | 4.008031 | ... | 71.560531 | 319.056152 | 9.384004 | 870 | 29 | 114.0 | 137.0 | 4.416667 | 4.638889 | c1nc(-c2cocc2C2NCNCN2c2cscn2)cs1 |
5 rows × 1390 columns
3D Descriptors from Mordred
Mordred requires explicit initialization to calculate 3D descriptors. Since XYZ geometry already exists, the generator will skip optimization unless specified.
[9]:
mord_3d = Mordred(ignore_3D=False)
features_3d = mord_3d.represent(mol_objs_list, quiet=False, force_optimize=True)
display(features_3d[:5])
Normalizing Molecule input: 100%|██████████| 500/500 [00:00<?, ?it/s]
Optimizing 3D geometries: 0%| | 0/500 [00:00<?, ?it/s]Optimizing 3D geometries: 100%|██████████| 500/500 [00:29<00:00, 16.79it/s]
100%|██████████| 500/500 [00:19<00:00, 25.94it/s]
| ABC | ABCGG | nAcid | nBase | SpAbs_A | SpMax_A | SpDiam_A | SpAD_A | SpMAD_A | LogEE_A | ... | TSRW10 | MW | AMW | WPath | WPol | Zagreb1 | Zagreb2 | mZagreb1 | mZagreb2 | SMILES | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 13.394589 | 11.392017 | 0 | 0 | 22.998278 | 2.356990 | 4.586123 | 22.998278 | 1.352840 | 3.780704 | ... | 64.739856 | 285.067963 | 7.918555 | 564 | 19 | 88.0 | 101.0 | 3.694444 | 3.777778 | c1nc(C2CSCCS2)sc1CC1CCCC1 |
| 1 | 14.130205 | 11.712802 | 0 | 0 | 24.114905 | 2.401132 | 4.700060 | 24.114905 | 1.339717 | 3.834684 | ... | 64.604946 | 240.064725 | 9.233259 | 624 | 25 | 94.0 | 110.0 | 4.555556 | 4.000000 | Oc1nccnc1-c1coc(-c2cnccn2)c1 |
| 2 | 17.556349 | 13.858237 | 0 | 1 | 30.133660 | 2.388743 | 4.753098 | 30.133660 | 1.369712 | 4.046753 | ... | 71.264318 | 313.099731 | 8.462155 | 1142 | 30 | 118.0 | 139.0 | 4.666667 | 4.833333 | c1cc(-c2ccc(N3CNCN(c4cncs4)C3)nc2)co1 |
| 3 | 10.065754 | 9.496809 | 0 | 0 | 16.550756 | 2.429396 | 4.799667 | 16.550756 | 1.273135 | 3.507942 | ... | 58.294869 | 218.007136 | 9.478571 | 226 | 18 | 68.0 | 80.0 | 4.284722 | 2.861111 | Oc1occc1C1(O)CSCCS1 |
| 4 | 16.768362 | 14.962063 | 0 | 2 | 28.597221 | 2.464180 | 4.847996 | 28.597221 | 1.361772 | 4.008031 | ... | 71.560531 | 319.056152 | 9.384004 | 870 | 29 | 114.0 | 137.0 | 4.416667 | 4.638889 | c1nc(-c2cocc2C2NCNCN2c2cscn2)cs1 |
5 rows × 1603 columns
Descriptors from PaDELPy
Note: This function requires PaDELPy and JRE 6+ to be installed from the link.
PaDEL-Descriptor: Open-source software for calculating molecular descriptors and fingerprints. Computes 797 descriptors (663 1D/2D, 134 3D) and 10 fingerprint types. Uses Chemistry Development Kit and custom implementations. Offers GUI and CLI, supports multiple file formats, and enables multithreading for efficient calculations.
[10]:
from chemml.chem import PadelDesc
padel = PadelDesc()
features = padel.represent(mol_objs_list[:10])
display(features[:5])
| nAcid | ALogP | ALogp2 | AMR | apol | naAromAtom | nAromBond | nAtom | nHeavyAtom | nH | ... | P2s | E1s | E2s | E3s | Ts | As | Vs | Ks | Ds | SMILES | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 0 | 1.4476999999999995 | 2.095835289999999 | 63.309999999999995 | 45.34906699999998 | 5 | 5 | 36 | 17 | 19 | ... | c1nc(C2CSCCS2)sc1CC1CCCC1 | |||||||||
| 1 | 0 | -0.6144000000000001 | 0.37748736000000005 | 4.1837 | 32.458344 | 17 | 19 | 26 | 18 | 8 | ... | Oc1nccnc1-c1coc(-c2cnccn2)c1 | |||||||||
| 2 | 0 | 0.05900000000000016 | 0.0034810000000000192 | 31.210299999999997 | 45.60389499999999 | 16 | 17 | 37 | 22 | 15 | ... | c1cc(-c2ccc(N3CNCN(c4cncs4)C3)nc2)co1 | |||||||||
| 3 | 0 | 0.16129999999999978 | 0.026017689999999927 | 38.772499999999994 | 28.953929999999986 | 5 | 5 | 23 | 13 | 10 | ... | Oc1occc1C1(O)CSCCS1 | |||||||||
| 4 | 0 | 0.3426999999999998 | 0.11744328999999985 | 38.349399999999996 | 43.650308999999986 | 15 | 16 | 34 | 21 | 13 | ... | c1nc(-c2cocc2C2NCNCN2c2cscn2)cs1 |
5 rows × 1876 columns