We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[DOC] Clarify original-dataframe mutation behavior in pyjanitor function docstrings
# Brief Description of Fix
Currently, some pyjanitor functions mutate the original dataframe and others return a copy. Solutions are heavily discussed in #79 but no conclusion has been reached. At the moment, it is unclear, without experimentation from the user, which behavior applies in each function. 

In the interim, I propose to explicitly clarify this behavior in each function's docstring so the user has a clear idea regarding the function's mutating behavior. Below is a sample of what this could look like for `.clean_names()`:

"""
Clean column names.
    Takes all column names, converts them to lowercase, then replaces all
    spaces with underscores. <b>Does not mutate original dataframe.</b>
"""

Happy to add this line somewhere else in the docstring if inappropriate here. 

- [Link to documentation page](https://pyjanitor.readthedocs.io/reference/index.html)
- [Link to exact file to be edited](https://github.com/ericmjl/pyjanitor/blob/dev/janitor/functions.py)


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `janitor/chemistry.py`
Content:
```
1 """
2 Chemistry and cheminformatics-oriented data cleaning functions.
3 """
4 
5 from typing import Union
6 
7 import numpy as np
8 import pandas as pd
9 import pandas_flavor as pf
10 
11 from .utils import deprecated_alias, import_message
12 
13 try:
14     from rdkit import Chem, DataStructs
15     from rdkit.Chem.rdMolDescriptors import (
16         GetHashedMorganFingerprint,
17         GetMorganFingerprintAsBitVect,
18         CalcChi0n,
19         CalcChi0v,
20         CalcChi1n,
21         CalcChi1v,
22         CalcChi2n,
23         CalcChi2v,
24         CalcChi3n,
25         CalcChi3v,
26         CalcChi4n,
27         CalcChi4v,
28         CalcExactMolWt,
29         CalcFractionCSP3,
30         CalcHallKierAlpha,
31         CalcKappa1,
32         CalcKappa2,
33         CalcKappa3,
34         CalcLabuteASA,
35         CalcNumAliphaticCarbocycles,
36         CalcNumAliphaticHeterocycles,
37         CalcNumAliphaticRings,
38         CalcNumAmideBonds,
39         CalcNumAromaticCarbocycles,
40         CalcNumAromaticHeterocycles,
41         CalcNumAromaticRings,
42         CalcNumAtomStereoCenters,
43         CalcNumBridgeheadAtoms,
44         CalcNumHBA,
45         CalcNumHBD,
46         CalcNumHeteroatoms,
47         CalcNumHeterocycles,
48         CalcNumLipinskiHBA,
49         CalcNumLipinskiHBD,
50         CalcNumRings,
51         CalcNumSaturatedCarbocycles,
52         CalcNumSaturatedHeterocycles,
53         CalcNumSaturatedRings,
54         CalcNumSpiroAtoms,
55         CalcNumUnspecifiedAtomStereoCenters,
56         CalcTPSA,
57         GetMACCSKeysFingerprint,
58     )
59 except ImportError:
60     import_message("chemistry", "rdkit", "conda install -c rdkit rdkit")
61 
62 try:
63     from tqdm import tqdm
64     from tqdm import tqdm_notebook as tqdmn
65 except ImportError:
66     import_message("chemistry", "tqdm", "conda install -c conda-forge tqdm")
67 
68 
69 @pf.register_dataframe_method
70 @deprecated_alias(smiles_col="smiles_column_name", mols_col="mols_column_name")
71 def smiles2mol(
72     df: pd.DataFrame,
73     smiles_column_name,
74     mols_column_name,
75     drop_nulls: bool = True,
76     progressbar: Union[None, str] = None,
77 ) -> pd.DataFrame:
78     """
79     Convert a column of SMILES strings into RDKit Mol objects.
80 
81     Automatically drops invalid SMILES, as determined by RDKIT.
82 
83     Method chaining usage:
84 
85     .. code-block:: python
86 
87         df = (
88             pd.DataFrame(...)
89             .smiles2mol(smiles_column_name='smiles', mols_column_name='mols')
90         )
91 
92     A progressbar can be optionally used.
93 
94     - Pass in "notebook" to show a tqdm notebook progressbar. (ipywidgets must
95       be enabled with your Jupyter installation.)
96     - Pass in "terminal" to show a tqdm progressbar. Better suited for use
97       with scripts.
98     - "none" is the default value - progress bar will be not be shown.
99 
100     :param df: pandas DataFrame.
101     :param smiles_column_name: Name of column that holds the SMILES strings.
102     :param mols_column_name: Name to be given to the new mols column.
103     :param drop_nulls: Whether to drop rows whose mols failed to be
104         constructed.
105     :param progressbar: Whether to show a progressbar or not.
106     """
107     valid_progress = ["notebook", "terminal", None]
108     if progressbar not in valid_progress:
109         raise ValueError(f"progressbar kwarg must be one of {valid_progress}")
110 
111     if progressbar is None:
112         df[mols_column_name] = df[smiles_column_name].apply(
113             lambda x: Chem.MolFromSmiles(x)
114         )
115     else:
116         if progressbar == "notebook":
117             tqdmn().pandas(desc="mols")
118         elif progressbar == "terminal":
119             tqdm.pandas(desc="mols")
120         df[mols_column_name] = df[smiles_column_name].progress_apply(
121             lambda x: Chem.MolFromSmiles(x)
122         )
123 
124     if drop_nulls:
125         df.dropna(subset=[mols_column_name], inplace=True)
126     df.reset_index(inplace=True, drop=True)
127     return df
128 
129 
130 @pf.register_dataframe_method
131 @deprecated_alias(mols_col="mols_column_name")
132 def morgan_fingerprint(
133     df: pd.DataFrame,
134     mols_column_name,
135     radius: int = 3,
136     nbits: int = 2048,
137     kind: str = "counts",
138 ) -> pd.DataFrame:
139     """
140     Convert a column of RDKIT Mol objects into Morgan Fingerprints.
141 
142     Returns a new dataframe without any of the original data. This is
143     intentional, as Morgan fingerprints are usually high-dimensional
144     features.
145 
146     Method chaining usage:
147 
148     .. code-block:: python
149 
150         df = pd.DataFrame(...)
151         morgans = df.morgan_fingerprint(mols_column_name='mols', radius=3,
152                                         nbits=2048)
153 
154     If you wish to join the Morgans back into the original dataframe, this
155     can be accomplished by doing a `join`, becuase the indices are
156     preserved:
157 
158     .. code-block:: python
159 
160         joined = df.join(morgans)
161 
162     :param df: A pandas DataFrame.
163     :param mols_column_name: The name of the column that has the RDKIT
164         mol objects
165     :param radius: Radius of Morgan fingerprints. Defaults to 3.
166     :param nbits: The length of the fingerprints. Defaults to 2048.
167     :param kind: Whether to return counts or bits. Defaults to counts.
168     :returns: A pandas DataFrame
169     """
170     acceptable_kinds = ["counts", "bits"]
171     if kind not in acceptable_kinds:
172         raise ValueError(f"`kind` must be one of {acceptable_kinds}")
173 
174     if kind == "bits":
175         fps = [
176             GetMorganFingerprintAsBitVect(m, radius, nbits)
177             for m in df[mols_column_name]
178         ]
179     elif kind == "counts":
180         fps = [
181             GetHashedMorganFingerprint(m, radius, nbits)
182             for m in df[mols_column_name]
183         ]
184 
185     np_fps = []
186     for fp in fps:
187         arr = np.zeros((1,))
188         DataStructs.ConvertToNumpyArray(fp, arr)
189         np_fps.append(arr)
190     np_fps = np.vstack(np_fps)
191     fpdf = pd.DataFrame(np_fps)
192     fpdf.index = df.index
193     return fpdf
194 
195 
196 @pf.register_dataframe_method
197 @deprecated_alias(mols_col="mols_column_name")
198 def molecular_descriptors(df: pd.DataFrame, mols_column_name) -> pd.DataFrame:
199     """
200     Convert a column of RDKIT mol objects into a Pandas DataFrame
201     of molecular descriptors.
202 
203     Returns a new dataframe without any of the original data. This is
204     intentional to leave the user only with the data requested.
205 
206     The molecular descriptors are from the rdkit.Chem.rdMolDescriptors:
207 
208         Chi0n, Chi0v, Chi1n, Chi1v, Chi2n, Chi2v, Chi3n, Chi3v,
209         Chi4n, Chi4v, ExactMolWt, FractionCSP3, HallKierAlpha, Kappa1,
210         Kappa2, Kappa3, LabuteASA, NumAliphaticCarbocycles,
211         NumAliphaticHeterocycles, NumAliphaticRings, NumAmideBonds,
212         NumAromaticCarbocycles, NumAromaticHeterocycles, NumAromaticRings,
213         NumAtomStereoCenters, NumBridgeheadAtoms, NumHBA, NumHBD,
214         NumHeteroatoms, NumHeterocycles, NumLipinskiHBA, NumLipinskiHBD,
215         NumRings, NumSaturatedCarbocycles, NumSaturatedHeterocycles,
216         NumSaturatedRings, NumSpiroAtoms, NumUnspecifiedAtomStereoCenters,
217         TPSA.
218 
219      Method chaining usage:
220 
221     .. code-block:: python
222 
223         df = pd.DataFrame(...)
224         mol_desc = df.molecular_descriptors(mols_column_name='mols')
225 
226     If you wish to join the molecular descriptors back into the original
227     dataframe, this can be accomplished by doing a `join`,
228     because the indices are preserved:
229 
230     .. code-block:: python
231 
232         joined = df.join(mol_desc)
233 
234     :param df: A pandas DataFrame.
235     :param mols_column_name: The name of the column that has the RDKIT mol
236         objects.
237     :returns: A pandas DataFrame
238     """
239     descriptors = [
240         CalcChi0n,
241         CalcChi0v,
242         CalcChi1n,
243         CalcChi1v,
244         CalcChi2n,
245         CalcChi2v,
246         CalcChi3n,
247         CalcChi3v,
248         CalcChi4n,
249         CalcChi4v,
250         CalcExactMolWt,
251         CalcFractionCSP3,
252         CalcHallKierAlpha,
253         CalcKappa1,
254         CalcKappa2,
255         CalcKappa3,
256         CalcLabuteASA,
257         CalcNumAliphaticCarbocycles,
258         CalcNumAliphaticHeterocycles,
259         CalcNumAliphaticRings,
260         CalcNumAmideBonds,
261         CalcNumAromaticCarbocycles,
262         CalcNumAromaticHeterocycles,
263         CalcNumAromaticRings,
264         CalcNumAtomStereoCenters,
265         CalcNumBridgeheadAtoms,
266         CalcNumHBA,
267         CalcNumHBD,
268         CalcNumHeteroatoms,
269         CalcNumHeterocycles,
270         CalcNumLipinskiHBA,
271         CalcNumLipinskiHBD,
272         CalcNumRings,
273         CalcNumSaturatedCarbocycles,
274         CalcNumSaturatedHeterocycles,
275         CalcNumSaturatedRings,
276         CalcNumSpiroAtoms,
277         CalcNumUnspecifiedAtomStereoCenters,
278         CalcTPSA,
279     ]
280     descriptors_mapping = {f.__name__.strip("Calc"): f for f in descriptors}
281 
282     feats = dict()
283     for name, func in descriptors_mapping.items():
284         feats[name] = [func(m) for m in df[mols_column_name]]
285     return pd.DataFrame(feats)
286 
287 
288 @pf.register_dataframe_method
289 @deprecated_alias(mols_col="mols_column_name")
290 def maccs_keys_fingerprint(df: pd.DataFrame, mols_column_name) -> pd.DataFrame:
291     """
292     Convert a column of RDKIT mol objects into MACCS Keys Fingeprints.
293 
294     Returns a new dataframe without any of the original data.
295     This is intentional to leave the user with the data requested.
296 
297     Method chaining usage:
298 
299     .. code-block:: python
300 
301         df = pd.DataFrame(...)
302         maccs = df.maccs_keys_fingerprint(mols_column_name='mols')
303 
304     If you wish to join the molecular descriptors back into the
305     original dataframe, this can be accomplished by doing a `join`,
306     because the indices are preserved:
307 
308     .. code-block:: python
309 
310         joined = df.join(maccs_keys_fingerprint)
311 
312 
313     :param df: A pandas DataFrame.
314     :param mols_column_name: The name of the column that has the RDKIT mol
315         objects.
316     :returns: A pandas DataFrame
317     """
318 
319     maccs = [GetMACCSKeysFingerprint(m) for m in df[mols_column_name]]
320 
321     np_maccs = []
322 
323     for macc in maccs:
324         arr = np.zeros((1,))
325         DataStructs.ConvertToNumpyArray(macc, arr)
326         np_maccs.append(arr)
327     np_maccs = np.vstack(np_maccs)
328     fmaccs = pd.DataFrame(np_maccs)
329     fmaccs.index = df.index
330     return fmaccs
331
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/janitor/chemistry.py b/janitor/chemistry.py
--- a/janitor/chemistry.py
+++ b/janitor/chemistry.py
@@ -80,6 +80,8 @@
 
     Automatically drops invalid SMILES, as determined by RDKIT.
 
+    This method mutates the original DataFrame.
+
     Method chaining usage:
 
     .. code-block:: python
@@ -143,6 +145,8 @@
     intentional, as Morgan fingerprints are usually high-dimensional
     features.
 
+    This method does not mutate the original DataFrame.
+
     Method chaining usage:
 
     .. code-block:: python
@@ -203,6 +207,8 @@
     Returns a new dataframe without any of the original data. This is
     intentional to leave the user only with the data requested.
 
+    This method does not mutate the original DataFrame.
+
     The molecular descriptors are from the rdkit.Chem.rdMolDescriptors:
 
         Chi0n, Chi0v, Chi1n, Chi1v, Chi2n, Chi2v, Chi3n, Chi3v,
@@ -294,6 +300,8 @@
     Returns a new dataframe without any of the original data.
     This is intentional to leave the user with the data requested.
 
+    This method does not mutate the original DataFrame.
+
     Method chaining usage:
 
     .. code-block:: python

{"golden_diff": "diff --git a/janitor/chemistry.py b/janitor/chemistry.py\n--- a/janitor/chemistry.py\n+++ b/janitor/chemistry.py\n@@ -80,6 +80,8 @@\n \n     Automatically drops invalid SMILES, as determined by RDKIT.\n \n+    This method mutates the original DataFrame.\n+\n     Method chaining usage:\n \n     .. code-block:: python\n@@ -143,6 +145,8 @@\n     intentional, as Morgan fingerprints are usually high-dimensional\n     features.\n \n+    This method does not mutate the original DataFrame.\n+\n     Method chaining usage:\n \n     .. code-block:: python\n@@ -203,6 +207,8 @@\n     Returns a new dataframe without any of the original data. This is\n     intentional to leave the user only with the data requested.\n \n+    This method does not mutate the original DataFrame.\n+\n     The molecular descriptors are from the rdkit.Chem.rdMolDescriptors:\n \n         Chi0n, Chi0v, Chi1n, Chi1v, Chi2n, Chi2v, Chi3n, Chi3v,\n@@ -294,6 +300,8 @@\n     Returns a new dataframe without any of the original data.\n     This is intentional to leave the user with the data requested.\n \n+    This method does not mutate the original DataFrame.\n+\n     Method chaining usage:\n \n     .. code-block:: python\n", "issue": "[DOC] Clarify original-dataframe mutation behavior in pyjanitor function docstrings\n# Brief Description of Fix\r\nCurrently, some pyjanitor functions mutate the original dataframe and others return a copy. Solutions are heavily discussed in #79 but no conclusion has been reached. At the moment, it is unclear, without experimentation from the user, which behavior applies in each function. \r\n\r\nIn the interim, I propose to explicitly clarify this behavior in each function's docstring so the user has a clear idea regarding the function's mutating behavior. Below is a sample of what this could look like for `.clean_names()`:\r\n\r\n\"\"\"\r\nClean column names.\r\n    Takes all column names, converts them to lowercase, then replaces all\r\n    spaces with underscores. <b>Does not mutate original dataframe.</b>\r\n\"\"\"\r\n\r\nHappy to add this line somewhere else in the docstring if inappropriate here. \r\n\r\n- [Link to documentation page](https://pyjanitor.readthedocs.io/reference/index.html)\r\n- [Link to exact file to be edited](https://github.com/ericmjl/pyjanitor/blob/dev/janitor/functions.py)\r\n\n", "before_files": [{"content": "\"\"\"\nChemistry and cheminformatics-oriented data cleaning functions.\n\"\"\"\n\nfrom typing import Union\n\nimport numpy as np\nimport pandas as pd\nimport pandas_flavor as pf\n\nfrom .utils import deprecated_alias, import_message\n\ntry:\n    from rdkit import Chem, DataStructs\n    from rdkit.Chem.rdMolDescriptors import (\n        GetHashedMorganFingerprint,\n        GetMorganFingerprintAsBitVect,\n        CalcChi0n,\n        CalcChi0v,\n        CalcChi1n,\n        CalcChi1v,\n        CalcChi2n,\n        CalcChi2v,\n        CalcChi3n,\n        CalcChi3v,\n        CalcChi4n,\n        CalcChi4v,\n        CalcExactMolWt,\n        CalcFractionCSP3,\n        CalcHallKierAlpha,\n        CalcKappa1,\n        CalcKappa2,\n        CalcKappa3,\n        CalcLabuteASA,\n        CalcNumAliphaticCarbocycles,\n        CalcNumAliphaticHeterocycles,\n        CalcNumAliphaticRings,\n        CalcNumAmideBonds,\n        CalcNumAromaticCarbocycles,\n        CalcNumAromaticHeterocycles,\n        CalcNumAromaticRings,\n        CalcNumAtomStereoCenters,\n        CalcNumBridgeheadAtoms,\n        CalcNumHBA,\n        CalcNumHBD,\n        CalcNumHeteroatoms,\n        CalcNumHeterocycles,\n        CalcNumLipinskiHBA,\n        CalcNumLipinskiHBD,\n        CalcNumRings,\n        CalcNumSaturatedCarbocycles,\n        CalcNumSaturatedHeterocycles,\n        CalcNumSaturatedRings,\n        CalcNumSpiroAtoms,\n        CalcNumUnspecifiedAtomStereoCenters,\n        CalcTPSA,\n        GetMACCSKeysFingerprint,\n    )\nexcept ImportError:\n    import_message(\"chemistry\", \"rdkit\", \"conda install -c rdkit rdkit\")\n\ntry:\n    from tqdm import tqdm\n    from tqdm import tqdm_notebook as tqdmn\nexcept ImportError:\n    import_message(\"chemistry\", \"tqdm\", \"conda install -c conda-forge tqdm\")\n\n\n@pf.register_dataframe_method\n@deprecated_alias(smiles_col=\"smiles_column_name\", mols_col=\"mols_column_name\")\ndef smiles2mol(\n    df: pd.DataFrame,\n    smiles_column_name,\n    mols_column_name,\n    drop_nulls: bool = True,\n    progressbar: Union[None, str] = None,\n) -> pd.DataFrame:\n    \"\"\"\n    Convert a column of SMILES strings into RDKit Mol objects.\n\n    Automatically drops invalid SMILES, as determined by RDKIT.\n\n    Method chaining usage:\n\n    .. code-block:: python\n\n        df = (\n            pd.DataFrame(...)\n            .smiles2mol(smiles_column_name='smiles', mols_column_name='mols')\n        )\n\n    A progressbar can be optionally used.\n\n    - Pass in \"notebook\" to show a tqdm notebook progressbar. (ipywidgets must\n      be enabled with your Jupyter installation.)\n    - Pass in \"terminal\" to show a tqdm progressbar. Better suited for use\n      with scripts.\n    - \"none\" is the default value - progress bar will be not be shown.\n\n    :param df: pandas DataFrame.\n    :param smiles_column_name: Name of column that holds the SMILES strings.\n    :param mols_column_name: Name to be given to the new mols column.\n    :param drop_nulls: Whether to drop rows whose mols failed to be\n        constructed.\n    :param progressbar: Whether to show a progressbar or not.\n    \"\"\"\n    valid_progress = [\"notebook\", \"terminal\", None]\n    if progressbar not in valid_progress:\n        raise ValueError(f\"progressbar kwarg must be one of {valid_progress}\")\n\n    if progressbar is None:\n        df[mols_column_name] = df[smiles_column_name].apply(\n            lambda x: Chem.MolFromSmiles(x)\n        )\n    else:\n        if progressbar == \"notebook\":\n            tqdmn().pandas(desc=\"mols\")\n        elif progressbar == \"terminal\":\n            tqdm.pandas(desc=\"mols\")\n        df[mols_column_name] = df[smiles_column_name].progress_apply(\n            lambda x: Chem.MolFromSmiles(x)\n        )\n\n    if drop_nulls:\n        df.dropna(subset=[mols_column_name], inplace=True)\n    df.reset_index(inplace=True, drop=True)\n    return df\n\n\n@pf.register_dataframe_method\n@deprecated_alias(mols_col=\"mols_column_name\")\ndef morgan_fingerprint(\n    df: pd.DataFrame,\n    mols_column_name,\n    radius: int = 3,\n    nbits: int = 2048,\n    kind: str = \"counts\",\n) -> pd.DataFrame:\n    \"\"\"\n    Convert a column of RDKIT Mol objects into Morgan Fingerprints.\n\n    Returns a new dataframe without any of the original data. This is\n    intentional, as Morgan fingerprints are usually high-dimensional\n    features.\n\n    Method chaining usage:\n\n    .. code-block:: python\n\n        df = pd.DataFrame(...)\n        morgans = df.morgan_fingerprint(mols_column_name='mols', radius=3,\n                                        nbits=2048)\n\n    If you wish to join the Morgans back into the original dataframe, this\n    can be accomplished by doing a `join`, becuase the indices are\n    preserved:\n\n    .. code-block:: python\n\n        joined = df.join(morgans)\n\n    :param df: A pandas DataFrame.\n    :param mols_column_name: The name of the column that has the RDKIT\n        mol objects\n    :param radius: Radius of Morgan fingerprints. Defaults to 3.\n    :param nbits: The length of the fingerprints. Defaults to 2048.\n    :param kind: Whether to return counts or bits. Defaults to counts.\n    :returns: A pandas DataFrame\n    \"\"\"\n    acceptable_kinds = [\"counts\", \"bits\"]\n    if kind not in acceptable_kinds:\n        raise ValueError(f\"`kind` must be one of {acceptable_kinds}\")\n\n    if kind == \"bits\":\n        fps = [\n            GetMorganFingerprintAsBitVect(m, radius, nbits)\n            for m in df[mols_column_name]\n        ]\n    elif kind == \"counts\":\n        fps = [\n            GetHashedMorganFingerprint(m, radius, nbits)\n            for m in df[mols_column_name]\n        ]\n\n    np_fps = []\n    for fp in fps:\n        arr = np.zeros((1,))\n        DataStructs.ConvertToNumpyArray(fp, arr)\n        np_fps.append(arr)\n    np_fps = np.vstack(np_fps)\n    fpdf = pd.DataFrame(np_fps)\n    fpdf.index = df.index\n    return fpdf\n\n\n@pf.register_dataframe_method\n@deprecated_alias(mols_col=\"mols_column_name\")\ndef molecular_descriptors(df: pd.DataFrame, mols_column_name) -> pd.DataFrame:\n    \"\"\"\n    Convert a column of RDKIT mol objects into a Pandas DataFrame\n    of molecular descriptors.\n\n    Returns a new dataframe without any of the original data. This is\n    intentional to leave the user only with the data requested.\n\n    The molecular descriptors are from the rdkit.Chem.rdMolDescriptors:\n\n        Chi0n, Chi0v, Chi1n, Chi1v, Chi2n, Chi2v, Chi3n, Chi3v,\n        Chi4n, Chi4v, ExactMolWt, FractionCSP3, HallKierAlpha, Kappa1,\n        Kappa2, Kappa3, LabuteASA, NumAliphaticCarbocycles,\n        NumAliphaticHeterocycles, NumAliphaticRings, NumAmideBonds,\n        NumAromaticCarbocycles, NumAromaticHeterocycles, NumAromaticRings,\n        NumAtomStereoCenters, NumBridgeheadAtoms, NumHBA, NumHBD,\n        NumHeteroatoms, NumHeterocycles, NumLipinskiHBA, NumLipinskiHBD,\n        NumRings, NumSaturatedCarbocycles, NumSaturatedHeterocycles,\n        NumSaturatedRings, NumSpiroAtoms, NumUnspecifiedAtomStereoCenters,\n        TPSA.\n\n     Method chaining usage:\n\n    .. code-block:: python\n\n        df = pd.DataFrame(...)\n        mol_desc = df.molecular_descriptors(mols_column_name='mols')\n\n    If you wish to join the molecular descriptors back into the original\n    dataframe, this can be accomplished by doing a `join`,\n    because the indices are preserved:\n\n    .. code-block:: python\n\n        joined = df.join(mol_desc)\n\n    :param df: A pandas DataFrame.\n    :param mols_column_name: The name of the column that has the RDKIT mol\n        objects.\n    :returns: A pandas DataFrame\n    \"\"\"\n    descriptors = [\n        CalcChi0n,\n        CalcChi0v,\n        CalcChi1n,\n        CalcChi1v,\n        CalcChi2n,\n        CalcChi2v,\n        CalcChi3n,\n        CalcChi3v,\n        CalcChi4n,\n        CalcChi4v,\n        CalcExactMolWt,\n        CalcFractionCSP3,\n        CalcHallKierAlpha,\n        CalcKappa1,\n        CalcKappa2,\n        CalcKappa3,\n        CalcLabuteASA,\n        CalcNumAliphaticCarbocycles,\n        CalcNumAliphaticHeterocycles,\n        CalcNumAliphaticRings,\n        CalcNumAmideBonds,\n        CalcNumAromaticCarbocycles,\n        CalcNumAromaticHeterocycles,\n        CalcNumAromaticRings,\n        CalcNumAtomStereoCenters,\n        CalcNumBridgeheadAtoms,\n        CalcNumHBA,\n        CalcNumHBD,\n        CalcNumHeteroatoms,\n        CalcNumHeterocycles,\n        CalcNumLipinskiHBA,\n        CalcNumLipinskiHBD,\n        CalcNumRings,\n        CalcNumSaturatedCarbocycles,\n        CalcNumSaturatedHeterocycles,\n        CalcNumSaturatedRings,\n        CalcNumSpiroAtoms,\n        CalcNumUnspecifiedAtomStereoCenters,\n        CalcTPSA,\n    ]\n    descriptors_mapping = {f.__name__.strip(\"Calc\"): f for f in descriptors}\n\n    feats = dict()\n    for name, func in descriptors_mapping.items():\n        feats[name] = [func(m) for m in df[mols_column_name]]\n    return pd.DataFrame(feats)\n\n\n@pf.register_dataframe_method\n@deprecated_alias(mols_col=\"mols_column_name\")\ndef maccs_keys_fingerprint(df: pd.DataFrame, mols_column_name) -> pd.DataFrame:\n    \"\"\"\n    Convert a column of RDKIT mol objects into MACCS Keys Fingeprints.\n\n    Returns a new dataframe without any of the original data.\n    This is intentional to leave the user with the data requested.\n\n    Method chaining usage:\n\n    .. code-block:: python\n\n        df = pd.DataFrame(...)\n        maccs = df.maccs_keys_fingerprint(mols_column_name='mols')\n\n    If you wish to join the molecular descriptors back into the\n    original dataframe, this can be accomplished by doing a `join`,\n    because the indices are preserved:\n\n    .. code-block:: python\n\n        joined = df.join(maccs_keys_fingerprint)\n\n\n    :param df: A pandas DataFrame.\n    :param mols_column_name: The name of the column that has the RDKIT mol\n        objects.\n    :returns: A pandas DataFrame\n    \"\"\"\n\n    maccs = [GetMACCSKeysFingerprint(m) for m in df[mols_column_name]]\n\n    np_maccs = []\n\n    for macc in maccs:\n        arr = np.zeros((1,))\n        DataStructs.ConvertToNumpyArray(macc, arr)\n        np_maccs.append(arr)\n    np_maccs = np.vstack(np_maccs)\n    fmaccs = pd.DataFrame(np_maccs)\n    fmaccs.index = df.index\n    return fmaccs\n", "path": "janitor/chemistry.py"}], "after_files": [{"content": "\"\"\"\nChemistry and cheminformatics-oriented data cleaning functions.\n\"\"\"\n\nfrom typing import Union\n\nimport numpy as np\nimport pandas as pd\nimport pandas_flavor as pf\n\nfrom .utils import deprecated_alias, import_message\n\ntry:\n    from rdkit import Chem, DataStructs\n    from rdkit.Chem.rdMolDescriptors import (\n        GetHashedMorganFingerprint,\n        GetMorganFingerprintAsBitVect,\n        CalcChi0n,\n        CalcChi0v,\n        CalcChi1n,\n        CalcChi1v,\n        CalcChi2n,\n        CalcChi2v,\n        CalcChi3n,\n        CalcChi3v,\n        CalcChi4n,\n        CalcChi4v,\n        CalcExactMolWt,\n        CalcFractionCSP3,\n        CalcHallKierAlpha,\n        CalcKappa1,\n        CalcKappa2,\n        CalcKappa3,\n        CalcLabuteASA,\n        CalcNumAliphaticCarbocycles,\n        CalcNumAliphaticHeterocycles,\n        CalcNumAliphaticRings,\n        CalcNumAmideBonds,\n        CalcNumAromaticCarbocycles,\n        CalcNumAromaticHeterocycles,\n        CalcNumAromaticRings,\n        CalcNumAtomStereoCenters,\n        CalcNumBridgeheadAtoms,\n        CalcNumHBA,\n        CalcNumHBD,\n        CalcNumHeteroatoms,\n        CalcNumHeterocycles,\n        CalcNumLipinskiHBA,\n        CalcNumLipinskiHBD,\n        CalcNumRings,\n        CalcNumSaturatedCarbocycles,\n        CalcNumSaturatedHeterocycles,\n        CalcNumSaturatedRings,\n        CalcNumSpiroAtoms,\n        CalcNumUnspecifiedAtomStereoCenters,\n        CalcTPSA,\n        GetMACCSKeysFingerprint,\n    )\nexcept ImportError:\n    import_message(\"chemistry\", \"rdkit\", \"conda install -c rdkit rdkit\")\n\ntry:\n    from tqdm import tqdm\n    from tqdm import tqdm_notebook as tqdmn\nexcept ImportError:\n    import_message(\"chemistry\", \"tqdm\", \"conda install -c conda-forge tqdm\")\n\n\n@pf.register_dataframe_method\n@deprecated_alias(smiles_col=\"smiles_column_name\", mols_col=\"mols_column_name\")\ndef smiles2mol(\n    df: pd.DataFrame,\n    smiles_column_name,\n    mols_column_name,\n    drop_nulls: bool = True,\n    progressbar: Union[None, str] = None,\n) -> pd.DataFrame:\n    \"\"\"\n    Convert a column of SMILES strings into RDKit Mol objects.\n\n    Automatically drops invalid SMILES, as determined by RDKIT.\n\n    This method mutates the original DataFrame.\n\n    Method chaining usage:\n\n    .. code-block:: python\n\n        df = (\n            pd.DataFrame(...)\n            .smiles2mol(smiles_column_name='smiles', mols_column_name='mols')\n        )\n\n    A progressbar can be optionally used.\n\n    - Pass in \"notebook\" to show a tqdm notebook progressbar. (ipywidgets must\n      be enabled with your Jupyter installation.)\n    - Pass in \"terminal\" to show a tqdm progressbar. Better suited for use\n      with scripts.\n    - \"none\" is the default value - progress bar will be not be shown.\n\n    :param df: pandas DataFrame.\n    :param smiles_column_name: Name of column that holds the SMILES strings.\n    :param mols_column_name: Name to be given to the new mols column.\n    :param drop_nulls: Whether to drop rows whose mols failed to be\n        constructed.\n    :param progressbar: Whether to show a progressbar or not.\n    \"\"\"\n    valid_progress = [\"notebook\", \"terminal\", None]\n    if progressbar not in valid_progress:\n        raise ValueError(f\"progressbar kwarg must be one of {valid_progress}\")\n\n    if progressbar is None:\n        df[mols_column_name] = df[smiles_column_name].apply(\n            lambda x: Chem.MolFromSmiles(x)\n        )\n    else:\n        if progressbar == \"notebook\":\n            tqdmn().pandas(desc=\"mols\")\n        elif progressbar == \"terminal\":\n            tqdm.pandas(desc=\"mols\")\n        df[mols_column_name] = df[smiles_column_name].progress_apply(\n            lambda x: Chem.MolFromSmiles(x)\n        )\n\n    if drop_nulls:\n        df.dropna(subset=[mols_column_name], inplace=True)\n    df.reset_index(inplace=True, drop=True)\n    return df\n\n\n@pf.register_dataframe_method\n@deprecated_alias(mols_col=\"mols_column_name\")\ndef morgan_fingerprint(\n    df: pd.DataFrame,\n    mols_column_name,\n    radius: int = 3,\n    nbits: int = 2048,\n    kind: str = \"counts\",\n) -> pd.DataFrame:\n    \"\"\"\n    Convert a column of RDKIT Mol objects into Morgan Fingerprints.\n\n    Returns a new dataframe without any of the original data. This is\n    intentional, as Morgan fingerprints are usually high-dimensional\n    features.\n\n    This method does not mutate the original DataFrame.\n\n    Method chaining usage:\n\n    .. code-block:: python\n\n        df = pd.DataFrame(...)\n        morgans = df.morgan_fingerprint(mols_column_name='mols', radius=3,\n                                        nbits=2048)\n\n    If you wish to join the Morgans back into the original dataframe, this\n    can be accomplished by doing a `join`, becuase the indices are\n    preserved:\n\n    .. code-block:: python\n\n        joined = df.join(morgans)\n\n    :param df: A pandas DataFrame.\n    :param mols_column_name: The name of the column that has the RDKIT\n        mol objects\n    :param radius: Radius of Morgan fingerprints. Defaults to 3.\n    :param nbits: The length of the fingerprints. Defaults to 2048.\n    :param kind: Whether to return counts or bits. Defaults to counts.\n    :returns: A pandas DataFrame\n    \"\"\"\n    acceptable_kinds = [\"counts\", \"bits\"]\n    if kind not in acceptable_kinds:\n        raise ValueError(f\"`kind` must be one of {acceptable_kinds}\")\n\n    if kind == \"bits\":\n        fps = [\n            GetMorganFingerprintAsBitVect(m, radius, nbits)\n            for m in df[mols_column_name]\n        ]\n    elif kind == \"counts\":\n        fps = [\n            GetHashedMorganFingerprint(m, radius, nbits)\n            for m in df[mols_column_name]\n        ]\n\n    np_fps = []\n    for fp in fps:\n        arr = np.zeros((1,))\n        DataStructs.ConvertToNumpyArray(fp, arr)\n        np_fps.append(arr)\n    np_fps = np.vstack(np_fps)\n    fpdf = pd.DataFrame(np_fps)\n    fpdf.index = df.index\n    return fpdf\n\n\n@pf.register_dataframe_method\n@deprecated_alias(mols_col=\"mols_column_name\")\ndef molecular_descriptors(df: pd.DataFrame, mols_column_name) -> pd.DataFrame:\n    \"\"\"\n    Convert a column of RDKIT mol objects into a Pandas DataFrame\n    of molecular descriptors.\n\n    Returns a new dataframe without any of the original data. This is\n    intentional to leave the user only with the data requested.\n\n    This method does not mutate the original DataFrame.\n\n    The molecular descriptors are from the rdkit.Chem.rdMolDescriptors:\n\n        Chi0n, Chi0v, Chi1n, Chi1v, Chi2n, Chi2v, Chi3n, Chi3v,\n        Chi4n, Chi4v, ExactMolWt, FractionCSP3, HallKierAlpha, Kappa1,\n        Kappa2, Kappa3, LabuteASA, NumAliphaticCarbocycles,\n        NumAliphaticHeterocycles, NumAliphaticRings, NumAmideBonds,\n        NumAromaticCarbocycles, NumAromaticHeterocycles, NumAromaticRings,\n        NumAtomStereoCenters, NumBridgeheadAtoms, NumHBA, NumHBD,\n        NumHeteroatoms, NumHeterocycles, NumLipinskiHBA, NumLipinskiHBD,\n        NumRings, NumSaturatedCarbocycles, NumSaturatedHeterocycles,\n        NumSaturatedRings, NumSpiroAtoms, NumUnspecifiedAtomStereoCenters,\n        TPSA.\n\n     Method chaining usage:\n\n    .. code-block:: python\n\n        df = pd.DataFrame(...)\n        mol_desc = df.molecular_descriptors(mols_column_name='mols')\n\n    If you wish to join the molecular descriptors back into the original\n    dataframe, this can be accomplished by doing a `join`,\n    because the indices are preserved:\n\n    .. code-block:: python\n\n        joined = df.join(mol_desc)\n\n    :param df: A pandas DataFrame.\n    :param mols_column_name: The name of the column that has the RDKIT mol\n        objects.\n    :returns: A pandas DataFrame\n    \"\"\"\n    descriptors = [\n        CalcChi0n,\n        CalcChi0v,\n        CalcChi1n,\n        CalcChi1v,\n        CalcChi2n,\n        CalcChi2v,\n        CalcChi3n,\n        CalcChi3v,\n        CalcChi4n,\n        CalcChi4v,\n        CalcExactMolWt,\n        CalcFractionCSP3,\n        CalcHallKierAlpha,\n        CalcKappa1,\n        CalcKappa2,\n        CalcKappa3,\n        CalcLabuteASA,\n        CalcNumAliphaticCarbocycles,\n        CalcNumAliphaticHeterocycles,\n        CalcNumAliphaticRings,\n        CalcNumAmideBonds,\n        CalcNumAromaticCarbocycles,\n        CalcNumAromaticHeterocycles,\n        CalcNumAromaticRings,\n        CalcNumAtomStereoCenters,\n        CalcNumBridgeheadAtoms,\n        CalcNumHBA,\n        CalcNumHBD,\n        CalcNumHeteroatoms,\n        CalcNumHeterocycles,\n        CalcNumLipinskiHBA,\n        CalcNumLipinskiHBD,\n        CalcNumRings,\n        CalcNumSaturatedCarbocycles,\n        CalcNumSaturatedHeterocycles,\n        CalcNumSaturatedRings,\n        CalcNumSpiroAtoms,\n        CalcNumUnspecifiedAtomStereoCenters,\n        CalcTPSA,\n    ]\n    descriptors_mapping = {f.__name__.strip(\"Calc\"): f for f in descriptors}\n\n    feats = dict()\n    for name, func in descriptors_mapping.items():\n        feats[name] = [func(m) for m in df[mols_column_name]]\n    return pd.DataFrame(feats)\n\n\n@pf.register_dataframe_method\n@deprecated_alias(mols_col=\"mols_column_name\")\ndef maccs_keys_fingerprint(df: pd.DataFrame, mols_column_name) -> pd.DataFrame:\n    \"\"\"\n    Convert a column of RDKIT mol objects into MACCS Keys Fingeprints.\n\n    Returns a new dataframe without any of the original data.\n    This is intentional to leave the user with the data requested.\n\n    This method does not mutate the original DataFrame.\n\n    Method chaining usage:\n\n    .. code-block:: python\n\n        df = pd.DataFrame(...)\n        maccs = df.maccs_keys_fingerprint(mols_column_name='mols')\n\n    If you wish to join the molecular descriptors back into the\n    original dataframe, this can be accomplished by doing a `join`,\n    because the indices are preserved:\n\n    .. code-block:: python\n\n        joined = df.join(maccs_keys_fingerprint)\n\n\n    :param df: A pandas DataFrame.\n    :param mols_column_name: The name of the column that has the RDKIT mol\n        objects.\n    :returns: A pandas DataFrame\n    \"\"\"\n\n    maccs = [GetMACCSKeysFingerprint(m) for m in df[mols_column_name]]\n\n    np_maccs = []\n\n    for macc in maccs:\n        arr = np.zeros((1,))\n        DataStructs.ConvertToNumpyArray(macc, arr)\n        np_maccs.append(arr)\n    np_maccs = np.vstack(np_maccs)\n    fmaccs = pd.DataFrame(np_maccs)\n    fmaccs.index = df.index\n    return fmaccs\n", "path": "janitor/chemistry.py"}]}