【问题标题】:Pandas dataframe.query to SQL 'LIKE' WITHOUT using engine='python'Pandas dataframe.query 到 SQL 'LIKE' 不使用 engine='python'
【发布时间】:2019-12-03 00:27:12
【问题描述】:

在 Pandas dataframe.query() 中有很多复制 SQL“LIKE”条件的相关示例。但是,我发现的所有示例基本上都归结为使用column.str.contains(),这需要额外的engine='python'

我正在处理非常大的数据集、频繁的查询和复杂的 WHERE 条件。在我的用例中,使用 python 引擎对性能的影响很快就会增加。

是否存在使用标准(和超快速)numexpr 引擎的等效句法?我知道我可以使用 .query() 之外的其他方法,但是对于复杂的查询,它们可能会变得非常冗长。

【问题讨论】:

    标签: python sql python-3.x pandas


    【解决方案1】:

    让我们先设置一些示例数据:

    import pandas as pd
    import string
    import numexpr as ne
    
    n = 10_000_000
    k = 5
    df = pd.DataFrame({'col1': pd.np.random.choice(list(string.ascii_lowercase),size=n*k).view((pd.np.str_,k)),
                       'col2': pd.np.random.random(n)})
    

    为了使containsnumexpr 引擎一起工作,您必须添加.values。但是,查询仍然很慢,原因是不是.values 操作,正如您在python 引擎的使用和不使用.values 的比较中看到的那样:

    %timeit df.query("col1.str.contains('a').values", engine='numexpr')
    #1 loop, best of 3: 3.48 s per loop
    
    %timeit df.query("col1.str.contains('a')", engine='python')
    #1 loop, best of 3: 3.55 s per loop
    
    %timeit df.query("col1.str.contains('a').values", engine='python')
    #1 loop, best of 3: 3.52 s per loop
    

    numexpr 的性能优势可以忽略不计,但是对于相对简单的查询(另请参阅https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#performance-of-query)。正如我们所见,.values 只增加了大约 60 毫秒:

    %timeit df.query("col2.values < .5", engine='numexpr')
    1 loop, best of 3: 363 ms per loop
    
    %timeit df.query("col2 < .5", engine='numexpr')
    1 loop, best of 3: 298 ms per loop
    
    %timeit df.query("col2 < .5", engine='python')
    1 loop, best of 3: 299 ms per loop
    


    Numexpr does support contains 但它没有在 pandas 中实现。一个 - 诚然不是很优雅 - 解决方法是将值从 pandas 复制到数组中,然后使用 numexpr.evaluate 进行布尔索引:

    %timeit df.query("col1.str.contains('a').values & (col2 < .5)", engine='numexpr')
    #1 loop, best of 3: 3.53 s per loop
    
    %timeit df.query("col1.str.contains('a') & (col2 < .5)", engine='python')
    #1 loop, best of 3: 3.45 s per loop
    
    %%timeit
    arr1 = df.col1.to_numpy(bytes)
    arr2 = df.col2.to_numpy()
    df[ne.evaluate("contains(arr1, 'a') & (arr2 < .5)")]
    #1 loop, best of 3: 1.18 s per loop
    

    这比 pandas 版本快大约 3 倍,包括复制到数组和布尔索引的开销。 ne.evaluate("contains(arr1, 'a') &amp; (arr2 &lt; .5)") 仅需要 280 毫秒。

    【讨论】:

      猜你喜欢
      • 2017-11-20
      • 2011-01-21
      • 1970-01-01
      • 1970-01-01
      • 2012-06-09
      • 1970-01-01
      • 2019-02-04
      • 2023-03-11
      • 2016-09-14
      相关资源
      最近更新 更多