我想为我的DataFrame添加一个唯一的ID,我基本上成功地使用了我在这里找到的Python Class Decorator。我在这里https://github.com/pydata/pandas/issues/2485知道添加自定义元数据尚未明确支持,但装饰器似乎是一种解决方法。
当我使用copy和groupby.agg等方法时,我装饰的DataFrames返回新的和类似装饰的DataFrame。如何让“全部”pandas函数(如pd.DataFrame()或pd.read_csv返回我的装饰DataFrames而不是原始的,未修饰的DataFrames而不单独装饰每个pandas函数?即,如何让我的装饰DataFrames替换库存DataFrames?
这是我的代码。首先,我有一个增强的pandas模块wrapPandas.py。
from pandas import *
import numpy as np
def addId(cls):
class withId(cls):
def __init__(self, *args, **kargs):
super(withId, self).__init__(*args, **kargs)
self._myId = np.random.randint(0,99999)
return withId
pandas.core.frame.DataFrame = addId(pandas.core.frame.DataFrame)
当我使用.copy()和.groupby()。agg()等方法时,运行以下代码片段会显示我的DataFrame返回修饰的DataFrame。然后我会通过显示像pd.DataFrame这样的pandas函数不返回我装饰的DataFrames来进行跟进(遗憾的是,这并不奇怪)。
编辑:根据Jonathan Eunice的回复添加了进口声明。
import wrapPandas as pd
d = {
'strCol': ['A', 'B', 'A', 'C', 'B', 'B', 'A', 'C', 'A'],
'intCol': [6,3,8,6,7,3,9,2,6],
}
#create "decorated" DataFrame
dfFoo = pd.core.frame.DataFrame.from_records(d)
print("dfFoo._myId = {}".format(dfFoo._myId))
#new DataFrame with new ._myId
dfBat = dfFoo.copy()
print("dfBat._myId = {}".format(dfBat._myId))
#new binding for old DataFrame, keeps old ._myId
dfRat = dfFoo
print("dfRat._myId = {}".format(dfRat._myId))
#new DataFrame with new ._myId
dfBird = dfFoo.groupby('strCol').agg({'intCol': 'sum'})
print("dfBird._myId = {}".format(dfBird._myId))
#all of these new DataFrames have the same type, "withId"
print("type(dfFoo) = {}".format(type(dfFoo)))
这会产生以下结果。
dfFoo._myId = 66622
dfBat._myId = 22527
dfRat._myId = 66622
dfBird._myId = 97593
type(dfFoo) = <class 'wrapPandas.withId'>
悲伤的部分。 dfBoo._myId
当然会提出AttributeError
。
#create "stock" DataFrame
dfBoo = pd.DataFrame(d)
print(type(dfBoo))
#doesn't have a ._myId (I wish it did, though)
print(dfBoo._myId)
答案 0 :(得分:1)
将您的猴子补丁修改为:
pd.DataFrame = pandas.core.frame.DataFrame = addId(pandas.core.frame.DataFrame)
即。所以你是&#34;锁定&#34;或者&#34;猴子补丁&#34;两个不同的名字。
鉴于pandas.core.frame.DataFrame is pd.DataFrame
,这种双重分配的需要可能看起来很奇怪。但是你实际上并没有修改DataFrame
类。您正在注入代理类。无论对代理的引用是什么。直接到原始类的那些没有获得代理行为。通过让您可能想要使用的所有名称指向代理来更改它。
以下是图形化的方式:
我假设您的文件中某处import pandas as pd
未显示,但dfBoo
的定义会因NameError: name 'pd' is not defined
而失败。
由于这样的原因,猴子修补很危险。你注射了东西......而且你不可能知道你是否抓住了所有的参考文献&#34;或者&#34;修补你需要的一切。&#34;我不能保证在代码中不会有其他调用,这些调用的地址低于此名称重新组合的结构。但是对于显示的代码,它的工作原理!
更新您稍后询问了如何为pd.read_csv
开展此工作。嗯,这可能是你需要修补的另一个地方。在这种情况下,请将上面的补丁代码修改为:
pd.DataFrame = pandas.io.parsers.DataFrame = pandas.core.frame.DataFrame = addId(pandas.core.frame.DataFrame)
在DataFrame
内修补pandas.io.parsers.DataFrame
的定义将为read_csv
提供帮助。同样的警告适用:可能有(也可能是)更多用途,您需要追踪全面覆盖。