We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[EHN] Add `jointly` option for `min_max_scale`
<!-- 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:

- Add an option for `min_max_scale` support to transform each column values or entire values
- Default transform each column, similar behavior to [sklearn.preprocessing.MinMaxScaler](http://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.MinMaxScaler.html)

<!-- 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 resolves #1067.**

<!-- 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. [x] 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. [x] If you're not on the contributors list, add yourself to `AUTHORS.md`.
<!-- We'd like to acknowledge your contributions! -->
3. [x] 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


--- 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/min_max_scale.py`
Content:
```
1 from __future__ import annotations
2 
3 import pandas_flavor as pf
4 import pandas as pd
5 
6 from janitor.utils import deprecated_alias
7 from janitor.utils import deprecated_kwargs
8 
9 
10 @pf.register_dataframe_method
11 @deprecated_kwargs(
12     "old_min",
13     "old_max",
14     "new_min",
15     "new_max",
16     message=(
17         "The keyword argument {argument!r} of {func_name!r} is deprecated. "
18         "Please use 'feature_range' instead."
19     ),
20 )
21 @deprecated_alias(col_name="column_name")
22 def min_max_scale(
23     df: pd.DataFrame,
24     feature_range: tuple[int | float, int | float] = (0, 1),
25     column_name: str | int | list[str | int] | pd.Index = None,
26     jointly: bool = False,
27 ) -> pd.DataFrame:
28     """
29     Scales DataFrame to between a minimum and maximum value.
30 
31     One can optionally set a new target **minimum** and **maximum** value
32     using the `feature_range` keyword argument.
33 
34     If `column_name` is specified, then only that column(s) of data is scaled.
35     Otherwise, the entire dataframe is scaled.
36     If `jointly` is `True`, the `column_names` provided entire dataframe will
37     be regnozied as the one to jointly scale. Otherwise, each column of data
38     will be scaled separately.
39 
40     Example: Basic usage.
41 
42         >>> import pandas as pd
43         >>> import janitor
44         >>> df = pd.DataFrame({'a':[1, 2], 'b':[0, 1]})
45         >>> df.min_max_scale()
46              a    b
47         0  0.0  0.0
48         1  1.0  1.0
49         >>> df.min_max_scale(jointly=True)
50              a    b
51         0  0.5  0.0
52         1  1.0  0.5
53 
54     Example: Setting custom minimum and maximum.
55 
56         >>> import pandas as pd
57         >>> import janitor
58         >>> df = pd.DataFrame({'a':[1, 2], 'b':[0, 1]})
59         >>> df.min_max_scale(feature_range=(0, 100))
60                a      b
61         0    0.0    0.0
62         1  100.0  100.0
63         >>> df.min_max_scale(feature_range=(0, 100), jointly=True)
64                a     b
65         0   50.0   0.0
66         1  100.0  50.0
67 
68     Example: Apply min-max to the selected columns.
69 
70         >>> import pandas as pd
71         >>> import janitor
72         >>> df = pd.DataFrame({'a':[1, 2], 'b':[0, 1], 'c': [1, 0]})
73         >>> df.min_max_scale(
74         ...     feature_range=(0, 100),
75         ...     column_name=["a", "c"],
76         ... )
77                a  b      c
78         0    0.0  0  100.0
79         1  100.0  1    0.0
80         >>> df.min_max_scale(
81         ...     feature_range=(0, 100),
82         ...     column_name=["a", "c"],
83         ...     jointly=True,
84         ... )
85                a  b     c
86         0   50.0  0  50.0
87         1  100.0  1   0.0
88         >>> df.min_max_scale(feature_range=(0, 100), column_name='a')
89                a  b  c
90         0    0.0  0  1
91         1  100.0  1  0
92 
93     The aforementioned example might be applied to something like scaling the
94     isoelectric points of amino acids. While technically they range from
95     approx 3-10, we can also think of them on the pH scale which ranges from
96     1 to 14. Hence, 3 gets scaled not to 0 but approx. 0.15 instead, while 10
97     gets scaled to approx. 0.69 instead.
98 
99     :param df: A pandas DataFrame.
100     :param feature_range: (optional) Desired range of transformed data.
101     :param column_name: (optional) The column on which to perform scaling.
102     :param jointly: (bool) Scale the entire data if Ture.
103     :returns: A pandas DataFrame with scaled data.
104     :raises ValueError: if `feature_range` isn't tuple type.
105     :raises ValueError: if the length of `feature_range` isn't equal to two.
106     :raises ValueError: if the element of `feature_range` isn't number type.
107     :raises ValueError: if `feature_range[1]` <= `feature_range[0]`.
108 
109     Changed in version 0.24.0: Deleted "old_min", "old_max", "new_min", and
110     "new_max" options.
111     Changed in version 0.24.0: Added "feature_range", and "jointly" options.
112     """
113 
114     if not (
115         isinstance(feature_range, (tuple, list))
116         and len(feature_range) == 2
117         and all((isinstance(i, (int, float))) for i in feature_range)
118         and feature_range[1] > feature_range[0]
119     ):
120         raise ValueError(
121             "`feature_range` should be a range type contains number element, "
122             "the first element must be greater than the second one"
123         )
124 
125     if column_name is not None:
126         df = df.copy()  # Avoid to change the original DataFrame.
127 
128         old_feature_range = df[column_name].pipe(min_max_value, jointly)
129         df[column_name] = df[column_name].pipe(
130             apply_min_max,
131             *old_feature_range,
132             *feature_range,
133         )
134     else:
135         old_feature_range = df.pipe(min_max_value, jointly)
136         df = df.pipe(
137             apply_min_max,
138             *old_feature_range,
139             *feature_range,
140         )
141 
142     return df
143 
144 
145 def min_max_value(df: pd.DataFrame, jointly: bool) -> tuple:
146     """
147     Return the minimum and maximum of DataFrame.
148 
149     Use the `jointly` flag to control returning entire data or each column.
150 
151     .. # noqa: DAR101
152     .. # noqa: DAR201
153     """
154 
155     if jointly:
156         mmin = df.min().min()
157         mmax = df.max().max()
158     else:
159         mmin = df.min()
160         mmax = df.max()
161 
162     return mmin, mmax
163 
164 
165 def apply_min_max(
166     df: pd.DataFrame,
167     old_min: int | float | pd.Series,
168     old_max: int | float | pd.Series,
169     new_min: int | float | pd.Series,
170     new_max: int | float | pd.Series,
171 ) -> pd.DataFrame:
172     """
173     Apply minimax scaler to DataFrame.
174 
175     Notes
176     -----
177     - Inputting minimum and maximum type
178         - int or float : It will apply minimax to the entire DataFrame.
179         - Series : It will apply minimax to each column.
180 
181     .. # noqa: DAR101
182     .. # noqa: DAR201
183     """
184 
185     old_range = old_max - old_min
186     new_range = new_max - new_min
187 
188     return (df - old_min) * new_range / old_range + new_min
189
```
--- 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/min_max_scale.py b/janitor/functions/min_max_scale.py
--- a/janitor/functions/min_max_scale.py
+++ b/janitor/functions/min_max_scale.py
@@ -96,6 +96,12 @@
     1 to 14. Hence, 3 gets scaled not to 0 but approx. 0.15 instead, while 10
     gets scaled to approx. 0.69 instead.
 
+    !!! summary "Version Changed"
+
+        - 0.24.0
+            - Deleted `old_min`, `old_max`, `new_min`, and `new_max` options.
+            - Added `feature_range`, and `jointly` options.
+
     :param df: A pandas DataFrame.
     :param feature_range: (optional) Desired range of transformed data.
     :param column_name: (optional) The column on which to perform scaling.
@@ -105,10 +111,6 @@
     :raises ValueError: if the length of `feature_range` isn't equal to two.
     :raises ValueError: if the element of `feature_range` isn't number type.
     :raises ValueError: if `feature_range[1]` <= `feature_range[0]`.
-
-    Changed in version 0.24.0: Deleted "old_min", "old_max", "new_min", and
-    "new_max" options.
-    Changed in version 0.24.0: Added "feature_range", and "jointly" options.
     """
 
     if not (
@@ -125,16 +127,16 @@
     if column_name is not None:
         df = df.copy()  # Avoid to change the original DataFrame.
 
-        old_feature_range = df[column_name].pipe(min_max_value, jointly)
+        old_feature_range = df[column_name].pipe(_min_max_value, jointly)
         df[column_name] = df[column_name].pipe(
-            apply_min_max,
+            _apply_min_max,
             *old_feature_range,
             *feature_range,
         )
     else:
-        old_feature_range = df.pipe(min_max_value, jointly)
+        old_feature_range = df.pipe(_min_max_value, jointly)
         df = df.pipe(
-            apply_min_max,
+            _apply_min_max,
             *old_feature_range,
             *feature_range,
         )
@@ -142,7 +144,7 @@
     return df
 
 
-def min_max_value(df: pd.DataFrame, jointly: bool) -> tuple:
+def _min_max_value(df: pd.DataFrame, jointly: bool) -> tuple:
     """
     Return the minimum and maximum of DataFrame.
 
@@ -162,7 +164,7 @@
     return mmin, mmax
 
 
-def apply_min_max(
+def _apply_min_max(
     df: pd.DataFrame,
     old_min: int | float | pd.Series,
     old_max: int | float | pd.Series,

{"golden_diff": "diff --git a/janitor/functions/min_max_scale.py b/janitor/functions/min_max_scale.py\n--- a/janitor/functions/min_max_scale.py\n+++ b/janitor/functions/min_max_scale.py\n@@ -96,6 +96,12 @@\n     1 to 14. Hence, 3 gets scaled not to 0 but approx. 0.15 instead, while 10\r\n     gets scaled to approx. 0.69 instead.\r\n \r\n+    !!! summary \"Version Changed\"\r\n+\r\n+        - 0.24.0\r\n+            - Deleted `old_min`, `old_max`, `new_min`, and `new_max` options.\r\n+            - Added `feature_range`, and `jointly` options.\r\n+\r\n     :param df: A pandas DataFrame.\r\n     :param feature_range: (optional) Desired range of transformed data.\r\n     :param column_name: (optional) The column on which to perform scaling.\r\n@@ -105,10 +111,6 @@\n     :raises ValueError: if the length of `feature_range` isn't equal to two.\r\n     :raises ValueError: if the element of `feature_range` isn't number type.\r\n     :raises ValueError: if `feature_range[1]` <= `feature_range[0]`.\r\n-\r\n-    Changed in version 0.24.0: Deleted \"old_min\", \"old_max\", \"new_min\", and\r\n-    \"new_max\" options.\r\n-    Changed in version 0.24.0: Added \"feature_range\", and \"jointly\" options.\r\n     \"\"\"\r\n \r\n     if not (\r\n@@ -125,16 +127,16 @@\n     if column_name is not None:\r\n         df = df.copy()  # Avoid to change the original DataFrame.\r\n \r\n-        old_feature_range = df[column_name].pipe(min_max_value, jointly)\r\n+        old_feature_range = df[column_name].pipe(_min_max_value, jointly)\r\n         df[column_name] = df[column_name].pipe(\r\n-            apply_min_max,\r\n+            _apply_min_max,\r\n             *old_feature_range,\r\n             *feature_range,\r\n         )\r\n     else:\r\n-        old_feature_range = df.pipe(min_max_value, jointly)\r\n+        old_feature_range = df.pipe(_min_max_value, jointly)\r\n         df = df.pipe(\r\n-            apply_min_max,\r\n+            _apply_min_max,\r\n             *old_feature_range,\r\n             *feature_range,\r\n         )\r\n@@ -142,7 +144,7 @@\n     return df\r\n \r\n \r\n-def min_max_value(df: pd.DataFrame, jointly: bool) -> tuple:\r\n+def _min_max_value(df: pd.DataFrame, jointly: bool) -> tuple:\r\n     \"\"\"\r\n     Return the minimum and maximum of DataFrame.\r\n \r\n@@ -162,7 +164,7 @@\n     return mmin, mmax\r\n \r\n \r\n-def apply_min_max(\r\n+def _apply_min_max(\r\n     df: pd.DataFrame,\r\n     old_min: int | float | pd.Series,\r\n     old_max: int | float | pd.Series,\n", "issue": "[EHN] Add `jointly` option for `min_max_scale`\n<!-- Thank you for your PR!\r\n\r\nBEFORE YOU CONTINUE! Please add the appropriate three-letter abbreviation to your title.\r\n\r\nThe abbreviations can be:\r\n- [DOC]: Documentation fixes.\r\n- [ENH]: Code contributions and new features.\r\n- [TST]: Test-related contributions.\r\n- [INF]: Infrastructure-related contributions.\r\n\r\nAlso, do not forget to tag the relevant issue here as well.\r\n\r\nFinally, as commits come in, don't forget to regularly rebase!\r\n-->\r\n\r\n# PR Description\r\n\r\nPlease describe the changes proposed in the pull request:\r\n\r\n- Add an option for `min_max_scale` support to transform each column values or entire values\r\n- Default transform each column, similar behavior to [sklearn.preprocessing.MinMaxScaler](http://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.MinMaxScaler.html)\r\n\r\n<!-- Doing so provides maintainers with context on what the PR is, and can help us more effectively review your PR. -->\r\n\r\n<!-- Please also identify below which issue that has been raised that you are going to close. -->\r\n\r\n**This PR resolves #1067.**\r\n\r\n<!-- As you go down the PR template, please feel free to delete sections that are irrelevant. -->\r\n\r\n# PR Checklist\r\n\r\n<!-- This checklist exists for newcomers who are not yet familiar with our requirements. If you are experienced with\r\nthe project, please feel free to delete this section. -->\r\n\r\nPlease ensure that you have done the following:\r\n\r\n1. [x] PR in from a fork off your branch. Do not PR from `<your_username>`:`dev`, but rather from `<your_username>`:`<feature-branch_name>`.\r\n<!-- Doing this helps us keep the commit history much cleaner than it would otherwise be. -->\r\n2. [x] If you're not on the contributors list, add yourself to `AUTHORS.md`.\r\n<!-- We'd like to acknowledge your contributions! -->\r\n3. [x] Add a line to `CHANGELOG.md` under the latest version header (i.e. the one that is \"on deck\") describing the contribution.\r\n    - Do use some discretion here; if there are multiple PRs that are related, keep them in a single line.\r\n\r\n# Automatic checks\r\n\r\nThere will be automatic checks run on the PR. These include:\r\n\r\n- Building a preview of the docs on Netlify\r\n- Automatically linting the code\r\n- Making sure the code is documented\r\n- Making sure that all tests are passed\r\n- Making sure that code coverage doesn't go down.\r\n\r\n# Relevant Reviewers\r\n\r\n<!-- Finally, please tag relevant maintainers to review. -->\r\n\r\nPlease tag maintainers to review.\r\n\r\n- @ericmjl\r\n\n", "before_files": [{"content": "from __future__ import annotations\r\n\r\nimport pandas_flavor as pf\r\nimport pandas as pd\r\n\r\nfrom janitor.utils import deprecated_alias\r\nfrom janitor.utils import deprecated_kwargs\r\n\r\n\r\n@pf.register_dataframe_method\r\n@deprecated_kwargs(\r\n    \"old_min\",\r\n    \"old_max\",\r\n    \"new_min\",\r\n    \"new_max\",\r\n    message=(\r\n        \"The keyword argument {argument!r} of {func_name!r} is deprecated. \"\r\n        \"Please use 'feature_range' instead.\"\r\n    ),\r\n)\r\n@deprecated_alias(col_name=\"column_name\")\r\ndef min_max_scale(\r\n    df: pd.DataFrame,\r\n    feature_range: tuple[int | float, int | float] = (0, 1),\r\n    column_name: str | int | list[str | int] | pd.Index = None,\r\n    jointly: bool = False,\r\n) -> pd.DataFrame:\r\n    \"\"\"\r\n    Scales DataFrame to between a minimum and maximum value.\r\n\r\n    One can optionally set a new target **minimum** and **maximum** value\r\n    using the `feature_range` keyword argument.\r\n\r\n    If `column_name` is specified, then only that column(s) of data is scaled.\r\n    Otherwise, the entire dataframe is scaled.\r\n    If `jointly` is `True`, the `column_names` provided entire dataframe will\r\n    be regnozied as the one to jointly scale. Otherwise, each column of data\r\n    will be scaled separately.\r\n\r\n    Example: Basic usage.\r\n\r\n        >>> import pandas as pd\r\n        >>> import janitor\r\n        >>> df = pd.DataFrame({'a':[1, 2], 'b':[0, 1]})\r\n        >>> df.min_max_scale()\r\n             a    b\r\n        0  0.0  0.0\r\n        1  1.0  1.0\r\n        >>> df.min_max_scale(jointly=True)\r\n             a    b\r\n        0  0.5  0.0\r\n        1  1.0  0.5\r\n\r\n    Example: Setting custom minimum and maximum.\r\n\r\n        >>> import pandas as pd\r\n        >>> import janitor\r\n        >>> df = pd.DataFrame({'a':[1, 2], 'b':[0, 1]})\r\n        >>> df.min_max_scale(feature_range=(0, 100))\r\n               a      b\r\n        0    0.0    0.0\r\n        1  100.0  100.0\r\n        >>> df.min_max_scale(feature_range=(0, 100), jointly=True)\r\n               a     b\r\n        0   50.0   0.0\r\n        1  100.0  50.0\r\n\r\n    Example: Apply min-max to the selected columns.\r\n\r\n        >>> import pandas as pd\r\n        >>> import janitor\r\n        >>> df = pd.DataFrame({'a':[1, 2], 'b':[0, 1], 'c': [1, 0]})\r\n        >>> df.min_max_scale(\r\n        ...     feature_range=(0, 100),\r\n        ...     column_name=[\"a\", \"c\"],\r\n        ... )\r\n               a  b      c\r\n        0    0.0  0  100.0\r\n        1  100.0  1    0.0\r\n        >>> df.min_max_scale(\r\n        ...     feature_range=(0, 100),\r\n        ...     column_name=[\"a\", \"c\"],\r\n        ...     jointly=True,\r\n        ... )\r\n               a  b     c\r\n        0   50.0  0  50.0\r\n        1  100.0  1   0.0\r\n        >>> df.min_max_scale(feature_range=(0, 100), column_name='a')\r\n               a  b  c\r\n        0    0.0  0  1\r\n        1  100.0  1  0\r\n\r\n    The aforementioned example might be applied to something like scaling the\r\n    isoelectric points of amino acids. While technically they range from\r\n    approx 3-10, we can also think of them on the pH scale which ranges from\r\n    1 to 14. Hence, 3 gets scaled not to 0 but approx. 0.15 instead, while 10\r\n    gets scaled to approx. 0.69 instead.\r\n\r\n    :param df: A pandas DataFrame.\r\n    :param feature_range: (optional) Desired range of transformed data.\r\n    :param column_name: (optional) The column on which to perform scaling.\r\n    :param jointly: (bool) Scale the entire data if Ture.\r\n    :returns: A pandas DataFrame with scaled data.\r\n    :raises ValueError: if `feature_range` isn't tuple type.\r\n    :raises ValueError: if the length of `feature_range` isn't equal to two.\r\n    :raises ValueError: if the element of `feature_range` isn't number type.\r\n    :raises ValueError: if `feature_range[1]` <= `feature_range[0]`.\r\n\r\n    Changed in version 0.24.0: Deleted \"old_min\", \"old_max\", \"new_min\", and\r\n    \"new_max\" options.\r\n    Changed in version 0.24.0: Added \"feature_range\", and \"jointly\" options.\r\n    \"\"\"\r\n\r\n    if not (\r\n        isinstance(feature_range, (tuple, list))\r\n        and len(feature_range) == 2\r\n        and all((isinstance(i, (int, float))) for i in feature_range)\r\n        and feature_range[1] > feature_range[0]\r\n    ):\r\n        raise ValueError(\r\n            \"`feature_range` should be a range type contains number element, \"\r\n            \"the first element must be greater than the second one\"\r\n        )\r\n\r\n    if column_name is not None:\r\n        df = df.copy()  # Avoid to change the original DataFrame.\r\n\r\n        old_feature_range = df[column_name].pipe(min_max_value, jointly)\r\n        df[column_name] = df[column_name].pipe(\r\n            apply_min_max,\r\n            *old_feature_range,\r\n            *feature_range,\r\n        )\r\n    else:\r\n        old_feature_range = df.pipe(min_max_value, jointly)\r\n        df = df.pipe(\r\n            apply_min_max,\r\n            *old_feature_range,\r\n            *feature_range,\r\n        )\r\n\r\n    return df\r\n\r\n\r\ndef min_max_value(df: pd.DataFrame, jointly: bool) -> tuple:\r\n    \"\"\"\r\n    Return the minimum and maximum of DataFrame.\r\n\r\n    Use the `jointly` flag to control returning entire data or each column.\r\n\r\n    .. # noqa: DAR101\r\n    .. # noqa: DAR201\r\n    \"\"\"\r\n\r\n    if jointly:\r\n        mmin = df.min().min()\r\n        mmax = df.max().max()\r\n    else:\r\n        mmin = df.min()\r\n        mmax = df.max()\r\n\r\n    return mmin, mmax\r\n\r\n\r\ndef apply_min_max(\r\n    df: pd.DataFrame,\r\n    old_min: int | float | pd.Series,\r\n    old_max: int | float | pd.Series,\r\n    new_min: int | float | pd.Series,\r\n    new_max: int | float | pd.Series,\r\n) -> pd.DataFrame:\r\n    \"\"\"\r\n    Apply minimax scaler to DataFrame.\r\n\r\n    Notes\r\n    -----\r\n    - Inputting minimum and maximum type\r\n        - int or float : It will apply minimax to the entire DataFrame.\r\n        - Series : It will apply minimax to each column.\r\n\r\n    .. # noqa: DAR101\r\n    .. # noqa: DAR201\r\n    \"\"\"\r\n\r\n    old_range = old_max - old_min\r\n    new_range = new_max - new_min\r\n\r\n    return (df - old_min) * new_range / old_range + new_min\r\n", "path": "janitor/functions/min_max_scale.py"}], "after_files": [{"content": "from __future__ import annotations\r\n\r\nimport pandas_flavor as pf\r\nimport pandas as pd\r\n\r\nfrom janitor.utils import deprecated_alias\r\nfrom janitor.utils import deprecated_kwargs\r\n\r\n\r\n@pf.register_dataframe_method\r\n@deprecated_kwargs(\r\n    \"old_min\",\r\n    \"old_max\",\r\n    \"new_min\",\r\n    \"new_max\",\r\n    message=(\r\n        \"The keyword argument {argument!r} of {func_name!r} is deprecated. \"\r\n        \"Please use 'feature_range' instead.\"\r\n    ),\r\n)\r\n@deprecated_alias(col_name=\"column_name\")\r\ndef min_max_scale(\r\n    df: pd.DataFrame,\r\n    feature_range: tuple[int | float, int | float] = (0, 1),\r\n    column_name: str | int | list[str | int] | pd.Index = None,\r\n    jointly: bool = False,\r\n) -> pd.DataFrame:\r\n    \"\"\"\r\n    Scales DataFrame to between a minimum and maximum value.\r\n\r\n    One can optionally set a new target **minimum** and **maximum** value\r\n    using the `feature_range` keyword argument.\r\n\r\n    If `column_name` is specified, then only that column(s) of data is scaled.\r\n    Otherwise, the entire dataframe is scaled.\r\n    If `jointly` is `True`, the `column_names` provided entire dataframe will\r\n    be regnozied as the one to jointly scale. Otherwise, each column of data\r\n    will be scaled separately.\r\n\r\n    Example: Basic usage.\r\n\r\n        >>> import pandas as pd\r\n        >>> import janitor\r\n        >>> df = pd.DataFrame({'a':[1, 2], 'b':[0, 1]})\r\n        >>> df.min_max_scale()\r\n             a    b\r\n        0  0.0  0.0\r\n        1  1.0  1.0\r\n        >>> df.min_max_scale(jointly=True)\r\n             a    b\r\n        0  0.5  0.0\r\n        1  1.0  0.5\r\n\r\n    Example: Setting custom minimum and maximum.\r\n\r\n        >>> import pandas as pd\r\n        >>> import janitor\r\n        >>> df = pd.DataFrame({'a':[1, 2], 'b':[0, 1]})\r\n        >>> df.min_max_scale(feature_range=(0, 100))\r\n               a      b\r\n        0    0.0    0.0\r\n        1  100.0  100.0\r\n        >>> df.min_max_scale(feature_range=(0, 100), jointly=True)\r\n               a     b\r\n        0   50.0   0.0\r\n        1  100.0  50.0\r\n\r\n    Example: Apply min-max to the selected columns.\r\n\r\n        >>> import pandas as pd\r\n        >>> import janitor\r\n        >>> df = pd.DataFrame({'a':[1, 2], 'b':[0, 1], 'c': [1, 0]})\r\n        >>> df.min_max_scale(\r\n        ...     feature_range=(0, 100),\r\n        ...     column_name=[\"a\", \"c\"],\r\n        ... )\r\n               a  b      c\r\n        0    0.0  0  100.0\r\n        1  100.0  1    0.0\r\n        >>> df.min_max_scale(\r\n        ...     feature_range=(0, 100),\r\n        ...     column_name=[\"a\", \"c\"],\r\n        ...     jointly=True,\r\n        ... )\r\n               a  b     c\r\n        0   50.0  0  50.0\r\n        1  100.0  1   0.0\r\n        >>> df.min_max_scale(feature_range=(0, 100), column_name='a')\r\n               a  b  c\r\n        0    0.0  0  1\r\n        1  100.0  1  0\r\n\r\n    The aforementioned example might be applied to something like scaling the\r\n    isoelectric points of amino acids. While technically they range from\r\n    approx 3-10, we can also think of them on the pH scale which ranges from\r\n    1 to 14. Hence, 3 gets scaled not to 0 but approx. 0.15 instead, while 10\r\n    gets scaled to approx. 0.69 instead.\r\n\r\n    !!! summary \"Version Changed\"\r\n\r\n        - 0.24.0\r\n            - Deleted `old_min`, `old_max`, `new_min`, and `new_max` options.\r\n            - Added `feature_range`, and `jointly` options.\r\n\r\n    :param df: A pandas DataFrame.\r\n    :param feature_range: (optional) Desired range of transformed data.\r\n    :param column_name: (optional) The column on which to perform scaling.\r\n    :param jointly: (bool) Scale the entire data if Ture.\r\n    :returns: A pandas DataFrame with scaled data.\r\n    :raises ValueError: if `feature_range` isn't tuple type.\r\n    :raises ValueError: if the length of `feature_range` isn't equal to two.\r\n    :raises ValueError: if the element of `feature_range` isn't number type.\r\n    :raises ValueError: if `feature_range[1]` <= `feature_range[0]`.\r\n    \"\"\"\r\n\r\n    if not (\r\n        isinstance(feature_range, (tuple, list))\r\n        and len(feature_range) == 2\r\n        and all((isinstance(i, (int, float))) for i in feature_range)\r\n        and feature_range[1] > feature_range[0]\r\n    ):\r\n        raise ValueError(\r\n            \"`feature_range` should be a range type contains number element, \"\r\n            \"the first element must be greater than the second one\"\r\n        )\r\n\r\n    if column_name is not None:\r\n        df = df.copy()  # Avoid to change the original DataFrame.\r\n\r\n        old_feature_range = df[column_name].pipe(_min_max_value, jointly)\r\n        df[column_name] = df[column_name].pipe(\r\n            _apply_min_max,\r\n            *old_feature_range,\r\n            *feature_range,\r\n        )\r\n    else:\r\n        old_feature_range = df.pipe(_min_max_value, jointly)\r\n        df = df.pipe(\r\n            _apply_min_max,\r\n            *old_feature_range,\r\n            *feature_range,\r\n        )\r\n\r\n    return df\r\n\r\n\r\ndef _min_max_value(df: pd.DataFrame, jointly: bool) -> tuple:\r\n    \"\"\"\r\n    Return the minimum and maximum of DataFrame.\r\n\r\n    Use the `jointly` flag to control returning entire data or each column.\r\n\r\n    .. # noqa: DAR101\r\n    .. # noqa: DAR201\r\n    \"\"\"\r\n\r\n    if jointly:\r\n        mmin = df.min().min()\r\n        mmax = df.max().max()\r\n    else:\r\n        mmin = df.min()\r\n        mmax = df.max()\r\n\r\n    return mmin, mmax\r\n\r\n\r\ndef _apply_min_max(\r\n    df: pd.DataFrame,\r\n    old_min: int | float | pd.Series,\r\n    old_max: int | float | pd.Series,\r\n    new_min: int | float | pd.Series,\r\n    new_max: int | float | pd.Series,\r\n) -> pd.DataFrame:\r\n    \"\"\"\r\n    Apply minimax scaler to DataFrame.\r\n\r\n    Notes\r\n    -----\r\n    - Inputting minimum and maximum type\r\n        - int or float : It will apply minimax to the entire DataFrame.\r\n        - Series : It will apply minimax to each column.\r\n\r\n    .. # noqa: DAR101\r\n    .. # noqa: DAR201\r\n    \"\"\"\r\n\r\n    old_range = old_max - old_min\r\n    new_range = new_max - new_min\r\n\r\n    return (df - old_min) * new_range / old_range + new_min\r\n", "path": "janitor/functions/min_max_scale.py"}]}