異なるデータフレームがあり、日付列に基づいてそれらを結合する必要があります。データフレームが2つしかない場合、df1.merge(df2, on='date')
を使用して3つのデータフレームで実行できますが、df1.merge(df2.merge(df3, on='date'), on='date')
を使用しますが、複数のデータフレームで実行すると非常に複雑で読みにくくなります。
すべてのデータフレームには共通の列が1つあります-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%
以下は、複雑なクエリが関与しない場合に複数のデータフレームをマージする最もクリーンでわかりやすい方法です。
単に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')
必要に応じて、マージされたデータをcsvファイルに書き込みます。
pd.DataFrame.to_csv(df_merged, 'merged.txt', sep=',', na_rep='.', index=False)
これはあなたに与えるはずです
DATE VALUE1 VALUE2 VALUE3 ....
データには同じ列があるように見えるので、次のことができます。
df1 = pd.DataFrame(data1)
df2 = pd.DataFrame(data2)
merged_df = pd.concat([df1, df2])
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)
これには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%
@dannyeuuの答えは正しいです。 axisオプションを1に設定すると、pd.concatは自然にインデックス列で結合します。デフォルトは外部結合ですが、内部結合も指定できます。以下に例を示します。
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)
共通の日付でフィルタリングする場合、これが返されます:
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)])
あなたの助けをありがとう@ 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