RaPTOR / mimis_preprocessor.py
Mimi Lavin
add preprocessor
79c9a60
Raw
History Blame Contribute Delete
4.82 kB
from __future__ import annotations
from typing import Dict, Tuple
import pandas as pd
from rdkit import Chem
from rdkit import RDLogger
RDLogger.DisableLog("rdApp.*") # disable RDKit warnings
def canonicalize_smiles(smiles: str):
"""
convert smiles to canonical smiles
:param smiles: smiles string
:return: returns canonical smiles string, or None if invalid
"""
if not isinstance(smiles, str):
print('not a valid smiles string')
return None
try:
mol = Chem.MolFromSmiles(smiles, sanitize=True) # create rdkit mol object from smiles
if mol is None:
return None
return Chem.MolToSmiles(mol, canonical=True)
except Exception:
print('exception occurred during canonicalization')
return None
def canonicalize_smiles_df(df, smiles_column_name, canonical_column_name: str = "canonical_smiles", drop_bad_smiles: bool = True):
"""
canonicalize a dataframe column of smiles strings
:param df: pandas dataframe
:param smiles_column_name: column name to look for smiles strings
:param canonical_column_name: name of new column to store canonical smiles
:param drop_bad_smiles: remove the rows with invalid smiles
:return: original dataframe with new column of canonical smiles and invalid smiles removed (if specified)
"""
df2 = df.copy()
df2[canonical_column_name] = df2[smiles_column_name].apply(canonicalize_smiles)
# find out how many smiles were sanitized by comparing the original and canonical columns
df2['changed'] = df2[canonical_column_name] != df2[smiles_column_name]
n_changed = df2['changed'].sum()
print(len(df2) - n_changed, 'smiles were already canonical,', n_changed, 'smiles were changed during canonicalization')
if drop_bad_smiles:
n_before = len(df2)
df2 = df2.dropna(subset=[canonical_column_name]).reset_index(drop=True)
n_after = len(df2)
print(f"dropped {n_before - n_after} invalid smiles")
return df2
def count_stereocenters_from_smiles(smiles: str) -> Dict[str, int]:
mol = Chem.MolFromSmiles(smiles)
if mol is None:
raise ValueError(f"Invalid SMILES: {smiles}")
# Ensure RDKit has identified potential stereo bonds
Chem.FindPotentialStereoBonds(mol)
stereocenters = Chem.FindMolChiralCenters(mol, includeUnassigned=True)
stereobonds = [
bond
for bond in mol.GetBonds()
if bond.GetStereo() is not Chem.rdchem.BondStereo.STEREONONE
]
atom_assigned = sum(1 for _, tag in stereocenters if tag != "?")
atom_unassigned = sum(1 for _, tag in stereocenters if tag == "?")
bond_assigned = sum(
1
for bond in stereobonds
if bond.GetStereo() is not Chem.rdchem.BondStereo.STEREOANY
)
bond_unassigned = sum(
1 for bond in stereobonds if bond.GetStereo() is Chem.rdchem.BondStereo.STEREOANY
)
return {
"atom_assigned": atom_assigned,
"atom_unassigned": atom_unassigned,
"bond_assigned": bond_assigned,
"bond_unassigned": bond_unassigned,
}
def filter_unassigned_stereo_rows(df: pd.DataFrame, smiles_col: str = "original_smiles", *, drop_invalid_smiles: bool = True, keep_stereo_counts: bool = False) -> pd.DataFrame:
counts = []
bad_smiles_idx = []
for idx, smi in df[smiles_col].items():
if not isinstance(smi, str) or not smi.strip():
bad_smiles_idx.append(idx)
counts.append({"atom_assigned": 0, "atom_unassigned": 0, "bond_assigned": 0, "bond_unassigned": 0})
continue
try:
c = count_stereocenters_from_smiles(smi)
counts.append(c)
except Exception:
if drop_invalid_smiles:
bad_smiles_idx.append(idx)
counts.append({"atom_assigned": 0, "atom_unassigned": 0, "bond_assigned": 0, "bond_unassigned": 0})
else:
raise
counts_df = pd.DataFrame(counts, index=df.index)
if keep_stereo_counts:
df2 = df.join(counts_df)
else:
df2 = df.copy()
# mask rows to keep: no unassigned stereo AND not bad smiles (if dropping invalid)
ok = (counts_df["atom_unassigned"] == 0) & (counts_df["bond_unassigned"] == 0)
if drop_invalid_smiles and bad_smiles_idx:
ok = ok & (~df2.index.isin(bad_smiles_idx))
return df2.loc[ok].reset_index(drop=True)
if __name__ == '__main__':
df = pd.read_csv('/Users/mimis_stuff/PycharmProjects/PythonProject/philicity_prediction/data/all_philicities.csv')
df = canonicalize_smiles_df(df, 'radical_smiles', 'canonical_radical_smiles', True)
df.to_csv('/Users/mimis_stuff/PycharmProjects/PythonProject/philicity_prediction/data/all_philicities_canonicalized.csv', index=False)