Python: pandas объединить несколько фреймов данных
У меня есть разные фреймы данных, и мне нужно объединить их на основе столбца даты. Если бы у меня было только два кадра данных, я мог бы использовать df1.merge(df2, on='date')
, чтобы сделать это с тремя кадрами данных, я бы использовал df1.merge(df2.merge(df3, on='date'), on='date')
, однако это становится действительно сложным и нечитаемым, чтобы сделать это с несколькими фреймами данных.
Все кадры данных имеют один общий столбец - date
, но они не имеют одинакового количества строк и столбцов, и мне нужны только те строки, в которых каждая дата является общей для каждого кадра данных.
Итак, я пытаюсь написать рекурсивную функцию, которая возвращает фрейм данных со всеми данными, но это не сработало. Как тогда объединить несколько фреймов данных?
Я пытался разными способами и получил ошибки, как out of range
, keyerror 0/1/2/3
и can not merge DataFrame with instance of type <class 'NoneType'>
.
Это сценарий, который я написал:
dfs = [df1, df2, df3] # list of dataframes
def mergefiles(dfs, countfiles, i=0):
if i == (countfiles - 2): # it gets to the second to last and merges it with the last
return
dfm = dfs[i].merge(mergefiles(dfs[i+1], countfiles, i=i+1), on='date')
return dfm
print(mergefiles(dfs, len(dfs)))
Пример: df_1:
May 19, 2017;1,200.00;0.1%
May 18, 2017;1,100.00;0.1%
May 17, 2017;1,000.00;0.1%
May 15, 2017;1,901.00;0.1%
df_2:
May 20, 2017;2,200.00;1000000;0.2%
May 18, 2017;2,100.00;1590000;0.2%
May 16, 2017;2,000.00;1230000;0.2%
May 15, 2017;2,902.00;1000000;0.2%
df_3:
May 21, 2017;3,200.00;2000000;0.3%
May 17, 2017;3,100.00;2590000;0.3%
May 16, 2017;3,000.00;2230000;0.3%
May 15, 2017;3,903.00;2000000;0.3%
Ожидаемый результат слияния:
May 15, 2017; 1,901.00;0.1%; 2,902.00;1000000;0.2%; 3,903.00;2000000;0.3%
Ответы
Ответ 1
Ниже представлен самый простой и понятный способ объединения нескольких фреймов данных, если сложные запросы не используются.
Просто объедините с DATE в качестве индекса и объедините, используя метод OUTER (чтобы получить все данные).
import pandas as pd
from functools import reduce
df1 = pd.read_table('file1.csv', sep=',')
df2 = pd.read_table('file2.csv', sep=',')
df3 = pd.read_table('file3.csv', sep=',')
Итак, в основном загрузите все файлы, которые у вас есть, как фрейм данных. Затем объедините файлы, используя функцию merge
или reduce
.
# compile the list of dataframes you want to merge
data_frames = [df1, df2, df3]
Вы можете добавить столько фреймов данных в приведенном выше коде. Это хорошая часть этого метода. Никаких сложных запросов.
Чтобы сохранить значения, относящиеся к одной и той же дате, необходимо объединить их в DATE
df_merged = reduce(lambda left,right: pd.merge(left,right,on=['DATE'],
how='outer'), data_frames)
# if you want to fill the values that don't exist in the lines of merged dataframe simply fill with required strings as
df_merged = reduce(lambda left,right: pd.merge(left,right,on=['DATE'],
how='outer'), data_frames).fillna('void')
- Таким образом, значения с той же даты находятся в тех же строках.
- Вы можете заполнить несуществующие данные из разных фреймов для разных столбцов, используя fillna().
Затем запишите объединенные данные в CSV файл, если это необходимо.
pd.DataFrame.to_csv(df_merged, 'merged.txt', sep=',', na_rep='.', index=False)
Это должно дать вам
DATE VALUE1 VALUE2 VALUE3....
Ответ 2
Похоже, что данные имеют одинаковые столбцы, поэтому вы можете:
df1 = pd.DataFrame(data1)
df2 = pd.DataFrame(data2)
merged_df = pd.concat([df1, df2])
Ответ 3
functools.reduce и pd.concat хорошие решения, но в перспективе времени исполнения pd.concat является лучшим.
from functools import reduce
import pandas as pd
dfs = [df1, df2, df3, ...]
nan_value = 0
# solution 1 (fast)
result_1 = pd.concat(dfs, join='outer', axis=1).fillna(nan_value)
# solution 2
result_2 = reduce(lambda left,right: pd.merge(df_left, df_right,
left_index=True, right_index=True,
how='outer'),
dfs).fillna(nan_value)
Ответ 4
Для этого есть 2 решения, но они возвращают все столбцы отдельно:
import functools
dfs = [df1, df2, df3]
df_final = functools.reduce(lambda left,right: pd.merge(left,right,on='date'), dfs)
print (df_final)
date a_x b_x a_y b_y c_x a b c_y
0 May 15,2017 900.00 0.2% 1,900.00 1000000 0.2% 2,900.00 2000000 0.2%
k = np.arange(len(dfs)).astype(str)
df = pd.concat([x.set_index('date') for x in dfs], axis=1, join='inner', keys=k)
df.columns = df.columns.map('_'.join)
print (df)
0_a 0_b 1_a 1_b 1_c 2_a 2_b 2_c
date
May 15,2017 900.00 0.2% 1,900.00 1000000 0.2% 2,900.00 2000000 0.2%
Ответ 5
@даннейу ответ правильный. pd.concat, естественно, присоединяется к столбцам индекса, если вы установите опцию оси в 1. По умолчанию используется внешнее соединение, но вы также можете указать внутреннее соединение. Вот пример:
x = pd.DataFrame({'a': [2,4,3,4,5,2,3,4,2,5], 'b':[2,3,4,1,6,6,5,2,4,2], 'val': [1,4,4,3,6,4,3,6,5,7], 'val2': [2,4,1,6,4,2,8,6,3,9]})
x.set_index(['a','b'], inplace=True)
x.sort_index(inplace=True)
y = x.__deepcopy__()
y.loc[(14,14),:] = [3,1]
y['other']=range(0,11)
y.sort_values('val', inplace=True)
z = x.__deepcopy__()
z.loc[(15,15),:] = [3,4]
z['another']=range(0,22,2)
z.sort_values('val2',inplace=True)
pd.concat([x,y,z],axis=1)
Ответ 6
Если вы фильтруете по общей дате, это вернет его:
dfs = [df1, df2, df3]
checker = dfs[-1]
check = set(checker.loc[:, 0])
for df in dfs[:-1]:
check = check.intersection(set(df.loc[:, 0]))
print(checker[checker.loc[:, 0].isin(check)])
Ответ 7
Спасибо за вашу помощь @jezrael, @zipa и @everestial007, оба ответа - то, что мне нужно. Если бы я хотел создать рекурсив, это также сработало бы так, как задумано:
def mergefiles(dfs=[], on=''):
"""Merge a list of files based on one column"""
if len(dfs) == 1:
return "List only have one element."
elif len(dfs) == 2:
df1 = dfs[0]
df2 = dfs[1]
df = df1.merge(df2, on=on)
return df
# Merge the first and second datafranes into new dataframe
df1 = dfs[0]
df2 = dfs[1]
df = dfs[0].merge(dfs[1], on=on)
# Create new list with merged dataframe
dfl = []
dfl.append(df)
# Join lists
dfl = dfl + dfs[2:]
dfm = mergefiles(dfl, on)
return dfm
Ответ 8
Посмотрите на это панда, трехстороннее объединение нескольких фреймов данных в столбцах
filenames = ['fn1', 'fn2', 'fn3', 'fn4',....]
dfs = [pd.read_csv(filename, index_col=index_col) for filename in filenames)]
dfs[0].join(dfs[1:])