Repository: pandas-dev/pandas

Issue:
API: implement __finalize__ for resample et al.
I'm really salivating at the chance to use the `._metadata` and `__finalize__` mechanisms in pandas-0.13-dev to create my own subclass of DataFrame and to have metadata propagate after calling functions inherited from DataFrame like `dropna()`, `resample()` etc.

Using the latest 0.13-dev version of Pandas, I think I might have bumped into a small bug (although I'm not sure if this is a bug or not??):

`._metadata` propagates after calling `.resample(rule='D')` and `dropna()` but not after calling `.resample(rule='D', how='max')`.

More details, including the full code of my subclass, are given under the "experiments" heading of this issue: https://github.com/nilmtk/nilmtk/issues/83



Discussion:
can you put up a small reproducible example ?

actually suprised it works at all with resample (**finalize** has to be explicity called, and I don't think was put in)

Sure, here's the small example:

``` python
import pandas as pd

class ElectricDataFrame(pd.DataFrame):
    def __init__(self, *args, **kwargs):
        super(ElectricDataFrame, self).__init__(*args, **kwargs)
        self._metadata = ['test']

    @property
    def _constructor(self):
        print("_constructor called")
        return ElectricDataFrame

edf = ElectricDataFrame([1,2,3,4,5], pd.date_range('2010', freq='D', periods=5))
```

Interactive:

``` python
In [5]: pd.__version__
Out[5]: '0.13.0-75-g7d9e9fa'

In [6]: edf._metadata
Out[6]: ['test']

In [7]: edf.dropna()._metadata
_constructor called
Out[7]: ['test']

In [8]: edf.resample(rule='D')._metadata
_constructor called
Out[8]: ['test']

In [9]: edf.resample(rule='D', how='max')._metadata
Out[9]: []

In [10]: edf.resample(rule='D', how='min')._metadata
Out[10]: []

In [11]: edf.resample(rule='D', how='mean')._metadata
Out[11]: []
```

thanks....I think since its daily data already and how is defaulted ,then its actually returning the same frame (that's why it works). This is not-implemented (though trivial to do)....will do for 0.13.1

> since its daily data already and how is defaulted ,then its actually returning the same frame (that's why it works)

ah, interesting!

> This is not-implemented (though trivial to do)....will do for 0.13.1

wonderful, thanks loads ;)

you could easily do it in your sub though...

```
class ElectricDataFrame(pd.DataFrame):

       def resample(self, *args, **kwargs):
           return super(ElectricDataFrame, self).resample(*args, **kwargs).__finalize__(self)
```

> you could easily do it in your sub though...

Good point, thank you 


Resolving patch:
diff --git a/pandas/core/generic.py b/pandas/core/generic.py
--- a/pandas/core/generic.py
+++ b/pandas/core/generic.py
@@ -2562,7 +2562,7 @@ def resample(self, rule, how=None, axis=0, fill_method=None,
                               axis=axis, kind=kind, loffset=loffset,
                               fill_method=fill_method, convention=convention,
                               limit=limit, base=base)
-        return sampler.resample(self)
+        return sampler.resample(self).__finalize__(self)
 
     def first(self, offset):
         """


Test patch:
diff --git a/pandas/tests/test_generic.py b/pandas/tests/test_generic.py
--- a/pandas/tests/test_generic.py
+++ b/pandas/tests/test_generic.py
@@ -285,7 +285,6 @@ def test_metadata_propagation(self):
         except (AttributeError):
             pass
 
-
         # ---------------------------
         # non-preserving (by default)
         # ---------------------------
@@ -428,6 +427,19 @@ def test_metadata_propagation_indiv(self):
         result = o.T
         self.check_metadata(o,result)
 
+        # resample
+        ts = Series(np.random.rand(1000),
+                    index=date_range('20130101',periods=1000,freq='s'),
+                    name='foo')
+        result = ts.resample('1T')
+        self.check_metadata(ts,result)
+
+        result = ts.resample('1T',how='min')
+        self.check_metadata(ts,result)
+
+        result = ts.resample('1T',how=lambda x: x.sum())
+        self.check_metadata(ts,result)
+
     def test_interpolate(self):
         ts = Series(np.arange(len(self.ts), dtype=float), self.ts.index)
 
@@ -768,6 +780,23 @@ def test_spline(self):
         expected = Series([1, 2, 3, 4, 5, 6, 7])
         assert_series_equal(result, expected)
 
+    def test_metadata_propagation_indiv(self):
+
+        # groupby
+        df = DataFrame({'A': ['foo', 'bar', 'foo', 'bar',
+                              'foo', 'bar', 'foo', 'foo'],
+                        'B': ['one', 'one', 'two', 'three',
+                              'two', 'two', 'one', 'three'],
+                        'C': np.random.randn(8),
+                        'D': np.random.randn(8)})
+        result = df.groupby('A').sum()
+        self.check_metadata(df,result)
+
+        # resample
+        df = DataFrame(np.random.randn(1000,2), index=date_range('20130101',periods=1000,freq='s'))
+        result = df.resample('1T')
+        self.check_metadata(df,result)
+
 class TestPanel(tm.TestCase, Generic):
     _typ = Panel
     _comparator = lambda self, x, y: assert_panel_equal(x, y)