【问题标题】:pandas: How to limit the results of str.contains?pandas:如何限制 str.contains 的结果?
【发布时间】:2018-08-24 16:04:20
【问题描述】:

我有一个超过 1M 行的 DataFrame。我想选择某个列包含某个子字符串的所有行:

matching = df['col2'].str.contains('substr', case=True, regex=False)
rows = df[matching].col1.drop_duplicates()

但是这个选择很慢,我想加快速度。假设我只需要前 n 个结果。在获得 n 个结果后,有没有办法停止 matching?我试过了:

matching = df['col2'].str.contains('substr', case=True, regex=False).head(n)

和:

matching = df['col2'].str.contains('substr', case=True, regex=False).sample(n)

但它们并没有更快。第二条语句是布尔型的并且非常快。如何加快第一条语句的速度?

【问题讨论】:

    标签: python performance pandas contains


    【解决方案1】:

    信不信由你,但 .str 访问器很慢。您可以使用性能更好的列表推导。

    df = pd.DataFrame({'col2':np.random.choice(['substring','midstring','nostring','substrate'],100000)})
    

    相等性检验

    all(df['col2'].str.contains('substr', case=True, regex=False) ==
        pd.Series(['substr' in i for i in df['col2']]))
    

    输出:

    True
    

    时间安排:

    %timeit df['col2'].str.contains('substr', case=True, regex=False)
    10 loops, best of 3: 37.9 ms per loop
    

    %timeit pd.Series(['substr' in i for i in df['col2']])
    100 loops, best of 3: 19.1 ms per loop
    

    【讨论】:

      【解决方案2】:

      你可以加快速度:

      matching = df['col2'].head(n).str.contains('substr', case=True, regex=False)
      rows = df['col1'].head(n)[matching==True]
      

      但是,此解决方案将检索第一个 n 行中的匹配结果,而不是第一个 n 匹配结果。

      如果您确实想要第一个 n 匹配结果,您应该使用:

      rows =  df['col1'][df['col2'].str.contains("substr")==True].head(n)
      

      但是这个选项当然要慢得多。

      受@ScottBoston 的回答启发,您可以使用以下方法获得更快的解决方案

      rows = df['col1'][pd.Series(['substr' in i for i in df['col2']])==True].head(n)
      

      这比使用此选项显示整个结果要快,但没有那么快。使用此解决方案,您可以获得第一个n 匹配结果。

      通过下面的测试代码,我们可以看到每个解决方案的速度及其结果:

      import pandas as pd
      import time
      
      n = 10
      a = ["Result", "from", "first", "column", "for", "this", "matching", "test", "end"]
      b = ["This", "is", "a", "test", "has substr", "also has substr", "end", "of", "test"]
      
      col1 = a*1000000
      col2 = b*1000000
      
      df = pd.DataFrame({"col1":col1,"col2":col2})
      
      # Original option
      start_time = time.time()
      matching = df['col2'].str.contains('substr', case=True, regex=False)
      rows = df[matching].col1.drop_duplicates()
      print("--- %s seconds ---" % (time.time() - start_time))
      
      # Faster option
      start_time = time.time()
      matching_fast = df['col2'].head(n).str.contains('substr', case=True, regex=False)
      rows_fast = df['col1'].head(n)[matching==True]
      print("--- %s seconds for fast solution ---" % (time.time() - start_time))
      
      
      # Other option
      start_time = time.time()
      rows_other =  df['col1'][df['col2'].str.contains("substr")==True].head(n)
      print("--- %s seconds for other solution ---" % (time.time() - start_time))
      
      # Complete option
      start_time = time.time()
      rows_complete = df['col1'][pd.Series(['substr' in i for i in df['col2']])==True].head(n)
      print("--- %s seconds for complete solution ---" % (time.time() - start_time))
      

      这将输出:

      >>> 
      --- 2.33899998665 seconds ---
      --- 0.302999973297 seconds for fast solution ---
      --- 4.56700015068 seconds for other solution ---
      --- 1.61599993706 seconds for complete solution ---
      

      结果系列将是:

      >>> rows
      4     for
      5    this
      Name: col1, dtype: object
      >>> rows_fast
      4     for
      5    this
      Name: col1, dtype: object
      >>> rows_other
      4      for
      5     this
      13     for
      14    this
      22     for
      23    this
      31     for
      32    this
      40     for
      41    this
      Name: col1, dtype: object
      >>> rows_complete
      4      for
      5     this
      13     for
      14    this
      22     for
      23    this
      31     for
      32    this
      40     for
      41    this
      Name: col1, dtype: object
      

      【讨论】:

      • 这并不能真正回答我的问题。我对限制搜索空间持怀疑态度:这显然会提高性能,但会以牺牲结果为代价。但是,在尝试了 n=10000 的“更快”解决方案后,结果还不错,而且时间改进很显着。但最后,我无法部署这个“更快”的解决方案,因为它假定前 n 个结果中会有匹配,这可能不是真的!我将编辑我的问题以澄清这一点。
      • 是的,我想你想要第一个 n 匹配而不是第一个 n 行中的匹配。如果有帮助你的话,我会检查一种改进时间的方法。也许@ScottBoston 的回答是一个相当不错的解决方案
      • 请注意,您的解决方案还会返回前 n 行中的匹配项。
      • 没错。实际上,您的“其他”解决方案会返回前 n 个匹配项,但它比根本不使用 .head() 慢,即不限制搜索。
      • 请看我的更新。我相信“完整的解决方案”是一个相当不错的方法。
      猜你喜欢
      • 2019-02-06
      • 2021-03-20
      • 2018-03-30
      • 1970-01-01
      • 2011-06-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多