正如mentioned a while back by @piRSquared,以文档中建议的方式或通过geopandas' GeoDataFrame对pandas DataFrame进行子类化,开启了原始对象不必要的变异的可能性:
class SubFrame(pd.DataFrame):
def __init__(self, *args, **kwargs):
attr = kwargs.pop('attr', None)
super(SubFrame, self).__init__(*args, **kwargs)
self.attr = attr
@property
def _constructor(self):
return SubFrame
def somefunc(self):
"""Add some extended functionality."""
pass
df = pd.DataFrame([[1, 2], [3, 4]])
sf = SubFrame(df, attr=1)
sf[:] = np.nan # Modifies `df`
print(df)
# 0 1
# 0 NaN NaN
# 1 NaN NaN
容易出错的“修复”是在实例化时传递一个副本:
sf = SubFrame(df.copy(), attr=1)
但这很容易受到用户错误的影响。我的问题是:我可以在self
本身内创建class SubFrame
(传递的DataFrame)的副本吗?我该怎么做呢?
如果答案是“不”,我也很欣赏,所以我可以在浪费时间之前废弃这项努力。
大熊猫文档suggest two alternatives:
pipe
的可扩展方法链我已经彻底考虑了这两个问题,所以如果答案可以避免一般性的讨论,为什么这两个选择更好/更安全,我会很感激。
Self
不是您传递的数据帧。无论如何,您可以在init函数中执行复制。
例如
import copy
def __init__(self, farg, **kwargs):
farg = copy.deepcopy(farg)
attr = kwargs.pop('attr', None)
super().__init__(farg)
self.attr = attr
应该告诉你,farg是你传递的df。
我对子类化DataFrame知之甚少,所以如果你想保留原始的init结构,你可以复制所有的* args。不能说这种方法的安全性。
def __init__(self, *args, **kwargs):
cargs = tuple(copy.deepcopy(arg) for arg in args)
attr = kwargs.pop('attr', None)
super().__init__(*cargs, **kwargs)
self.attr = attr