Repository: pyjanitor-devs/pyjanitor (python)

Issue:
[DOC] Adding minimal working examples to docstrings; a checklist
## Background

This thread is borne out of the discussion from #968 , in an effort to make documentation more beginner-friendly & more understandable.
One of the subtasks mentioned in that thread was to go through the function docstrings and include a *minimal* working example to each of the public functions in pyjanitor.

Criteria reiterated here for the benefit of discussion:

>     It should fit with our existing choice to go with mkdocs, mkdocstrings, and mknotebooks.
>     The examples should be minimal and executable and complete execution within 5 seconds per function.
>     The examples should display in rich HTML on our docs page.
>     We should have an automatic way of identifying whether a function has an example provided or not so that every function has an example.

Sample of what MWE should look like is shown [here](https://github.com/pyjanitor-devs/pyjanitor/issues/968#issuecomment-1003672331).

---

I'm thinking we can create a task list so that 1. we can encourage more users to join in the effort, and 2. make sure we don't do duplicate work. A lot of the groundwork can be covered by selectively copying one or two examples over from the software test suite.

Then we can label this issue as a Help Wanted / Low-Hanging Fruit and get people to mention in this thread if they're intending to work on the files?

### Task list

- [X] functions/add_columns.py
- [x] functions/also.py
- [x] functions/bin_numeric.py
- [x] functions/case_when.py
- [x] functions/change_type.py
- [x] functions/clean_names.py
- [x] functions/coalesce.py
- [x] functions/collapse_levels.py
- [x] functions/complete.py
- [x] functions/concatenate_columns.py
- [x] functions/conditional_join.py
- [x] functions/convert_date.py
- [x] functions/count_cumulative_unique.py
- [x] functions/currency_column_to_numeric.py
- [x] functions/deconcatenate_column.py
- [x] functions/drop_constant_columns.py
- [x] functions/drop_duplicate_columns.py
- [x] functions/dropnotnull.py
- [x] functions/encode_categorical.py
- [x] functions/expand_column.py
- [x] functions/expand_grid.py
- [x] functions/factorize_columns.py
- [x] functions/fill.py
- [x] functions/filter.py
- [x] functions/find_replace.py
- [x] functions/flag_nulls.py
- [x] functions/get_dupes.py
- [x] functions/groupby_agg.py
- [x] functions/groupby_topk.py
- [x] functions/impute.py
- [x] functions/jitter.py
- [x] functions/join_apply.py
- [x] functions/label_encode.py
- [x] functions/limit_column_characters.py
- [ ] functions/min_max_scale.py
- [x] functions/move.py
- [x] functions/pivot.py
- [x] functions/process_text.py
- [x] functions/remove_columns.py
- [x] functions/remove_empty.py
- [x] functions/rename_columns.py
- [x] functions/reorder_columns.py
- [x] functions/round_to_fraction.py
- [x] functions/row_to_names.py
- [x] functions/select_columns.py
- [x] functions/shuffle.py
- [x] functions/sort_column_value_order.py
- [x] functions/sort_naturally.py
- [x] functions/take_first.py
- [x] functions/then.py
- [x] functions/to_datetime.py
- [x] functions/toset.py
- [x] functions/transform_columns.py
- [x] functions/truncate_datetime.py
- [x] functions/update_where.py
- [ ] spark/backend.py
- [ ] spark/functions.py
- [x] xarray/functions.py
- [x] biology.py
- [x] chemistry.py
- [x] engineering.py
- [ ] errors.py
- [x] finance.py
- [x] io.py
- [x] math.py
- [x] ml.py
- [x] timeseries.py
B

Pull request:
[DOC]: Method-chaining examples for fill.py
<!-- Thank you for your PR!

BEFORE YOU CONTINUE! Please add the appropriate three-letter abbreviation to your title.

The abbreviations can be:
- [DOC]: Documentation fixes.
- [ENH]: Code contributions and new features.
- [TST]: Test-related contributions.
- [INF]: Infrastructure-related contributions.

Also, do not forget to tag the relevant issue here as well.

Finally, as commits come in, don't forget to regularly rebase!
-->

# PR Description

Please describe the changes proposed in the pull request:

Provided method chaining examples for the following
- fill_empty
- fill_direction

<!-- Doing so provides maintainers with context on what the PR is, and can help us more effectively review your PR. -->

<!-- Please also identify below which issue that has been raised that you are going to close. -->

**This PR is related to #972.**

<!-- As you go down the PR template, please feel free to delete sections that are irrelevant. -->

# PR Checklist

<!-- This checklist exists for newcomers who are not yet familiar with our requirements. If you are experienced with
the project, please feel free to delete this section. -->

Please ensure that you have done the following:

1. [ ] PR in from a fork off your branch. Do not PR from `<your_username>`:`dev`, but rather from `<your_username>`:`<feature-branch_name>`.
<!-- Doing this helps us keep the commit history much cleaner than it would otherwise be. -->
2. [ ] If you're not on the contributors list, add yourself to `AUTHORS.rst`.
<!-- We'd like to acknowledge your contributions! -->
3. [ ] Add a line to `CHANGELOG.md` under the latest version header (i.e. the one that is "on deck") describing the contribution.
    - Do use some discretion here; if there are multiple PRs that are related, keep them in a single line.

# Automatic checks

There will be automatic checks run on the PR. These include:

- Building a preview of the docs on Netlify
- Automatically linting the code
- Making sure the code is documented
- Making sure that all tests are passed
- Making sure that code coverage doesn't go down.

# Relevant Reviewers

<!-- Finally, please tag relevant maintainers to review. -->

Please tag maintainers to review.

- @ericmjl


Patch:
diff --git a/AUTHORS.md b/AUTHORS.md
index 2c04fb0..2baa06a 100644
--- a/AUTHORS.md
+++ b/AUTHORS.md
@@ -105,3 +105,4 @@ Contributors
 - [@robertmitchellv](https://github.com/robertmitchellv) | [contributions](https://github.com/pyjanitor-devs/pyjanitor/issues?q=is%3Aclosed+mentions%3Arobertmitchellv)
 - [@ashenafiyb](https://github.com/ashenafiya) | [contributions](https://github.com/pyjanitor-devs/pyjanitor/issues?q=is%3Aclosed+mentions%3Aashenafiyb)
 - [@gahjelle](https://github.com/gahjelle) | [contributions](https://github.com/pyjanitor-devs/pyjanitor/issues?q=is%3Aclosed+mentions%3Agahjelle)
+- [@apatao](https://github.com/apatao) | [contributions](https://github.com/pyjanitor-devs/pyjanitor/issues?q=is%3Aclosed+mentions%3Aapatao)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 21b0534..2aabc30 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -29,6 +29,7 @@
 -   [DOC] Added a step in the dev guide to install `Remote Container` in VS Code. @ashenafiyb
 -   [DOC] Convert `expand_column` and `find_replace` code examples to doctests, issue #972. @gahjelle
 -   [DOC] Convert `engineering` code examples to doctests, issue #972 @ashenafiyb
+-   [DOC] Add doctests to `math`, issue #972. @gahjelle
 
 ## [v0.22.0] - 2021-11-21
 
diff --git a/janitor/biology.py b/janitor/biology.py
index 07da6dd..41465ed 100644
--- a/janitor/biology.py
+++ b/janitor/biology.py
@@ -39,36 +39,34 @@ def join_fasta(
 
     For more advanced functions, please use phylopandas.
 
-    Functional usage example:
-
-    ```python
-    import pandas as pd
-    import janitor.biology
-
-    df = pd.DataFrame(...)
-
-    df = janitor.biology.join_fasta(
-        df=df,
-        filename='fasta_file.fasta',
-        id_col='sequence_accession',
-        column_name='sequence',
-    )
-    ```
-
     Method chaining usage example:
 
-    ```python
-    import pandas as pd
-    import janitor.biology
-
-    df = pd.DataFrame(...)
-
-    df = df.join_fasta(
-        filename='fasta_file.fasta',
-        id_col='sequence_accession',
-        column_name='sequence',
-    )
-    ```
+    >>> import tempfile
+    >>> import pandas as pd
+    >>> import janitor.biology
+
+    >>> tf = tempfile.NamedTemporaryFile()
+    >>> tf.write('''>SEQUENCE_1
+    ... MTEITAAMVKELRESTGAGMMDCK
+    ... >SEQUENCE_2
+    ... SATVSEINSETDFVAKN'''.encode('utf8'))
+    66
+    >>> tf.seek(0)
+    0
+
+    >>> df = pd.DataFrame({"sequence_accession":
+    ... ["SEQUENCE_1", "SEQUENCE_2", ]})
+
+    >>> df = df.join_fasta(
+    ...     filename=tf.name,
+    ...     id_col='sequence_accession',
+    ...     column_name='sequence',
+    ... )
+
+    >>> df.sequence
+    0    MTEITAAMVKELRESTGAGMMDCK
+    1           SATVSEINSETDFVAKN
+    Name: sequence, dtype: object
 
     :param df: A pandas DataFrame.
     :param filename: Path to the FASTA file.
diff --git a/janitor/chemistry.py b/janitor/chemistry.py
index 6725c2c..dc9a24b 100644
--- a/janitor/chemistry.py
+++ b/janitor/chemistry.py
@@ -94,32 +94,34 @@ def smiles2mol(
 
     Functional usage example:
 
-    ```python
-    import pandas as pd
-    import janitor.chemistry
+    >>> import pandas as pd
+    >>> import janitor.chemistry
 
-    df = pd.DataFrame(...)
+    >>> df = pd.DataFrame({"smiles": ["O=C=O", "CCC(=O)O"]})
 
-    df = janitor.chemistry.smiles2mol(
-        df=df,
-        smiles_column_name='smiles',
-        mols_column_name='mols'
-    )
-    ```
+    >>> df = janitor.chemistry.smiles2mol(
+    ...    df=df,
+    ...    smiles_column_name='smiles',
+    ...    mols_column_name='mols'
+    ... )
+
+    >>> df.mols[0].GetNumAtoms(), df.mols[0].GetNumBonds()
+    (3, 2)
+    >>> df.mols[1].GetNumAtoms(), df.mols[1].GetNumBonds()
+    (5, 4)
 
     Method chaining usage example:
 
-    ```python
-    import pandas as pd
-    import janitor.chemistry
+    >>> import pandas as pd
+    >>> import janitor.chemistry
 
-    df = pd.DataFrame(...)
+    >>> df = df.smiles2mol(
+    ...     smiles_column_name='smiles',
+    ...     mols_column_name='rdkmol'
+    ... )
 
-    df = df.smiles2mol(
-        smiles_column_name='smiles',
-        mols_column_name='mols'
-    )
-    ```
+    >>> df.rdkmol[0].GetNumAtoms(), df.rdkmol[0].GetNumBonds()
+    (3, 2)
 
     A progressbar can be optionally used.
 
@@ -182,70 +184,78 @@ def morgan_fingerprint(
 
     Functional usage example:
 
-    ```python
-    import pandas as pd
-    import janitor.chemistry
+    >>> import pandas as pd
+    >>> import janitor.chemistry
 
-    df = pd.DataFrame(...)
+    >>> df = pd.DataFrame({"smiles": ["O=C=O", "CCC(=O)O"]})
 
     # For "counts" kind
-    morgans = janitor.chemistry.morgan_fingerprint(
-        df=df.smiles2mol('smiles', 'mols'),
-        mols_column_name='mols',
-        radius=3,      # Defaults to 3
-        nbits=2048,    # Defaults to 2048
-        kind='counts'  # Defaults to "counts"
-    )
+    >>> morgans = janitor.chemistry.morgan_fingerprint(
+    ...     df=df.smiles2mol('smiles', 'mols'),
+    ...     mols_column_name='mols',
+    ...     radius=3,      # Defaults to 3
+    ...     nbits=2048,    # Defaults to 2048
+    ...     kind='counts'  # Defaults to "counts"
+    ... )
+
+    >>> set(morgans.iloc[0])
+    {0.0, 1.0, 2.0}
 
     # For "bits" kind
-    morgans = janitor.chemistry.morgan_fingerprint(
-        df=df.smiles2mol('smiles', 'mols'),
-        mols_column_name='mols',
-        radius=3,      # Defaults to 3
-        nbits=2048,    # Defaults to 2048
-        kind='bits'    # Defaults to "counts"
-    )
-    ```
+    >>> morgans = janitor.chemistry.morgan_fingerprint(
+    ...     df=df.smiles2mol('smiles', 'mols'),
+    ...     mols_column_name='mols',
+    ...     radius=3,      # Defaults to 3
+    ...     nbits=2048,    # Defaults to 2048
+    ...     kind='bits'    # Defaults to "counts"
+    ...  )
+
+    >>> set(morgans.iloc[0])
+    {0.0, 1.0}
 
     Method chaining usage example:
 
-    ```python
-    import pandas as pd
-    import janitor.chemistry
+    >>> import pandas as pd
+    >>> import janitor.chemistry
 
-    df = pd.DataFrame(...)
+    >>> df = pd.DataFrame({"smiles": ["O=C=O", "CCC(=O)O"]})
 
     # For "counts" kind
-    morgans = (
-        df.smiles2mol('smiles', 'mols')
-        .morgan_fingerprint(
-            mols_column_name='mols',
-            radius=3,      # Defaults to 3
-            nbits=2048,    # Defaults to 2048
-            kind='counts'  # Defaults to "counts"
-        )
-    )
+    >>> morgans = (
+    ...     df.smiles2mol('smiles', 'mols')
+    ...     .morgan_fingerprint(
+    ...         mols_column_name='mols',
+    ...         radius=3,      # Defaults to 3
+    ...         nbits=2048,    # Defaults to 2048
+    ...         kind='counts'  # Defaults to "counts"
+    ...     )
+    ... )
+
+    >>> set(morgans.iloc[0])
+    {0.0, 1.0, 2.0}
 
     # For "bits" kind
-    morgans = (
-        df
-        .smiles2mol('smiles', 'mols')
-        .morgan_fingerprint(
-            mols_column_name='mols',
-            radius=3,    # Defaults to 3
-            nbits=2048,  # Defaults to 2048
-            kind='bits'  # Defaults to "counts"
-        )
-    )
-    ```
+    >>> morgans = (
+    ...     df
+    ...     .smiles2mol('smiles', 'mols')
+    ...     .morgan_fingerprint(
+    ...         mols_column_name='mols',
+    ...         radius=3,    # Defaults to 3
+    ...         nbits=2048,  # Defaults to 2048
+    ...         kind='bits'  # Defaults to "counts"
+    ...     )
+    ... )
+
+    >>> set(morgans.iloc[0])
+    {0.0, 1.0}
 
     If you wish to join the morgan fingerprints back into the original
     dataframe, this can be accomplished by doing a `join`,
     because the indices are preserved:
 
-    ```python
-    joined = df.join(morgans)
-    ```
+    >>> joined = df.join(morgans)
+    >>> len(joined.columns)
+    2050
 
     :param df: A pandas DataFrame.
     :param mols_column_name: The name of the column that has the RDKIT
@@ -314,42 +324,47 @@ def molecular_descriptors(
 
     Functional usage example:
 
-    ```python
-    import pandas as pd
-    import janitor.chemistry
+    >>> import pandas as pd
+    >>> import janitor.chemistry
 
-    df = pd.DataFrame(...)
+    >>> df = pd.DataFrame({"smiles": ["O=C=O", "CCC(=O)O"]})
 
-    mol_desc = (
-        janitor.chemistry.molecular_descriptors(
-            df=df.smiles2mol('smiles', 'mols'),
-            mols_column_name='mols'
-        )
-    )
-    ```
+    >>> mol_desc = (
+    ...     janitor.chemistry.molecular_descriptors(
+    ...         df=df.smiles2mol('smiles', 'mols'),
+    ...         mols_column_name='mols'
+    ...     )
+    ... )
+
+    >>> mol_desc.TPSA
+    0    34.14
+    1    37.30
+    Name: TPSA, dtype: float64
 
     Method chaining usage example:
 
-    ```python
+    >>> import pandas as pd
+    >>> import janitor.chemistry
 
-    import pandas as pd
-    import janitor.chemistry
+    >>> df = pd.DataFrame({"smiles": ["O=C=O", "CCC(=O)O"]})
 
-    df = pd.DataFrame(...)
+    >>> mol_desc = (
+    ...     df.smiles2mol('smiles', 'mols')
+    ...     .molecular_descriptors(mols_column_name='mols')
+    ... )
 
-    mol_desc = (
-        df.smiles2mol('smiles', 'mols')
-        .molecular_descriptors(mols_column_name='mols')
-    )
-    ```
+    >>> mol_desc.TPSA
+    0    34.14
+    1    37.30
+    Name: TPSA, dtype: float64
 
     If you wish to join the molecular descriptors back into the original
     dataframe, this can be accomplished by doing a `join`,
     because the indices are preserved:
 
-    ```python
-    joined = df.join(mol_desc)
-    ```
+    >>> joined = df.join(mol_desc)
+    >>> len(joined.columns)
+    41
 
     :param df: A pandas DataFrame.
     :param mols_column_name: The name of the column that has the RDKIT mol
@@ -420,39 +435,42 @@ def maccs_keys_fingerprint(
 
     Functional usage example:
 
-    ```python
-    import pandas as pd
-    import janitor.chemistry
+    >>> import pandas as pd
+    >>> import janitor.chemistry
 
-    df = pd.DataFrame(...)
+    >>> df = pd.DataFrame({"smiles": ["O=C=O", "CCC(=O)O"]})
 
-    maccs = janitor.chemistry.maccs_keys_fingerprint(
-        df=df.smiles2mol('smiles', 'mols'),
-        mols_column_name='mols'
-    )
-    ```
+    >>> maccs = janitor.chemistry.maccs_keys_fingerprint(
+    ...     df=df.smiles2mol('smiles', 'mols'),
+    ...     mols_column_name='mols'
+    ... )
+
+    >>> len(maccs.columns)
+    167
 
     Method chaining usage example:
 
-    ```python
-    import pandas as pd
-    import janitor.chemistry
+    >>> import pandas as pd
+    >>> import janitor.chemistry
 
-    df = pd.DataFrame(...)
+    >>> df = pd.DataFrame({"smiles": ["O=C=O", "CCC(=O)O"]})
 
-    maccs = (
-        df.smiles2mol('smiles', 'mols')
-            .maccs_keys_fingerprint(mols_column_name='mols')
-    )
-    ```
+    >>> maccs = (
+    ...     df.smiles2mol('smiles', 'mols')
+    ...         .maccs_keys_fingerprint(mols_column_name='mols')
+    ... )
+
+    >>> len(maccs.columns)
+    167
 
     If you wish to join the maccs keys fingerprints back into the
     original dataframe, this can be accomplished by doing a `join`,
     because the indices are preserved:
 
-    ```python
-    joined = df.join(maccs_keys_fingerprint)
-    ```
+    >>> joined = df.join(maccs)
+
+    >>> len(joined.columns)
+    169
 
     :param df: A pandas DataFrame.
     :param mols_column_name: The name of the column that has the RDKIT mol
diff --git a/janitor/functions/convert_date.py b/janitor/functions/convert_date.py
index e49e12a..0472d90 100644
--- a/janitor/functions/convert_date.py
+++ b/janitor/functions/convert_date.py
@@ -21,19 +21,21 @@ def convert_excel_date(
     Implementation is also from
     [Stack Overflow](https://stackoverflow.com/questions/38454403/convert-excel-style-date-with-pandas)
 
-    Functional usage syntax:
-
-    ```python
-        df = convert_excel_date(df, column_name='date')
-    ```
-
     Method chaining syntax:
 
-    ```python
-        import pandas as pd
-        import janitor
-        df = pd.DataFrame(...).convert_excel_date('date')
-    ```
+        >>> import pandas as pd
+        >>> import janitor
+        >>> df = pd.DataFrame({"date": [39690, 39690, 37118]})
+        >>> df
+            date
+        0  39690
+        1  39690
+        2  37118
+        >>> df.convert_excel_date('date')
+                date
+        0 2008-08-30
+        1 2008-08-30
+        2 2001-08-15
 
     :param df: A pandas DataFrame.
     :param column_name: A column name.
@@ -68,19 +70,23 @@ def convert_matlab_date(
 
     This method mutates the original DataFrame.
 
-    Functional usage syntax:
-
-    ```python
-        df = convert_matlab_date(df, column_name='date')
-    ```
-
     Method chaining syntax:
 
-    ```python
-        import pandas as pd
-        import janitor
-        df = pd.DataFrame(...).convert_matlab_date('date')
-    ```
+        >>> import pandas as pd
+        >>> import janitor
+        >>> df = pd.DataFrame({"date": [737125.0, 737124.815863, 737124.4985, 737124]})
+        >>> df
+                    date
+        0  737125.000000
+        1  737124.815863
+        2  737124.498500
+        3  737124.000000
+        >>> df.convert_matlab_date('date')
+                                date
+        0 2018-03-06 00:00:00.000000
+        1 2018-03-05 19:34:50.563200
+        2 2018-03-05 11:57:50.399999
+        3 2018-03-05 00:00:00.000000
 
     :param df: A pandas DataFrame.
     :param column_name: A column name.
@@ -106,19 +112,21 @@ def convert_unix_date(df: pd.DataFrame, column_name: Hashable) -> pd.DataFrame:
 
     This method mutates the original DataFrame.
 
-    Functional usage syntax:
-
-    ```python
-        df = convert_unix_date(df, column_name='date')
-    ```
-
     Method chaining syntax:
 
-    ```python
-        import pandas as pd
-        import janitor
-        df = pd.DataFrame(...).convert_unix_date('date')
-    ```
+        >>> import pandas as pd
+        >>> import janitor
+        >>> df = pd.DataFrame({"date": [1651510462, 53394822, 1126233195]})
+        >>> df
+                 date
+        0  1651510462
+        1    53394822
+        2  1126233195
+        >>> df.convert_unix_date('date')
+                         date
+        0 2022-05-02 16:54:22
+        1 1971-09-10 23:53:42
+        2 2005-09-09 02:33:15
 
     :param df: A pandas DataFrame.
     :param column_name: A column name.
diff --git a/janitor/functions/fill.py b/janitor/functions/fill.py
index dd899dd..d758d5b 100644
--- a/janitor/functions/fill.py
+++ b/janitor/functions/fill.py
@@ -36,16 +36,34 @@ def fill_direction(df: pd.DataFrame, **kwargs) -> pd.DataFrame:
 
     Method-chaining usage syntax:
 
-    ```python
-    import pandas as pd
-    import janitor as jn
-
-    df = pd.DataFrame(...)
-            .fill_direction(
-                column_1 = direction_1,
-                column_2 = direction_2,
-            )
-    ```
+    >>> import pandas as pd
+    >>> import janitor as jn
+    >>> df = pd.DataFrame(
+    ...    {
+    ...        'col1': [1, 2, 3, 4],
+    ...        'col2': [None, 5, 6, 7],
+    ...        'col3': [8, 9, 10, None],
+    ...        'col4': [None, None, 11, None],
+    ...        'col5': [None, 12, 13, None]
+    ...    }
+    ... )
+    >>> df
+       col1  col2  col3  col4  col5
+    0     1   NaN   8.0   NaN   NaN
+    1     2   5.0   9.0   NaN  12.0
+    2     3   6.0  10.0  11.0  13.0
+    3     4   7.0   NaN   NaN   NaN
+    >>> df.fill_direction(
+    ... col2 = 'up',
+    ... col3 = 'down',
+    ... col4 = 'downup',
+    ... col5 = 'updown'
+    ... )
+       col1  col2  col3  col4  col5
+    0     1   5.0   8.0  11.0  12.0
+    1     2   5.0   9.0  11.0  12.0
+    2     3   6.0  10.0  11.0  13.0
+    3     4   7.0  10.0  11.0  13.0
 
     :param df: A pandas DataFrame.
     :param kwargs: Key - value pairs of columns and directions.
@@ -128,11 +146,32 @@ def fill_empty(
 
     Method chaining syntax:
 
-    ```python
-        import pandas as pd
-        import janitor
-        df = pd.DataFrame(...).fill_empty(column_names=col1, value=0)
-    ```
+
+    >>> import pandas as pd
+    >>> import janitor
+    >>> df = pd.DataFrame(
+    ...        {
+    ...            'col1': [1, 2, 3],
+    ...            'col2': [None, 4, None ],
+    ...            'col3': [None, 5, 6]
+    ...        }
+    ...    )
+    >>> df
+       col1  col2  col3
+    0     1   NaN   NaN
+    1     2   4.0   5.0
+    2     3   NaN   6.0
+    >>> df.fill_empty(column_names = 'col2', value = 0)
+       col1  col2  col3
+    0     1   0.0   NaN
+    1     2   4.0   5.0
+    2     3   0.0   6.0
+    >>> df.fill_empty(column_names = ['col2', 'col3'], value = 0)
+       col1  col2  col3
+    0     1   0.0   0.0
+    1     2   4.0   5.0
+    2     3   0.0   6.0
+
 
     :param df: A pandas DataFrame.
     :param column_names: column_names: A column name or an iterable (list
diff --git a/janitor/math.py b/janitor/math.py
index 3bfedac..7a023c2 100644
--- a/janitor/math.py
+++ b/janitor/math.py
@@ -8,6 +8,7 @@ import pandas as pd
 import pandas_flavor as pf
 from pandas.api.types import is_numeric_dtype
 from scipy.special import expit
+from scipy.special import logit as scipy_logit
 from scipy.special import softmax as scipy_softmax
 from scipy.stats import norm
 
@@ -17,6 +18,18 @@ def log(s: pd.Series, error: str = "warn") -> pd.Series:
     """
     Take natural logarithm of the Series.
 
+    Each value in the series should be positive. Use `error` to control the
+    behavior if there are nonpositive entries in the series.
+
+        >>> import pandas as pd
+        >>> import janitor
+        >>> s = pd.Series([0, 1, 3], name="numbers")
+        >>> s.log(error="ignore")
+        0         NaN
+        1    0.000000
+        2    1.098612
+        Name: numbers, dtype: float64
+
     :param s: Input Series.
     :param error: Determines behavior when taking the log of nonpositive
         entries. If `'warn'` then a `RuntimeWarning` is thrown. If `'raise'`,
@@ -45,6 +58,15 @@ def exp(s: pd.Series) -> pd.Series:
     """
     Take the exponential transform of the series.
 
+        >>> import pandas as pd
+        >>> import janitor
+        >>> s = pd.Series([0, 1, 3], name="numbers")
+        >>> s.exp()
+        0     1.000000
+        1     2.718282
+        2    20.085537
+        Name: numbers, dtype: float64
+
     :param s: Input Series.
     :return: Transformed Series.
     """
@@ -60,6 +82,15 @@ def sigmoid(s: pd.Series) -> pd.Series:
     sigmoid(x) = 1 / (1 + exp(-x))
     ```
 
+        >>> import pandas as pd
+        >>> import janitor
+        >>> s = pd.Series([-1, 0, 4], name="numbers")
+        >>> s.sigmoid()
+        0    0.268941
+        1    0.500000
+        2    0.982014
+        Name: numbers, dtype: float64
+
     :param s: Input Series.
     :return: Transformed Series.
     """
@@ -81,6 +112,15 @@ def softmax(s: pd.Series) -> pd.Series:
     softmax(x) = exp(x)/sum(exp(x))
     ```
 
+        >>> import pandas as pd
+        >>> import janitor
+        >>> s = pd.Series([0, 1, 3], name="numbers")
+        >>> s.softmax()
+        0    0.042010
+        1    0.114195
+        2    0.843795
+        Name: numbers, dtype: float64
+
     :param s: Input Series.
     :return: Transformed Series.
     """
@@ -96,8 +136,20 @@ def logit(s: pd.Series, error: str = "warn") -> pd.Series:
     logit(p) = log(p/(1-p))
     ```
 
+    Each value in the series should be between 0 and 1. Use `error` to
+    control the behavior if any series entries are outside of (0, 1).
+
+        >>> import pandas as pd
+        >>> import janitor
+        >>> s = pd.Series([0.1, 0.5, 0.9], name="numbers")
+        >>> s.logit()
+        0   -2.197225
+        1    0.000000
+        2    2.197225
+        Name: numbers, dtype: float64
+
     :param s: Input Series.
-    :param error: Determines behavior when `s / (1-s)` is outside of `(0, 1)`.
+    :param error: Determines behavior when `s` is outside of `(0, 1)`.
         If `'warn'` then a `RuntimeWarning` is thrown. If `'raise'`, then a
         `RuntimeError` is thrown. Otherwise, nothing is thrown and `np.nan`
         is returned for the problematic entries; defaults to `'warn'`.
@@ -105,19 +157,17 @@ def logit(s: pd.Series, error: str = "warn") -> pd.Series:
     :raises RuntimeError: if `error` is set to `'raise'`.
     """
     s = s.copy()
-    odds_ratio = s / (1 - s)
-    outside_support = (odds_ratio <= 0) | (odds_ratio >= 1)
+    outside_support = (s <= 0) | (s >= 1)
     if (outside_support).any():
-        msg = f"Odds ratio for {outside_support.sum()} value(s) \
-are outside of (0, 1)"
+        msg = f"{outside_support.sum()} value(s) are outside of (0, 1)"
         if error.lower() == "warn":
             warnings.warn(msg, RuntimeWarning)
         if error.lower() == "raise":
             raise RuntimeError(msg)
         else:
             pass
-    odds_ratio[outside_support] = np.nan
-    return odds_ratio.log(error="ignore")
+    s[outside_support] = np.nan
+    return scipy_logit(s)
 
 
 @pf.register_series_method
@@ -125,6 +175,15 @@ def normal_cdf(s: pd.Series) -> pd.Series:
     """
     Transforms the Series via the CDF of the Normal distribution.
 
+        >>> import pandas as pd
+        >>> import janitor
+        >>> s = pd.Series([-1, 0, 3], name="numbers")
+        >>> s.normal_cdf()
+        0    0.158655
+        1    0.500000
+        2    0.998650
+        dtype: float64
+
     :param s: Input Series.
     :return: Transformed Series.
     """
@@ -136,6 +195,18 @@ def probit(s: pd.Series, error: str = "warn") -> pd.Series:
     """
     Transforms the Series via the inverse CDF of the Normal distribution.
 
+    Each value in the series should be between 0 and 1. Use `error` to
+    control the behavior if any series entries are outside of (0, 1).
+
+        >>> import pandas as pd
+        >>> import janitor
+        >>> s = pd.Series([0.1, 0.5, 0.8], name="numbers")
+        >>> s.probit()
+        0   -1.281552
+        1    0.000000
+        2    0.841621
+        dtype: float64
+
     :param s: Input Series.
     :param error: Determines behavior when `s` is outside of `(0, 1)`.
         If `'warn'` then a `RuntimeWarning` is thrown. If `'raise'`, then
@@ -174,6 +245,15 @@ def z_score(
     z = (s - s.mean()) / s.std()
     ```
 
+        >>> import pandas as pd
+        >>> import janitor
+        >>> s = pd.Series([0, 1, 3], name="numbers")
+        >>> s.z_score()
+        0   -0.872872
+        1   -0.218218
+        2    1.091089
+        Name: numbers, dtype: float64
+
     :param s: Input Series.
     :param moments_dict: If not `None`, then the mean and standard
         deviation used to compute the z-score transformation is
@@ -216,6 +296,15 @@ def ecdf(s: pd.Series) -> Tuple[np.ndarray, np.ndarray]:
     Also, if the `dtype` of the series is not numeric,
     a `TypeError` is raised.
 
+        >>> import pandas as pd
+        >>> import janitor
+        >>> df = pd.DataFrame({"numbers": [0, 4, 0, 1, 2, 1, 1, 3]})
+        >>> x, y = df["numbers"].ecdf()
+        >>> x
+        array([0, 0, 1, 1, 1, 2, 3, 4])
+        >>> y
+        array([0.125, 0.25 , 0.375, 0.5  , 0.625, 0.75 , 0.875, 1.   ])
+
     :param s: A pandas series. `dtype` should be numeric.
     :returns: `(x, y)`.
         `x`: sorted array of values.


Test patch:
diff --git a/tests/io/test_read_commandline.py b/tests/io/test_read_commandline.py
index 9c9f40c..9885ac0 100644
--- a/tests/io/test_read_commandline.py
+++ b/tests/io/test_read_commandline.py
@@ -1,10 +1,13 @@
 import os
+import sys
 from subprocess import CalledProcessError
 
+
 import pandas as pd
 import pytest
 
 import janitor.io
+import tempfile
 
 
 def test_read_commandline(dataframe):
@@ -16,18 +19,26 @@ def test_read_commandline(dataframe):
 
     """
     # create a temporary .csv file from test data
-    dataframe.to_csv("/tmp/dataframe.csv", index=0)
+    temp_dir = tempfile.gettempdir()
+
+    dataframe.to_csv(f"{temp_dir}/dataframe.csv", index=0)
 
     # create a new dataframe from the temporary .csv using
     #   the cat command from the bash commandline
-    df = janitor.io.read_commandline("cat /tmp/dataframe.csv")
+
+    if sys.platform in ["win32"]:
+        # cat is not an operable command for Windows command line
+        # "type" is a similar call
+        df = janitor.io.read_commandline(f"type {temp_dir}\\dataframe.csv")
+    else:
+        df = janitor.io.read_commandline(f"cat {temp_dir}/dataframe.csv")
 
     # Make assertion that new dataframe created with read_commandline
     #   is equal to the test dataframe
     assert df.equals(dataframe)
 
     # clean up after the test
-    os.unlink("/tmp/dataframe.csv")
+    os.unlink(f"{temp_dir}/dataframe.csv")
 
 
 def test_read_commandline_bad_cmd(dataframe):
@@ -44,8 +55,10 @@ def test_read_commandline_bad_cmd(dataframe):
         is a valid bash command, however results
         in the shell not creating a dataframe.
     """
+    temp_dir = tempfile.gettempdir()
+
     # create a temporary .csv file
-    dataframe.to_csv("/tmp/dataframe.csv")
+    dataframe.to_csv(f"{temp_dir}/dataframe.csv")
 
     # Test 1
     with pytest.raises(TypeError):
@@ -56,8 +69,17 @@ def test_read_commandline_bad_cmd(dataframe):
         janitor.io.read_commandline("bad command")
 
     # Test 3
-    with pytest.raises(pd.errors.EmptyDataError):
-        janitor.io.read_commandline("cat")
+    # windows does not support "cat" in commandline
+    # "type" command must be used and it returns a different error
+    cmd = "cat"
+
+    ExpectedError = pd.errors.EmptyDataError
+    if sys.platform in ["win32"]:
+        cmd = "type"
+        ExpectedError = CalledProcessError
+
+    with pytest.raises(ExpectedError):
+        janitor.io.read_commandline(cmd)
 
     # clean up after the tests
-    os.unlink("/tmp/dataframe.csv")
+    os.unlink(f"{temp_dir}/dataframe.csv")
diff --git a/tests/math/test_logit.py b/tests/math/test_logit.py
index a6f52b5..0ae84d3 100644
--- a/tests/math/test_logit.py
+++ b/tests/math/test_logit.py
@@ -5,9 +5,9 @@ import pytest
 
 @pytest.mark.functions
 def test_logit():
-    s = pd.Series([0, 0.1, 0.2, 0.3, 1, 2])
+    s = pd.Series([0, 0.1, 0.2, 0.3, 0.5, 0.9, 1, 2])
     inside = (0 < s) & (s < 1)
-    valid = np.array([0.1, 0.2, 0.3])
+    valid = np.array([0.1, 0.2, 0.3, 0.5, 0.9])
     ans = np.log(valid / (1 - valid))
 
     with pytest.raises(RuntimeError):
@@ -17,13 +17,13 @@ def test_logit():
         out = s.logit(error="warn")
 
     assert out[inside].notna().all()
-    assert (out[inside] == ans).all()
+    assert out[inside].to_numpy() == pytest.approx(ans)
     assert (out.index == s.index).all()
     assert out[~inside].isna().all()
 
     out = s.logit(error="ignore")
 
     assert out[inside].notna().all()
-    assert (out[inside] == ans).all()
+    assert out[inside].to_numpy() == pytest.approx(ans)
     assert (out.index == s.index).all()
     assert out[~inside].isna().all()