Repository: skrub-data/skrub
Pull request #1512 (2025-07-10 18:02:49)

Add method that checks if a column is sorted
Working on #1398 

skrub sprint at Probabl's 2025-07-09

Adds a column in Stats:
For  `df = pl.DataFrame(dict(a=[1, 2, 1], b=['one', 'three', 'two'], c=[1, 2, 3]))`


<img width="724" height="176" alt="Screenshot 2025-07-10 at 20 01 32" src="https://github.com/user-attachments/assets/3d58bc8d-f50c-40b6-8e9c-0a201812e46f" />

~~I need to add tests.~~

Edit: codecov seems fine. So waiting for feedback now.



Patch:
diff --git a/CHANGES.rst b/CHANGES.rst
index 2ca882b..bfdca1a 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -100,6 +100,8 @@ Changes
 - The :class:`Cleaner` now exposes a parameter to convert numerical values to float32. :pr:`1440` by
   :user:`Riccardo Cappuzzo<rcap107>`.
 
+- The :class:`TableReport` now shows if columns are sorted. :pr:`1512` by :user:`Dea María Léon<DeaMariaLeon>`.
+
 - The ``tabular_learner`` function has been deprecated in favor of :func:`tabular_pipeline` to honor
   its scikit-learn pipeline cultural heritage, and remove the ambiguity with the data
   ops Learner. :pr:`1493` by :user:`Vincent Maladiere <Vincent-Maladiere>`.
diff --git a/skrub/_dataframe/_common.py b/skrub/_dataframe/_common.py
index 9e527b3..c362135 100644
--- a/skrub/_dataframe/_common.py
+++ b/skrub/_dataframe/_common.py
@@ -107,6 +107,7 @@ __all__ = [
     "with_columns",
     "abs",
     "total_seconds",
+    "is_sorted",
 ]
 
 pandas_version = parse_version(parse_version(pd.__version__).base_version)
@@ -1387,3 +1388,19 @@ def _total_seconds_pandas(col):
 @total_seconds.specialize("polars", argument_type="Column")
 def _total_seconds_polars(col):
     return col.dt.total_microseconds().cast(float) * 1e-6
+
+
+@dispatch
+def is_sorted(col):
+    """Check if a column is sorted."""
+    raise _raise(col, kind="Series")
+
+
+@is_sorted.specialize("pandas", argument_type="Column")
+def _is_sorted_pandas(col):
+    return col.is_monotonic_increasing or col.is_monotonic_decreasing
+
+
+@is_sorted.specialize("polars", argument_type="Column")
+def _is_sorted_polars(col):
+    return col.is_sorted()
diff --git a/skrub/_reporting/_data/templates/summary-statistics.html b/skrub/_reporting/_data/templates/summary-statistics.html
index 589f629..2bd6881 100644
--- a/skrub/_reporting/_data/templates/summary-statistics.html
+++ b/skrub/_reporting/_data/templates/summary-statistics.html
@@ -37,6 +37,7 @@
                     {{ th("Column", "from first column to last column", "from last column to first column", True) }}
                     {{ th("Column name", "by column name from A to Z", "by column name from Z to A", False) }}
                     {{ th("dtype", "by dtype from A to Z", "by dtype from Z to A", False) }}
+                    {{ th("Is sorted", "False on top", "True on top", False)}}
                     {{ th1("Null values", "the fewest null values", "the most null values", True) }}
                     {{ th1("Unique values", "the fewest unique values", "the most unique values", True) }}
                     {{ th1("Mean", "the lowest mean", "the highest mean", True) }}
@@ -54,6 +55,7 @@
                     </td>
                     <td class="elided">{{ column.name }}</td>
                     <td>{{ column.dtype }}</td>
+                    <td>{{column.is_ordered}}</td>
                     <td class="{{ column.nulls_level }}" data-value="{{ column.null_count }}" data-numeric>
                         {{ column.null_count }} ({{ column.null_proportion | format_percent }})
                     </td>
diff --git a/skrub/_reporting/_summarize.py b/skrub/_reporting/_summarize.py
index c4d88a4..1e87bf8 100644
--- a/skrub/_reporting/_summarize.py
+++ b/skrub/_reporting/_summarize.py
@@ -145,6 +145,7 @@ def _summarize_column(
         "name": sbd.name(column),
         "dtype": _utils.get_dtype_name(column),
         "value_is_constant": False,
+        "is_ordered": False,
     }
     _add_nulls_summary(summary, column, dataframe_summary=dataframe_summary)
     if summary["null_count"] == dataframe_summary["n_rows"]:
@@ -171,6 +172,8 @@ def _summarize_column(
     )
     _add_datetime_summary(summary, column, with_plots=with_plots)
     summary["plot_names"] = [k for k in summary.keys() if k.endswith("_plot")]
+    _add_is_sorted(summary, column)
+
     return summary
 
 
@@ -275,3 +278,7 @@ def _add_numeric_summary(
         )
     else:
         summary["line_plot"] = _plotting.line(order_by_column, column)
+
+
+def _add_is_sorted(summary, column):
+    summary["is_ordered"] = sbd.is_sorted(column)


Test patch:
diff --git a/skrub/_reporting/tests/test_summarize.py b/skrub/_reporting/tests/test_summarize.py
index 5178962..a8e6fea 100644
--- a/skrub/_reporting/tests/test_summarize.py
+++ b/skrub/_reporting/tests/test_summarize.py
@@ -57,6 +57,7 @@ def test_summarize(
     assert c == {
         "idx": 0,
         "dtype": "string",
+        "is_ordered": False,
         "n_unique": 2,
         "name": "city",
         "null_count": 0,