通过联接传播熊猫系列元数据

bea*_*rdc 4 python metadata pandas

我希望能够将元数据附加到一系列数据帧(特别是原始文件名)上,以便在加入两个数据帧之后,我可以看到每个系列来自何处的元数据。

我看到关于GitHub的问题_metadata在这里在这里),包括一些与当前_metadata属性(在这里),但没有在大熊猫文档。

到目前为止,我可以修改_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
Run Code Online (Sandbox Code Playgroud)

有什么办法可以保留吗?

更新:结语

作为讨论在这里__finalize__无法跟踪系列是一个数据帧的成员中,只有独立的系列。因此,现在我将通过维护附加到数据帧的元数据字典来跟踪系列级别的元数据。我的代码如下:

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
Run Code Online (Sandbox Code Playgroud)

Jef*_*eff 5

我认为像这样的事情会起作用(如果不能,请这样提交一个错误报告,尽管受支持有些前沿,但是现在联接方法可能不会一直调用它。这有点未经测试) )。

看到这个问题的更详细的例子/错误修复。

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__
Run Code Online (Sandbox Code Playgroud)

因此,这用您的自定义变量替换了DataFrame的默认终结器。在我已经指出的地方,您需要放置一些可以在冲突之间进行仲裁的代码。这就是默认情况下不执行此操作的原因,例如frame1的名称为“ foo”,frame2的名称为“ bar”,当该方法为时,您会怎么做__add__,另一种方法又如何呢?让我们知道您的工作及其运作方式。

这仅替代了DataFrame(如果需要,您可以简单地执行默认操作),从而将其他对象推广给自己;您也不能设置任何东西,除非在特殊情况下使用方法。

如果要使用子类,则应覆盖此方法,这就是为什么要在此处修补猴子(而不是子类,这在大多数情况下是过大的)。