новый столбец, основанный на определенных условиях существующих столбцов,
Я использую DataFrame, предоставленный @zipa:
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)
Примечания:
Я предполагаю значение по умолчанию 0
для A/B[x-1]
. Если к первому ряду следует относиться иначе, удалите или замените .fillna(0)
. Результаты будут другими.
Условия проверяются последовательно. В зависимости от того, должны ли обновления использовать исходные значения в A
или те, которые были обновлены в предыдущем условии, вам может не понадобиться вспомогательный столбец A_updated
Смотрите предыдущие версии этого ответа для истории того, как я попал сюда