Repository: pandas-dev/pandas
Pull request #57787 (2024-03-08 23:48:13)

PERF: Categorical(range).categories returns RangeIndex instead of Index


Patch:
diff --git a/ci/code_checks.sh b/ci/code_checks.sh
index d37f4bcf44..8227047839 100755
--- a/ci/code_checks.sh
+++ b/ci/code_checks.sh
@@ -499,9 +499,6 @@ if [[ -z "$CHECK" || "$CHECK" == "docstrings" ]]; then
 
     MSG='Partially validate docstrings (PR07)' ;  echo $MSG
     $BASE_DIR/scripts/validate_docstrings.py --format=actions --errors=PR07 --ignore_functions \
-        pandas.DataFrame.get\
-        pandas.DataFrame.rolling\
-        pandas.DataFrame.to_hdf\
         pandas.DatetimeIndex.indexer_between_time\
         pandas.DatetimeIndex.mean\
         pandas.HDFStore.append\
@@ -623,25 +620,12 @@ if [[ -z "$CHECK" || "$CHECK" == "docstrings" ]]; then
         pandas.DataFrame.mean\
         pandas.DataFrame.median\
         pandas.DataFrame.min\
-        pandas.DataFrame.nsmallest\
-        pandas.DataFrame.nunique\
-        pandas.DataFrame.pipe\
-        pandas.DataFrame.plot.box\
-        pandas.DataFrame.plot.density\
-        pandas.DataFrame.plot.kde\
-        pandas.DataFrame.plot.scatter\
-        pandas.DataFrame.pop\
         pandas.DataFrame.prod\
         pandas.DataFrame.product\
-        pandas.DataFrame.reindex\
-        pandas.DataFrame.reorder_levels\
         pandas.DataFrame.sem\
         pandas.DataFrame.skew\
         pandas.DataFrame.std\
         pandas.DataFrame.sum\
-        pandas.DataFrame.swapaxes\
-        pandas.DataFrame.to_numpy\
-        pandas.DataFrame.to_orc\
         pandas.DataFrame.to_parquet\
         pandas.DataFrame.unstack\
         pandas.DataFrame.value_counts\
@@ -865,25 +849,18 @@ if [[ -z "$CHECK" || "$CHECK" == "docstrings" ]]; then
         pandas.read_sas\
         pandas.read_spss\
         pandas.read_stata\
-        pandas.set_eng_float_format\
-        pandas.timedelta_range\
-        pandas.util.hash_pandas_object # There should be no backslash in the final line, please keep this comment in the last ignored function
+        pandas.set_eng_float_format # There should be no backslash in the final line, please keep this comment in the last ignored function
     RET=$(($RET + $?)) ; echo $MSG "DONE"
 
     MSG='Partially validate docstrings (SA01)' ;  echo $MSG
     $BASE_DIR/scripts/validate_docstrings.py --format=actions --errors=SA01 --ignore_functions \
-        pandas.BooleanDtype\
         pandas.Categorical.__array__\
-        pandas.Categorical.as_ordered\
-        pandas.Categorical.as_unordered\
         pandas.Categorical.codes\
         pandas.Categorical.dtype\
         pandas.Categorical.from_codes\
         pandas.Categorical.ordered\
         pandas.CategoricalDtype.categories\
         pandas.CategoricalDtype.ordered\
-        pandas.CategoricalIndex.as_ordered\
-        pandas.CategoricalIndex.as_unordered\
         pandas.CategoricalIndex.codes\
         pandas.CategoricalIndex.ordered\
         pandas.DataFrame.__dataframe__\
@@ -1080,8 +1057,6 @@ if [[ -z "$CHECK" || "$CHECK" == "docstrings" ]]; then
         pandas.Series.backfill\
         pandas.Series.bfill\
         pandas.Series.cat\
-        pandas.Series.cat.as_ordered\
-        pandas.Series.cat.as_unordered\
         pandas.Series.cat.codes\
         pandas.Series.cat.ordered\
         pandas.Series.copy\
@@ -1184,7 +1159,6 @@ if [[ -z "$CHECK" || "$CHECK" == "docstrings" ]]; then
         pandas.Series.update\
         pandas.Series.var\
         pandas.SparseDtype\
-        pandas.StringDtype\
         pandas.Timedelta\
         pandas.Timedelta.as_unit\
         pandas.Timedelta.asm8\
diff --git a/doc/source/whatsnew/v3.0.0.rst b/doc/source/whatsnew/v3.0.0.rst
index ca2ca07ff2..d3bc98526b 100644
--- a/doc/source/whatsnew/v3.0.0.rst
+++ b/doc/source/whatsnew/v3.0.0.rst
@@ -256,6 +256,7 @@ Removal of prior version deprecations/changes
 
 Performance improvements
 ~~~~~~~~~~~~~~~~~~~~~~~~
+- :attr:`Categorical.categories` returns a :class:`RangeIndex` columns instead of an :class:`Index` if the constructed ``values`` was a ``range``. (:issue:`57787`)
 - :func:`concat` returns a :class:`RangeIndex` level in the :class:`MultiIndex` result when ``keys`` is a ``range`` or :class:`RangeIndex` (:issue:`57542`)
 - :meth:`RangeIndex.append` returns a :class:`RangeIndex` instead of a :class:`Index` when appending values that could continue the :class:`RangeIndex` (:issue:`57467`)
 - :meth:`Series.str.extract` returns a :class:`RangeIndex` columns instead of an :class:`Index` column when possible (:issue:`57542`)
@@ -280,6 +281,7 @@ Performance improvements
 
 Bug fixes
 ~~~~~~~~~
+- Fixed bug in :class:`SparseDtype` for equal comparison with na fill value. (:issue:`54770`)
 - Fixed bug in :meth:`DataFrame.join` inconsistently setting result index name (:issue:`55815`)
 - Fixed bug in :meth:`DataFrame.to_string` that raised ``StopIteration`` with nested DataFrames. (:issue:`16098`)
 - Fixed bug in :meth:`DataFrame.update` bool dtype being converted to object (:issue:`55509`)
diff --git a/doc/sphinxext/announce.py b/doc/sphinxext/announce.py
index 89361eb756..66e999e251 100755
--- a/doc/sphinxext/announce.py
+++ b/doc/sphinxext/announce.py
@@ -32,6 +32,7 @@ From the bash command line with $GITHUB token.
     $ ./scripts/announce.py $GITHUB v1.11.0..v1.11.1 > announce.rst
 
 """
+
 import codecs
 import os
 import re
diff --git a/doc/sphinxext/contributors.py b/doc/sphinxext/contributors.py
index c2b21e40ca..06f205b5cc 100644
--- a/doc/sphinxext/contributors.py
+++ b/doc/sphinxext/contributors.py
@@ -14,6 +14,7 @@ use::
 While the v0.23.1 tag does not exist, that will use the HEAD of the
 branch as the end of the revision range.
 """
+
 from announce import build_components
 from docutils import nodes
 from docutils.parsers.rst import Directive
diff --git a/pandas/compat/pickle_compat.py b/pandas/compat/pickle_compat.py
index 26c44c2613..28985a1380 100644
--- a/pandas/compat/pickle_compat.py
+++ b/pandas/compat/pickle_compat.py
@@ -64,7 +64,7 @@ _class_locations_map = {
 # our Unpickler sub-class to override methods and some dispatcher
 # functions for compat and uses a non-public class of the pickle module.
 class Unpickler(pickle._Unpickler):
-    def find_class(self, module, name):
+    def find_class(self, module: str, name: str) -> Any:
         key = (module, name)
         module, name = _class_locations_map.get(key, key)
         return super().find_class(module, name)
diff --git a/pandas/core/array_algos/datetimelike_accumulations.py b/pandas/core/array_algos/datetimelike_accumulations.py
index 8737381890..55942f2c93 100644
--- a/pandas/core/array_algos/datetimelike_accumulations.py
+++ b/pandas/core/array_algos/datetimelike_accumulations.py
@@ -18,7 +18,7 @@ def _cum_func(
     values: np.ndarray,
     *,
     skipna: bool = True,
-):
+) -> np.ndarray:
     """
     Accumulations for 1D datetimelike arrays.
 
@@ -61,9 +61,9 @@ def cumsum(values: np.ndarray, *, skipna: bool = True) -> np.ndarray:
     return _cum_func(np.cumsum, values, skipna=skipna)
 
 
-def cummin(values: np.ndarray, *, skipna: bool = True):
+def cummin(values: np.ndarray, *, skipna: bool = True) -> np.ndarray:
     return _cum_func(np.minimum.accumulate, values, skipna=skipna)
 
 
-def cummax(values: np.ndarray, *, skipna: bool = True):
+def cummax(values: np.ndarray, *, skipna: bool = True) -> np.ndarray:
     return _cum_func(np.maximum.accumulate, values, skipna=skipna)
diff --git a/pandas/core/array_algos/masked_accumulations.py b/pandas/core/array_algos/masked_accumulations.py
index fb4fbd5377..b31d32a606 100644
--- a/pandas/core/array_algos/masked_accumulations.py
+++ b/pandas/core/array_algos/masked_accumulations.py
@@ -22,7 +22,7 @@ def _cum_func(
     mask: npt.NDArray[np.bool_],
     *,
     skipna: bool = True,
-):
+) -> tuple[np.ndarray, npt.NDArray[np.bool_]]:
     """
     Accumulations for 1D masked array.
 
@@ -74,17 +74,25 @@ def _cum_func(
     return values, mask
 
 
-def cumsum(values: np.ndarray, mask: npt.NDArray[np.bool_], *, skipna: bool = True):
+def cumsum(
+    values: np.ndarray, mask: npt.NDArray[np.bool_], *, skipna: bool = True
+) -> tuple[np.ndarray, npt.NDArray[np.bool_]]:
     return _cum_func(np.cumsum, values, mask, skipna=skipna)
 
 
-def cumprod(values: np.ndarray, mask: npt.NDArray[np.bool_], *, skipna: bool = True):
+def cumprod(
+    values: np.ndarray, mask: npt.NDArray[np.bool_], *, skipna: bool = True
+) -> tuple[np.ndarray, npt.NDArray[np.bool_]]:
     return _cum_func(np.cumprod, values, mask, skipna=skipna)
 
 
-def cummin(values: np.ndarray, mask: npt.NDArray[np.bool_], *, skipna: bool = True):
+def cummin(
+    values: np.ndarray, mask: npt.NDArray[np.bool_], *, skipna: bool = True
+) -> tuple[np.ndarray, npt.NDArray[np.bool_]]:
     return _cum_func(np.minimum.accumulate, values, mask, skipna=skipna)
 
 
-def cummax(values: np.ndarray, mask: npt.NDArray[np.bool_], *, skipna: bool = True):
+def cummax(
+    values: np.ndarray, mask: npt.NDArray[np.bool_], *, skipna: bool = True
+) -> tuple[np.ndarray, npt.NDArray[np.bool_]]:
     return _cum_func(np.maximum.accumulate, values, mask, skipna=skipna)
diff --git a/pandas/core/arrays/boolean.py b/pandas/core/arrays/boolean.py
index e347281a19..813b10eef5 100644
--- a/pandas/core/arrays/boolean.py
+++ b/pandas/core/arrays/boolean.py
@@ -56,6 +56,10 @@ class BooleanDtype(BaseMaskedDtype):
     -------
     None
 
+    See Also
+    --------
+    StringDtype : Extension dtype for string data.
+
     Examples
     --------
     >>> pd.BooleanDtype()
diff --git a/pandas/core/arrays/categorical.py b/pandas/core/arrays/categorical.py
index af8dc08c1e..60529c1c22 100644
--- a/pandas/core/arrays/categorical.py
+++ b/pandas/core/arrays/categorical.py
@@ -431,6 +431,10 @@ class Categorical(NDArrayBackedExtensionArray, PandasObject, ObjectStringArrayMi
         if isinstance(vdtype, CategoricalDtype):
             if dtype.categories is None:
                 dtype = CategoricalDtype(values.categories, dtype.ordered)
+        elif isinstance(values, range):
+            from pandas.core.indexes.range import RangeIndex
+
+            values = RangeIndex(values)
         elif not isinstance(values, (ABCIndex, ABCSeries, ExtensionArray)):
             values = com.convert_to_list_like(values)
             if isinstance(values, list) and len(values) == 0:
@@ -988,6 +992,10 @@ class Categorical(NDArrayBackedExtensionArray, PandasObject, ObjectStringArrayMi
         Categorical
             Ordered Categorical.
 
+        See Also
+        --------
+        as_unordered : Set the Categorical to be unordered.
+
         Examples
         --------
         For :class:`pandas.Series`:
@@ -1019,6 +1027,10 @@ class Categorical(NDArrayBackedExtensionArray, PandasObject, ObjectStringArrayMi
         Categorical
             Unordered Categorical.
 
+        See Also
+        --------
+        as_ordered : Set the Categorical to be ordered.
+
         Examples
         --------
         For :class:`pandas.Series`:
diff --git a/pandas/core/arrays/string_.py b/pandas/core/arrays/string_.py
index 06c5430318..291cc2e62b 100644
--- a/pandas/core/arrays/string_.py
+++ b/pandas/core/arrays/string_.py
@@ -92,6 +92,10 @@ class StringDtype(StorageExtensionDtype):
     -------
     None
 
+    See Also
+    --------
+    BooleanDtype : Extension dtype for boolean data.
+
     Examples
     --------
     >>> pd.StringDtype()
diff --git a/pandas/core/config_init.py b/pandas/core/config_init.py
index d9a8b4dfd9..1a5d0842d6 100644
--- a/pandas/core/config_init.py
+++ b/pandas/core/config_init.py
@@ -37,7 +37,7 @@ use_bottleneck_doc = """
 """
 
 
-def use_bottleneck_cb(key) -> None:
+def use_bottleneck_cb(key: str) -> None:
     from pandas.core import nanops
 
     nanops.set_use_bottleneck(cf.get_option(key))
@@ -51,7 +51,7 @@ use_numexpr_doc = """
 """
 
 
-def use_numexpr_cb(key) -> None:
+def use_numexpr_cb(key: str) -> None:
     from pandas.core.computation import expressions
 
     expressions.set_use_numexpr(cf.get_option(key))
@@ -65,7 +65,7 @@ use_numba_doc = """
 """
 
 
-def use_numba_cb(key) -> None:
+def use_numba_cb(key: str) -> None:
     from pandas.core.util import numba_
 
     numba_.set_use_numba(cf.get_option(key))
@@ -287,7 +287,7 @@ pc_memory_usage_doc = """
 """
 
 
-def table_schema_cb(key) -> None:
+def table_schema_cb(key: str) -> None:
     from pandas.io.formats.printing import enable_data_resource_formatter
 
     enable_data_resource_formatter(cf.get_option(key))
@@ -612,7 +612,7 @@ plotting_backend_doc = """
 """
 
 
-def register_plotting_backend_cb(key) -> None:
+def register_plotting_backend_cb(key: str | None) -> None:
     if key == "matplotlib":
         # We defer matplotlib validation, since it's the default
         return
@@ -626,7 +626,7 @@ with cf.config_prefix("plotting"):
         "backend",
         defval="matplotlib",
         doc=plotting_backend_doc,
-        validator=register_plotting_backend_cb,
+        validator=register_plotting_backend_cb,  # type: ignore[arg-type]
     )
 
 
@@ -638,7 +638,7 @@ register_converter_doc = """
 """
 
 
-def register_converter_cb(key) -> None:
+def register_converter_cb(key: str) -> None:
     from pandas.plotting import (
         deregister_matplotlib_converters,
         register_matplotlib_converters,
diff --git a/pandas/core/dtypes/concat.py b/pandas/core/dtypes/concat.py
index f702d5a60e..3a34481ab3 100644
--- a/pandas/core/dtypes/concat.py
+++ b/pandas/core/dtypes/concat.py
@@ -42,7 +42,7 @@ if TYPE_CHECKING:
     )
 
 
-def _is_nonempty(x, axis) -> bool:
+def _is_nonempty(x: ArrayLike, axis: AxisInt) -> bool:
     # filter empty arrays
     # 1-d dtypes always are included here
     if x.ndim <= axis:
diff --git a/pandas/core/dtypes/dtypes.py b/pandas/core/dtypes/dtypes.py
index 2bb2556c88..f94d32a3b8 100644
--- a/pandas/core/dtypes/dtypes.py
+++ b/pandas/core/dtypes/dtypes.py
@@ -1705,17 +1705,15 @@ class SparseDtype(ExtensionDtype):
 
         if isinstance(other, type(self)):
             subtype = self.subtype == other.subtype
-            if self._is_na_fill_value:
+            if self._is_na_fill_value or other._is_na_fill_value:
                 # this case is complicated by two things:
                 # SparseDtype(float, float(nan)) == SparseDtype(float, np.nan)
                 # SparseDtype(float, np.nan)     != SparseDtype(float, pd.NaT)
                 # i.e. we want to treat any floating-point NaN as equal, but
                 # not a floating-point NaN and a datetime NaT.
-                fill_value = (
-                    other._is_na_fill_value
-                    and isinstance(self.fill_value, type(other.fill_value))
-                    or isinstance(other.fill_value, type(self.fill_value))
-                )
+                fill_value = isinstance(
+                    self.fill_value, type(other.fill_value)
+                ) or isinstance(other.fill_value, type(self.fill_value))
             else:
                 with warnings.catch_warnings():
                     # Ignore spurious numpy warning
diff --git a/pandas/core/frame.py b/pandas/core/frame.py
index 25501ff245..2a6daf4bab 100644
--- a/pandas/core/frame.py
+++ b/pandas/core/frame.py
@@ -1883,6 +1883,7 @@ class DataFrame(NDFrame, OpsMixin):
         Returns
         -------
         numpy.ndarray
+            The NumPy array representing the values in the DataFrame.
 
         See Also
         --------
@@ -2930,7 +2931,7 @@ class DataFrame(NDFrame, OpsMixin):
         engine_kwargs: dict[str, Any] | None = None,
     ) -> bytes | None:
         """
-        Write a DataFrame to the ORC format.
+        Write a DataFrame to the Optimized Row Columnar (ORC) format.
 
         .. versionadded:: 1.5.0
 
@@ -2957,7 +2958,8 @@ class DataFrame(NDFrame, OpsMixin):
 
         Returns
         -------
-        bytes if no path argument is provided else None
+        bytes if no ``path`` argument is provided else None
+            Bytes object with DataFrame data if ``path`` is not specified else None.
 
         Raises
         ------
@@ -2977,6 +2979,8 @@ class DataFrame(NDFrame, OpsMixin):
 
         Notes
         -----
+        * Find more information on ORC
+          `here <https://en.wikipedia.org/wiki/Apache_ORC>`__.
         * Before using this function you should read the :ref:`user guide about
           ORC <io.orc>` and :ref:`install optional dependencies <install.warn_orc>`.
         * This function requires `pyarrow <https://arrow.apache.org/docs/python/>`_
@@ -5473,7 +5477,7 @@ class DataFrame(NDFrame, OpsMixin):
 
     def pop(self, item: Hashable) -> Series:
         """
-        Return item and drop from frame. Raise KeyError if not found.
+        Return item and drop it from DataFrame. Raise KeyError if not found.
 
         Parameters
         ----------
@@ -5483,6 +5487,7 @@ class DataFrame(NDFrame, OpsMixin):
         Returns
         -------
         Series
+            Series representing the item that is dropped.
 
         Examples
         --------
@@ -7432,6 +7437,7 @@ class DataFrame(NDFrame, OpsMixin):
         Returns
         -------
         DataFrame
+            DataFrame with the first `n` rows ordered by `columns` in ascending order.
 
         See Also
         --------
@@ -7611,7 +7617,9 @@ class DataFrame(NDFrame, OpsMixin):
 
     def reorder_levels(self, order: Sequence[int | str], axis: Axis = 0) -> DataFrame:
         """
-        Rearrange index levels using input order. May not drop or duplicate levels.
+        Rearrange index or column levels using input ``order``.
+
+        May not drop or duplicate levels.
 
         Parameters
         ----------
@@ -7624,6 +7632,7 @@ class DataFrame(NDFrame, OpsMixin):
         Returns
         -------
         DataFrame
+            DataFrame with indices or columns with reordered levels.
 
         Examples
         --------
@@ -11898,6 +11907,7 @@ class DataFrame(NDFrame, OpsMixin):
         Returns
         -------
         Series
+            Series with counts of unique values per row or column, depending on `axis`.
 
         See Also
         --------
diff --git a/pandas/core/generic.py b/pandas/core/generic.py
index e65764b564..a7a69a6b83 100644
--- a/pandas/core/generic.py
+++ b/pandas/core/generic.py
@@ -583,7 +583,7 @@ class NDFrame(PandasObject, indexing.IndexingMixin):
     @final
     def _get_cleaned_column_resolvers(self) -> dict[Hashable, Series]:
         """
-        Return the special character free column resolvers of a dataframe.
+        Return the special character free column resolvers of a DataFrame.
 
         Column names with special characters are 'cleaned up' so that they can
         be referred to by backtick quoting.
@@ -2646,6 +2646,7 @@ class NDFrame(PandasObject, indexing.IndexingMixin):
             See the errors argument for :func:`open` for a full list
             of options.
         encoding : str, default "UTF-8"
+            Set character encoding.
 
         See Also
         --------
@@ -5077,7 +5078,8 @@ class NDFrame(PandasObject, indexing.IndexingMixin):
 
         Returns
         -------
-        {klass} with changed index.
+        {klass}
+            {klass} with changed index.
 
         See Also
         --------
@@ -5095,7 +5097,7 @@ class NDFrame(PandasObject, indexing.IndexingMixin):
         We *highly* recommend using keyword arguments to clarify your
         intent.
 
-        Create a dataframe with some fictional data.
+        Create a DataFrame with some fictional data.
 
         >>> index = ["Firefox", "Chrome", "Safari", "IE10", "Konqueror"]
         >>> columns = ["http_status", "response_time"]
@@ -5112,9 +5114,9 @@ class NDFrame(PandasObject, indexing.IndexingMixin):
         IE10               404           0.08
         Konqueror          301           1.00
 
-        Create a new index and reindex the dataframe. By default
+        Create a new index and reindex the DataFrame. By default
         values in the new index that do not have corresponding
-        records in the dataframe are assigned ``NaN``.
+        records in the DataFrame are assigned ``NaN``.
 
         >>> new_index = ["Safari", "Iceweasel", "Comodo Dragon", "IE10", "Chrome"]
         >>> df.reindex(new_index)
@@ -5167,7 +5169,7 @@ class NDFrame(PandasObject, indexing.IndexingMixin):
         Konqueror          301         NaN
 
         To further illustrate the filling functionality in
-        ``reindex``, we will create a dataframe with a
+        ``reindex``, we will create a DataFrame with a
         monotonically increasing index (for example, a sequence
         of dates).
 
@@ -5184,7 +5186,7 @@ class NDFrame(PandasObject, indexing.IndexingMixin):
         2010-01-05    89.0
         2010-01-06    88.0
 
-        Suppose we decide to expand the dataframe to cover a wider
+        Suppose we decide to expand the DataFrame to cover a wider
         date range.
 
         >>> date_index2 = pd.date_range("12/29/2009", periods=10, freq="D")
@@ -5222,12 +5224,12 @@ class NDFrame(PandasObject, indexing.IndexingMixin):
         2010-01-06    88.0
         2010-01-07     NaN
 
-        Please note that the ``NaN`` value present in the original dataframe
+        Please note that the ``NaN`` value present in the original DataFrame
         (at index value 2010-01-03) will not be filled by any of the
         value propagation schemes. This is because filling while reindexing
-        does not look at dataframe values, but only compares the original and
+        does not look at DataFrame values, but only compares the original and
         desired indexes. If you do want to fill in the ``NaN`` values present
-        in the original dataframe, use the ``fillna()`` method.
+        in the original DataFrame, use the ``fillna()`` method.
 
         See the :ref:`user guide <basics.reindexing>` for more.
         """
@@ -5846,7 +5848,8 @@ class NDFrame(PandasObject, indexing.IndexingMixin):
 
         Returns
         -------
-        the return type of ``func``.
+        The return type of ``func``.
+            The result of applying ``func`` to the Series or DataFrame.
 
         See Also
         --------
@@ -5862,7 +5865,7 @@ class NDFrame(PandasObject, indexing.IndexingMixin):
 
         Examples
         --------
-        Constructing a income DataFrame from a dictionary.
+        Constructing an income DataFrame from a dictionary.
 
         >>> data = [[8000, 1000], [9500, np.nan], [5000, 2000]]
         >>> df = pd.DataFrame(data, columns=["Salary", "Others"])
@@ -8372,7 +8375,7 @@ class NDFrame(PandasObject, indexing.IndexingMixin):
         See Also
         --------
         Series.clip : Trim values at input threshold in series.
-        DataFrame.clip : Trim values at input threshold in dataframe.
+        DataFrame.clip : Trim values at input threshold in DataFrame.
         numpy.clip : Clip (limit) the values in an array.
 
         Examples
@@ -10908,7 +10911,7 @@ class NDFrame(PandasObject, indexing.IndexingMixin):
         among those with the highest count.
 
         For mixed data types provided via a ``DataFrame``, the default is to
-        return only an analysis of numeric columns. If the dataframe consists
+        return only an analysis of numeric columns. If the DataFrame consists
         only of object and categorical data without any numeric columns, the
         default is to return an analysis of both the object and categorical
         columns. If ``include='all'`` is provided as an option, the result
@@ -12051,7 +12054,7 @@ True
 
 **DataFrames**
 
-Create a dataframe from a dictionary.
+Create a DataFrame from a dictionary.
 
 >>> df = pd.DataFrame({'col1': [True, True], 'col2': [True, False]})
 >>> df
diff --git a/pandas/core/groupby/categorical.py b/pandas/core/groupby/categorical.py
index 6ab98cf4fe..037ca81477 100644
--- a/pandas/core/groupby/categorical.py
+++ b/pandas/core/groupby/categorical.py
@@ -50,7 +50,7 @@ def recode_for_groupby(
         # In cases with c.ordered, this is equivalent to
         #  return c.remove_unused_categories(), c
 
-        unique_codes = unique1d(c.codes)
+        unique_codes = unique1d(c.codes)  # type: ignore[no-untyped-call]
 
         take_codes = unique_codes[unique_codes != -1]
         if sort:
@@ -74,7 +74,7 @@ def recode_for_groupby(
     # xref GH:46909: Re-ordering codes faster than using (set|add|reorder)_categories
     all_codes = np.arange(c.categories.nunique())
     # GH 38140: exclude nan from indexer for categories
-    unique_notnan_codes = unique1d(c.codes[c.codes != -1])
+    unique_notnan_codes = unique1d(c.codes[c.codes != -1])  # type: ignore[no-untyped-call]
     if sort:
         unique_notnan_codes = np.sort(unique_notnan_codes)
     if len(all_codes) > len(unique_notnan_codes):
diff --git a/pandas/core/indexes/timedeltas.py b/pandas/core/indexes/timedeltas.py
index 4a4b0ac144..6a2c04b0dd 100644
--- a/pandas/core/indexes/timedeltas.py
+++ b/pandas/core/indexes/timedeltas.py
@@ -304,6 +304,7 @@ def timedelta_range(
     Returns
     -------
     TimedeltaIndex
+        Fixed frequency, with day as the default.
 
     Notes
     -----
diff --git a/pandas/core/ops/mask_ops.py b/pandas/core/ops/mask_ops.py
index 427ae2fb87..86a93e5a3c 100644
--- a/pandas/core/ops/mask_ops.py
+++ b/pandas/core/ops/mask_ops.py
@@ -190,6 +190,6 @@ def kleene_and(
     return result, mask
 
 
-def raise_for_nan(value, method: str) -> None:
+def raise_for_nan(value: object, method: str) -> None:
     if lib.is_float(value) and np.isnan(value):
         raise ValueError(f"Cannot perform logical '{method}' with floating NaN")
diff --git a/pandas/core/reshape/pivot.py b/pandas/core/reshape/pivot.py
index 36edf61166..424af58958 100644
--- a/pandas/core/reshape/pivot.py
+++ b/pandas/core/reshape/pivot.py
@@ -904,13 +904,7 @@ def _build_names_mapper(
         a list of column names with duplicate names replaced by dummy names
 
     """
-
-    def get_duplicates(names):
-        seen: set = set()
-        return {name for name in names if name not in seen}
-
-    shared_names = set(rownames).intersection(set(colnames))
-    dup_names = get_duplicates(rownames) | get_duplicates(colnames) | shared_names
+    dup_names = set(rownames) | set(colnames)
 
     rownames_mapper = {
         f"row_{i}": name for i, name in enumerate(rownames) if name in dup_names
diff --git a/pandas/core/util/hashing.py b/pandas/core/util/hashing.py
index f7e9ff220e..3b9dd40a92 100644
--- a/pandas/core/util/hashing.py
+++ b/pandas/core/util/hashing.py
@@ -106,7 +106,8 @@ def hash_pandas_object(
 
     Returns
     -------
-    Series of uint64, same length as the object
+    Series of uint64
+        Same length as the object.
 
     Examples
     --------
diff --git a/pandas/core/window/rolling.py b/pandas/core/window/rolling.py
index 52eb8cf45d..07998cdbd4 100644
--- a/pandas/core/window/rolling.py
+++ b/pandas/core/window/rolling.py
@@ -925,13 +925,12 @@ class Window(BaseWindow):
         Default ``None`` (``'right'``).
 
     step : int, default None
-
-        .. versionadded:: 1.5.0
-
         Evaluate the window at every ``step`` result, equivalent to slicing as
         ``[::step]``. ``window`` must be an integer. Using a step argument other
         than None or 1 will produce a result with a different shape than the input.
 
+        .. versionadded:: 1.5.0
+
     method : str {'single', 'table'}, default 'single'
 
         .. versionadded:: 1.3.0
diff --git a/pandas/io/formats/console.py b/pandas/io/formats/console.py
index 99a790388f..d76593b41a 100644
--- a/pandas/io/formats/console.py
+++ b/pandas/io/formats/console.py
@@ -63,7 +63,7 @@ def in_interactive_session() -> bool:
     """
     from pandas import get_option
 
-    def check_main():
+    def check_main() -> bool:
         try:
             import __main__ as main
         except ModuleNotFoundError:
diff --git a/pandas/io/formats/css.py b/pandas/io/formats/css.py
index dc18ef2fcd..d3f4072b2f 100644
--- a/pandas/io/formats/css.py
+++ b/pandas/io/formats/css.py
@@ -36,7 +36,7 @@ def _side_expander(prop_fmt: str) -> Callable:
         function: Return to call when a 'border(-{side}): {value}' string is encountered
     """
 
-    def expand(self, prop, value: str) -> Generator[tuple[str, str], None, None]:
+    def expand(self, prop: str, value: str) -> Generator[tuple[str, str], None, None]:
         """
         Expand shorthand property into side-specific property (top, right, bottom, left)
 
@@ -81,7 +81,7 @@ def _border_expander(side: str = "") -> Callable:
     if side != "":
         side = f"-{side}"
 
-    def expand(self, prop, value: str) -> Generator[tuple[str, str], None, None]:
+    def expand(self, prop: str, value: str) -> Generator[tuple[str, str], None, None]:
         """
         Expand border into color, style, and width tuples
 
diff --git a/pandas/io/stata.py b/pandas/io/stata.py
index c3101683b9..9374b3c7af 100644
--- a/pandas/io/stata.py
+++ b/pandas/io/stata.py
@@ -1835,7 +1835,7 @@ the string values returned are correct."""
             fmtlist = []
             lbllist = []
             for col in columns:
-                i = data.columns.get_loc(col)
+                i = data.columns.get_loc(col)  # type: ignore[no-untyped-call]
                 dtyplist.append(self._dtyplist[i])
                 typlist.append(self._typlist[i])
                 fmtlist.append(self._fmtlist[i])
@@ -2155,7 +2155,7 @@ def _dtype_to_stata_type(dtype: np.dtype, column: Series) -> int:
 
 
 def _dtype_to_default_stata_fmt(
-    dtype, column: Series, dta_version: int = 114, force_strl: bool = False
+    dtype: np.dtype, column: Series, dta_version: int = 114, force_strl: bool = False
 ) -> str:
     """
     Map numpy dtype to stata's default format for this type. Not terribly
@@ -3467,7 +3467,7 @@ class StataWriter117(StataWriter):
         self._update_map("characteristics")
         self._write_bytes(self._tag(b"", "characteristics"))
 
-    def _write_data(self, records) -> None:
+    def _write_data(self, records: np.rec.recarray) -> None:
         self._update_map("data")
         self._write_bytes(b"<data>")
         self._write_bytes(records.tobytes())
diff --git a/pandas/plotting/_core.py b/pandas/plotting/_core.py
index edd619c264..c9d1e5a376 100644
--- a/pandas/plotting/_core.py
+++ b/pandas/plotting/_core.py
@@ -1335,6 +1335,7 @@ class PlotAccessor(PandasObject):
         Returns
         -------
         :class:`matplotlib.axes.Axes` or numpy.ndarray of them
+            The matplotlib axes containing the box plot.
 
         See Also
         --------
@@ -1466,6 +1467,7 @@ class PlotAccessor(PandasObject):
         Returns
         -------
         matplotlib.axes.Axes or numpy.ndarray of them
+            The matplotlib axes containing the KDE plot.
 
         See Also
         --------
@@ -1745,6 +1747,7 @@ class PlotAccessor(PandasObject):
         Returns
         -------
         :class:`matplotlib.axes.Axes` or numpy.ndarray of them
+            The matplotlib axes containing the scatter plot.
 
         See Also
         --------
diff --git a/pandas/util/__init__.py b/pandas/util/__init__.py
index da109a5144..dfab207635 100644
--- a/pandas/util/__init__.py
+++ b/pandas/util/__init__.py
@@ -25,5 +25,5 @@ def __getattr__(key: str):
     raise AttributeError(f"module 'pandas.util' has no attribute '{key}'")
 
 
-def __dir__():
+def __dir__() -> list[str]:
     return list(globals().keys()) + ["hash_array", "hash_pandas_object"]
diff --git a/pandas/util/_print_versions.py b/pandas/util/_print_versions.py
index e39c2f7bad..6cdd96996c 100644
--- a/pandas/util/_print_versions.py
+++ b/pandas/util/_print_versions.py
@@ -33,7 +33,7 @@ def _get_commit_hash() -> str | None:
     except ImportError:
         from pandas._version import get_versions
 
-        versions = get_versions()
+        versions = get_versions()  # type: ignore[no-untyped-call]
         return versions["full-revisionid"]
 
 
diff --git a/pandas/util/version/__init__.py b/pandas/util/version/__init__.py
index 3a5efbbb09..153424e339 100644
--- a/pandas/util/version/__init__.py
+++ b/pandas/util/version/__init__.py
@@ -131,7 +131,7 @@ class InvalidVersion(ValueError):
 
     Examples
     --------
-    >>> pd.util.version.Version('1.')
+    >>> pd.util.version.Version("1.")
     Traceback (most recent call last):
     InvalidVersion: Invalid version: '1.'
     """
diff --git a/pyproject.toml b/pyproject.toml
index 5a06e22f4b..bbcaa73b55 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -185,6 +185,8 @@ environment = {CFLAGS="-g0"}
 line-length = 88
 target-version = "py310"
 fix = true
+
+[tool.ruff.lint]
 unfixable = []
 typing-modules = ["pandas._typing"]
 
@@ -271,8 +273,8 @@ ignore = [
   "PLW0603",
   # Use `typing.NamedTuple` instead of `collections.namedtuple`
   "PYI024",
-  # No builtin `eval()` allowed
-  "PGH001",
+  # Use of possibly insecure function; consider using ast.literal_eval
+  "S307",
   # while int | float can be shortened to float, the former is more explicit
   "PYI041",
   # incorrect-dict-iterator, flags valid Series.items usage
@@ -345,7 +347,7 @@ exclude = [
 [tool.ruff.lint.flake8-import-conventions.aliases]
 "pandas.core.construction.array" = "pd_array"
 
-[tool.ruff.per-file-ignores]
+[tool.ruff.lint.per-file-ignores]
 # relative imports allowed for asv_bench
 "asv_bench/*" = ["TID", "NPY002"]
 # to be enabled gradually
@@ -565,13 +567,9 @@ module = [
   "pandas._config.config", # TODO
   "pandas._libs.*",
   "pandas._testing.*", # TODO
-  "pandas.arrays", # TODO
   "pandas.compat.numpy.function", # TODO
   "pandas.compat.compressors", # TODO
-  "pandas.compat.pickle_compat", # TODO
   "pandas.core._numba.executor", # TODO
-  "pandas.core.array_algos.datetimelike_accumulations", # TODO
-  "pandas.core.array_algos.masked_accumulations", # TODO
   "pandas.core.array_algos.masked_reductions", # TODO
   "pandas.core.array_algos.putmask", # TODO
   "pandas.core.array_algos.quantile", # TODO
@@ -586,7 +584,6 @@ module = [
   "pandas.core.dtypes.dtypes", # TODO
   "pandas.core.dtypes.generic", # TODO
   "pandas.core.dtypes.missing", # TODO
-  "pandas.core.groupby.categorical", # TODO
   "pandas.core.groupby.generic", # TODO
   "pandas.core.groupby.grouper", # TODO
   "pandas.core.groupby.groupby", # TODO
@@ -601,7 +598,6 @@ module = [
   "pandas.core.ops.array_ops", # TODO
   "pandas.core.ops.common", # TODO
   "pandas.core.ops.invalid", # TODO
-  "pandas.core.ops.mask_ops", # TODO
   "pandas.core.ops.missing", # TODO
   "pandas.core.reshape.*", # TODO
   "pandas.core.strings.*", # TODO
@@ -618,7 +614,6 @@ module = [
   "pandas.core.arraylike", # TODO
   "pandas.core.base", # TODO
   "pandas.core.common", # TODO
-  "pandas.core.config_init", # TODO
   "pandas.core.construction", # TODO
   "pandas.core.flags", # TODO
   "pandas.core.frame", # TODO
@@ -640,11 +635,9 @@ module = [
   "pandas.io.excel._pyxlsb", # TODO
   "pandas.io.excel._xlrd", # TODO
   "pandas.io.excel._xlsxwriter", # TODO
-  "pandas.io.formats.console", # TODO
   "pandas.io.formats.css", # TODO
   "pandas.io.formats.excel", # TODO
   "pandas.io.formats.format", # TODO
-  "pandas.io.formats.info", # TODO
   "pandas.io.formats.printing", # TODO
   "pandas.io.formats.style", # TODO
   "pandas.io.formats.style_render", # TODO
@@ -659,7 +652,6 @@ module = [
   "pandas.io.parquet", # TODO
   "pandas.io.pytables", # TODO
   "pandas.io.sql", # TODO
-  "pandas.io.stata", # TODO
   "pandas.io.xml", # TODO
   "pandas.plotting.*", # TODO
   "pandas.tests.*",
@@ -667,7 +659,6 @@ module = [
   "pandas.tseries.holiday", # TODO
   "pandas.util._decorators", # TODO
   "pandas.util._doctools", # TODO
-  "pandas.util._print_versions", # TODO
   "pandas.util._test_decorators", # TODO
   "pandas.util._validators", # TODO
   "pandas.util", # TODO


Test patch:
diff --git a/pandas/tests/arrays/categorical/test_constructors.py b/pandas/tests/arrays/categorical/test_constructors.py
index 03678fb64d..857b14e2a2 100644
--- a/pandas/tests/arrays/categorical/test_constructors.py
+++ b/pandas/tests/arrays/categorical/test_constructors.py
@@ -24,6 +24,7 @@ from pandas import (
     IntervalIndex,
     MultiIndex,
     NaT,
+    RangeIndex,
     Series,
     Timestamp,
     date_range,
@@ -779,3 +780,17 @@ class TestCategoricalConstructors:
         result = cat.categories.freq
 
         assert expected == result
+
+    @pytest.mark.parametrize(
+        "values, categories",
+        [
+            [range(5), None],
+            [range(4), range(5)],
+            [[0, 1, 2, 3], range(5)],
+            [[], range(5)],
+        ],
+    )
+    def test_range_values_preserves_rangeindex_categories(self, values, categories):
+        result = Categorical(values=values, categories=categories).categories
+        expected = RangeIndex(range(5))
+        tm.assert_index_equal(result, expected, exact=True)
diff --git a/pandas/tests/arrays/sparse/test_dtype.py b/pandas/tests/arrays/sparse/test_dtype.py
index 6fcbfe96a3..6f0d41333f 100644
--- a/pandas/tests/arrays/sparse/test_dtype.py
+++ b/pandas/tests/arrays/sparse/test_dtype.py
@@ -68,6 +68,14 @@ def test_nans_equal():
     assert b == a
 
 
+def test_nans_not_equal():
+    # GH 54770
+    a = SparseDtype(float, 0)
+    b = SparseDtype(float, pd.NA)
+    assert a != b
+    assert b != a
+
+
 with warnings.catch_warnings():
     msg = "Allowing arbitrary scalar fill_value in SparseDtype is deprecated"
     warnings.filterwarnings("ignore", msg, category=FutureWarning)
diff --git a/pandas/tests/extension/test_sparse.py b/pandas/tests/extension/test_sparse.py
index c3a1d58417..cbca306ab0 100644
--- a/pandas/tests/extension/test_sparse.py
+++ b/pandas/tests/extension/test_sparse.py
@@ -240,10 +240,6 @@ class TestSparseArray(base.ExtensionTests):
             super().test_fillna_limit_backfill(data_missing)
 
     def test_fillna_no_op_returns_copy(self, data, request):
-        if np.isnan(data.fill_value):
-            request.applymarker(
-                pytest.mark.xfail(reason="returns array with different fill value")
-            )
         super().test_fillna_no_op_returns_copy(data)
 
     @pytest.mark.xfail(reason="Unsupported")
@@ -400,6 +396,8 @@ class TestSparseArray(base.ExtensionTests):
             "rmul",
             "floordiv",
             "rfloordiv",
+            "truediv",
+            "rtruediv",
             "pow",
             "mod",
             "rmod",