我希望能够将元数据附加到一系列数据帧(特别是原始文件名),以便在连接两个数据帧后,我可以看到每个系列来自哪里的元数据.
我看到有关_Metadata(here,here)的github问题,包括一些与当前_Metadata属性(here)相关的问题,但在pandas文档中没有任何内容.
到目前为止,我可以修改_Metadata属性以允许保留元数据,但在连接后获取AttributeError.
df1 = pd.DataFrame(np.random.randint(0, 4, (6, 3)))
df2 = pd.DataFrame(np.random.randint(0, 4, (6, 3)))
df1._Metadata.append('filename')
df1[df1.columns[0]]._Metadata.append('filename')
for c in df1:
df1[c].filename = 'fname1.csv'
df2[c].filename = 'fname2.csv'
df1[0]._Metadata # ['name', 'filename']
df1[0].filename # fname1.csv
df2[0].filename # fname2.csv
df1[0][:3].filename # fname1.csv
mgd = pd.merge(df1, df2, on=[0])
mgd['1_x']._Metadata # ['name', 'filename']
mgd['1_x'].filename # raises AttributeError
有什么方法可以保留这个?
更新:结语
正如here所讨论的那样,__ finalize__无法跟踪作为数据帧成员的Series,只能跟踪独立序列.因此,现在我将通过维护附加到数据帧的元数据字典来跟踪系列级元数据.我的代码看起来像:
def cust_merge(d1, d2):
"Custom merge function for 2 dicts"
...
def finalize_df(self, other, method=None, **kwargs):
for name in self._Metadata:
if method == 'merge':
lMeta = getattr(other.left, name, {})
rMeta = getattr(other.right, name, {})
newMeta = cust_merge(lMeta, rMeta)
object.__setattr__(self, name, newMeta)
else:
object.__setattr__(self, name, getattr(other, name, None))
return self
df1.filenames = {c: 'fname1.csv' for c in df1}
df2.filenames = {c: 'fname2.csv' for c in df2}
pd.DataFrame._Metadata = ['filenames']
pd.DataFrame.__finalize__ = finalize_df
解决方法:
我觉得这样的东西会起作用(如果不是这样的话,请提交一个bug报告,虽然支持它有点混淆边缘,但是有可能连接方法不会一直调用它.这有点未经测试).
DataFrame._Metadata = ['name','filename']
def __finalize__(self, other, method=None, **kwargs):
"""
propagate Metadata from other to self
Parameters
----------
other : the object from which to get the attributes that we are going
to propagate
method : optional, a passed method name ; possibly to take different
types of propagation actions based on this
"""
### you need to arbitrate when their are conflicts
for name in self._Metadata:
object.__setattr__(self, name, getattr(other, name, None))
return self
DataFrame.__finalize__ = __finalize__
因此,这将替换DataFrame的默认终结器与您的自定义终结器.在我指出的地方,您需要放置一些可以在冲突之间进行仲裁的代码.这是默认情况下不会这样做的原因,例如frame1的名称为’foo’,frame2的名称为’bar’,当方法为__add__时你会怎么做,另一种方法怎么办?让我们知道您的工作以及工作原理.
这只是替换DataFrame(如果你愿意,你可以简单地执行默认操作),这是为了自我传播;除特殊情况下,你也不能设置任何东西.