панда трехстороннее объединение нескольких фреймов данных на столбцах


191

У меня есть 3 файла CSV. Каждый из них имеет первый столбец в виде (строковых) имен людей, в то время как все остальные столбцы в каждом кадре данных являются атрибутами этого человека.

Как я могу «объединить» все три CSV-документа, чтобы создать единый CSV-файл, в котором каждая строка имеет все атрибуты для каждого уникального значения имени строки человека?

join()Функция панд специфицирует , что мне нужно мультииндексных, но я путать о том, что иерархическая схема индексации имеет отношение к созданию объединения на основе единого индекса.


2
Вам не нужен мультииндекс. В соединительных документах говорится, что у вас нет мультииндекса при передаче нескольких столбцов для присоединения, тогда он справится с этим.
cwharland

1
В моих испытаниях df1.join([df2, df3], on=[df2_col1, df3_col1])не сработало.
Кафе на палубе

Вам нужно связать их вместе, как в ответе. Объедините df1 и df2, затем объедините результат с df3
cwharland

Ответы:


475

Предполагаемый импорт:

import pandas as pd

Ответ Джона Галта - в основном reduceоперация. Если бы у меня было больше, чем несколько фреймов данных, я бы поместил их в список, подобный этому (сгенерированный через списки, циклы или еще много чего):

dfs = [df0, df1, df2, dfN]

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

df_final = reduce(lambda left,right: pd.merge(left,right,on='name'), dfs)

Таким образом, ваш код должен работать с любым количеством фреймов данных, которые вы хотите объединить.

Редактировать 1 августа 2016 года : для тех, кто использует Python 3: reduceбыл перемещен в functools. Итак, чтобы использовать эту функцию, вам сначала нужно импортировать этот модуль:

from functools import reduce

11
Я только попытался использовать это, и это потерпело неудачу, потому что reduceбыло заменено functools.reduceSoimport functools functools.reduce(.......)
MattR

3
Как будет работать это решение, если я различаю имена полей для присоединения? Например, в трех кадрах данных я мог бы иметь name1, name2и name3соответственно.
ps0604

2
Разве это не значит, что у нас есть n-1вызовы функции слияния? Я полагаю, что в этом случае, когда число фреймов данных невелико, это не имеет значения, но мне интересно, есть ли более масштабируемое решение.
eapolinario

1
Это не совсем работало для моих dfs с многоиндексными столбцами (это было добавление 'on' как столбца, который работал для первого слияния, но последующие слияния не удавались), вместо этого я заставил его работать с:df = reduce(lambda left, right: left.join(right, how='outer', on='Date'), dfs)
Adrian Torrie

+1 к пс0604. Что, если соединительные столбцы отличаются, это работает? мы должны пойти с pd.merge, если столбцы соединения отличаются? спасибо
Стив

107

Вы можете попробовать это, если у вас есть 3 кадра

# Merge multiple dataframes
df1 = pd.DataFrame(np.array([
    ['a', 5, 9],
    ['b', 4, 61],
    ['c', 24, 9]]),
    columns=['name', 'attr11', 'attr12'])
df2 = pd.DataFrame(np.array([
    ['a', 5, 19],
    ['b', 14, 16],
    ['c', 4, 9]]),
    columns=['name', 'attr21', 'attr22'])
df3 = pd.DataFrame(np.array([
    ['a', 15, 49],
    ['b', 4, 36],
    ['c', 14, 9]]),
    columns=['name', 'attr31', 'attr32'])

pd.merge(pd.merge(df1,df2,on='name'),df3,on='name')

в качестве альтернативы, как упомянуто cwharland

df1.merge(df2,on='name').merge(df3,on='name')

34
Для более чистой внешности вы можете df1.merge(df2,on='name').merge(df3,on='name')
приковать

1
Как будет работать это решение, если я различаю имена полей для присоединения? Например, в трех кадрах данных я мог бы иметь name1, name2и name3соответственно
ps0604

4
@ ps0604df1.merge(df2,left_on='name1', right_on='name2').merge(df3,left_on='name1', right_on='name3').drop(columns=['name2', 'name3']).rename(columns={'name1':'name'})
Майкл Х.

и далее, как это сделать с помощью индекса. Кажется, не работает, если «имя» является индексом, а не именем столбца.
Брайан Д

86

Это идеальная ситуация для joinметода

joinМетод построен именно для таких ситуаций. Вы можете присоединиться к любому количеству DataFrames вместе с ним. Вызывающий DataFrame объединяется с индексом коллекции переданных DataFrame. Для работы с несколькими фреймами данных необходимо поместить соединительные столбцы в индекс.

Код будет выглядеть примерно так:

filenames = ['fn1', 'fn2', 'fn3', 'fn4',....]
dfs = [pd.read_csv(filename, index_col=index_col) for filename in filenames)]
dfs[0].join(dfs[1:])

С данными @ zero вы можете сделать это:

df1 = pd.DataFrame(np.array([
    ['a', 5, 9],
    ['b', 4, 61],
    ['c', 24, 9]]),
    columns=['name', 'attr11', 'attr12'])
df2 = pd.DataFrame(np.array([
    ['a', 5, 19],
    ['b', 14, 16],
    ['c', 4, 9]]),
    columns=['name', 'attr21', 'attr22'])
df3 = pd.DataFrame(np.array([
    ['a', 15, 49],
    ['b', 4, 36],
    ['c', 14, 9]]),
    columns=['name', 'attr31', 'attr32'])

dfs = [df1, df2, df3]
dfs = [df.set_index('name') for df in dfs]
dfs[0].join(dfs[1:])

     attr11 attr12 attr21 attr22 attr31 attr32
name                                          
a         5      9      5     19     15     49
b         4     61     14     16      4     36
c        24      9      4      9     14      9

4
Присоединение всех ДФС к пустому dataframe также работает: pd.DataFrame().join(dfs, how="outer"). Это может быть чище в некоторых ситуациях.
Доминик

4
Это хороший совет, и теперь он включен в слияние 101 панд (см. Раздел о слиянии нескольких фреймов данных). Стоит отметить , что если ваши присоединиться ключи уникальны, используя pd.concatприведет к более простой синтаксис: pd.concat([df.set_index('name') for df in dfs], axis=1, join='inner').reset_index(). concatтакже более универсален при работе с дублирующимися именами столбцов в нескольких dfs ( joinне так хорош в этом), хотя вы можете выполнять с ним только внутренние или внешние объединения.
cs95,

dfs[0].join(dfs[1:])должно быть отредактировано, dfs[0].join(dfs[1:], sort=False) потому что иначе FutureWarningвсплывет. Спасибо за хороший пример.
gies0r

Я получаю сообщение об ошибке при попытке этого: ValueError: Indexes have overlapping valuesхотя, проверяя отдельные кадры данных в списке, они не имеют перекрывающихся значений.
СомЮра

17

Это также можно сделать следующим образом для списка фреймов данных df_list:

df = df_list[0]
for df_ in df_list[1:]:
    df = df.merge(df_, on='join_col_name')

или если кадры данных находятся в объекте генератора (например, чтобы уменьшить потребление памяти):

df = next(df_list)
for df_ in df_list:
    df = df.merge(df_, on='join_col_name')

11

В python3.6.3 с pandas0.22.0 вы также можете использовать concatдо тех пор, пока вы указали в качестве индекса столбцы, которые вы хотите использовать для присоединения

pd.concat(
    (iDF.set_index('name') for iDF in [df1, df2, df3]),
    axis=1, join='inner'
).reset_index()

где df1, df2и df3определяются как в ответ Джон Галт в

import pandas as pd
df1 = pd.DataFrame(np.array([
    ['a', 5, 9],
    ['b', 4, 61],
    ['c', 24, 9]]),
    columns=['name', 'attr11', 'attr12']
)
df2 = pd.DataFrame(np.array([
    ['a', 5, 19],
    ['b', 14, 16],
    ['c', 4, 9]]),
    columns=['name', 'attr21', 'attr22']
)
df3 = pd.DataFrame(np.array([
    ['a', 15, 49],
    ['b', 4, 36],
    ['c', 14, 9]]),
    columns=['name', 'attr31', 'attr32']
)

2
Это должен быть принятый ответ. Это самый быстрый.
Р. Чжу

4

Для выполнения операций соединения не требуется мультииндекс . Нужно просто правильно установить столбец индекса для выполнения операций соединения (например, какая команда df.set_index('Name'))

По joinумолчанию операция выполняется над индексом. В вашем случае вам просто нужно указать, что Nameстолбец соответствует вашему индексу. Ниже приведен пример

Учебное пособие может быть полезным.

# Simple example where dataframes index are the name on which to perform the join operations
import pandas as pd
import numpy as np
name = ['Sophia' ,'Emma' ,'Isabella' ,'Olivia' ,'Ava' ,'Emily' ,'Abigail' ,'Mia']
df1 = pd.DataFrame(np.random.randn(8, 3), columns=['A','B','C'], index=name)
df2 = pd.DataFrame(np.random.randn(8, 1), columns=['D'],         index=name)
df3 = pd.DataFrame(np.random.randn(8, 2), columns=['E','F'],     index=name)
df = df1.join(df2)
df = df.join(df3)

# If you a 'Name' column that is not the index of your dataframe, one can set this column to be the index
# 1) Create a column 'Name' based on the previous index
df1['Name']=df1.index
# 1) Select the index from column 'Name'
df1=df1.set_index('Name')

# If indexes are different, one may have to play with parameter how
gf1 = pd.DataFrame(np.random.randn(8, 3), columns=['A','B','C'], index=range(8))
gf2 = pd.DataFrame(np.random.randn(8, 1), columns=['D'], index=range(2,10))
gf3 = pd.DataFrame(np.random.randn(8, 2), columns=['E','F'], index=range(4,12))

gf = gf1.join(gf2, how='outer')
gf = gf.join(gf3, how='outer')

4

Вот метод для объединения словаря фреймов данных с синхронизацией имен столбцов со словарем. Также он заполняет пропущенные значения при необходимости:

Эта функция объединяет данные фреймов данных.

def MergeDfDict(dfDict, onCols, how='outer', naFill=None):
  keys = dfDict.keys()
  for i in range(len(keys)):
    key = keys[i]
    df0 = dfDict[key]
    cols = list(df0.columns)
    valueCols = list(filter(lambda x: x not in (onCols), cols))
    df0 = df0[onCols + valueCols]
    df0.columns = onCols + [(s + '_' + key) for s in valueCols] 

    if (i == 0):
      outDf = df0
    else:
      outDf = pd.merge(outDf, df0, how=how, on=onCols)   

  if (naFill != None):
    outDf = outDf.fillna(naFill)

  return(outDf)

Хорошо, давайте создадим данные и протестируем это:

def GenDf(size):
  df = pd.DataFrame({'categ1':np.random.choice(a=['a', 'b', 'c', 'd', 'e'], size=size, replace=True),
                      'categ2':np.random.choice(a=['A', 'B'], size=size, replace=True), 
                      'col1':np.random.uniform(low=0.0, high=100.0, size=size), 
                      'col2':np.random.uniform(low=0.0, high=100.0, size=size)
                      })
  df = df.sort_values(['categ2', 'categ1', 'col1', 'col2'])
  return(df)


size = 5
dfDict = {'US':GenDf(size), 'IN':GenDf(size), 'GER':GenDf(size)}   
MergeDfDict(dfDict=dfDict, onCols=['categ1', 'categ2'], how='outer', naFill=0)

3

Простое решение:

Если имена столбцов похожи:

 df1.merge(df2,on='col_name').merge(df3,on='col_name')

Если имена столбцов разные:

df1.merge(df2,left_on='col_name1', right_on='col_name2').merge(df3,left_on='col_name1', right_on='col_name3').drop(columns=['col_name2', 'col_name3']).rename(columns={'col_name1':'col_name'})

2

Есть другое решение из документации панд (которого я не вижу здесь),

используя .append

>>> df = pd.DataFrame([[1, 2], [3, 4]], columns=list('AB'))
   A  B
0  1  2
1  3  4
>>> df2 = pd.DataFrame([[5, 6], [7, 8]], columns=list('AB'))
   A  B
0  5  6
1  7  8
>>> df.append(df2, ignore_index=True)
   A  B
0  1  2
1  3  4
2  5  6
3  7  8

ignore_index=TrueИспользуются для игнорирования индекса приложенного dataframe, заменив его на следующий индекс доступен в исходных один.

Если есть разные имена столбцов, Nanбудут введены.


это семантически, для кого-то, использующего слово «присоединиться», чтобы сказать, соединяя два кадра данных. (не обязательно как операция соединения SQL)
Sylhare

1

Три кадра данных

введите описание изображения здесь

введите описание изображения здесь

Давайте объединить эти кадры, используя вложенный pd.merge

введите описание изображения здесь

Здесь мы идем, у нас есть объединенный фрейм данных.

Удачного анализа !!!

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.