切换Pandas行与字符串匹配慢

时间:2017-07-20 17:14:20

标签: python pandas numpy optimization

我基本上想学习一种基于正则表达式的条件切片切片Pandas数据帧的更快方法。例如,以下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进行了多次切片,但我无法弄清楚如何做得少。此外,有更快的方法来切片吗?

这是一个优化问题,因为我知道我的代码能够满足我的需要。任何提示将不胜感激!

4 个答案:

答案 0 :(得分:2)

您可以通过不使用&而不是scipy.logical_and()代替

来将逻辑比较速度提高50倍
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

答案 1 :(得分: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]

答案 2 :(得分: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()如何知道在示例代码中选择最大值的列?

答案 3 :(得分: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()