Как объединить много DataFrames путем индексации значений, где столбцы перекрываются? - PullRequest
1 голос
/ 07 октября 2019

У меня есть много DataFrames, которые мне нужно объединить.

Скажем:

base: id  constraint
      1   'a'
      2   'b'
      3   'c'

df_1: id value constraint
      1  1     'a'
      2  2     'a'
      3  3     'a'

df_2: id value constraint
      1  1     'b'
      2  2     'b'
      3  3     'b'


df_3: id value constraint
      1  1     'c'
      2  2     'c'
      3  3     'c'

Если я попытаюсь объединить их все (это будет в цикле), яget:

a = pd.merge(base, df_1, on=['id', 'constraint'], how='left')
b = pd.merge(a, df_2, on=['id', 'constraint'], how='left')
c = pd.merge(b, df_3, on=['id', 'constraint'], how='left')
id constraint value   value_x  value_y
1  'a'        1       NaN      NaN
2  'b'        NaN     2        NaN
3  'c'        NaN     NaN      3

Желаемый результат будет:

id constraint value
1  'a'        1 
2  'b'        2
3  'c'        3

Я знаю о combine_first, и он работает, но у меня не может быть такого подхода, потому что онв тысячи раз медленнее.

Есть ли merge, который может заменить значения в случае перекрытия столбцов?

Это чем-то похоже на этот вопрос , без ответов.

Ответы [ 4 ]

3 голосов
/ 07 октября 2019

Вы можете использовать ffill() для цели:

df_1 = pd.DataFrame({'val':[1]}, index=[1])
df_2 = pd.DataFrame({'val':[2]}, index=[2])
df_3 = pd.DataFrame({'val':[3]}, index=[3])

(pd.concat((df_1,df_2,df_3), axis=1)
   .ffill(1)
   .iloc[:,-1]
)

Вывод:

1    1.0
2    2.0
3    3.0
Name: val, dtype: float64

Для новых данных:

base.merge(pd.concat((df1,df2,df3)),
           on=['id','constraint'],
           how='left')

Вывод:

   id constraint  value
0   1        'a'      1
1   2        'b'      2
2   3        'c'      3

Вывод: вы действительно ищете опцию how='left' в merge

3 голосов
/ 07 октября 2019

Учитывая ваш MCVE:

import pandas as pd

base = pd.DataFrame([1,2,3], columns=['id'])
df1 = pd.DataFrame([[1,1]], columns=['id', 'value'])
df2 = pd.DataFrame([[2,2]], columns=['id', 'value'])
df3 = pd.DataFrame([[3,3]], columns=['id', 'value'])

Я бы предложил сначала объединить ваш фрейм данных (при необходимости используя цикл):

df = pd.concat([df1, df2, df3])

А затем объединить:

pd.merge(base, df, on='id')

Это дает:

   id  value
0   1      1
1   2      2
2   3      3

Обновление

Запуск кода с новой версией вашего вопроса и вводом, предоставленным @Celius Stingher:

a = {'id':[1,2,3],'constrains':['a','b','c']}
b = {'id':[1,2,3],'value':[1,2,3],'constrains':['a','a','a']}
c = {'id':[1,2,3],'value':[1,2,3],'constrains':['b','b','b']}
d = {'id':[1,2,3],'value':[1,2,3],'constrains':['c','c','c']}
base = pd.DataFrame(a)
df1 = pd.DataFrame(b)
df2 = pd.DataFrame(c)
df3 = pd.DataFrame(d)

Мы получаем:

   id constrains  value
0   1          a      1
1   2          b      2
2   3          c      3

Что соответствует ожидаемому результату.

1 голос
/ 07 октября 2019

Если вы должны объединить только все кадры данных с базой:

На основе редактирования

import pandas as pd
a = {'id':[1,2,3],'constrains':['a','b','c']}
b = {'id':[1,2,3],'value':[1,2,3],'constrains':['a','a','a']}
c = {'id':[1,2,3],'value':[1,2,3],'constrains':['b','b','b']}
d = {'id':[1,2,3],'value':[1,2,3],'constrains':['c','c','c']}
base = pd.DataFrame(a)
df_1 = pd.DataFrame(b)
df_2 = pd.DataFrame(c)
df_3 = pd.DataFrame(d)

dataframes = [df_1,df_2,df_3]
for i in dataframes:
    base = base.merge(i,how='left',on=['id','constrains'])
summation = [col for col in base if col.startswith('value')]
base['value'] = base[summation].sum(axis=1)
base = base.dropna(how='any',axis=1)
print(base)

Вывод:

   id constrains  value
0   1          a    1.0
1   2          b    2.0
2   3          c    3.0
0 голосов
/ 08 октября 2019

Для тех, кто хочет просто сделать merge, переопределяя значения (что в моем случае), можно добиться этого с помощью этого метода, который действительно похож на ответ Целиуса Стингера .

Документированная версия находится на оригинальной сущности .

import pandas as pa

def rmerge(left,right,**kwargs):
    # Function to flatten lists from http://rosettacode.org/wiki/Flatten_a_list#Python
    def flatten(lst):
        return sum( ([x] if not isinstance(x, list) else flatten(x) for x in lst), [] )

    # Set default for removing overlapping columns in "left" to be true
    myargs = {'replace':'left'}
    myargs.update(kwargs)

    # Remove the replace key from the argument dict to be sent to
    # pandas merge command
    kwargs = {k:v for k,v in myargs.items() if k is not 'replace'}

    if myargs['replace'] is not None:
        # Generate a list of overlapping column names not associated with the join
        skipcols = set(flatten([v for k, v in myargs.items() if k in ['on','left_on','right_on']]))
        leftcols = set(left.columns)
        rightcols = set(right.columns)
        dropcols = list((leftcols & rightcols).difference(skipcols))

        # Remove the overlapping column names from the appropriate DataFrame
        if myargs['replace'].lower() == 'left':
            left = left.copy().drop(dropcols,axis=1)
        elif myargs['replace'].lower() == 'right':
            right = right.copy().drop(dropcols,axis=1)

    df = pa.merge(left,right,**kwargs)

    return df
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...