Pandas: объединить фрейм данных в другой фрейм данных, если записи изменились

#python #pandas

#python #pandas

Вопрос:

У меня есть два фрейма данных Pandas (Master amp; Input) с одинаковым индексом, но разным количеством столбцов, например

Фрейм данных 1 (мастер):

 ID  | A  | B  | C
101 | .. | .. | ..
102 | .. | .. | ..
103 | .. | .. | ..
  

Фрейм данных 2 (ввод):

 ID  | A  | B  | C  | E  | F 
101 | .. | .. | .. | .. | ..
102 | .. | XY | YZ | .. | ..
223 | .. | .. | .. | .. | ..
351 | .. | .. | .. | .. | ..
  

Я хочу объединить входной DF с основным DF, если были добавлены новые строки (см. ID 223, 351) ИЛИ элемент во входном DF был обновлен (см. ID 102).

Дополнительные столбцы во входном DF можно игнорировать.

Цель:

 ID  | A  | B  | C
101 | .. | .. | ..
102 | .. | XY | YZ
103 | .. | .. | ..
223 | .. | .. | ..
351 | .. | .. | ..
  

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

1. ваш df2 имеет значения для столбца E с идентификатором 102, но эти данные передаются в идентификатор 102 в столбце C в df1. Это предназначено? Имена столбцов разные

2. @SandervandenOord Извиняется, это не было предназначено. Я исправил сообщение.

Ответ №1:

Используйте .update() для обновления уже существующих строк и столбцов.
Затем я пытаюсь выяснить, какие новые строки еще не находятся в вашем df1, и добавляю их с помощью pd.concat():

 import pandas as pd

df1 = pd.DataFrame(
    {'ID': [101, 102, 103],
     'A': ['..', '..', '..'],
     'B': ['..', '..', '..'],
     'C': ['..', '..', '..'],    
}).set_index('ID')

df2 = pd.DataFrame(
    {'ID': [101, 102, 223],
     'A': ['..', '..', '..'],
     'B': ['..', 'XY', '..'],
     'C': ['..', 'ZZ', '..'], 
     'F': ['..', '..', '..'],
}).set_index('ID')

# update existing rows    
df1.update(df2)

# find out which ids are new    
ids_of_new_rows = set(df2.index) - set(df1.index)

# get new rows that should be added to master   
rows_to_add = df2.loc[ids_of_new_rows, df1.columns amp; df2.columns]

# add new rows to existing master
df_result = pd.concat([df1, rows_to_add])

df_result
  

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

1. Отличный ответ! Я бы добавил условия, требуемые пользователем: if len(ids_of_new_rows)>0 or not (df1_match == df2_match).values().all(): ... , где df1_match и df2_match — это фреймы данных, отфильтрованные по общим индексам ( df.loc[set(df1.index) amp; set(df2.index) ]) .

2. Спасибо за ваш ответ, я думаю, это именно то, что мне нужно. К сожалению, мне нужно было добавить несколько столбцов вручную в основной лист, которые не включены во входной лист. Теперь я всегда получаю сообщение об ошибке при попытке добавить новые строки: «KeyError: «Передача списка-нравится . loc или [] с отсутствующими метками больше не поддерживаются. Отсутствовали следующие метки: Index([‘Ручной столбец A’, ‘Ручной столбец B’], dtype=’object’). »

3. @maxiw46: я скорректировал ответ, чтобы добавлять только столбцы, которые находятся в обоих df. В частности, я изменил код для: переменной rows_to_add

4. @SandervandenOord Теперь я получаю следующую ошибку: «KeyError: «Ни один из [Index([‘CR9885’, ‘CR9890’, ‘CR9886’, ‘CR9889’, ‘CR9887’, ‘CR9888’, ‘CR9883’,n ‘CR9882’, ‘CR9884′], ndtype=’object’, name =’ID’)] находятся в [индексе] «. Обратите внимание, что CR123 и т. Д. Являются Моими фактическими идентификаторами.

5. хм, мне трудно сказать, что происходит сейчас, поскольку я не вижу ваших реальных данных: (

Ответ №2:

Предполагая, что столбцы в Input на самом деле A,B,C,D :

 useless_col = set(master.columns) ^ set(input.columns)
input = input.drop(columns=useless_col)

master_only = master[~master.id.isin(input.id)]
master_overriden = input[input.id.isin(master.id)]

master_only.append(master_overriden)
  

Я пытался найти способ, используя join , но не смог найти ничего такого простого, как указано выше.

Ниже приведена демонстрация в оболочке :

 >>> master
    id   A   B   C
0  101  ..  ..  ..
1  102  ..  ..  ..
2  103  ..  ..  ..
>>> input
    id   A   B   C   D
0  101  ..  ..  ..  ..
1  102  ..  XY  YZ  ..
2  223  ..  ..  ..  ..
3  251  ..  ..  ..  ..
>>> 
>>> input = input.drop(columns=set(master.columns) ^ set(input.columns))
>>> 
>>> master_only = master[~master.id.isin(input.id)]
>>> master_overriden = input[input.id.isin(master.id)]
>>> 
>>> master_only.append(master_overriden)
    id   A   B   C
2  103  ..  ..  ..
0  101  ..  ..  ..
1  102  ..  XY  YZ
>>> d = {'A': '..', 'B': '..', 'C': '..'}
>>> master = pd.DataFrame([{'id': 101, **d}, {'id': 102, **d}, {'id': 103, **d}])
>>> input = pd.DataFrame([{'id': 101, **d, 'D': '..'}, {'id': 102, 'A': '..', 'B':'XY', 'C': 'YZ', 'D': '..'}, {'id': 223, **d, 'D': '..'}, {'id':251, **d, 'D': '..'}])
>>> 
>>> master
    id   A   B   C
0  101  ..  ..  ..
1  102  ..  ..  ..
2  103  ..  ..  ..
>>> input
    id   A   B   C   D
0  101  ..  ..  ..  ..
1  102  ..  XY  YZ  ..
2  223  ..  ..  ..  ..
3  251  ..  ..  ..  ..
>>> 
>>> useless_col = set(master.columns) ^ set(input.columns)
>>> useless_col
{'D'}
>>> 
>>> input = input.drop(columns=useless_col)
>>> 
>>> master_only = master[~master.id.isin(input.id)]
>>> master_overriden = input[input.id.isin(master.id)]
>>> 
>>> master_only.append(master_overriden)
    id   A   B   C
2  103  ..  ..  ..
0  101  ..  ..  ..
1  102  ..  XY  YZ