<jupyter_start><jupyter_text>Replace atom symbols with atom numbers, so RDKit does not raise errors when creating Mol from the SMARTS<jupyter_code>old_smarts = []
new_smarts = []
for line in lines:
    smarts = line.strip().split("\t")[1]
    old_smarts.append(smarts)
    new = smarts.replace("Cl", "[#17]").replace("Mg", "[#12]").replace("Al", "[#13]")
    new = new.replace("Li", "[#3]").replace("Si", "[#14]").replace("Br", "[#35]")
    new = new.replace("Se", "[#34]").replace("[As]", "[#33]").replace("As", "[#33]").replace("Na", "[#11]")
    new = new.replace("C", "[#6]").replace("N", "[#7]").replace("O", "[#8]")
    new = new.replace("B", "[#5]").replace("H", "[#1]").replace("K", "[#19]")
    new_smarts.append(new)<jupyter_output><empty_output><jupyter_text>Atom pairs are not sensitive to the bonds between them<jupyter_code>for i in range(64):
    new_smarts[i] = new_smarts[i].replace("-", "~")<jupyter_output><empty_output><jupyter_text>The section 5 and section 6 single and double bonds of PubChem fingerprint also matches aromatic bonds<jupyter_code>for i in range(153, 450):
    new_smarts[i] = new_smarts[i].replace("-", "-,:")
    new_smarts[i] = new_smarts[i].replace("=", "=,:")<jupyter_output><empty_output><jupyter_text>Check if all the SMARTS are convertable to RDKit Mol object<jupyter_code>for i, smart in enumerate(new_smarts):
    patt = Chem.MolFromSmarts(smart)
    if patt is None:
        print(i, smart)<jupyter_output><empty_output><jupyter_text>Write the original and transformed SMARTS to pandas dataframe<jupyter_code>data = {
    "PubChem_pattern": old_smarts,
    "SMARTS": new_smarts
}
df = pd.DataFrame(data)
df.head(10)<jupyter_output><empty_output><jupyter_text>Create images for the SMARTS<jupyter_code>images = []
for smart in df["SMARTS"]:
    patt = Chem.MolFromSmarts(smart)
    img = Chem.Draw.MolToImage(patt)
    images.append(img)<jupyter_output><empty_output><jupyter_text>Save images to disk and log their paths<jupyter_code>image_paths = []
os.makedirs(os.path.join("..", "data", "pubchemfp_pattern_images"), exist_ok=True)
for i, img in enumerate(images):
    save_path = os.path.join("..", "data", "pubchemfp_pattern_images", f"{i}.png")
    img.save(save_path)
    image_paths.append(save_path)<jupyter_output><empty_output><jupyter_text>Display images by converting the dataframe to HTML with paths to the images<jupyter_code>def path_to_image_html(path):
    return f'<img src="{path}" width="120" >'
df["images"] = image_paths
HTML(df.to_html(escape=False ,formatters=dict(images=path_to_image_html)))<jupyter_output><empty_output><jupyter_text>Save the dataframe to data/ directory<jupyter_code>df.to_csv(os.path.join("..", "data", "pubchemFPKeys_to_SMARTSpattern.csv"))<jupyter_output><empty_output><jupyter_text>The class to create fragment labels for each atom of a molecule<jupyter_code>class MolFragmentsLabel:
    """ Label atoms in a molecule with the fragments they belong to. The fragment library is
    built from PubChem fingerprint section 3 to section 7. The labels are fingerprint like
    vectors for each atom of the molecule.
    
    Args:
        ref_file (str): path to the reference file (csv format) that contains the SMARTS strings
            to match molecular fragments.
    """
    
    ref_smarts = None
    
    def __init__(self, ref_file=None):
        if ref_file is None:
            self.ref_file = os.path.join("data", "pubchemFPKeys_to_SMARTSpattern.csv")
        else:
            self.ref_file = ref_file
        if MolFragmentsLabel.ref_smarts is None:
            self._build_ref(self.ref_file)
    
    @classmethod
    def _build_ref(cls, ref_file):
        df = pd.read_csv(ref_file)
        cls.ref_smarts = [Chem.MolFromSmarts(smarts) for smarts in df["SMARTS"]]
        
    def create_labels_for(self, mol, sparse=True):
        """ Create fragment labels for a molecule:
        
        Args:
            mol (SMILES str or RDKit Mol object): the molecule to create labels for.
            sparse (bool): return the matrix in sparse format. Default: True.
        """
        if isinstance(mol, str):
            mol = Chem.MolFromSmiles(mol)
            if mol is None:
                raise ValueError(f"{mol} is not a valid SMILES string.")
        # add hydrogens to the molecule
        mol = Chem.AddHs(mol)
        # initiate the vectors
        labels = np.zeros((len(self.ref_smarts), mol.GetNumAtoms()), dtype=np.int)
        # search for the fragments in the molecule
        for i, pattern in enumerate(self.ref_smarts):
            mat_substructs = mol.GetSubstructMatches(pattern)
            # convert tuple of tuples to a set
            mat_atoms = set()
            for atoms in mat_substructs:
                mat_atoms = mat_atoms.union(set(atoms))
            mat_atoms = list(mat_atoms)
            labels[i, mat_atoms] = 1
        if sparse:
            labels = scipy.sparse.coo_matrix(labels)
        return labels<jupyter_output><empty_output><jupyter_text>Test the class<jupyter_code>mfl = MolFragmentsLabel(ref_file=os.path.join("..", "data", "pubchemFPKeys_to_SMARTSpattern.csv"))
%time
labels = mfl.create_labels_for("Cl-c1cccc(C)c1", sparse=True)
print(labels.todense()[[20, 21, 31, 69, 70, 79, 81, 92, 93]])
for i in range(labels.shape[0]):
    if labels.getrow(i).sum() > 0:
        print(i, end=", ")<jupyter_output>20, 21, 31, 69, 70, 79, 81, 92, 93, 99, 107, 108, 111, 121, 153, 167, 171, 178, 183, 207, 227, 238, 240, 253, 257, 261, 287, 289, 293, 301, 307, 315, 319, 321, 328, 332, 335, 336, 340, 345, 355, 371, 377, 397, 401, 405, 414, 415, 416, 425, 445, 446, 447, 475, 538,<jupyter_text>The real PubChem fingerprint<jupyter_code>pubchemfp = All_Fingerprint.cdk_fingerprint("Cl-c1cccc(C)c1", fp_type="pubchem")
reduced_fp = [d - 263 for d in pubchemfp]
print(reduced_fp[6:])
print("="*40)
print("The fingerprint matches to the labels I generated very well.")<jupyter_output>[20, 21, 31, 69, 70, 79, 81, 92, 93, 99, 107, 108, 111, 121, 153, 167, 171, 178, 183, 207, 227, 238, 240, 253, 257, 261, 287, 289, 293, 301, 307, 315, 319, 321, 328, 332, 335, 336, 340, 345, 355, 371, 377, 397, 401, 405, 414, 415, 416, 425, 445, 446, 447, 475, 538]
========================================
The fingerprint matches to the labels I generated very well.<jupyter_text>Another test<jupyter_code>labels = mfl.create_labels_for("CC(=O)OC1=CC=CC=C1C(=O)O", sparse=True)
print(labels.todense()[[20, 21, 23, 45, 69, 70, 78, 81, 89, 92, 93]])
for i in range(labels.shape[0]):
    if labels.getrow(i).sum() > 0:
        print(i, end=", ")
pubchemfp = All_Fingerprint.cdk_fingerprint("CC(=O)OC1=CC=CC=C1C(=O)O", fp_type="pubchem")
reduced_fp = [d - 263 for d in pubchemfp]
print(reduced_fp[9:])
print("="*40)
print("The fingerprint matches to the labels I generated very well again.")<jupyter_output>[20, 21, 23, 45, 69, 70, 78, 81, 89, 92, 93, 107, 108, 111, 117, 118, 119, 121, 142, 143, 153, 157, 167, 171, 177, 178, 180, 183, 189, 207, 213, 227, 230, 235, 253, 257, 261, 272, 278, 279, 285, 289, 290, 293, 301, 302, 307, 310, 311, 312, 315, 316, 318, 319, 321, 326, 331, 332, 334, 336, 340, 341, 343, 345, 351, 354, 355, 356, 357, 359, 360, 362, 363, 369, 371, 374, 376, 377, 378, 379, 388, 392, 397, 401, 403, 404, 405, 408, 409, 414, 415, 416, 417, 421, 425, 426, 429, 435, 441, 445, 446, 447, 493, 556]
========================================
The fingerprint matches to the labels I generated very well again.
StarcoderdataJupyterStructuredCleanDedup