0

我有一个用于分析我的锻炼数据的 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' 实际定义时的值。

任何人都可以解决这个问题吗?我可以想到两个,但它们似乎并不特别理想:

  1. 定义__getattr__以检查属性是否为活动类型并返回适当的行。

  2. 使用递归函数调用而不是 for 循环,以便每一级递归都有自己的命名空间。

这两个对我的口味来说都太聪明了,而且pandas.DataFrame已经有了一个__getattr__,如果我也做了一个,我必须小心翼翼地与之互动。并且递归会起作用,但感觉非常错误,因为类型集没有任何内在的树状结构。它是平坦的,在代码中应该看起来平坦!

4

2 回答 2

2

修改lambda以将值拉入新范围。

method = property(lambda self=self, type=type: self[self['type'] == type])
于 2012-05-01T04:15:00.973 回答
0

老实说,如果可以避免的话,我建议不要创建 DataFrame 的子类。根据我的经验,古老的 Java 格言“偏好组合胜于继承”往往更可取。

于 2012-05-01T21:17:26.173 回答