在熊猫数据框列中找到匹配后的行数

时间:2021-03-20 05:40:02

标签: python pandas dataframe

我有以下数据框和预期输出,我在 True 列中搜索 balance 的值。找到后,我对该行使用 balPrice 列值并将其与 endPrice 列进行比较,以查找 endPrice 低于 balPrice 的第一个实例并计算balance == True 行中的行以及找到的较低值行的第一个实例。如果没有找到更低的值,则行数设置为 0。

balance balPrice    endPrice
0   False   5.34    5.34
1   False   5.34    5.34
2   False   5.34    5.34
3   False   5.34    5.27
4   False   5.44    5.25
5   False   5.28    5.12
6   True    5.31    5.2
7   False   5.44    5.35
8   False   5.485   5.44
9   False   5.525   5.5
10  False   5.53    5.53
11  False   5.58    5.51
12  False   5.65    5.52
13  False   5.3     5.3
14  False   5.58    5.54
15  False   5.64    5.55
16  True    5.69    5.65
17  False   5.69    5.59
18  False   5.7     5.62
19  False   5.81    5.77
20  False   5.65    5.73
21  False   5.65    5.86
22  True    6.00    5.89
23  False   5.65    5.85
24  False   5.65    5.83
25  False   5.9     5.88

这是我尝试过的,看起来很复杂。正在寻找更好的解决方案。

df_filtered = df[df.balance == True]
idx = []
for i in df_filtered.index:
    pos = np.where(df.endPrice[i+1:] <= df.balPrice[i])[0]
    if pos.size > 0:
        idx.append(pos[0]+1)
    else:
        idx.append(0)

df_filtered['numrows'] = idx     

预期输出:

balance balPrice    endPrice    numrows
True    5.31        5.2           7
True    5.69        5.65          1
True    6.00        5.89          1

2 个答案:

答案 0 :(得分:0)

更新

对于较大的数据集,使用屏蔽/索引似乎比使用连接方法(至少在我的本地测试中)快得多。

start_idxs = df.loc[ df.balance ].index
df.loc[start_idxs, 'Price'] = df.loc[start_idxs, 'balPrice']
df.ffill(inplace=True)

end_rows = (df.balance == False) & (df.endPrice <= df.Price)
df.loc[end_rows, 'index'] = df.loc[end_rows].index
df.bfill(inplace=True)

df.loc[start_idxs, 'index'] -= start_idxs
df.rename(dict(index='numrows'), axis=1, inplace=True)
df.drop(columns='Price', inplace=True)
df.loc[df.balance == False, 'numrows'] = 0

join() 方法

不确定您是否会认为它更好,但一种方法是将您的条件所需的列添加到过滤后的行中 - 然后执行连接。

df['numrows'] = 0

has_balance = df.loc[ df.balance ]
has_balance = has_balance.assign(
    index=has_balance.index,  
    lastPrice=has_balance.balPrice
)
 
df1 = df.join(has_balance, rsuffix='_r').ffill()
df1 = df1.query('balance == False and endPrice <= lastPrice').drop_duplicates('index')
df1['numrows'] = df1.index - df1['index']

has_balance.update(df1.set_index('index')['numrows'])
df.update(has_balance)

<头>
平衡 balPrice endPrice numrows
6 5.31 5.2 7
16 5.69 5.65 1
22 6 5.89 1

在没有 df1 变量的情况下编写

df['numrows'] = 0

has_balance = df.loc[ df.balance ]
has_balance = has_balance.assign(
    index=has_balance.index, 
    lastPrice=has_balance.balPrice
)

has_balance.update((
    df.join(has_balance, rsuffix='_r')
      .ffill()
      .query('balance == False and endPrice <= lastPrice')
      .drop_duplicates('index')
      .assign(numrows=lambda df: df.index - df['index'])
      .set_index('index')
      .numrows
))

df.update(has_balance)

答案 1 :(得分:0)

您可以使用 balance == Truegroupby(df.balance.cumsum()) 将行从一个 apply(numrows) 分组到下一个组:

def numrows(group):
    index = (group.endPrice.iloc[1:] <= group.balPrice.iloc[0]).idxmax()
    result = index - group.index[0]
    return result

numrows = df.groupby(df.balance.cumsum()).apply(numrows)[1:]
df['numrows'] = numrows.set_axis(df[df.balance].index)
df['numrows'] = df.numrows.fillna(0).astype(int)

这给出了除 numrows 行之外的 balance == True 列:

df.tail()

#    balance  balPrice  endPrice  numrows
# ...
# 21   False      5.65      5.86        0
# 22    True      6.00      5.89        1
# 23   False      5.65      5.85        0
# 24   False      5.65      5.83        0
# 25   False      5.90      5.88        0

所以最后你可以这样做:

df[df.balance]

#    balance  balPrice  endPrice  numrows
#  6    True      5.31      5.20        7
# 16    True      5.69      5.65        1
# 22    True      6.00      5.89        1