Добавьте значение из другого столбца на основе последнего отрицательного значения, найденного для текущей строки

#python #arrays #pandas #dataframe #numpy

Вопрос:

У меня есть следующий фрейм данных:

 data = {'Column A':[400, 522, 633, 744, 150, 140, 119, 744, 150, 400, 390, 315, 744, 150, 400, 522, 500, 633, 744, 204, 215, 150],
    'Column B':[400, 0, 0, 0, -150, 0, 0, 744, 0, -400, 0, 0, -744, 0, -400, 0, 0, -633, 0, 0, 0, 150]}
df = pd.DataFrame(data)
 

Что я хочу сделать, так это сказать программе, чтобы она шла строка за строкой и сравнивала абсолютное значение последнего отрицательного значения в столбце B со значением столбца A для текущей строки, и если оно на 20% ниже, скопируйте его в столбец B. Вот как будет выглядеть фрейм данных с измененным столбцом B:

 data = {'Column A':[400, 522, 633, 744, 150, 140, 119, 744, 150, 400, 390, 315, 744, 150, 400, 522, 500, 633, 744, 204, 215, 150],
    'Column B':[400, 0, 0, 0, -150, 0, 119, 744, 0, -400, 0, 315, -744, 150, -400, 0, 0, -633, 0, 204, 215, 150],
    }
 

Так, например, в 6-й строке последнее отрицательное значение в столбце B равно -150, поэтому его абсолютное значение будет равно 150, когда вы сравните это значение со значением 140 в столбце A, тогда значение не копируется, так как 140 все еще находится внутри порога 20%, однако в 7-й строке 119 более чем на 20% ниже 150, поэтому копируется в столбец B. В 10-й строке последний минус становится -400, поэтому он начнет использовать это значение для сравнения, затем снова пропустит 390, но скопирует 315.

Я надеюсь, что это достаточно ясно, и заранее благодарю вас за ваши ответы.

Ответ №1:

Вы можете попробовать что-то в этом роде :

 last_value = 0
for i, n in df.iterrows():
   if n["Column B"] < 0:
       last_value = abs(n["Column B"])
   if n["Column A"] < last_value*0.8:
       df.loc[df.index == i, 'Column B'] = n["Column A"]
 

Комментарии:

1. Это решает проблему, но поскольку мой исходный набор данных содержит тысячи строк, нет ли более эффективного способа без использования операторов If или циклов for?

2. ..честно говоря, у меня нет возможности отказаться от использования условных операторов и цикла, по крайней мере, для повторения вашего фрейма данных…

Ответ №2:

Попробуй:

 df['New Column B'] = np.where(df['Column B'].ge(0), np.nan, df['Column B'])
df['New Column B'].ffill(inplace=True)
condition = df['Column A'] < abs(df['New Column B']*0.8))
df['New Column B'].mask(condition, df['Column A'], inplace=True)
condition = df['Column B'].ne(df['New Column B']) amp; df['New Column B'].ne(df['Column A'])
df['New Column B'].mask(condition, df['Column B'], inplace=True)
df['New Column B'] = df['New Column B'].astype(int)
 

Комментарии:

1. Мухаммад спасибо вам за это, ваше решение намного эффективнее, однако, поскольку мне нужно заменить «Столбец B» на «Новый столбец B», я хотел спросить вас, есть ли способ избежать использования вспомогательного столбца, такого как «Новый столбец B», и просто перезаписать столбец B на месте, чтобы «Новый столбец B» стал фактическим столбцом B с изменениями.

Ответ №3:

Вот одна строка с np.where ffill последним отрицательным значением в столбце B. и после него.

 df['newColB'] = ( # you can reassign directly to Column B
    np.where(df['Column A']<0.8*df['Column B'].where(df['Column B']<0).ffill().abs(), 
             df['Column A'], df['Column B'])
)
print(df)
    Column A  Column B  New Column B  newColB
0        400       400           400      400
1        522         0             0        0
2        633         0             0        0
3        744         0             0        0
4        150      -150          -150     -150
5        140         0             0        0
6        119         0           119      119
7        744       744           744      744
8        150         0             0        0
9        400      -400          -400     -400
10       390         0             0        0
11       315         0           315      315
12       744      -744          -744     -744
13       150         0           150      150
14       400      -400          -400     -400
15       522         0             0        0
16       500         0             0        0
17       633      -633          -633     -633
...