We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[EHN] let `filter_string` suit parameters of `Series.str.contains`
# Brief Description

<!-- Please provide a brief description of what you'd like to propose. -->

- `Series.str.contains(pat, case=True, flags=0, na=None, regex=True)`
- `filter_string(df, column_name, search_string, complement=False)`

`filter_string` is a super sugary syntax for `contains`.
`contains` has five arguements, but `filter_string` only has one arguement `search_string`.

I thought we should complete parameters of `filter string`.

# Example API

<!-- One of the selling points of pyjanitor is the API. Hence, we guard the API very carefully, and want to
make sure that it is accessible and understandable to many people. Please provide a few examples of what the API
of the new function you're proposing will look like. We have provided an example that you should modify. -->

A example `filter_string` would get error result.

```python
import pandas as pd
import janitor  # noqa

df = pd.Series(["JoseChen", "Brian.Salvi"], name="Name").to_frame()

# find any '.' rows
df[df.Name.str.contains(".", regex=False)]
#           Name
# 1  Brian.Salvi

# error
df.filter_string(".")
#             Name
# 0     JoseChen
# 1    Brian.Salvi
```


--- 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/functions/filter.py`
Content:
```
1 import datetime as dt
2 import warnings
3 from functools import reduce
4 from typing import Dict, Hashable, Iterable, List, Optional
5 
6 import numpy as np
7 import pandas as pd
8 import pandas_flavor as pf
9 from janitor.utils import deprecated_alias
10 
11 
12 @pf.register_dataframe_method
13 @deprecated_alias(column="column_name")
14 def filter_string(
15     df: pd.DataFrame,
16     column_name: Hashable,
17     search_string: str,
18     complement: bool = False,
19 ) -> pd.DataFrame:
20     """Filter a string-based column according to whether it contains a substring.
21 
22     This is super sugary syntax that builds on top of
23     `pandas.Series.str.contains`. It is meant to be the method-chaining
24     equivalent of the following:
25 
26     ```python
27     df = df[df[column_name].str.contains(search_string)]]
28     ```
29 
30     This method does not mutate the original DataFrame.
31 
32     Example: Retain rows whose column values contain a particular substring.
33 
34         >>> import pandas as pd
35         >>> import janitor
36         >>> df = pd.DataFrame({"a": range(3, 6), "b": ["bear", "peel", "sail"]})
37         >>> df
38            a     b
39         0  3  bear
40         1  4  peel
41         2  5  sail
42         >>> df.filter_string(column_name="b", search_string="ee")
43            a     b
44         1  4  peel
45 
46     `search_string` is also permitted to be any valid regex pattern.
47 
48     :param df: A pandas DataFrame.
49     :param column_name: The column to filter. The column should contain strings.
50     :param search_string: A regex pattern or a (sub-)string to search.
51     :param complement: Whether to return the complement of the filter or not. If
52         set to True, then the rows for which the string search fails are retained
53         instead.
54     :returns: A filtered pandas DataFrame.
55     """  # noqa: E501
56     criteria = df[column_name].str.contains(search_string)
57     if complement:
58         return df[~criteria]
59     return df[criteria]
60 
61 
62 @pf.register_dataframe_method
63 def filter_on(
64     df: pd.DataFrame,
65     criteria: str,
66     complement: bool = False,
67 ) -> pd.DataFrame:
68     """Return a dataframe filtered on a particular criteria.
69 
70     This method does not mutate the original DataFrame.
71 
72     This is super-sugary syntax that wraps the pandas `.query()` API, enabling
73     users to use strings to quickly specify filters for filtering their
74     dataframe. The intent is that `filter_on` as a verb better matches the
75     intent of a pandas user than the verb `query`.
76 
77     This is intended to be the method-chaining equivalent of the following:
78     ```python
79     df = df[df["score"] < 3]
80     ```
81 
82     Example: Filter students who failed an exam (scored less than 50).
83 
84         >>> import pandas as pd
85         >>> import janitor
86         >>> df = pd.DataFrame({
87         ...     "student_id": ["S1", "S2", "S3"],
88         ...     "score": [40, 60, 85],
89         ... })
90         >>> df
91           student_id  score
92         0         S1     40
93         1         S2     60
94         2         S3     85
95         >>> df.filter_on("score < 50", complement=False)
96           student_id  score
97         0         S1     40
98 
99     Credit to Brant Peterson for the name.
100 
101     :param df: A pandas DataFrame.
102     :param criteria: A filtering criteria that returns an array or Series of
103         booleans, on which pandas can filter on.
104     :param complement: Whether to return the complement of the filter or not.
105         If set to True, then the rows for which the criteria is False are
106         retained instead.
107     :returns: A filtered pandas DataFrame.
108     """
109     if complement:
110         return df.query(f"not ({criteria})")
111     return df.query(criteria)
112 
113 
114 @pf.register_dataframe_method
115 @deprecated_alias(column="column_name", start="start_date", end="end_date")
116 def filter_date(
117     df: pd.DataFrame,
118     column_name: Hashable,
119     start_date: Optional[dt.date] = None,
120     end_date: Optional[dt.date] = None,
121     years: Optional[List] = None,
122     months: Optional[List] = None,
123     days: Optional[List] = None,
124     column_date_options: Optional[Dict] = None,
125     format: Optional[str] = None,  # skipcq: PYL-W0622
126 ) -> pd.DataFrame:
127     """Filter a date-based column based on certain criteria.
128 
129     This method does not mutate the original DataFrame.
130 
131     Dates may be finicky and this function builds on top of the *magic* from
132     the pandas `to_datetime` function that is able to parse dates well.
133 
134     Additional options to parse the date type of your column may be found at
135     the official pandas [documentation][datetime].
136 
137     [datetime]: https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.to_datetime.html
138 
139     Example:
140 
141         >>> import pandas as pd
142         >>> import janitor
143         >>> df = pd.DataFrame({
144         ...     "a": range(5, 9),
145         ...     "dt": ["2021-11-12", "2021-12-15", "2022-01-03", "2022-01-09"],
146         ... })
147         >>> df
148            a          dt
149         0  5  2021-11-12
150         1  6  2021-12-15
151         2  7  2022-01-03
152         3  8  2022-01-09
153         >>> df.filter_date("dt", start_date="2021-12-01", end_date="2022-01-05")
154            a         dt
155         1  6 2021-12-15
156         2  7 2022-01-03
157         >>> df.filter_date("dt", years=[2021], months=[12])
158            a         dt
159         1  6 2021-12-15
160 
161     !!!note
162 
163         This method will cast your column to a Timestamp!
164 
165     !!!note
166 
167         This only affects the format of the `start_date` and `end_date`
168         parameters. If there's an issue with the format of the DataFrame being
169         parsed, you would pass `{'format': your_format}` to `column_date_options`.
170 
171     :param df: The dataframe to filter on.
172     :param column_name: The column which to apply the fraction transformation.
173     :param start_date: The beginning date to use to filter the DataFrame.
174     :param end_date: The end date to use to filter the DataFrame.
175     :param years: The years to use to filter the DataFrame.
176     :param months: The months to use to filter the DataFrame.
177     :param days: The days to use to filter the DataFrame.
178     :param column_date_options: Special options to use when parsing the date
179         column in the original DataFrame. The options may be found at the
180         official Pandas documentation.
181     :param format: If you're using a format for `start_date` or `end_date`
182         that is not recognized natively by pandas' `to_datetime` function, you
183         may supply the format yourself. Python date and time formats may be
184         found [here](http://strftime.org/).
185     :returns: A filtered pandas DataFrame.
186     """  # noqa: E501
187 
188     def _date_filter_conditions(conditions):
189         """Taken from: https://stackoverflow.com/a/13616382."""
190         return reduce(np.logical_and, conditions)
191 
192     if column_date_options:
193         df.loc[:, column_name] = pd.to_datetime(
194             df.loc[:, column_name], **column_date_options
195         )
196     else:
197         df.loc[:, column_name] = pd.to_datetime(df.loc[:, column_name])
198 
199     _filter_list = []
200 
201     if start_date:
202         start_date = pd.to_datetime(start_date, format=format)
203         _filter_list.append(df.loc[:, column_name] >= start_date)
204 
205     if end_date:
206         end_date = pd.to_datetime(end_date, format=format)
207         _filter_list.append(df.loc[:, column_name] <= end_date)
208 
209     if years:
210         _filter_list.append(df.loc[:, column_name].dt.year.isin(years))
211 
212     if months:
213         _filter_list.append(df.loc[:, column_name].dt.month.isin(months))
214 
215     if days:
216         _filter_list.append(df.loc[:, column_name].dt.day.isin(days))
217 
218     if start_date and end_date and start_date > end_date:
219         warnings.warn(
220             f"Your start date of {start_date} is after your end date of "
221             f"{end_date}. Is this intended?"
222         )
223 
224     return df.loc[_date_filter_conditions(_filter_list), :]
225 
226 
227 @pf.register_dataframe_method
228 @deprecated_alias(column="column_name")
229 def filter_column_isin(
230     df: pd.DataFrame,
231     column_name: Hashable,
232     iterable: Iterable,
233     complement: bool = False,
234 ) -> pd.DataFrame:
235     """Filter a dataframe for values in a column that exist in the given iterable.
236 
237     This method does not mutate the original DataFrame.
238 
239     Assumes exact matching; fuzzy matching not implemented.
240 
241     Example: Filter the dataframe to retain rows for which `names`
242         are exactly `James` or `John`.
243 
244         >>> import pandas as pd
245         >>> import janitor
246         >>> df = pd.DataFrame({"names": ["Jane", "Jeremy", "John"], "foo": list("xyz")})
247         >>> df
248             names foo
249         0    Jane   x
250         1  Jeremy   y
251         2    John   z
252         >>> df.filter_column_isin(column_name="names", iterable=["James", "John"])
253           names foo
254         2  John   z
255 
256     This is the method-chaining alternative to:
257 
258     ```python
259     df = df[df["names"].isin(["James", "John"])]
260     ```
261 
262     If `complement=True`, then we will only get rows for which the names
263     are neither `James` nor `John`.
264 
265     :param df: A pandas DataFrame.
266     :param column_name: The column on which to filter.
267     :param iterable: An iterable. Could be a list, tuple, another pandas
268         Series.
269     :param complement: Whether to return the complement of the selection or
270         not.
271     :returns: A filtered pandas DataFrame.
272     :raises ValueError: If `iterable` does not have a length of `1`
273         or greater.
274     """  # noqa: E501
275     if len(iterable) == 0:
276         raise ValueError(
277             "`iterable` kwarg must be given an iterable of length 1 "
278             "or greater."
279         )
280     criteria = df[column_name].isin(iterable)
281 
282     if complement:
283         return df[~criteria]
284     return df[criteria]
285
```
--- 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/functions/filter.py b/janitor/functions/filter.py
--- a/janitor/functions/filter.py
+++ b/janitor/functions/filter.py
@@ -16,12 +16,15 @@
     column_name: Hashable,
     search_string: str,
     complement: bool = False,
+    case: bool = True,
+    flags: int = 0,
+    na=None,
+    regex: bool = True,
 ) -> pd.DataFrame:
     """Filter a string-based column according to whether it contains a substring.
 
-    This is super sugary syntax that builds on top of
-    `pandas.Series.str.contains`. It is meant to be the method-chaining
-    equivalent of the following:
+    This is super sugary syntax that builds on top of `pandas.Series.str.contains`.
+    It is meant to be the method-chaining equivalent of the following:
 
     ```python
     df = df[df[column_name].str.contains(search_string)]]
@@ -33,17 +36,32 @@
 
         >>> import pandas as pd
         >>> import janitor
-        >>> df = pd.DataFrame({"a": range(3, 6), "b": ["bear", "peel", "sail"]})
+        >>> df = pd.DataFrame({"a": range(3, 6), "b": ["bear", "peeL", "sail"]})
         >>> df
            a     b
         0  3  bear
-        1  4  peel
+        1  4  peeL
         2  5  sail
         >>> df.filter_string(column_name="b", search_string="ee")
            a     b
-        1  4  peel
+        1  4  peeL
+        >>> df.filter_string(column_name="b", search_string="L", case=False)
+           a     b
+        1  4  peeL
+        2  5  sail
 
-    `search_string` is also permitted to be any valid regex pattern.
+    Example: Filter names does not contain `'.'` (disable regex mode).
+
+        >>> import pandas as pd
+        >>> import janitor
+        >>> df = pd.Series(["JoseChen", "Brian.Salvi"], name="Name").to_frame()
+        >>> df
+                  Name
+        0     JoseChen
+        1  Brian.Salvi
+        >>> df.filter_string(column_name="Name", search_string=".", regex=False, complement=True)
+               Name
+        0  JoseChen
 
     :param df: A pandas DataFrame.
     :param column_name: The column to filter. The column should contain strings.
@@ -51,11 +69,27 @@
     :param complement: Whether to return the complement of the filter or not. If
         set to True, then the rows for which the string search fails are retained
         instead.
+    :param case: If True, case sensitive.
+    :param flags: Flags to pass through to the re module, e.g. re.IGNORECASE.
+    :param na: Fill value for missing values. The default depends on dtype of
+        the array. For object-dtype, `numpy.nan` is used. For `StringDtype`,
+        `pandas.NA` is used.
+    :param regex: If True, assumes `search_string` is a regular expression. If False,
+        treats the `search_string` as a literal string.
     :returns: A filtered pandas DataFrame.
     """  # noqa: E501
-    criteria = df[column_name].str.contains(search_string)
+
+    criteria = df[column_name].str.contains(
+        pat=search_string,
+        case=case,
+        flags=flags,
+        na=na,
+        regex=regex,
+    )
+
     if complement:
         return df[~criteria]
+
     return df[criteria]

{"golden_diff": "diff --git a/janitor/functions/filter.py b/janitor/functions/filter.py\n--- a/janitor/functions/filter.py\n+++ b/janitor/functions/filter.py\n@@ -16,12 +16,15 @@\n     column_name: Hashable,\n     search_string: str,\n     complement: bool = False,\n+    case: bool = True,\n+    flags: int = 0,\n+    na=None,\n+    regex: bool = True,\n ) -> pd.DataFrame:\n     \"\"\"Filter a string-based column according to whether it contains a substring.\n \n-    This is super sugary syntax that builds on top of\n-    `pandas.Series.str.contains`. It is meant to be the method-chaining\n-    equivalent of the following:\n+    This is super sugary syntax that builds on top of `pandas.Series.str.contains`.\n+    It is meant to be the method-chaining equivalent of the following:\n \n     ```python\n     df = df[df[column_name].str.contains(search_string)]]\n@@ -33,17 +36,32 @@\n \n         >>> import pandas as pd\n         >>> import janitor\n-        >>> df = pd.DataFrame({\"a\": range(3, 6), \"b\": [\"bear\", \"peel\", \"sail\"]})\n+        >>> df = pd.DataFrame({\"a\": range(3, 6), \"b\": [\"bear\", \"peeL\", \"sail\"]})\n         >>> df\n            a     b\n         0  3  bear\n-        1  4  peel\n+        1  4  peeL\n         2  5  sail\n         >>> df.filter_string(column_name=\"b\", search_string=\"ee\")\n            a     b\n-        1  4  peel\n+        1  4  peeL\n+        >>> df.filter_string(column_name=\"b\", search_string=\"L\", case=False)\n+           a     b\n+        1  4  peeL\n+        2  5  sail\n \n-    `search_string` is also permitted to be any valid regex pattern.\n+    Example: Filter names does not contain `'.'` (disable regex mode).\n+\n+        >>> import pandas as pd\n+        >>> import janitor\n+        >>> df = pd.Series([\"JoseChen\", \"Brian.Salvi\"], name=\"Name\").to_frame()\n+        >>> df\n+                  Name\n+        0     JoseChen\n+        1  Brian.Salvi\n+        >>> df.filter_string(column_name=\"Name\", search_string=\".\", regex=False, complement=True)\n+               Name\n+        0  JoseChen\n \n     :param df: A pandas DataFrame.\n     :param column_name: The column to filter. The column should contain strings.\n@@ -51,11 +69,27 @@\n     :param complement: Whether to return the complement of the filter or not. If\n         set to True, then the rows for which the string search fails are retained\n         instead.\n+    :param case: If True, case sensitive.\n+    :param flags: Flags to pass through to the re module, e.g. re.IGNORECASE.\n+    :param na: Fill value for missing values. The default depends on dtype of\n+        the array. For object-dtype, `numpy.nan` is used. For `StringDtype`,\n+        `pandas.NA` is used.\n+    :param regex: If True, assumes `search_string` is a regular expression. If False,\n+        treats the `search_string` as a literal string.\n     :returns: A filtered pandas DataFrame.\n     \"\"\"  # noqa: E501\n-    criteria = df[column_name].str.contains(search_string)\n+\n+    criteria = df[column_name].str.contains(\n+        pat=search_string,\n+        case=case,\n+        flags=flags,\n+        na=na,\n+        regex=regex,\n+    )\n+\n     if complement:\n         return df[~criteria]\n+\n     return df[criteria]\n", "issue": "[EHN] let `filter_string` suit parameters of `Series.str.contains`\n# Brief Description\r\n\r\n<!-- Please provide a brief description of what you'd like to propose. -->\r\n\r\n- `Series.str.contains(pat, case=True, flags=0, na=None, regex=True)`\r\n- `filter_string(df, column_name, search_string, complement=False)`\r\n\r\n`filter_string` is a super sugary syntax for `contains`.\r\n`contains` has five arguements, but `filter_string` only has one arguement `search_string`.\r\n\r\nI thought we should complete parameters of `filter string`.\r\n\r\n# Example API\r\n\r\n<!-- One of the selling points of pyjanitor is the API. Hence, we guard the API very carefully, and want to\r\nmake sure that it is accessible and understandable to many people. Please provide a few examples of what the API\r\nof the new function you're proposing will look like. We have provided an example that you should modify. -->\r\n\r\nA example `filter_string` would get error result.\r\n\r\n```python\r\nimport pandas as pd\r\nimport janitor  # noqa\r\n\r\ndf = pd.Series([\"JoseChen\", \"Brian.Salvi\"], name=\"Name\").to_frame()\r\n\r\n# find any '.' rows\r\ndf[df.Name.str.contains(\".\", regex=False)]\r\n#           Name\r\n# 1  Brian.Salvi\r\n\r\n# error\r\ndf.filter_string(\".\")\r\n#             Name\r\n# 0     JoseChen\r\n# 1    Brian.Salvi\r\n```\r\n\n", "before_files": [{"content": "import datetime as dt\nimport warnings\nfrom functools import reduce\nfrom typing import Dict, Hashable, Iterable, List, Optional\n\nimport numpy as np\nimport pandas as pd\nimport pandas_flavor as pf\nfrom janitor.utils import deprecated_alias\n\n\n@pf.register_dataframe_method\n@deprecated_alias(column=\"column_name\")\ndef filter_string(\n    df: pd.DataFrame,\n    column_name: Hashable,\n    search_string: str,\n    complement: bool = False,\n) -> pd.DataFrame:\n    \"\"\"Filter a string-based column according to whether it contains a substring.\n\n    This is super sugary syntax that builds on top of\n    `pandas.Series.str.contains`. It is meant to be the method-chaining\n    equivalent of the following:\n\n    ```python\n    df = df[df[column_name].str.contains(search_string)]]\n    ```\n\n    This method does not mutate the original DataFrame.\n\n    Example: Retain rows whose column values contain a particular substring.\n\n        >>> import pandas as pd\n        >>> import janitor\n        >>> df = pd.DataFrame({\"a\": range(3, 6), \"b\": [\"bear\", \"peel\", \"sail\"]})\n        >>> df\n           a     b\n        0  3  bear\n        1  4  peel\n        2  5  sail\n        >>> df.filter_string(column_name=\"b\", search_string=\"ee\")\n           a     b\n        1  4  peel\n\n    `search_string` is also permitted to be any valid regex pattern.\n\n    :param df: A pandas DataFrame.\n    :param column_name: The column to filter. The column should contain strings.\n    :param search_string: A regex pattern or a (sub-)string to search.\n    :param complement: Whether to return the complement of the filter or not. If\n        set to True, then the rows for which the string search fails are retained\n        instead.\n    :returns: A filtered pandas DataFrame.\n    \"\"\"  # noqa: E501\n    criteria = df[column_name].str.contains(search_string)\n    if complement:\n        return df[~criteria]\n    return df[criteria]\n\n\n@pf.register_dataframe_method\ndef filter_on(\n    df: pd.DataFrame,\n    criteria: str,\n    complement: bool = False,\n) -> pd.DataFrame:\n    \"\"\"Return a dataframe filtered on a particular criteria.\n\n    This method does not mutate the original DataFrame.\n\n    This is super-sugary syntax that wraps the pandas `.query()` API, enabling\n    users to use strings to quickly specify filters for filtering their\n    dataframe. The intent is that `filter_on` as a verb better matches the\n    intent of a pandas user than the verb `query`.\n\n    This is intended to be the method-chaining equivalent of the following:\n    ```python\n    df = df[df[\"score\"] < 3]\n    ```\n\n    Example: Filter students who failed an exam (scored less than 50).\n\n        >>> import pandas as pd\n        >>> import janitor\n        >>> df = pd.DataFrame({\n        ...     \"student_id\": [\"S1\", \"S2\", \"S3\"],\n        ...     \"score\": [40, 60, 85],\n        ... })\n        >>> df\n          student_id  score\n        0         S1     40\n        1         S2     60\n        2         S3     85\n        >>> df.filter_on(\"score < 50\", complement=False)\n          student_id  score\n        0         S1     40\n\n    Credit to Brant Peterson for the name.\n\n    :param df: A pandas DataFrame.\n    :param criteria: A filtering criteria that returns an array or Series of\n        booleans, on which pandas can filter on.\n    :param complement: Whether to return the complement of the filter or not.\n        If set to True, then the rows for which the criteria is False are\n        retained instead.\n    :returns: A filtered pandas DataFrame.\n    \"\"\"\n    if complement:\n        return df.query(f\"not ({criteria})\")\n    return df.query(criteria)\n\n\n@pf.register_dataframe_method\n@deprecated_alias(column=\"column_name\", start=\"start_date\", end=\"end_date\")\ndef filter_date(\n    df: pd.DataFrame,\n    column_name: Hashable,\n    start_date: Optional[dt.date] = None,\n    end_date: Optional[dt.date] = None,\n    years: Optional[List] = None,\n    months: Optional[List] = None,\n    days: Optional[List] = None,\n    column_date_options: Optional[Dict] = None,\n    format: Optional[str] = None,  # skipcq: PYL-W0622\n) -> pd.DataFrame:\n    \"\"\"Filter a date-based column based on certain criteria.\n\n    This method does not mutate the original DataFrame.\n\n    Dates may be finicky and this function builds on top of the *magic* from\n    the pandas `to_datetime` function that is able to parse dates well.\n\n    Additional options to parse the date type of your column may be found at\n    the official pandas [documentation][datetime].\n\n    [datetime]: https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.to_datetime.html\n\n    Example:\n\n        >>> import pandas as pd\n        >>> import janitor\n        >>> df = pd.DataFrame({\n        ...     \"a\": range(5, 9),\n        ...     \"dt\": [\"2021-11-12\", \"2021-12-15\", \"2022-01-03\", \"2022-01-09\"],\n        ... })\n        >>> df\n           a          dt\n        0  5  2021-11-12\n        1  6  2021-12-15\n        2  7  2022-01-03\n        3  8  2022-01-09\n        >>> df.filter_date(\"dt\", start_date=\"2021-12-01\", end_date=\"2022-01-05\")\n           a         dt\n        1  6 2021-12-15\n        2  7 2022-01-03\n        >>> df.filter_date(\"dt\", years=[2021], months=[12])\n           a         dt\n        1  6 2021-12-15\n\n    !!!note\n\n        This method will cast your column to a Timestamp!\n\n    !!!note\n\n        This only affects the format of the `start_date` and `end_date`\n        parameters. If there's an issue with the format of the DataFrame being\n        parsed, you would pass `{'format': your_format}` to `column_date_options`.\n\n    :param df: The dataframe to filter on.\n    :param column_name: The column which to apply the fraction transformation.\n    :param start_date: The beginning date to use to filter the DataFrame.\n    :param end_date: The end date to use to filter the DataFrame.\n    :param years: The years to use to filter the DataFrame.\n    :param months: The months to use to filter the DataFrame.\n    :param days: The days to use to filter the DataFrame.\n    :param column_date_options: Special options to use when parsing the date\n        column in the original DataFrame. The options may be found at the\n        official Pandas documentation.\n    :param format: If you're using a format for `start_date` or `end_date`\n        that is not recognized natively by pandas' `to_datetime` function, you\n        may supply the format yourself. Python date and time formats may be\n        found [here](http://strftime.org/).\n    :returns: A filtered pandas DataFrame.\n    \"\"\"  # noqa: E501\n\n    def _date_filter_conditions(conditions):\n        \"\"\"Taken from: https://stackoverflow.com/a/13616382.\"\"\"\n        return reduce(np.logical_and, conditions)\n\n    if column_date_options:\n        df.loc[:, column_name] = pd.to_datetime(\n            df.loc[:, column_name], **column_date_options\n        )\n    else:\n        df.loc[:, column_name] = pd.to_datetime(df.loc[:, column_name])\n\n    _filter_list = []\n\n    if start_date:\n        start_date = pd.to_datetime(start_date, format=format)\n        _filter_list.append(df.loc[:, column_name] >= start_date)\n\n    if end_date:\n        end_date = pd.to_datetime(end_date, format=format)\n        _filter_list.append(df.loc[:, column_name] <= end_date)\n\n    if years:\n        _filter_list.append(df.loc[:, column_name].dt.year.isin(years))\n\n    if months:\n        _filter_list.append(df.loc[:, column_name].dt.month.isin(months))\n\n    if days:\n        _filter_list.append(df.loc[:, column_name].dt.day.isin(days))\n\n    if start_date and end_date and start_date > end_date:\n        warnings.warn(\n            f\"Your start date of {start_date} is after your end date of \"\n            f\"{end_date}. Is this intended?\"\n        )\n\n    return df.loc[_date_filter_conditions(_filter_list), :]\n\n\n@pf.register_dataframe_method\n@deprecated_alias(column=\"column_name\")\ndef filter_column_isin(\n    df: pd.DataFrame,\n    column_name: Hashable,\n    iterable: Iterable,\n    complement: bool = False,\n) -> pd.DataFrame:\n    \"\"\"Filter a dataframe for values in a column that exist in the given iterable.\n\n    This method does not mutate the original DataFrame.\n\n    Assumes exact matching; fuzzy matching not implemented.\n\n    Example: Filter the dataframe to retain rows for which `names`\n        are exactly `James` or `John`.\n\n        >>> import pandas as pd\n        >>> import janitor\n        >>> df = pd.DataFrame({\"names\": [\"Jane\", \"Jeremy\", \"John\"], \"foo\": list(\"xyz\")})\n        >>> df\n            names foo\n        0    Jane   x\n        1  Jeremy   y\n        2    John   z\n        >>> df.filter_column_isin(column_name=\"names\", iterable=[\"James\", \"John\"])\n          names foo\n        2  John   z\n\n    This is the method-chaining alternative to:\n\n    ```python\n    df = df[df[\"names\"].isin([\"James\", \"John\"])]\n    ```\n\n    If `complement=True`, then we will only get rows for which the names\n    are neither `James` nor `John`.\n\n    :param df: A pandas DataFrame.\n    :param column_name: The column on which to filter.\n    :param iterable: An iterable. Could be a list, tuple, another pandas\n        Series.\n    :param complement: Whether to return the complement of the selection or\n        not.\n    :returns: A filtered pandas DataFrame.\n    :raises ValueError: If `iterable` does not have a length of `1`\n        or greater.\n    \"\"\"  # noqa: E501\n    if len(iterable) == 0:\n        raise ValueError(\n            \"`iterable` kwarg must be given an iterable of length 1 \"\n            \"or greater.\"\n        )\n    criteria = df[column_name].isin(iterable)\n\n    if complement:\n        return df[~criteria]\n    return df[criteria]\n", "path": "janitor/functions/filter.py"}], "after_files": [{"content": "import datetime as dt\nimport warnings\nfrom functools import reduce\nfrom typing import Dict, Hashable, Iterable, List, Optional\n\nimport numpy as np\nimport pandas as pd\nimport pandas_flavor as pf\nfrom janitor.utils import deprecated_alias\n\n\n@pf.register_dataframe_method\n@deprecated_alias(column=\"column_name\")\ndef filter_string(\n    df: pd.DataFrame,\n    column_name: Hashable,\n    search_string: str,\n    complement: bool = False,\n    case: bool = True,\n    flags: int = 0,\n    na=None,\n    regex: bool = True,\n) -> pd.DataFrame:\n    \"\"\"Filter a string-based column according to whether it contains a substring.\n\n    This is super sugary syntax that builds on top of `pandas.Series.str.contains`.\n    It is meant to be the method-chaining equivalent of the following:\n\n    ```python\n    df = df[df[column_name].str.contains(search_string)]]\n    ```\n\n    This method does not mutate the original DataFrame.\n\n    Example: Retain rows whose column values contain a particular substring.\n\n        >>> import pandas as pd\n        >>> import janitor\n        >>> df = pd.DataFrame({\"a\": range(3, 6), \"b\": [\"bear\", \"peeL\", \"sail\"]})\n        >>> df\n           a     b\n        0  3  bear\n        1  4  peeL\n        2  5  sail\n        >>> df.filter_string(column_name=\"b\", search_string=\"ee\")\n           a     b\n        1  4  peeL\n        >>> df.filter_string(column_name=\"b\", search_string=\"L\", case=False)\n           a     b\n        1  4  peeL\n        2  5  sail\n\n    Example: Filter names does not contain `'.'` (disable regex mode).\n\n        >>> import pandas as pd\n        >>> import janitor\n        >>> df = pd.Series([\"JoseChen\", \"Brian.Salvi\"], name=\"Name\").to_frame()\n        >>> df\n                  Name\n        0     JoseChen\n        1  Brian.Salvi\n        >>> df.filter_string(column_name=\"Name\", search_string=\".\", regex=False, complement=True)\n               Name\n        0  JoseChen\n\n    :param df: A pandas DataFrame.\n    :param column_name: The column to filter. The column should contain strings.\n    :param search_string: A regex pattern or a (sub-)string to search.\n    :param complement: Whether to return the complement of the filter or not. If\n        set to True, then the rows for which the string search fails are retained\n        instead.\n    :param case: If True, case sensitive.\n    :param flags: Flags to pass through to the re module, e.g. re.IGNORECASE.\n    :param na: Fill value for missing values. The default depends on dtype of\n        the array. For object-dtype, `numpy.nan` is used. For `StringDtype`,\n        `pandas.NA` is used.\n    :param regex: If True, assumes `search_string` is a regular expression. If False,\n        treats the `search_string` as a literal string.\n    :returns: A filtered pandas DataFrame.\n    \"\"\"  # noqa: E501\n\n    criteria = df[column_name].str.contains(\n        pat=search_string,\n        case=case,\n        flags=flags,\n        na=na,\n        regex=regex,\n    )\n\n    if complement:\n        return df[~criteria]\n\n    return df[criteria]\n\n\n@pf.register_dataframe_method\ndef filter_on(\n    df: pd.DataFrame,\n    criteria: str,\n    complement: bool = False,\n) -> pd.DataFrame:\n    \"\"\"Return a dataframe filtered on a particular criteria.\n\n    This method does not mutate the original DataFrame.\n\n    This is super-sugary syntax that wraps the pandas `.query()` API, enabling\n    users to use strings to quickly specify filters for filtering their\n    dataframe. The intent is that `filter_on` as a verb better matches the\n    intent of a pandas user than the verb `query`.\n\n    This is intended to be the method-chaining equivalent of the following:\n    ```python\n    df = df[df[\"score\"] < 3]\n    ```\n\n    Example: Filter students who failed an exam (scored less than 50).\n\n        >>> import pandas as pd\n        >>> import janitor\n        >>> df = pd.DataFrame({\n        ...     \"student_id\": [\"S1\", \"S2\", \"S3\"],\n        ...     \"score\": [40, 60, 85],\n        ... })\n        >>> df\n          student_id  score\n        0         S1     40\n        1         S2     60\n        2         S3     85\n        >>> df.filter_on(\"score < 50\", complement=False)\n          student_id  score\n        0         S1     40\n\n    Credit to Brant Peterson for the name.\n\n    :param df: A pandas DataFrame.\n    :param criteria: A filtering criteria that returns an array or Series of\n        booleans, on which pandas can filter on.\n    :param complement: Whether to return the complement of the filter or not.\n        If set to True, then the rows for which the criteria is False are\n        retained instead.\n    :returns: A filtered pandas DataFrame.\n    \"\"\"\n    if complement:\n        return df.query(f\"not ({criteria})\")\n    return df.query(criteria)\n\n\n@pf.register_dataframe_method\n@deprecated_alias(column=\"column_name\", start=\"start_date\", end=\"end_date\")\ndef filter_date(\n    df: pd.DataFrame,\n    column_name: Hashable,\n    start_date: Optional[dt.date] = None,\n    end_date: Optional[dt.date] = None,\n    years: Optional[List] = None,\n    months: Optional[List] = None,\n    days: Optional[List] = None,\n    column_date_options: Optional[Dict] = None,\n    format: Optional[str] = None,  # skipcq: PYL-W0622\n) -> pd.DataFrame:\n    \"\"\"Filter a date-based column based on certain criteria.\n\n    This method does not mutate the original DataFrame.\n\n    Dates may be finicky and this function builds on top of the *magic* from\n    the pandas `to_datetime` function that is able to parse dates well.\n\n    Additional options to parse the date type of your column may be found at\n    the official pandas [documentation][datetime].\n\n    [datetime]: https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.to_datetime.html\n\n    Example:\n\n        >>> import pandas as pd\n        >>> import janitor\n        >>> df = pd.DataFrame({\n        ...     \"a\": range(5, 9),\n        ...     \"dt\": [\"2021-11-12\", \"2021-12-15\", \"2022-01-03\", \"2022-01-09\"],\n        ... })\n        >>> df\n           a          dt\n        0  5  2021-11-12\n        1  6  2021-12-15\n        2  7  2022-01-03\n        3  8  2022-01-09\n        >>> df.filter_date(\"dt\", start_date=\"2021-12-01\", end_date=\"2022-01-05\")\n           a         dt\n        1  6 2021-12-15\n        2  7 2022-01-03\n        >>> df.filter_date(\"dt\", years=[2021], months=[12])\n           a         dt\n        1  6 2021-12-15\n\n    !!!note\n\n        This method will cast your column to a Timestamp!\n\n    !!!note\n\n        This only affects the format of the `start_date` and `end_date`\n        parameters. If there's an issue with the format of the DataFrame being\n        parsed, you would pass `{'format': your_format}` to `column_date_options`.\n\n    :param df: The dataframe to filter on.\n    :param column_name: The column which to apply the fraction transformation.\n    :param start_date: The beginning date to use to filter the DataFrame.\n    :param end_date: The end date to use to filter the DataFrame.\n    :param years: The years to use to filter the DataFrame.\n    :param months: The months to use to filter the DataFrame.\n    :param days: The days to use to filter the DataFrame.\n    :param column_date_options: Special options to use when parsing the date\n        column in the original DataFrame. The options may be found at the\n        official Pandas documentation.\n    :param format: If you're using a format for `start_date` or `end_date`\n        that is not recognized natively by pandas' `to_datetime` function, you\n        may supply the format yourself. Python date and time formats may be\n        found [here](http://strftime.org/).\n    :returns: A filtered pandas DataFrame.\n    \"\"\"  # noqa: E501\n\n    def _date_filter_conditions(conditions):\n        \"\"\"Taken from: https://stackoverflow.com/a/13616382.\"\"\"\n        return reduce(np.logical_and, conditions)\n\n    if column_date_options:\n        df.loc[:, column_name] = pd.to_datetime(\n            df.loc[:, column_name], **column_date_options\n        )\n    else:\n        df.loc[:, column_name] = pd.to_datetime(df.loc[:, column_name])\n\n    _filter_list = []\n\n    if start_date:\n        start_date = pd.to_datetime(start_date, format=format)\n        _filter_list.append(df.loc[:, column_name] >= start_date)\n\n    if end_date:\n        end_date = pd.to_datetime(end_date, format=format)\n        _filter_list.append(df.loc[:, column_name] <= end_date)\n\n    if years:\n        _filter_list.append(df.loc[:, column_name].dt.year.isin(years))\n\n    if months:\n        _filter_list.append(df.loc[:, column_name].dt.month.isin(months))\n\n    if days:\n        _filter_list.append(df.loc[:, column_name].dt.day.isin(days))\n\n    if start_date and end_date and start_date > end_date:\n        warnings.warn(\n            f\"Your start date of {start_date} is after your end date of \"\n            f\"{end_date}. Is this intended?\"\n        )\n\n    return df.loc[_date_filter_conditions(_filter_list), :]\n\n\n@pf.register_dataframe_method\n@deprecated_alias(column=\"column_name\")\ndef filter_column_isin(\n    df: pd.DataFrame,\n    column_name: Hashable,\n    iterable: Iterable,\n    complement: bool = False,\n) -> pd.DataFrame:\n    \"\"\"Filter a dataframe for values in a column that exist in the given iterable.\n\n    This method does not mutate the original DataFrame.\n\n    Assumes exact matching; fuzzy matching not implemented.\n\n    Example: Filter the dataframe to retain rows for which `names`\n        are exactly `James` or `John`.\n\n        >>> import pandas as pd\n        >>> import janitor\n        >>> df = pd.DataFrame({\"names\": [\"Jane\", \"Jeremy\", \"John\"], \"foo\": list(\"xyz\")})\n        >>> df\n            names foo\n        0    Jane   x\n        1  Jeremy   y\n        2    John   z\n        >>> df.filter_column_isin(column_name=\"names\", iterable=[\"James\", \"John\"])\n          names foo\n        2  John   z\n\n    This is the method-chaining alternative to:\n\n    ```python\n    df = df[df[\"names\"].isin([\"James\", \"John\"])]\n    ```\n\n    If `complement=True`, then we will only get rows for which the names\n    are neither `James` nor `John`.\n\n    :param df: A pandas DataFrame.\n    :param column_name: The column on which to filter.\n    :param iterable: An iterable. Could be a list, tuple, another pandas\n        Series.\n    :param complement: Whether to return the complement of the selection or\n        not.\n    :returns: A filtered pandas DataFrame.\n    :raises ValueError: If `iterable` does not have a length of `1`\n        or greater.\n    \"\"\"  # noqa: E501\n    if len(iterable) == 0:\n        raise ValueError(\n            \"`iterable` kwarg must be given an iterable of length 1 \"\n            \"or greater.\"\n        )\n    criteria = df[column_name].isin(iterable)\n\n    if complement:\n        return df[~criteria]\n    return df[criteria]\n", "path": "janitor/functions/filter.py"}]}