Создание нового столбца с помощью apply () два раза приводит к перезаписи нового столбца - PullRequest
0 голосов
/ 15 февраля 2019

Я написал некоторый код для панд, эквивалентный этому игрушечному примеру:

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

Я ожидаю, что столбец made_profitИстинно для строк 0 и 1 вместо NaN, но, очевидно, второй apply () перезаписывает столбец made_profit, созданный первым apply ().

Как получить ожидаемый столбец?Я не хочу создать столбец 'product0_made_profit' с первым apply () и столбец 'product1_made_profit' со вторым apply (), чтобы я мог объединить оба столбца в один столбец 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: "None of [Index(['made_profit', 'profit_amount'], dtype='object')] are in the [columns]"

I может заставить его работать со следующим кодом:

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 () и, как сказано выше, становится немного утомительным в реальном коде (но выполнимым путем построения цикла по всем значениям продукта) - возможно, есть элегантное решение и для нескольких столбцов.

1 Ответ

0 голосов
/ 15 февраля 2019

Вам необходимо присвоить отфильтрованным строкам с таким же условием 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...