【问题标题】:Fastest way to find which of two lists of columns of each row is true in a pandas dataframe在熊猫数据框中查找每行的两个列列表中哪一个为真的最快方法
【发布时间】:2017-04-06 10:01:29
【问题描述】:

我正在寻找执行以下操作的最快方法:

我们有一个 pd.DataFrame:

df = pd.DataFrame({
    'High': [1.3,1.2,1.1],
    'Low': [1.3,1.2,1.1],
    'High1': [1.1, 1.1, 1.1],
    'High2': [1.2, 1.2, 1.2],
    'High3': [1.3, 1.3, 1.3],
    'Low1': [1.3, 1.3, 1.3],
    'Low2': [1.2, 1.2, 1.2],
    'Low3': [1.1, 1.1, 1.1]})

看起来像:

In [4]: df
Out[4]:
   High  High1  High2  High3  Low  Low1  Low2  Low3
0   1.3    1.1    1.2    1.3  1.3   1.3   1.2   1.1
1   1.2    1.1    1.2    1.3  1.2   1.3   1.2   1.1
2   1.1    1.1    1.2    1.3  1.1   1.3   1.2   1.1

我想知道的是,High1、High2、High3 浮点值中的哪一个是第一个大于或等于 High 值的。如果没有,应该是np.nan

对于 Low1、Low2、Low3 值也是如此,但在这种情况下,它们中的哪一个是第一个低于或等于 High 值的值。如果没有,应该是np.nan

最后,我需要知道先出现的是低位还是高位。

解决此问题的一种方法是:

df['LowIs'] = np.nan
df['HighIs'] = np.nan

for i in range(1,4):
    df['LowIs'] = np.where((np.isnan(df['LowIs'])) & (
        df['Low'] >= df['Low'+str(i)]), i, df['LowIs'])
    df['HighIs'] = np.where((np.isnan(df['HighIs'])) & (
        df['High'] <= df['High'+str(i)]), i, df['HighIs'])

df['IsFirst'] = np.where(
    df.LowIs < df.HighIs,
    'Low',
    np.where(df.LowIs > df.HighIs, 'High', 'None')
)

这给了我:

In [8]: df
Out[8]:
   High  High1  High2  High3  Low  Low1  Low2  Low3  LowIs  HighIs IsFirst
0   1.3    1.1    1.2    1.3  1.3   1.3   1.2   1.1    1.0     3.0     Low
1   1.2    1.1    1.2    1.3  1.2   1.3   1.2   1.1    2.0     2.0    None
2   1.1    1.1    1.2    1.3  1.1   1.3   1.2   1.1    3.0     1.0    High

由于我必须在高/低将不同的许多迭代中一遍又一遍地执行此操作,因此执行此操作时的性能是关键。

所以我不介意 High1、High2、High3 和 Low1、Low2、Low3 是否在一个单独的转置的 DataFrame 中,或者它是否在一个 dict 中。因此,以任何能提供最佳性能的方式准备数据的过程可能会很慢而且很尴尬。

我研究过的一个解决方案是:

df.loc[(df.index == 0), 'HighIs'] = np.where(
    df.loc[(df.index == 0), ['High1', 'High2', 'High3']] >= 1.3
)[1][0] + 1

所以检查第一行中哪一列是真的,然后查看 np.where() 的索引号。

期待任何建议,希望能学到新东西! :)

【问题讨论】:

  • 这是您最终希望解决的问题,还是您在解决其他问题时遇到的问题?只是问,因为这似乎是一个可能的x-y problem

标签: python performance pandas numpy vectorization


【解决方案1】:

如果我没听错的话,这是一个半向量化的版本:

df = pd.DataFrame({
    'High': [1.3,1.7,1.1],
    'Low': [1.3,1.2,1.1],
    'High1': [1.1, 1.1, 1.1],
    'High2': [1.2, 1.2, 1.2],
    'High3': [1.3, 1.3, 1.3],
    'Low1': [1.3, 1.3, 1.3],
    'Low2': [1.2, 1.2, 1.2],
    'Low3': [1.1, 1.1, 1.1]})

highs = ['High{:d}'.format(x) for x in range(0,4)]

for h in highs[::-1]:
    mask = df['High'] <= df[h]
    df.loc[mask, 'FirstHigh'] = h

生产:

   High  High1  High2  High3  Low  Low1  Low2  Low3 FirstHigh
0   1.3    1.1    1.2    1.3  1.3   1.3   1.2   1.1     High3
1   1.7    1.1    1.2    1.3  1.2   1.3   1.2   1.1       NaN
2   1.1    1.1    1.2    1.3  1.1   1.3   1.2   1.1     High1

说明: 这里的关键是我们反向迭代列。也就是说,我们从High3 开始,检查它是否大于High,并相应地设置FirstHigh。然后我们转到High2。如果这也更大,我们只需覆盖先前的结果,否则它将保持原样。由于我们以这种相反的顺序进行迭代,因此结果是 first 列较高的列将作为最终结果。

【讨论】:

  • 我计时了,你的原始版本比我的稍快。所以忘记我写的一切吧。
  • 是的,它可以工作,但速度较慢。仍然感谢您的意见!
【解决方案2】:

对照 High 列测试您的 High-n 列:

a = df.iloc[:,1:4].ge(df.High, axis=0)

a
Out[67]: 
   High1  High2  High3
0  False  False   True
1  False  False  False
2   True   True   True

现在将 False 替换为 np.nan 并询问最小值或最大值的列索引(没关系,因为 np.nan 的全部为 True):

a.replace(False, np.nan).idxmax(1)

0    High3
1      NaN
2    High1

使用le 作为比较运算符的 Low 列的原理相同。

【讨论】:

  • 谢谢,它的工作原理比我的方法略快,这是我想出的最终代码:
     a = df.iloc[:,1:4].rename(columns={'High1': 1, 'High2': 2, 'High3': 3}).ge(df.High, axis=0) b = df.iloc[:,5:8].rename(columns={'Low1': 1, 'Low2': 2, 'Low3': 3}).le(df.Low, axis=0) df['HighIs'] = a.replace(False, np.nan).idxmax(1) df['LowIs'] = b.replace(False, np.nan).idxmax(1) `df['IsFirst' ] = np.where(df.LowIs  df.HighIs, '高', '无'))``
【解决方案3】:

这是NumPy broadcasting 的矢量化方法-

a = df.values
out1 = (a[:,1:4] >= a[:,0,None]).argmax(1)+1
out2 = (a[:,5:8] <= a[:,4,None]).argmax(1)+1
df['LowIs'] = out2
df['HighIs'] = out1
df['IsFirst'] = np.where(out1!=out2,np.where(out1 > out2, 'Low', 'High'),None)

样本输出 -

In [195]: df
Out[195]: 
   High  High1  High2  High3  Low  Low1  Low2  Low3  LowIs  HighIs IsFirst
0   1.3    1.1    1.2    1.3  1.3   1.3   1.2   1.1      1       3     Low
1   1.2    1.1    1.2    1.3  1.2   1.3   1.2   1.1      2       2    None
2   1.1    1.1    1.2    1.3  1.1   1.3   1.2   1.1      3       1    High

【讨论】:

  • 谢谢,这是迄今为止性能最好的解决方案,比我的方法快大约 3 倍!也许我应该考虑做我在 numpy 中所做的部分工作,而将 pandas 排除在外......
  • @Marco 这就是要走的路!根据我的经验,在进行数字运算和寻找性能时,NumPy 值得考虑。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-10-26
  • 2020-08-30
  • 2018-07-10
  • 2018-03-21
  • 1970-01-01
  • 2014-11-07
  • 1970-01-01
相关资源
最近更新 更多