【问题标题】:How to select rows in a DataFrame between two values, in Python Pandas?如何在 Python Pandas 中的两个值之间选择 DataFrame 中的行?
【发布时间】:2015-10-15 13:23:37
【问题描述】:

我正在尝试将 DataFrame df 修改为仅包含 closing_price 列中的值介于 99 和 101 之间的行,并尝试使用下面的代码执行此操作。

但是,我得到了错误

ValueError:Series 的真值不明确。使用 a.empty、a.bool()、a.item()、a.any() 或 a.all()

我想知道是否有办法在不使用循环的情况下做到这一点。

df = df[(99 <= df['closing_price'] <= 101)]

【问题讨论】:

  • 这里的问题是您不能将标量与数组进行比较,因此会出现错误,对于比较,您必须使用按位运算符并将它们括在括号中,因为运算符优先级
  • df.querypd.eval 似乎很适合这个用例。有关pd.eval() 系列函数、它们的特性和用例的信息,请访问Dynamic Expression Evaluation in pandas using pd.eval()

标签: python pandas


【解决方案1】:

您应该使用() 对您的布尔向量进行分组以消除歧义。

df = df[(df['closing_price'] >= 99) & (df['closing_price'] <= 101)]

【讨论】:

    【解决方案2】:

    还有一个更好的选择——使用query() 方法:

    In [58]: df = pd.DataFrame({'closing_price': np.random.randint(95, 105, 10)})
    
    In [59]: df
    Out[59]:
       closing_price
    0            104
    1             99
    2             98
    3             95
    4            103
    5            101
    6            101
    7             99
    8             95
    9             96
    
    In [60]: df.query('99 <= closing_price <= 101')
    Out[60]:
       closing_price
    1             99
    5            101
    6            101
    7             99
    

    更新:回复评论:

    我喜欢这里的语法,但在尝试与 表达; df.query('(mean + 2 *sd) <= closing_price <=(mean + 2 *sd)')

    In [161]: qry = "(closing_price.mean() - 2*closing_price.std())" +\
         ...:       " <= closing_price <= " + \
         ...:       "(closing_price.mean() + 2*closing_price.std())"
         ...:
    
    In [162]: df.query(qry)
    Out[162]:
       closing_price
    0             97
    1            101
    2             97
    3             95
    4            100
    5             99
    6            100
    7            101
    8             99
    9             95
    

    【讨论】:

    • 我喜欢这里的语法,但在尝试与表达式结合时失败了; df.query('(mean + 2 *sd)
    • @mappingdom,meansd 是什么?这些是列名吗?
    • 不,它们是存储为浮点数的计算平均值和标准差
    • @mappingdom,你说的“存储”是什么意思?
    • @ManojKumar, df.query('closing_price.between(99, 101, inclusive=True)', engine="python") - 但这会比“numexpr”引擎慢。
    【解决方案3】:

    也可以考虑series between

    df = df[df['closing_price'].between(99, 101)]
    

    【讨论】:

    • pandas 中是否有“不在之间”功能?我没找到。
    • @dsugasa,使用 tilde operatorbetween
    • @dsugasa 例如df = df[~df['closing_price'].between(99, 101)]
    • 这应该是答案。 tnx
    • 有没有可能我们可以在.query() 中使用.between() ??我很想知道这一点。
    【解决方案4】:
    newdf = df.query('closing_price.mean() <= closing_price <= closing_price.std()')
    

    mean = closing_price.mean()
    std = closing_price.std()
    
    newdf = df.query('@mean <= closing_price <= @std')
    

    【讨论】:

    • 我想知道我们是否可以在.query() 中使用.between() ??
    【解决方案5】:

    而不是这个

    df = df[(99 <= df['closing_price'] <= 101)]
    

    你应该使用这个

    df = df[(df['closing_price']>=99 ) & (df['closing_price']<=101)]
    

    我们必须使用 NumPy 的按位逻辑运算符 |、&、~、^ 来进行复合查询。 此外,括号对于运算符优先级很重要。

    更多信息,您可以访问链接 :Comparisons, Masks, and Boolean Logic

    【讨论】:

      【解决方案6】:

      你也可以使用.between()方法

      emp = pd.read_csv("C:\\py\\programs\\pandas_2\\pandas\\employees.csv")
      
      emp[emp["Salary"].between(60000, 61000)]
      

      输出

      【讨论】:

        【解决方案7】:

        如果您要处理多个值和多个输入,您还可以像这样设置一个应用函数。在这种情况下,为特定范围内的 GPS 位置过滤数据帧。

        def filter_values(lat,lon):
            if abs(lat - 33.77) < .01 and abs(lon - -118.16) < .01:
                return True
            elif abs(lat - 37.79) < .01 and abs(lon - -122.39) < .01:
                return True
            else:
                return False
        
        
        df = df[df.apply(lambda x: filter_values(x['lat'],x['lon']),axis=1)]
        

        【讨论】:

          【解决方案8】:

          如果必须调用pd.Series.between(l,r) 重复(对于不同的界限lr),很多工作都是不必要的重复。在这种情况下,最好对帧/系列进行一次排序,然后使用pd.Series.searchsorted()。我测量了高达 25 倍的加速,见下文。

          def between_indices(x, lower, upper, inclusive=True):
              """
              Returns smallest and largest index i for which holds 
              lower <= x[i] <= upper, under the assumption that x is sorted.
              """
              i = x.searchsorted(lower, side="left" if inclusive else "right")
              j = x.searchsorted(upper, side="right" if inclusive else "left")
              return i, j
          
          # Sort x once before repeated calls of between()
          x = x.sort_values().reset_index(drop=True)
          # x = x.sort_values(ignore_index=True) # for pandas>=1.0
          ret1 = between_indices(x, lower=0.1, upper=0.9)
          ret2 = between_indices(x, lower=0.2, upper=0.8)
          ret3 = ...
          

          基准测试

          针对lowerupper的不同参数测量pd.Series.between()的重复评估(n_reps=100)以及基于pd.Series.searchsorted()的方法。在我的带有 Python v3.8.0 和 Pandas v1.0.3 的 MacBook Pro 2015 上,下面的代码会产生以下输出

          # pd.Series.searchsorted()
          # 5.87 ms ± 321 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
          # pd.Series.between(lower, upper)
          # 155 ms ± 6.08 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
          # Logical expressions: (x>=lower) & (x<=upper)
          # 153 ms ± 3.52 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
          
          import numpy as np
          import pandas as pd
          
          def between_indices(x, lower, upper, inclusive=True):
              # Assumption: x is sorted.
              i = x.searchsorted(lower, side="left" if inclusive else "right")
              j = x.searchsorted(upper, side="right" if inclusive else "left")
              return i, j
          
          def between_fast(x, lower, upper, inclusive=True):
              """
              Equivalent to pd.Series.between() under the assumption that x is sorted.
              """
              i, j = between_indices(x, lower, upper, inclusive)
              if True:
                  return x.iloc[i:j]
              else:
                  # Mask creation is slow.
                  mask = np.zeros_like(x, dtype=bool)
                  mask[i:j] = True
                  mask = pd.Series(mask, index=x.index)
                  return x[mask]
          
          def between(x, lower, upper, inclusive=True):
              mask = x.between(lower, upper, inclusive=inclusive)
              return x[mask]
          
          def between_expr(x, lower, upper, inclusive=True):
              if inclusive:
                  mask = (x>=lower) & (x<=upper)
              else:
                  mask = (x>lower) & (x<upper)
              return x[mask]
          
          def benchmark(func, x, lowers, uppers):
              for l,u in zip(lowers, uppers):
                  func(x,lower=l,upper=u)
          
          n_samples = 1000
          n_reps = 100
          x = pd.Series(np.random.randn(n_samples))
          # Sort the Series.
          # For pandas>=1.0:
          # x = x.sort_values(ignore_index=True)
          x = x.sort_values().reset_index(drop=True)
          
          # Assert equivalence of different methods.
          assert(between_fast(x, 0, 1, True ).equals(between(x, 0, 1, True)))
          assert(between_expr(x, 0, 1, True ).equals(between(x, 0, 1, True)))
          assert(between_fast(x, 0, 1, False).equals(between(x, 0, 1, False)))
          assert(between_expr(x, 0, 1, False).equals(between(x, 0, 1, False)))
          
          # Benchmark repeated evaluations of between().
          uppers = np.linspace(0, 3, n_reps)
          lowers = -uppers
          %timeit benchmark(between_fast, x, lowers, uppers)
          %timeit benchmark(between, x, lowers, uppers)
          %timeit benchmark(between_expr, x, lowers, uppers)
          

          【讨论】:

            猜你喜欢
            • 2023-02-21
            • 2014-06-21
            • 2023-03-24
            • 2020-06-11
            • 2015-06-04
            相关资源
            最近更新 更多