我有 3 个 CSV 文件。每个都有第一列作为人的(字符串)名称,而每个数据框中的所有其他列都是该人的属性。
如何将所有三个 CSV 文档“连接”在一起以创建单个 CSV,其中每一行都具有人员字符串名称的每个唯一值的所有属性?
pandas 中的 join()
函数指定我需要一个多索引,但我对分层索引方案与基于单个索引进行连接有什么关系感到困惑。
df1.join([df2, df3], on=[df2_col1, df3_col1])
不起作用。
Zero's answer 基本上是一个 reduce
操作。如果我有多个数据框,我会将它们放在这样的列表中(通过列表推导或循环或诸如此类的生成):
dfs = [df0, df1, df2, ..., dfN]
假设它们有一个公共列,例如您的示例中的 name
,我将执行以下操作:
import functools as ft
df_final = ft.reduce(lambda left, right: pd.merge(left, right, on='name'), dfs)
这样,您的代码应该适用于您想要合并的任何数量的数据帧。
如果你有 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')
df1.merge(df2,on='name').merge(df3,on='name')
name1
、name2
和 name3
df1.merge(df2,left_on='name1', right_on='name2').merge(df3,left_on='name1', right_on='name3').drop(columns=['name2', 'name3']).rename(columns={'name1':'name'})
这是 join 方法的理想情况
join
方法专为这些类型的情况而构建。您可以将任意数量的 DataFrame 与其连接在一起。调用 DataFrame 与传递的 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
pd.DataFrame().join(dfs, how="outer")
。在某些情况下,这可能更干净。
pd.concat
将导致更简单的语法:pd.concat([df.set_index('name') for df in dfs], axis=1, join='inner').reset_index()
。 concat
在处理跨多个 dfs 的重复列名时也更加通用(join
不擅长于此),尽管您只能使用它执行内部或外部联接。
dfs[0].join(dfs[1:])
应编辑为 dfs[0].join(dfs[1:], sort=False)
,否则会弹出 FutureWarning
。谢谢你的好例子。
ValueError: Indexes have overlapping values
,尽管通过检查列表中的各个数据框,它们似乎没有重叠的值。
在带有 pandas
0.22.0 的 python
3.6.3 中,您也可以使用 concat
,只要您将要用于连接的列设置为索引
pd.concat(
(iDF.set_index('name') for iDF in [df1, df2, df3]),
axis=1, join='inner'
).reset_index()
其中 df1
、df2
和 df3
在 John Galt's answer 中定义
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']
)
name
列在所有数据框中都不相同),那么 join='outer'
应该保留它们,但是您将缺少值。对于不同的列集没有问题,只要它们都共享用于索引的 name
列
对于数据帧列表 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')
简单的解决方案:
如果列名相似:
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'})
这是一种合并数据框字典同时保持列名与字典同步的方法。如果需要,它还会填充缺失值:
这是合并数据帧字典的功能
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)
不需要多索引来执行 join 操作。只需正确设置执行连接操作的索引列(例如哪个命令 df.set_index('Name')
)
join
操作默认在索引上执行。在您的情况下,您只需指定 Name
列对应于您的索引。下面是一个例子
tutorial 可能有用。
# 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 have 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')
pandas documentation 还有另一个解决方案(我在这里看不到),
使用 .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
用于忽略附加数据帧的索引,将其替换为源数据帧中可用的下一个索引。
如果有不同的列名,将引入 Nan
。
我调整了接受的答案以使用 reduce
在不同的 suffix
参数上执行多个数据帧的操作,我猜它也可以扩展到不同的 on
参数。
from functools import reduce
dfs_with_suffixes = [(df2,suffix2), (df3,suffix3),
(df4,suffix4)]
merge_one = lambda x,y,sfx:pd.merge(x,y,on=['col1','col2'..], suffixes=sfx)
merged = reduce(lambda left,right:merge_one(left,*right), dfs_with_suffixes, df1)
""
。最终的合并函数可能如下:merge_one = lambda x,y,sfx:pd.merge(x,y,on=['col1','col2'..], suffixes=('', sfx)) # Left gets no suffix, right gets something identifiable
不定期副业成功案例分享
reduce
被替换为functools.reduce
所以import functools functools.reduce(.......)
name1
、name2
和name3
。n-1
调用了合并函数吗?我想在这种情况下,数据帧的数量很少,这并不重要,但我想知道是否有更可扩展的解决方案。df
来说不太适用(它将“on”作为第一次合并工作的列注入,但随后的合并失败),而是让我使用它:{2 }