diff --git a/leanframe/core/series.py b/leanframe/core/series.py index ce7521b..12be45b 100644 --- a/leanframe/core/series.py +++ b/leanframe/core/series.py @@ -179,6 +179,42 @@ def count(self) -> int: """Return the number of non-null observations in the Series.""" return self._data.count().to_pyarrow().as_py() + def cummax(self) -> "Series": + """Return a Series with the cumulative maximum of each element.""" + return Series(self._data.cummax()) + + def cummin(self) -> "Series": + """Return a Series with the cumulative minimum of each element.""" + return Series(self._data.cummin()) + + def cumprod(self) -> "Series": + """Return a Series with the cumulative product of each element.""" + return Series(self._data.log().cumsum().exp().cast(self._data.type())) + + def cumsum(self) -> "Series": + """Return a Series with the cumulative sum of each element.""" + return Series(self._data.cumsum()) + + def describe(self) -> pd.Series: + """Return a Series with descriptive statistics.""" + stats = { + "count": self.count(), + "mean": self.mean(), + "std": self.std(), + "min": self.min(), + "25%": self._data.quantile(0.25).to_pyarrow().as_py(), + "50%": self._data.quantile(0.50).to_pyarrow().as_py(), + "75%": self._data.quantile(0.75).to_pyarrow().as_py(), + "max": self.max(), + } + + index = ["count", "mean", "std", "min", "25%", "50%", "75%", "max"] + return pd.Series(stats, name=self.name, index=index) + + def diff(self) -> "Series": + """Return a Series with the difference between each element and the previous element.""" + return Series(self._data - self._data.lag()) + def copy(self) -> Series: """Return a copy of the Series.""" return Series(self._data) diff --git a/tests/unit/test_series.py b/tests/unit/test_series.py index 23995dd..355b4d2 100644 --- a/tests/unit/test_series.py +++ b/tests/unit/test_series.py @@ -310,6 +310,86 @@ def test_series_abs(session): ) +def test_series_cummax(session): + pandas_df = pd.DataFrame( + {"a": [1, 2, 3, 2, 1]}, + dtype=pd.ArrowDtype(pa.int64()), + ) + df = session.DataFrame(pandas_df) + series = df["a"] + result = series.cummax() + expected = pd.Series( + [1, 2, 3, 3, 3], + name="a", + dtype=pd.ArrowDtype(pa.int64()), + ) + pd.testing.assert_series_equal( + result.to_pandas(), + expected, + check_names=False, + ) + + +def test_series_cummin(session): + pandas_df = pd.DataFrame( + {"a": [3, 2, 1, 2, 3]}, + dtype=pd.ArrowDtype(pa.int64()), + ) + df = session.DataFrame(pandas_df) + series = df["a"] + result = series.cummin() + expected = pd.Series( + [3, 2, 1, 1, 1], + name="a", + dtype=pd.ArrowDtype(pa.int64()), + ) + pd.testing.assert_series_equal( + result.to_pandas(), + expected, + check_names=False, + ) + + +def test_series_cumprod(session): + pandas_df = pd.DataFrame( + {"a": [1, 2, 3, 4, 5]}, + dtype=pd.ArrowDtype(pa.int64()), + ) + df = session.DataFrame(pandas_df) + series = df["a"] + result = series.cumprod() + expected = pd.Series( + [1, 2, 6, 24, 120], + name="a", + dtype=pd.ArrowDtype(pa.int64()), + ) + pd.testing.assert_series_equal( + result.to_pandas(), + expected, + check_names=False, + ) + + +def test_series_cumsum(session): + pandas_df = pd.DataFrame( + {"a": [1, 2, 3, 4, 5]}, + dtype=pd.ArrowDtype(pa.int64()), + ) + df = session.DataFrame(pandas_df) + series = df["a"] + result = series.cumsum() + expected = pd.Series( + [1, 3, 6, 10, 15], + name="a", + dtype=pd.ArrowDtype(pa.int64()), + ) + pd.testing.assert_series_equal( + result.to_pandas(), + expected, + check_names=False, + ) + + def test_series_astype(session): pandas_df = pd.DataFrame( {"a": [1, 2, 3]}, @@ -330,6 +410,43 @@ def test_series_astype(session): ) +def test_series_describe(session): + pandas_df = pd.DataFrame( + {"a": [1, 2, 3, 4, 5]}, + dtype=pd.ArrowDtype(pa.int64()), + ) + df = session.DataFrame(pandas_df) + series = df["a"] + result = series.describe() + expected = pandas_df["a"].describe().astype("float64") + pd.testing.assert_series_equal( + result, + expected, + check_names=False, + rtol=0.01, + ) + + +def test_series_diff(session): + pandas_df = pd.DataFrame( + {"a": [1, 2, 3, 4, 5]}, + dtype=pd.ArrowDtype(pa.int64()), + ) + df = session.DataFrame(pandas_df) + series = df["a"] + result = series.diff() + expected = pd.Series( + [None, 1, 1, 1, 1], + name="a", + dtype=pd.ArrowDtype(pa.int64()), + ) + pd.testing.assert_series_equal( + result.to_pandas(), + expected, + check_names=False, + ) + + def test_series_all(bool_series): assert bool_series["all_true"].all() assert not bool_series["some_true"].all()