панди тристоронні приєднання декількох фреймів даних на стовпцях


191

У мене є 3 CSV файли. У кожному є перший стовпець як (рядок) імена людей, тоді як усі інші стовпці у кожному кадрі даних є атрибутами цієї людини.

Як я можу "об'єднати" всі три документи CSV, щоб створити єдиний CSV, в кожному рядку якого є всі атрибути для кожного унікального значення імені рядка людини?

join()Функція панд специфицирует , що мені потрібно мультііндексних, але я плутати про те, що ієрархічна схема індексації має відношення до створення об'єднання на основі єдиного індексу.


2
Вам не потрібен мультиіндекс. У документах приєднання зазначено, що у вас немає мультиіндекс при передачі декількох стовпців, щоб приєднатись до цього, тоді він це впорається.
cwharland

1
На моїх випробуваннях df1.join([df2, df3], on=[df2_col1, df3_col1])не вийшло.
ковзанка

Вам потрібно зв'язати їх ланцюжком, як у наведеній відповіді. Об'єднайте df1 і df2, потім злийте результат з df3
cwharland

Відповіді:


474

Передбачається імпорт:

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
Це не дуже спрацювало з моїми dfмультиіндексами стовпців (він вводив 'on' у вигляді стовпця, який працював на перше злиття, але наступні злиття не вдалися), замість цього я змусив його працювати:df = reduce(lambda left, right: left.join(right, how='outer', on='Date'), dfs)
Адріан Торрі

+1 до ps0604 Що робити, якщо стовпці приєднання відрізняються, це працює? ми повинні піти з pd.merge у випадку, коли стовпчики приєднання відрізняються? дякую
steve

106

Ви можете спробувати це, якщо у вас є 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'})
Майкл Х.

і далі, як це зробити за допомогою індексу. Здається, це не працює, якщо "ім'я" - це індекс, а не назва стовпця.
Брайан Д

85

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

joinМетод побудований саме для таких ситуацій. Ви можете приєднатись до будь-якої кількості DataFrames разом із ним. Виклик DataFrame приєднується до індексу колекції переданих DataFrames. Щоб працювати з декількома DataFrames, ви повинні поставити стовпці приєднання в індекс.

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

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хоча, перевіряючи окремі фрейми даних у списку, вони, схоже, не мають значень, що перекриваються.
SomJura

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.