2016-05-30 96 views
2

我有一個數據幀,有很多行。我正在使用自定義函數生成的數據添加一列,如下所示:熊貓,基於列值的唯一子集追加列

import numpy 

df['new_column'] = numpy.vectorize(fx)(df['col_a'], df['col_b']) 
# takes 180964.377 ms 

它工作的很好,我想要做的是加快速度。實際上只有一小組的col_acol_b的獨特組合。許多迭代是多餘的。我在想也許pandas只會自己弄清楚,但我不認爲是這樣。試想一下:

print len(df.index) #prints 127255 
df_unique = df.copy().drop_duplicates(['col_a', 'col_b']) 
print len(df_unique.index) #prints 9834 

我也相信自己可能加速通過運行這個:

df_unique['new_column'] = numpy.vectorize(fx)(df_unique['col_a'], df_unique['col_b']) 
# takes 14611.357 ms 

由於存在大量的冗餘數據,我所要做的是更新的大數據幀( df 127255行),但只需要運行fx函數的最小次數(9834次)。這是因爲col_acol_b的所有重複行。當然這意味着df中會有多個行的值爲col_acol_b,但是沒有問題,df的其他列是不同的,並且使每行都是唯一的。

在我創建一個循環的正常迭代以循環遍歷df_unique數據框並在df上執行條件更新之前,我想問一下是否有更加「pythonic」乾淨的方式來執行這種更新。非常感謝。

**更新**

我創建的簡單的上述循環,就像這樣:

df = ... 
df_unique = df.copy().drop_duplicates(['col_a', 'col_b']) 
df_unique['new_column'] = np.vectorize(fx)(df_unique['col_a'], df_unique['col_b']) 
for index, row in df_unique.iterrows():   
    df.loc[(df['col_a'] == row['col_a']) & (df['col_b'] == row['col_b']),'new_column'] = row['new_column'] 
# takes 165971.890 
這個for循環

所以可能會有輕微的性能增加,但幾乎沒有什麼,我會有預料。

FYI

這是fx功能。它查詢一個mysql數據庫。

def fx(d): 
    exp_date = datetime.strptime(d.col_a, '%m/%d/%Y') 
    if exp_date.weekday() == 5: 
     exp_date -= timedelta(days=1) 

    p = pandas.read_sql("select stat from table where a = '%s' and b_date = '%s';" % (d.col_a,exp_date.strftime('%Y-%m-%d')),engine) 
    if len(p.index) == 0: 
     return None 
    else: 
     return p.iloc[0].close 
+0

'col_a','col_b'中有什麼樣的數據?他們已經排序了嗎? – ptrj

+0

它們都是字符串,但col_b是日期字符串。相當肯定他們是排序的。他們似乎是。 –

回答

1

UPDATE:

,如果你能設法讀了你的三列['stat','a','b_date']屬於table表爲tab DF,那麼你可以像這樣把它合併:

tab = pd.read_sql('select stat,a,b_date from table', engine) 
df.merge(tab, left_on=[...], right_on=[...], how='left') 

OLD回答:

你可以合併/與原df DF加入您的預先計算df_unique DF:

df['new_column'] = df.merge(df_unique, on=['col_a','col_b'], how='left')['new_column'] 
+0

非常好,很快,謝謝。我測量了14960毫秒,相比之下,更新'df_unique' DF的結果大致相同,因此更多的是我期待的可能性。 –

+1

@jeffery_the_wind,請考慮[接受](http://meta.stackexchange.com/a/5235)最有用的答案 - 這也將表明您的問題已得到解答。 PS我也建議你發佈你的'fx'函數的代碼,所以社區可以嘗試優化它... – MaxU

+0

我只是沒有時間去經歷一切,但這個答案是最簡單的速度真的很好。我也認爲用dataframe.apply替換numpy.vectorize,從另一個答案也略微提高了速度。 –

1

MaxU的答案可能已經你想要的東西。但我會展示另一種方法,可能會更快一點(我沒有測量)。

我認爲:

  1. df[['col_a', 'col_b']]進行排序,以便所有相同的條目是在連續的行(這很重要)

  2. df具有唯一索引(如果沒有,你可能會創建一些臨時唯一索引)。

我會用事實df_unique.indexdf.index一個子集。

# (keep='first' is actually default) 
df_unique = df[['col_a', 'col_b']].drop_duplicates(keep='first').copy() 

# You may try .apply instead of np.vectorize (I think it may be faster): 
df_unique['result'] = df_unique.apply(fx, axis=1) 

# Main part: 
df['result'] = df_unique['result']      # uses 2. 
df['result'].fillna(method='ffill', inplace=True)  # uses 1. 
+0

用於「應用」功能。我已經看到了這一點,但沒有意識到它會把整行作爲論據。我認爲填寫方法對我來說並不完美。我想我沒有提到該函數有時會返回'None',所以有一些行需要保持NaN,但是我認爲這個填充會填滿它們。無論如何,感謝您的幫助。 –

+0

@jeffery_the_wind哦,我明白了。解決方法是將'df_unique'中的'None'填充爲中性值(例如-1),然後分配給'df','fillna',並將-1更改回None/np.nan。但是,除此之外,整個解決方案是相當黑客。如果你不需要更多的加速,使用'merge'就更安全了。 – ptrj