Repository: pandas-dev/pandas

Issue:
DataFrame.__finalize__ not called in pd.concat
When I assign metadata to a df

```
import numpy as np
import pandas as pd
np.random.seed(10)

pd.DataFrame._metadata = ['filename']
df1 = pd.DataFrame(np.random.randint(0, 4, (3, 2)), columns=list('ab'))
df1.filenames = {'a': 'f1', 'b': 'f2'}
df1
       a  b
    0  1  1
    1  0  3
    2  0  1
```

and define a `__finalize__` that prints when it's called

```
def finalize_df(self, other, method=None, **kwargs):
    print 'finalize called'
    for name in self._metadata:
        object.__setattr__(self, name, getattr(other, name, None))
    return self

pd.DataFrame.__finalize__ = finalize_df
```

nothing is preserved when `pd.concat` is called:

```
stacked = pd.concat([df1, df1])  # Nothing printed
stacked
       a  b
    0  1  1
    1  0  3
    2  0  1
    0  1  1
    1  0  3
    2  0  1
stacked.finalize  # => AttributeError 
```

For this specific case it seems reasonable that `__finalize__` should be used since all of the elements are from the same dataframe, though I'm not sure about the general use since `concat` can also take types other than a DataFrame. But should we/do we have some method to stack dataframes that preserves metadata?

Similar to #6923.



Discussion:

Resolving patch:
diff --git a/pandas/tools/merge.py b/pandas/tools/merge.py
--- a/pandas/tools/merge.py
+++ b/pandas/tools/merge.py
@@ -1064,18 +1064,18 @@ def get_result(self):
             new_data = com._concat_compat([x.get_values() for x in self.objs])
             name = com._consensus_name_attr(self.objs)
             new_data = self._post_merge(new_data)
-            return Series(new_data, index=self.new_axes[0], name=name)
+            return Series(new_data, index=self.new_axes[0], name=name).__finalize__(self, method='concat')
         elif self._is_series:
             data = dict(zip(range(len(self.objs)), self.objs))
             index, columns = self.new_axes
             tmpdf = DataFrame(data, index=index)
             if columns is not None:
                 tmpdf.columns = columns
-            return tmpdf
+            return tmpdf.__finalize__(self, method='concat')
         else:
             new_data = self._get_concatenated_data()
             new_data = self._post_merge(new_data)
-            return self.objs[0]._from_axes(new_data, self.new_axes)
+            return self.objs[0]._from_axes(new_data, self.new_axes).__finalize__(self, method='concat')
 
     def _post_merge(self, data):
         if isinstance(data, BlockManager):


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
@@ -440,6 +440,32 @@ def test_metadata_propagation_indiv(self):
         result = ts.resample('1T',how=lambda x: x.sum())
         self.check_metadata(ts,result)
 
+        _metadata = Series._metadata
+        _finalize = Series.__finalize__
+        Series._metadata = ['name','filename']
+        o.filename = 'foo'
+        o2.filename = 'bar'
+
+        def finalize(self, other, method=None, **kwargs):
+            for name in self._metadata:
+                if method == 'concat' and name == 'filename':
+                    value = '+'.join([ getattr(o,name) for o in other.objs if getattr(o,name,None) ])
+                    object.__setattr__(self, name, value)
+                else:
+                    object.__setattr__(self, name, getattr(other, name, None))
+
+            return self
+
+        Series.__finalize__ = finalize
+
+        result = pd.concat([o, o2])
+        self.assertEquals(result.filename,'foo+bar')
+        self.assertIsNone(result.name)
+
+        # reset
+        Series._metadata = _metadata
+        Series.__finalize__ = _finalize
+
     def test_interpolate(self):
         ts = Series(np.arange(len(self.ts), dtype=float), self.ts.index)
 
@@ -894,6 +920,28 @@ def finalize(self, other, method=None, **kwargs):
         result = df1.merge(df2, left_on=['a'], right_on=['c'], how='inner')
         self.assertEquals(result.filename,'fname1.csv|fname2.csv')
 
+        # concat
+        # GH 6927
+        DataFrame._metadata = ['filename']
+        df1 = DataFrame(np.random.randint(0, 4, (3, 2)), columns=list('ab'))
+        df1.filename = 'foo'
+
+        def finalize(self, other, method=None, **kwargs):
+            for name in self._metadata:
+                if method == 'concat':
+                    value = '+'.join([ getattr(o,name) for o in other.objs if getattr(o,name,None) ])
+                    object.__setattr__(self, name, value)
+                else:
+                    object.__setattr__(self, name, getattr(other, name, None))
+
+            return self
+
+        DataFrame.__finalize__ = finalize
+
+        result = pd.concat([df1, df1])
+        self.assertEquals(result.filename,'foo+foo')
+
+        # reset
         DataFrame._metadata = _metadata
         DataFrame.__finalize__ = _finalize