【问题标题】:Python calcualte new column based on condition of existing columnsPython根据现有列的条件计算新列
【发布时间】:2019-04-09 00:42:55
【问题描述】:

想要一个基于现有列的某些条件的新列,下面是我现在正在做的事情,但是对于大量数据来说需要太多时间。有没有更有效或更快的方法来做到这一点。

DF["A"][0] = 0
for x in range(1,rows):
    if(DF["B"][x]>DF["B"][x-1]):
        DF["A"][x] = DF["A"][x-1] + DF["C"][x]
    elif(DF["B"][x]<DF["B"][x-1]):
        DF["A"][x] = DF["A"][x-1] - DF["C"][x]
    else:
        DF["A"][x] = DF["A"][x-1]

【问题讨论】:

  • 对于您要完成的工作,可能有一种更简单、更有效的布尔掩码方法。我们可以使用示例数据框以及您期望的输出外观更好地帮助您解决问题。

标签: python dataframe conditional-statements multiple-columns


【解决方案1】:

如果我猜对了,这就是你想要的:

import pandas as pd
import numpy as np

df = pd.DataFrame({'A': [1, 2, 3, 4, 5],
                   'B': [12, 15, 9, 8, 15],
                   'C': [3, 9, 12, 6, 8]})

df['A'] = np.where(df.index==0,
                   0,
                   np.where(df['B']>df['B'].shift(),
                            df['A']-df['A'].shift(),
                            np.where(df['B']<df['B'].shift(),
                                     df['A'].shift()-df['C'],
                                     df['A'].shift())))
df
#      A   B   C
#0   0.0  12   3
#1   1.0  15   9
#2 -10.0   9  12
#3  -3.0   8   6
#4   1.0  15   8

【讨论】:

  • 使用np.where 的良好测试数据框和有趣的方法。请检查我的答案,不知道为什么,但我们得到不同的结果。通过手动检查,您的解决方案似乎在更新期间使用了 A[x-1] 的中间结果——这可能是也可能不是 OP 想要的。
【解决方案2】:

基于现有列的某些条件的新列,

我正在使用@zipa 提供的DataFrame:

df = pd.DataFrame({'A': [1, 2, 3, 4, 5],
                   'B': [12, 15, 9, 8, 15],
                   'C': [3, 9, 12, 6, 8]})

第一种方法

这是一个按您指定的高效实现的函数。它通过利用 Pandas 的索引功能,特别是行掩码来工作

def update(df):
    cond_larger = df['B'] > df['B'].shift().fillna(0)
    cond_smaller = df['B'] < df['B'].shift().fillna(0)
    cond_else = ~(cond_larger | cond_smaller)
    for cond, sign in [(cond_larger, +1),  # A[x-1] + C[x] 
                       (cond_smaller, -1), # A[x-1] - C[x]
                       (cond_else, 0)]:    # A[x-1] + 0
        if any(cond):
            df.loc[cond, 'A_updated'] = (df['A'].shift().fillna(0) + 
                                         sign * df[cond]['C'])
    df['A'] = df['A_updated']
    df.drop(columns=['A_updated'], inplace=True)
    return df

update(df)
=> 
      A   B   C
0   3.0  12   3
1  10.0  15   9
2 -10.0   9  12
3  -3.0   8   6
4  12.0  15   8

优化

事实证明,您可以使用DataFrame.mask 来实现与上述相同的效果。请注意,您可以将条件组合到mask 的调用中,但是我发现这样更容易阅读:

# specify conditions
cond_larger = df['B'] > df['B'].shift().fillna(0)
cond_smaller = df['B'] < df['B'].shift().fillna(0)
cond_else = ~(cond_larger | cond_smaller)
# apply
A_shifted = (df['A'].shift().fillna(0)).copy()
df.mask(cond_larger, A_shifted + df['C'], axis=0, inplace=True)
df.mask(cond_smaller, A_shifted - df['C'], axis=0, inplace=True)
df.mask(cond_else, A_shifted, axis=0, inplace=True)
=>
(same results as above)

注意事项:

  • 我假设A/B[x-1] 的默认值为0。如果应该区别对待第一行,请删除或替换.fillna(0)。结果会有所不同。

  • 按顺序检查条件。根据更新是应该使用A 中的原始值还是在先前条件中更新的值,您可能不需要帮助器列A_updated

  • 查看此答案的先前版本,了解我是如何到达这里的历史

【讨论】:

    猜你喜欢
    • 2018-04-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多