運動データを分析するためのAPIがあります(これは、ランニングキーパーのWebサイトをスクレイプしています)。
私のメインクラスは、pandas.DataFrame
基本的に表形式データのコンテナであるのサブクラスです。列名によるインデックス付けをサポートし、列値の配列を返します。
データに存在する「フィットネス活動」のタイプに基づいて、いくつかの便利なプロパティを追加したいと思います。たとえば、プロパティ'running'を追加したいと思います。
@property
def running(self):
return self[self['type'] == 'running']
DataFrame
これは、「type」列に「running」があるのすべての行を返します。
データに存在するすべてのタイプに対してこれを動的に実行しようとしました。これが私が素朴にしたことです:
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)
その結果、これらのプロパティはすべて、同じタイプのアクティビティ(「ウォーキング」)のデータのテーブルを返すことになりました。
何が起こっているのかというと、プロパティにアクセスするとラムダが呼び出され、定義されたスコープで「type」という名前が検索されます。それがforループの最後の反復だったので、彼らはそれが文字列'walking'にバインドされていることを発見しました。forループの各反復には独自の名前空間がないため、すべてのラムダは、実際に定義されたときに「type」が持っていた値ではなく、最後の反復のみを参照します。
誰かがこれを回避する方法はありますか?私は2つ考えることができますが、それらは特に理想的ではないようです。
__getattr__
属性がアクティビティタイプであることを確認し、適切な行を返すように定義します。forループの代わりに再帰関数呼び出しを使用して、再帰の各レベルが独自の名前空間を持つようにします。
どちらも私の好みには少し賢すぎて、私も作った場合は慎重にやりとりしなければならないことがpandas.DataFrame
すでにあります。__getattr__
再帰は機能しますが、型のセットには固有のツリーのような構造がないため、非常に間違っていると感じます。それはフラットであり、コードではフラットに見えるはずです!