Repository: PSLmodels/microdf

Issue:
Changing a MicroDataFrame's index makes its columns Series instead of MicroSeries
```
d = mdf.MicroDataFrame(dict(x=[1, 2, 3]), weights=[4, 5, 6])
d.x.__class__
```
>microdf.generic.MicroSeries
```
d = mdf.MicroDataFrame(dict(x=[1, 2, 3]), weights=[4, 5, 6])
d.index = [1, 2, 3]
d.x.__class__
```
>pandas.core.series.Series

Discussion:

Patch:
diff --git a/microdf/generic.py b/microdf/generic.py
index 9c19d0d..f6a91ad 100644
--- a/microdf/generic.py
+++ b/microdf/generic.py
@@ -239,6 +239,19 @@ class MicroSeries(pd.Series):
         b50 = self.bottom_50_pct_share()
         return t10 / b50
 
+    @vector_function
+    def cumsum(self) -> pd.Series:
+        return pd.Series(self * self.weights).cumsum()
+
+    @vector_function
+    def rank(self, pct=False) -> pd.Series:
+        order = np.argsort(self.values)
+        inverse_order = np.argsort(order)
+        ranks = np.array(self.weights)[order].cumsum()[inverse_order]
+        if pct:
+            ranks /= self.weights.sum()
+        return pd.Series(ranks)
+
     def groupby(self, *args, **kwargs):
         gb = super().groupby(*args, **kwargs)
         gb.__class__ = MicroSeriesGroupBy
@@ -584,6 +597,20 @@ class MicroDataFrame(pd.DataFrame):
             return MicroDataFrame(result, weights=weights)
         return result
 
+    def catch_series_relapse(self):
+        for col in self.columns:
+            if self[col].__class__ == pd.Series:
+                self._link_weights(col)
+
+    def __setattr__(self, key, value):
+        super().__setattr__(key, value)
+        self.catch_series_relapse()
+
+    def reset_index(self):
+        res = super().reset_index()
+        res = MicroDataFrame(res, weights=self.weights)
+        return res
+
     def groupby(self, by: Union[str, list], *args, **kwargs):
         """Returns a GroupBy object with MicroSeriesGroupBy objects for each column
 


Test patch:
diff --git a/microdf/tests/test_generic.py b/microdf/tests/test_generic.py
index ca55fc8..aadd6b0 100644
--- a/microdf/tests/test_generic.py
+++ b/microdf/tests/test_generic.py
@@ -1,4 +1,4 @@
-from microdf.generic import MicroDataFrame
+from microdf.generic import MicroDataFrame, MicroSeries
 import numpy as np
 import microdf as mdf
 import pandas as pd
@@ -118,3 +118,37 @@ def test_concat():
     mdf_wide = mdf.concat([df1, df2], axis=1)
     assert isinstance(mdf_wide, mdf.MicroDataFrame)
     assert mdf_wide.weights.equals(df1.weights)
+
+
+def test_set_index():
+    d = mdf.MicroDataFrame(dict(x=[1, 2, 3]), weights=[4, 5, 6])
+    assert d.x.__class__ == MicroSeries
+    d.index = [1, 2, 3]
+    assert d.x.__class__ == MicroSeries
+
+
+def test_reset_index():
+    d = mdf.MicroDataFrame(dict(x=[1, 2, 3]), weights=[4, 5, 6])
+    assert d.reset_index().__class__ == MicroDataFrame
+
+
+def test_cumsum():
+    s = mdf.MicroSeries([1, 2, 3], weights=[4, 5, 6])
+    assert np.array_equal(s.cumsum().values, [4, 14, 32])
+
+    s = mdf.MicroSeries([2, 1, 3], weights=[5, 4, 6])
+    assert np.array_equal(s.cumsum().values, [10, 14, 32])
+
+    s = mdf.MicroSeries([3, 1, 2], weights=[6, 4, 5])
+    assert np.array_equal(s.cumsum().values, [18, 22, 32])
+
+
+def test_rank():
+    s = mdf.MicroSeries([1, 2, 3], weights=[4, 5, 6])
+    assert np.array_equal(s.rank().values, [4, 9, 15])
+
+    s = mdf.MicroSeries([3, 1, 2], weights=[6, 4, 5])
+    assert np.array_equal(s.rank().values, [15, 4, 9])
+
+    s = mdf.MicroSeries([2, 1, 3], weights=[5, 4, 6])
+    assert np.array_equal(s.rank().values, [9, 4, 15])