2017-03-02 8 views
1

最初に:これはコードレビューの方が適しているかもしれませんが、ここではより多くのパンダのアフィンユーザーです。あなたが考える場合、それはよくあるそう他の列に応じて新しい列を作成してください:DRYと機能します

、1つは、既存のものから新しい列を計算したいことを自由に動くことを感じる: 次パンダを考えてみましょうDATAFRAME

df = pd.DataFrame({'ItemNumber':np.arange(1, 10), 
    'A':[1,1,1,2,2,2,3,3,3], 'B': [1,2,3]*3}) 
print(df) 
    A B ItemNumber 
0 1 1   1 
1 1 2   2 
2 1 3   3 
3 2 1   4 
4 2 2   5 
5 2 3   6 
6 3 1   7 
7 3 2   8 
8 3 3   9 

に我々が計算しましょう

df.loc[(df["A"] == 1) & (df["B"] > 1), 'C'] = 1 
df.loc[(df["B"] == 1) & (df["A"] > 1), 'C'] = 2 
df.loc[(df["A"] > 1) & (df["B"] > 1), 'C' ] = 3 
df.loc[(df["A"] == 1) & (df["B"] == 1), 'C' ] = 4 

を経由して新しい列「C」反復的なアプローチと比較すると、これが大きなデータフレームの上にも非常に高速実行するには、hereを発表しました。 特に、そのアプローチのパフォーマンスに関する問題は、上記のコードにつながります。

ただし、このコードはDRYの原則に違反します。コピー貼り付けは気分がいい。上、私たちは私たちの比較を定義し、そこから

def col_equals_value(col, value): 
    def filter_df(df): 
     return df[col] == value 
    return filter_df 

def col_greater_value(col, value): 
    def filter_df(df): 
     return df[col] > value 
    return filter_df 

a1 = col_equals_value('A', 1) 
b1 = col_equals_value('B', 1) 
agt1 = col_greater_value('A', 1) 
bgt1 = col_greater_value('B', 1) 

別の機能がに値を割り当てるために必要とされる

それでは、もう少し機能を手放すと2つのカリー化関数を定義列:

def assign_value(cond_1, cond_2, value): 
    def assign_col_value(df, col): 
     df.loc[df.apply(cond_1, axis=1) & df.apply(cond_2, axis=1), col] =value 
    return assign_col_value 

最後に、値のマッチンを定義することができます

mapping = [(a1, b1, 4), 
      (a1, bgt1, 1), 
      (agt1, b1, 2), 
      (agt1, bgt1, 3)] 

としてグラムはassign_value_functions

m = [assign_value(x, y, z) for (x,y,z) in mapping] 

を構築し、データフレームに各機能を適用します。

for f in m: 
    f(df, 'C') 
print(df) 

    A B ItemNumber 
0 1 1   1 
1 1 2   2 
2 1 3   3 
3 2 1   4 
4 2 2   5 
5 2 3   6 
6 3 1   7 
7 3 2   8 
8 3 3   9 

だから、質問は何ですか? このアプローチはあまりスケーラブルではないようです。各比較演算子について、全く新しい関数を定義する必要があるようです。比較演算子を変数にすることはできますか? 現在、私は&演算子で連結された2つの条件のみをサポートしています。それを一般化する方法? 私はapplyメソッドの呼び出しについては不明です。もっと簡単な方法があるはずです。

すべてのヘルプは

答えて

1

あなたがここにpandas.DataFrame.evalを利用することができ大歓迎です。まず、適用する変換を含む辞書transを定義します。私はパンダを使用することが正当化されるより大きな読みやすくするためにここににeval思い

trans = {"C": {"A == 1 and B > 1": 1, 
       "B == 1 and A > 1": 2, 
       "A > 1 and B > 1": 3, 
       "A == 1 and B == 1": 4}} 

def apply(sub_df, trans_dict): 
    # sub_df = sub_df.copy() # in case you don't want change original df 
    for column, transforms in trans_dict.items(): 
     for transform, value in transforms.items(): 
      sub_df.loc[sub_df.eval(transform), column] = value 

    return sub_df 

apply(df, trans) 

    A B ItemNumber C 
0 1 1 1   4.0 
1 1 2 2   1.0 
2 1 3 3   1.0 
3 2 1 4   2.0 
4 2 2 5   3.0 
5 2 3 6   3.0 
6 3 1 7   2.0 
7 3 2 8   3.0 
8 3 3 9   3.0 

:第二に、evalのを活用するヘルパー関数applyを採用。 trans dictに任意の列条件値の組み合わせを指定できるようになりました。

しかし、A == 1のようなすべての原子条件は、提供されている例のように複数回評価されるのではなく、DRYに違反しています。しかし、私は、これらの論理的なシリーズを効率的に暗記するための回避策があると思います。

+0

ねえ、私は本当にあなたのアイデアが好きです、それはまっすぐだ、マッピングは比較的正確に定義されています。しかし、もっと機能的なソリューションを探しているかどうかはまだ分かりません。私は、任意の多くの条件を可能にするために、演算子モジュールとreduce関数を使用して、私の解を改善できると思います。しかし、マッピング/変換はあまり読みにくいかもしれません。 – Quickbeam2k1

関連する問題