python - 两次使用 apply() 创建新列会导致覆盖新列

标签 python pandas dataframe

我编写了一些与这个玩具示例等效的 pandas 代码:

df_test = pd.DataFrame({'product': [0, 0, 1, 1], 'sold_for': [5000, 4500, 10000, 8000]})

def product0_makes_profit(row, product0_cost):
    return row['sold_for'] > product0_cost

def product1_makes_profit(row, product1_cost):
    return row['sold_for'] > product1_cost

df_test['made_profit'] = df_test[df_test['product']==0].apply(product0_makes_profit, args=[4000], axis=1, result_type="expand")
df_test['made_profit'] = df_test[df_test['product']==1].apply(product1_makes_profit, args=[9000], axis=1, result_type="expand")
df_test

我得到以下结果:

    product sold_for    made_profit
0   0       5000        NaN
1   0       4500        NaN
2   1       10000       True
3   1       8000        False

我希望第 0 行和第 1 行的“made_profit”列为 True,而不是 NaN,但显然第二个 apply() 会覆盖由第一个 apply() 创建的 made_profit 列。

如何获得我想要的专栏?我不想希望使用第一个 apply() 创建一个列“product0_made_profit”,并使用第二个 apply() 创建一个列“product1_made_profit”,因此我可以将这两列合并到一个“made_profit”中我想要获取的列,因为在我的实际代码中,我在产品列中有很多不同的值(意味着要应用很多不同的函数)。

编辑

我的玩具示例太简单了,我实际上创建了两个新列:

def product0_makes_profit(row, product0_cost):
    return [row['sold_for'] > product0_cost, row['sold_for'] - product0_cost]

def product1_makes_profit(row, product1_cost):
    return [row['sold_for'] > product1_cost, row['sold_for'] - product1_cost]

使用当前答案,我做了这个:

is_prod0 = (df_test['product']==0)
df_test.loc[is_prod0, ['made_profit', 'profit_amount']] = df_test[is_prod0].apply(product0_makes_profit, args=[4000], axis=1, result_type="expand")
is_prod1 = (df_test['product']==1)
df_test.loc[is_profd1, ['made_profit', 'profit_amount']] = df_test[is_prod1].apply(product1_makes_profit, args=[9000], axis=1, result_type="expand")
print(df_test)

但这给了我以下错误(在第一次使用 .loc 时):

KeyError:“[列] 中没有 [Index(['made_profit', 'profit_amount'], dtype='object')]”

可以使用以下代码使其工作:

is_prod0 = (df_test['product']==0)
newdf = df_test[is_prod0].apply(product0_makes_profit, args=[4000], axis=1, result_type="expand")
is_prod1 = (df_test['product']==1)
newerdf = df_test[is_prod1].apply(product1_makes_profit, args=[9000], axis=1, result_type="expand")
newcols = pd.concat([newdf, newerdf])
newcols.columns = ['was_profit_made', 'profit_amount']
df_test.join(newcols)

然而,这涉及 concat() 和 join() ,如上所述,在现实生活中的代码中会有点乏味(但通过在所有产品值上构建循环是可行的) - 也许有一个优雅的解决方案可以解决多个问题列也是如此。

最佳答案

您需要使用loc分配给具有相同条件的过滤行,因此仅在条件为 True 时处理行:

m1 = df_test['product']==0
m2 = df_test['product']==1
df_test.loc[m1, 'made_profit'] = df_test[m1].apply(product0_makes_profit, args=[4000], axis=1, result_type="expand")
df_test.loc[m2, 'made_profit'] = df_test[m2].apply(product1_makes_profit, args=[9000], axis=1, result_type="expand")
print (df_test)
   product  sold_for  made_profit
0        0      5000         True
1        0      4500         True
2        1     10000         True
3        1      8000        False

编辑:

如果从function返回多个值需要返回带有新列名称索引的Series,还需要创建填充一些默认值的新列(例如NaN) 在 loc 之前:

cols = ['made_profit', 'profit_amount']
def product0_makes_profit(row, product0_cost):
    return pd.Series([row['sold_for'] > product0_cost, row['sold_for'] - product0_cost], index=cols)

def product1_makes_profit(row, product1_cost):
    return pd.Series([row['sold_for'] > product1_cost, row['sold_for'] - product1_cost], index=cols)

for c in cols:
    df_test[c] = np.nan

is_prod0 = (df_test['product']==0)
df_test.loc[is_prod0, cols] = df_test[is_prod0].apply(product0_makes_profit, args=[4000], axis=1, result_type="expand")
is_prod1 = (df_test['product']==1)
df_test.loc[is_prod1, cols] = df_test[is_prod1].apply(product1_makes_profit, args=[9000], axis=1, result_type="expand")
print(df_test)

   product  sold_for  made_profit  profit_amount
0        0      5000         True         1000.0
1        0      4500         True          500.0
2        1     10000         True         1000.0
3        1      8000        False        -1000.0

关于python - 两次使用 apply() 创建新列会导致覆盖新列,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54710363/

相关文章:

python - 如何合并两个数据框与重叠日期时间范围内的列

Python - 读取逗号分隔文件,创建两个列表

python - 使用 Pandas value.counts() 获取一个值

python - 如何在python电子邮件脚本中的发件人地址之前添加发件人姓名

python - 如何根据时间条件进行 cumsum - 对 pandas 进行重新采样?

python - 如何有条件地切片 pandas 中的数据框

python - 在 DataFrame 的顶部添加具有特定索引名称的新行

r - 如何将参与者数量更改为特定值?

python - 如何使python函数将封闭变量绑定(bind)到值而不是名称

Python检查列表中是否有字符串的任何部分