2017-03-02 119 views
1

第一:也許這適合代碼審查更好,但我認爲這裏是更多熊貓仿射用戶。隨意移動它,如果你認爲否則根據其他列創建新列:DRY並啓用功能

這是常有的情況下,一個要計算一個新列了現有的: 考慮以下大熊貓據幀

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」這會相比,迭代的方法,在大型dataframes執行相當快也呈現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) 

另一個函數的值賦給列:

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 

最後,我們可以定義condtion-to-values mappin G作爲

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。其次,採用輔助函數apply它接受的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這裏獲得更好的可讀性。您現在可以向您的trans字典提供任何列條件值組合。

但是,我們還是有點違反DRY,因爲像A == 1這樣的每個原子條件都會被多次評估,而不是像您提供的示例那樣只評估一次。但我想會有解決方法來有效地記住這些布爾序列。

+0

嘿,我真的很喜歡你的想法,它很簡單,映射的定義比較精確。但是,我還不確定我是否在尋找更多功能的解決方案。我想,我的解決方案可以通過使用運算符模塊和reduce函數來改進,以允許任意多個條件。但映射/轉換可能不太可讀。 – Quickbeam2k1