【问题标题】:How to speed up pandas row filtering by string matching?如何通过字符串匹配加速熊猫行过滤?
【发布时间】:2013-04-29 08:35:01
【问题描述】:

我经常需要通过df[df['col_name']=='string_value']过滤pandas数据框df,我想加快行选择操作,有没有快速的方法?

例如,

In [1]: df = mul_df(3000,2000,3).reset_index()

In [2]: timeit df[df['STK_ID']=='A0003']
1 loops, best of 3: 1.52 s per loop

1.52s可以缩短吗?

注意:

mul_df() 是创建多级数据框的函数:

>>> mul_df(4,2,3)
                 COL000  COL001  COL002
STK_ID RPT_Date                        
A0000  B000      0.6399  0.0062  1.0022
       B001     -0.2881 -2.0604  1.2481
A0001  B000      0.7070 -0.9539 -0.5268
       B001      0.8860 -0.5367 -2.4492
A0002  B000     -2.4738  0.9529 -0.9789
       B001      0.1392 -1.0931 -0.2077
A0003  B000     -1.1377  0.5455 -0.2290
       B001      1.0083  0.2746 -0.3934

下面是mul_df()的代码:

import itertools
import numpy as np
import pandas as pd

def mul_df(level1_rownum, level2_rownum, col_num, data_ty='float32'):
    ''' create multilevel dataframe, for example: mul_df(4,2,6)'''

    index_name = ['STK_ID','RPT_Date']
    col_name = ['COL'+str(x).zfill(3) for x in range(col_num)]

    first_level_dt = [['A'+str(x).zfill(4)]*level2_rownum for x in range(level1_rownum)]
    first_level_dt = list(itertools.chain(*first_level_dt)) #flatten the list
    second_level_dt = ['B'+str(x).zfill(3) for x in range(level2_rownum)]*level1_rownum

    dt = pd.DataFrame(np.random.randn(level1_rownum*level2_rownum, col_num), columns=col_name, dtype = data_ty)
    dt[index_name[0]] = first_level_dt
    dt[index_name[1]] = second_level_dt

    rst = dt.set_index(index_name, drop=True, inplace=False)
    return rst

【问题讨论】:

  • 为什么不将其保留为索引(或者如果它不是索引,则将其设置为索引)?在索引上选择要快得多 (df.ix['A0003']):对我来说 194 us vs 646 ms。
  • 这只是一个示例。很多时候,我们需要按列而不是索引来选择;将其设置为索引是一种选择,但它会引入其他问题(索引已存在,更改数据帧结构等)(如果我们将数据帧视为数据库表,则很常见:SELECT * FROM table_name WHERE col_name='string_value ',不是吗?)

标签: python filter pandas


【解决方案1】:

我一直想为 DataFrame 对象添加二进制搜索索引。您可以采用按列排序的 DIY 方法并自己执行此操作:

In [11]: df = df.sort('STK_ID') # skip this if you're sure it's sorted

In [12]: df['STK_ID'].searchsorted('A0003', 'left')
Out[12]: 6000

In [13]: df['STK_ID'].searchsorted('A0003', 'right')
Out[13]: 8000

In [14]: timeit df[6000:8000]
10000 loops, best of 3: 134 µs per loop

这很快,因为它总是检索视图并且不复制任何数据。

【讨论】:

  • 直观的解决方案,谢谢。希望它可以嵌入到 pandas 行选择语法中,例如 "df[df['col_name']=='string_value']"
  • sort 方法在 Pandas 19.2 及更高版本中已弃用。请改用sort_values 方法。
  • 这个答案被严重低估了。关于 SO 的几个类似答案建议使用 in1d 或 OPs 原始方法,但这比 in1d 节省了大约 23 秒,在 1M+ 行数据帧上创建了 1200 多个数据快照(in1d 需要约 24.5 秒,此方法需要约 1.5 秒)
【解决方案2】:

有点令人惊讶的是,使用 .values 数组而不是 Series 对我来说要快得多:

>>> time df = mul_df(3000, 2000, 3).reset_index()
CPU times: user 5.96 s, sys: 0.81 s, total: 6.78 s
Wall time: 6.78 s
>>> timeit df[df["STK_ID"] == "A0003"]
1 loops, best of 3: 841 ms per loop
>>> timeit df[df["STK_ID"].values == "A0003"]
1 loops, best of 3: 210 ms per loop

【讨论】:

    【解决方案3】:

    根据您之后要对选择执行的操作,如果您必须进行此类多项选择,groupby 功能也可以使事情变得更快(至少在示例中)。

    即使你只需要为一个 string_value 选择行,它也快一点(但不多):

    In [11]: %timeit df[df['STK_ID']=='A0003']
    1 loops, best of 3: 626 ms per loop
    
    In [12]: %timeit df.groupby("STK_ID").get_group("A0003")
    1 loops, best of 3: 459 ms per loop
    

    但是随后对 GroupBy 对象的调用会非常快(例如选择其他 sting_values 的行):

    In [25]: grouped = df.groupby("STK_ID")
    
    In [26]: %timeit grouped.get_group("A0003")
    1 loops, best of 3: 333 us per loop
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-14
      • 2015-09-24
      • 2019-08-22
      • 2021-09-21
      • 1970-01-01
      • 2019-02-17
      相关资源
      最近更新 更多