我有一个用于分析我的锻炼数据的 API(我从 runkeeper的网站上抓取)。
我的主类是 a 的子类pandas.DataFrame
,它基本上是表格数据的容器。它支持按列名索引,返回列值的数组。
我想根据数据中存在的“健身活动”类型添加一些便利属性。因此,例如,我想添加一个属性“正在运行”:
@property
def running(self):
return self[self['type'] == 'running']
这将返回DataFrame
在“类型”列中具有“正在运行”的所有行。
我尝试对数据中存在的所有类型动态地执行此操作。这是我天真地做的事情:
class Activities(pandas.DataFrame):
def __init__(self,data):
pandas.DataFrame.__init__(self,data)
# The set of unique types in the 'type' column:
types = set(self['type'])
for type in types:
method = property(lambda self: self[self['type'] == type])
setattr(self.__class__,type,method)
结果是所有这些属性最终都返回了相同类型活动(“步行”)的数据表。
发生的事情是,当访问属性时,会调用 lambda,它们会在为名称“type”定义的范围内查找。他们发现它绑定到字符串 'walking',因为那是 for 循环的最后一次迭代。for 循环的每次迭代都没有自己的命名空间,因此所有 lambda 只看到最后一次迭代,而不是 'type' 实际定义时的值。
任何人都可以解决这个问题吗?我可以想到两个,但它们似乎并不特别理想:
定义
__getattr__
以检查属性是否为活动类型并返回适当的行。使用递归函数调用而不是 for 循环,以便每一级递归都有自己的命名空间。
这两个对我的口味来说都太聪明了,而且pandas.DataFrame
已经有了一个__getattr__
,如果我也做了一个,我必须小心翼翼地与之互动。并且递归会起作用,但感觉非常错误,因为类型集没有任何内在的树状结构。它是平坦的,在代码中应该看起来平坦!