2017-07-20 125 views
7

我基本上想學習更快的方式來切片基於正則表達式的條件切片熊貓數據框。例如,下面的DF(有一個在string_column超過4個變型中,它們僅用於說明的目的):切片與字符串匹配的熊貓行緩慢

index, string_col1, string_col2, value 
0, 'apple', 'this', 10 
1, 'pen', 'is', 123 
2, 'pineapple', 'sparta', 20 
3, 'pen pineapple apple pen', 'this', 234 
4, 'apple', 'is', 212 
5, 'pen', 'sparta', 50 
6, 'pineapple', 'this', 69 
7, 'pen pineapple apple pen', 'is', 79 
8, 'apple pen', 'sparta again', 78 
... 
100000, 'pen pineapple apple pen', 'this is sparta', 392 

我有根據使用的正則表達式的string_column做布爾條件切片,而發現具有最小的指數並在值列中輸入最大值,然後最後找到最小值和最大值之間的差值。我這樣做是通過下面的方法,但它是超慢的時候我一定要搭配很多不同的正則表達式模式:

pat1 = re.compile('apple') 
pat2 = re.compile('sparta') 
mask = (df['string_col1'].str.contains(pat1)) & (df['string_col2'].str.contains(pat2)) 
max_idx = df[mask].idxmax() 
min_idx = df[mask].idxmin() 
difference = df['value'].loc[max_idx] - df['value'].loc[min_idx] 

我想獲得一個「差」的答案,我切了DF過很多次,但我無法弄清楚如何少做。此外,還有更快的方法來切片嗎?

這是一個優化問題,因爲我知道我的代碼得到我所需要的。任何提示將不勝感激!

+0

可以將正則表達式組合成一個正則表達式,然後掩碼可能會更快。 patX = re.compile( '(蘋果| sprata)')。這是否使它更快?另外,在整個DataFrame上製作一個掩碼來獲取第一個索引可能不是最快的。 –

+0

我需要爲兩個不同的列進行兩種不同的模式檢查,所以我不確定將它們組合成一個正則表達式,並且跨兩列匹配是個不錯的主意。 –

+0

看起來非常類似於https://stackoverflow.com/questions/40183800/pandas-difference-between-largest-and-smallest-value-within-group –

回答

1

通過每個面具到數據幀的下一個子集,每一個新的濾波發生在原始數據幀的一個較小的子集:

pat1 = re.compile('apple') 
pat2 = re.compile('sparta') 
mask1 = df['string_col1'].str.contains(pat1) 
mask = (df[mask1]['string_col2'].str.contains(pat2)) 
df1=df[mask1][mask] 
max_idx = df1['value'].idxmax() 
min_idx = df1['value'].idxmin() 
a,b=df1['value'].loc[max_idx],df1['value'].loc[min_idx] 
+0

你能解釋爲什麼這會更快? –

+0

,因爲每個新的過濾發生在原始數據幀的較小子集上 – denfromufa

+0

這是一個理智的想法,但如果所有模式匹配都不會有多大幫助 – ead

1

我一直在試圖分析您的例子,但我卻越來越漂亮我的綜合數據有很好的表現,所以我可能需要一些澄清。 (另外,出於某種原因,每當我在我的數據框中有一個字符串時,.idxmax()會中斷我)。

這裏是我的測試代碼:

import pandas as pd 
import re 
import numpy as np 
import random 
import IPython 
from timeit import default_timer as timer 

possibilities_col1 = ['apple', 'pen', 'pineapple', 'joseph', 'cauliflower'] 
possibilities_col2 = ['sparta', 'this', 'is', 'again'] 
entries = 100000 
potential_words_col1 = 4 
potential_words_col2 = 3 
def create_function_col1(): 
    result = [] 
    for x in range(random.randint(1, potential_words_col1)): 
     result.append(random.choice(possibilities_col1)) 
    return " ".join(result) 

def create_function_col2(): 
    result = [] 
    for x in range(random.randint(1, potential_words_col2)): 
     result.append(random.choice(possibilities_col2)) 
    return " ".join(result) 

data = {'string_col1': pd.Series([create_function_col1() for _ in range(entries)]), 
     'string_col2': pd.Series([create_function_col2() for _ in range(entries)]), 
     'value': pd.Series([random.randint(1, 500) for _ in range(entries)])} 


df = pd.DataFrame(data) 
pat1 = re.compile('apple') 
pat2 = re.compile('sparta') 
pat3 = re.compile('pineapple') 
pat4 = re.compile('this') 
#IPython.embed() 
start = timer() 
mask = df['string_col1'].str.contains(pat1) & \ 
     df['string_col1'].str.contains(pat3) & \ 
     df['string_col2'].str.contains(pat2) & \ 
     df['string_col2'].str.contains(pat4) 
valid = df[mask] 
max_idx = valid['value'].argmax() 
min_idx = valid['value'].argmin() 
#max_idx = result['max'] 
#min_idx = result['min'] 
difference = df.loc[max_idx, 'value'] - df.loc[min_idx, 'value'] 
end = timer() 
print("Difference: {}".format(difference)) 
print("# Valid: {}".format(len(valid))) 
print("Time Elapsed: {}".format(end-start)) 

你能解釋一下你有多少條件申請? (我添加的每個正則表達式只會增加大致線性增加的時間(即2到3次正則表示運行時間增加1.5倍))。我還對條目數量和潛在字符串長度(potential_words變量)進行了線性縮放。

作爲參考,此代碼在我的機器上以~15秒爲單位進行評估(100萬條需要〜1.5秒)。

編輯:我是一個白癡,並沒有做同樣的事情你是我(在數據集中的最小和最大指數值,而不是最小值和最大值之間的差異),但修復它並沒有真正增加運行時的方式。

編輯2:在示例代碼中,idxmax()如何知道哪一列要選擇最大值?

0

我認爲使用你的面具縮小你的數據框,然後在那個較小的框架上執行一組更簡潔的操作將會有很大的幫助。查找索引只使用那些作爲查找是不必要的 - 只要找到最大/最小徹底:

pat1 = re.compile('apple') 
pat2 = re.compile('sparta') 
mask = (df['string_col1'].str.contains(pat1)) & (df['string_col2'].str.contains(pat2)) 

result = df.loc[mask, 'value'] 
difference = result.max() - result.min() 
+0

你在哪裏縮小df,我沒有看到它? – denfromufa

+0

OP爲他執行的每項操作重新過濾他的整個DF。我執行一次過濾器,然後對較小的結果集進行操作。參見'result = df.loc [mask,'value']'。 – jack6e

+0

這是便宜的操作,請嘗試測量 – denfromufa

2

您可以通過不使用&scipy.logical_and()代替

a = pd.Series(sp.rand(10000) > 0.5) 
b = pd.Series(sp.rand(10000) > 0.5) 

%timeit sp.logical_and(a.values,b.values) 
100000 loops, best of 3: 6.31 µs per loop 

%timeit a & b 
1000 loops, best of 3: 390 µs per loop 
速度的50倍邏輯比較
+0

這不是scipy.logical_and()的函數,而是使用.values()。用a.values&b.values重試%timeit,你就可以得到同樣的時間。 –

+0

確實。意外!感謝您指出了這一點。關於程序的問題,我現在應該刪除這個答案嗎? –