Repository: dask/dask
Pull request #10756 (2024-01-08 12:10:30)

Review tests for is_monotonic
- Closes https://github.com/dask-contrib/dask-expr/issues/660
- Mirrors https://github.com/dask-contrib/dask-expr/pull/658


Patch:
diff --git a/dask/dataframe/core.py b/dask/dataframe/core.py
index e1e7904a4..9ac54c8ae 100644
--- a/dask/dataframe/core.py
+++ b/dask/dataframe/core.py
@@ -4652,8 +4652,17 @@ Dask Name: {name}, {layers}""".format(
             token="monotonic_decreasing",
         )
 
+    @_deprecated(
+        message=(
+            "Will be removed in a future version. "
+            "Use `Series.astype()` as an alternative to change the dtype."
+        )
+    )
     @derived_from(pd.Series)
     def view(self, dtype):
+        return self._view(dtype)
+
+    def _view(self, dtype):
         meta = self._meta.view(dtype)
         return self.map_partitions(M.view, dtype, meta=meta)
 
@@ -8721,11 +8730,19 @@ def series_map(base_series, map_series):
 
 
 def _convert_to_numeric(series, skipna):
-    if skipna:
-        return series.dropna().view("i8")
+    if PANDAS_GE_200:
+        if skipna:
+            return series.dropna().astype("i8")
+
+        # series.view("i8") with pd.NaT produces -9223372036854775808 is why we need to do this
+        return series.astype("i8").mask(series.isnull(), np.nan)
+    else:
+        view = "_view" if isinstance(series, Series) else "view"
+        if skipna:
+            return getattr(series.dropna(), view)("i8")
 
-    # series.view("i8") with pd.NaT produces -9223372036854775808 is why we need to do this
-    return series.view("i8").mask(series.isnull(), np.nan)
+        # series.view("i8") with pd.NaT produces -9223372036854775808 is why we need to do this
+        return getattr(series, view)("i8").mask(series.isnull(), np.nan)
 
 
 def _sqrt_and_convert_to_timedelta(partition, axis, dtype=None, *args, **kwargs):
diff --git a/dask/dataframe/methods.py b/dask/dataframe/methods.py
index c061571a8..a288f0bc0 100644
--- a/dask/dataframe/methods.py
+++ b/dask/dataframe/methods.py
@@ -381,7 +381,7 @@ def value_counts_combine(x, sort=True, ascending=False, **groupby_kwargs):
 
 
 def value_counts_aggregate(
-    x, sort=True, ascending=False, normalize=False, total_length=None, **groupby_kwargs
+    x, total_length=None, sort=True, ascending=False, normalize=False, **groupby_kwargs
 ):
     out = value_counts_combine(x, **groupby_kwargs)
     if normalize:


Test patch:
diff --git a/dask/dataframe/tests/test_dataframe.py b/dask/dataframe/tests/test_dataframe.py
index 82fa757db..449a63edc 100644
--- a/dask/dataframe/tests/test_dataframe.py
+++ b/dask/dataframe/tests/test_dataframe.py
@@ -5673,8 +5673,11 @@ def test_view():
 
     df = pd.DataFrame(data)
     ddf = dd.from_pandas(df, npartitions=2)
-    assert_eq(ddf["x"].view("uint8"), df["x"].view("uint8"))
-    assert_eq(ddf["y"].view("int64"), df["y"].view("int64"))
+
+    msg = "Will be removed in a future version. Use "
+    with pytest.raises(FutureWarning, match=msg):
+        assert_eq(ddf["x"].view("uint8"), df["x"].view("uint8"))
+        assert_eq(ddf["y"].view("int64"), df["y"].view("int64"))
 
 
 def test_simple_map_partitions():
@@ -5875,19 +5878,29 @@ def test_use_of_weakref_proxy():
     isinstance(res.compute(), pd.Series)
 
 
-def test_is_monotonic_numeric():
-    s = pd.Series(range(20))
-    ds = dd.from_pandas(s, npartitions=5)
-    assert_eq(s.is_monotonic_increasing, ds.is_monotonic_increasing)
-
-    s_2 = pd.Series(range(20, 0, -1))
-    ds_2 = dd.from_pandas(s_2, npartitions=5)
-    assert_eq(s_2.is_monotonic_decreasing, ds_2.is_monotonic_decreasing)
+@pytest.mark.parametrize(
+    "series",
+    [
+        [0, 1, 0, 0, 1, 0],  # not monotonic
+        [0, 1, 1, 2, 2, 3],  # monotonic increasing
+        [0, 1, 2, 3, 4, 5],  # strictly monotonic increasing
+        [0, 0, 0, 0, 0, 0],  # both monotonic increasing and monotonic decreasing
+        [0, 1, 2, 0, 1, 2],  # Partitions are individually monotonic; whole series isn't
+    ],
+)
+@pytest.mark.parametrize("reverse", [False, True])
+@pytest.mark.parametrize("cls", ["Series", "Index"])
+def test_is_monotonic_numeric(series, reverse, cls):
+    if reverse:
+        series = series[::-1]
+    pds = pd.Series(series, index=series)
+    ds = dd.from_pandas(pds, chunksize=3, sort=False)
+    if cls == "Index":
+        pds = pds.index
+        ds = ds.index
 
-    s_3 = pd.Series(list(range(0, 5)) + list(range(0, 20)))
-    ds_3 = dd.from_pandas(s_3, npartitions=5)
-    assert_eq(s_3.is_monotonic_increasing, ds_3.is_monotonic_increasing)
-    assert_eq(s_3.is_monotonic_decreasing, ds_3.is_monotonic_decreasing)
+    assert ds.is_monotonic_increasing.compute() == pds.is_monotonic_increasing
+    assert ds.is_monotonic_decreasing.compute() == pds.is_monotonic_decreasing
 
 
 @pytest.mark.skipif(PANDAS_GE_200, reason="pandas removed is_monotonic")
@@ -5910,26 +5923,25 @@ def test_is_monotonic_dt64():
     s = pd.Series(pd.date_range("20130101", periods=10))
     ds = dd.from_pandas(s, npartitions=5)
     assert_eq(s.is_monotonic_increasing, ds.is_monotonic_increasing)
+    assert_eq(s.is_monotonic_decreasing, ds.is_monotonic_decreasing)
 
     s_2 = pd.Series(list(reversed(s)))
     ds_2 = dd.from_pandas(s_2, npartitions=5)
+    assert_eq(s_2.is_monotonic_increasing, ds_2.is_monotonic_increasing)
     assert_eq(s_2.is_monotonic_decreasing, ds_2.is_monotonic_decreasing)
 
 
-def test_index_is_monotonic_numeric():
-    s = pd.Series(1, index=range(20))
+def test_index_is_monotonic_dt64():
+    s = pd.Series(1, index=pd.date_range("20130101", periods=10))
     ds = dd.from_pandas(s, npartitions=5, sort=False)
     assert_eq(s.index.is_monotonic_increasing, ds.index.is_monotonic_increasing)
+    assert_eq(s.index.is_monotonic_decreasing, ds.index.is_monotonic_decreasing)
 
-    s_2 = pd.Series(1, index=range(20, 0, -1))
+    s_2 = s[::-1]
     ds_2 = dd.from_pandas(s_2, npartitions=5, sort=False)
+    assert_eq(s_2.index.is_monotonic_increasing, ds_2.index.is_monotonic_increasing)
     assert_eq(s_2.index.is_monotonic_decreasing, ds_2.index.is_monotonic_decreasing)
 
-    s_3 = pd.Series(1, index=list(range(0, 5)) + list(range(0, 20)))
-    ds_3 = dd.from_pandas(s_3, npartitions=5, sort=False)
-    assert_eq(s_3.index.is_monotonic_increasing, ds_3.index.is_monotonic_increasing)
-    assert_eq(s_3.index.is_monotonic_decreasing, ds_3.index.is_monotonic_decreasing)
-
 
 @pytest.mark.skipif(PANDAS_GE_200, reason="pandas removed is_monotonic")
 def test_index_is_monotonic_deprecated():
@@ -5947,16 +5959,6 @@ def test_index_is_monotonic_deprecated():
     assert_eq(expected, result)
 
 
-def test_index_is_monotonic_dt64():
-    s = pd.Series(1, index=pd.date_range("20130101", periods=20))
-    ds = dd.from_pandas(s, npartitions=10, sort=False)
-    assert_eq(s.index.is_monotonic_increasing, ds.index.is_monotonic_increasing)
-
-    s_2 = s[::-1]
-    ds_2 = dd.from_pandas(s_2, npartitions=10, sort=False)
-    assert_eq(s_2.index.is_monotonic_decreasing, ds_2.index.is_monotonic_decreasing)
-
-
 def test_is_monotonic_empty_partitions():
     df = pd.DataFrame({"a": [1, 2, 3, 4], "b": [4, 3, 2, 1]})
     ddf = dd.from_pandas(df, npartitions=2)