Изменить строку по условию

Задача в следующем: необходимо добавить в конец датафрейма строку, но если такая строка уже существует по индексу, то просто изменить значения ячеек этой строки на значения добавляемой строки.

Пример:

  A B C
K 1 2 3
L 1 3 5
M 2 5 3

Добавляем строку в конец:

df2 = DataFrame({'A': 2, 'B': 3, 'C': 6}, index=['N'])
df = df.concat(df, df2)

Получаем:

  A B C
K 1 2 3
L 1 3 5
M 2 5 3
N 2 3 6

Далее добавляем строку DataFrame({'A': 2, 'B': 7, 'C': 7}, index=['N']), получаем:

  A B C
K 1 2 3
L 1 3 5
M 2 5 3
N 2 7 7

Проблема не в способе добавления , а в скорости этой операции, я пытался:

  1. Проверить существует ли добавляемый индекс и если да, то просто изменить значения ячеек в оригинальном датафрейме функциями at, set_value - по скорости это очень дорого выходит, примерно 60мс

  2. Просто приравнять по индексу новый датафрейм df['N'] = [{'A': 2, 'B': 7, 'C': 7}], так же дорого, примерно 58мс

  3. Каждый раз удалять строку по индексу, от датафрейма которого присоединяем drop и затем вызываем df.concat(df, df2) - получается просто нереально дорого 112мс

  4. Если использовать просто df.concat(df, df2), то это самый лучший вариант, примерно 6-7мс, но когда пытаешь присоединить новую строку, а в оригинальном датафрейме уже есть такая строка с индексом, то изменение значений не произойдет, тупо добавиться в конец еще одна строка и нет возможности прописать какие-либо условия в функции concat, чтобы не добавлялся новый индекс, а заменялся старый, новыми значениями в ячейках(столбцах)

Так вот вопрос, можно ли это сделать, чтобы работало по скорости как и concat, знаю что датафреймы не предназначены для редактирования, а только для анализа, но вдруг...


Ответы (2 шт):

Автор решения: CrazyElf

Могу предложить ещё несколько способов, быстрее которых вряд ли мне кажется можно что-то придумать. Какой будет быстрее и устроят ли они вас по скорости - это вы сами проверьте, у меня ведь нет ваших данных.

Подготовка примера данных, общая для последующих версий кода:

import pandas as pd

df = pd.DataFrame({'A': [1,2,3], 'B': [4,5,6], 'C': [7,8,9]}, index=list('NMK'))
df2 = pd.DataFrame({'A': [2,3], 'B': [3,4], 'C': [6,7]}, index=['N','Q'])

Собственно сами способы:

    • Выбираем в df2 те индексы, которые есть и в df
    • "Векторно" присваиваем сразу по всем этим индексам в df новые значения из df2
    • Для остальных индексов из df2 делаем конкатенацию значений df и df2
mask = df2.index.isin(df.index)
idx_set = df2.index[mask]
idx_concat = df2.index[~mask]
df.loc[idx_set] = df2.loc[idx_set]
df = pd.concat([df, df2.loc[idx_concat]])
    • Выбираем индексы df, которых нет в df2
    • Конкатенируем только к части df согласно этих индексов датафрейм df2
idx_keep = df.index[~df.index.isin(df2.index)]
df = pd.concat([df.loc[idx_keep], df2])

Таким образом, в этом способе мы фактически удаляем из df предварительно совпадающие индексы перед конкатенацией, но мне кажется это будет быстрее, чем через отдельный drop (хотя тоже смотря как вы его делаете). Вот тоже самое через drop. Тут скорость будет зависеть от того, что возвращает drop - вью или новый датафрейм. Если вью, то, возможно, скорость будет как через .loc, но это нужно проверять:

idx_drop = df.index[df.index.isin(df2.index)]
df = pd.concat([df.drop(index=idx_drop), df2])
    • Просто объединяем датафреймы
    • Для индексов-дубликатов выкидываем старые значения
df = pd.concat([df, df2])
df = df.loc[~df.index.duplicated(keep='last')]
→ Ссылка
Автор решения: SergFSM

если буквально следовать тому что вы написали - добавить в конец датафрейма строку, но если такая строка уже существует по индексу, то просто изменить значения ячеек этой строки на значения добавляемой строки, то можно обойтись простым присвоением:

df.loc['N'] = [2,3,6]
df.loc['K'] = [2,7,7]

>>> df
'''
   A  B  C
K  2  7  7
L  1  3  5
M  2  5  3
N  2  3  6
→ Ссылка