我有一个 API 用于分析我的锻炼数据(我抓取的数据)跑卫 http://runkeeper.com/的网站)。
我的主类是一个子类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”所具有的值。
任何人都可以解决这个问题吗?我能想到两个,但它们似乎不是特别理想:
define __getattr__
检查该属性是否为活动类型并返回适当的行。
使用递归函数调用而不是 for 循环,以便每一级递归都有自己的命名空间。
这两个对于我的口味来说都有点太聪明了,而且pandas.DataFrame
已经有一个__getattr__
如果我也做了一个,我就必须谨慎地与之互动。递归可以工作,但感觉非常错误,因为类型集没有任何内在的树状结构。它是扁平的,并且在代码中看起来应该是扁平的!