实战讲解pandas中merge, join, concat的区别

您所在的位置:网站首页 merge和concat区别 实战讲解pandas中merge, join, concat的区别

实战讲解pandas中merge, join, concat的区别

2024-07-13 21:05| 来源: 网络整理| 查看: 265

1.从一个需求说起

最近经常有这么一堆数据需要处理一下,而且是很常见的需求: 有一个数据集,数据集里全是数字,需要对数据集按区间段进行个数统计,并计算各区间段的占比,所以本质上就是个算占比的事情。 有的同志对此不屑一顾,这算哪门子事,搞个excel还不是很简单。 当然excel是可以解决上面的问题。问题在于,第一,程序猿是很讨厌使用excel这种带.xxx的文件的,.xxx意味着通用性很差,必须用特定的软件程序才能打开。第二,大部分的开发环境是linux或者macos,这两开发环境不像windows,对于office系列的支持很差,而且office系列的东西都相当耗资源占CPU占内存,个人非常反感为了处理一个数据去开一个特别重的客户端软件。第三,excel再强大,毕竟没有写代码灵活,平时还是有很多稀奇古怪的需求,尤其是字符串处理,用代码处理还是首选。 所以,如果处理数据第一反应想到的是excel而不是写code解决,那一定是个运营汪而不是程序猿… 所以说了这么多,先直接上代码看看怎么搞定上面的事情。

2.pandas cut concat import pandas as pd def t1(): data1 = [552, 462, 565, 810, 720, 753, 602, 485, 475, 380, 590, 402, 501] data2 = [553, 362, 585, 710, 720, 559, 760, 785, 375, 680, 690, 403, 512] bins = [300, 400, 450, 550, 650, 750, 850] cut1 = pd.cut(data1, bins) cut2 = pd.cut(data2, bins) ret1 = pd.value_counts(cut1, ascending=False) ret2 = pd.value_counts(cut2, ascending=False) nret1 = pd.value_counts(cut1, normalize=True, ascending=False) nret2 = pd.value_counts(cut2, normalize=True, ascending=False) concat1 = pd.concat([ret1, nret1, ret2, nret2], axis=1) print(concat1) t1()

上面的输出结果:

0 1 2 3 (300, 400] 1 0.076923 2 0.153846 (400, 450] 1 0.076923 1 0.076923 (450, 550] 4 0.307692 1 0.076923 (550, 650] 4 0.307692 3 0.230769 (650, 750] 1 0.076923 4 0.307692 (750, 850] 2 0.153846 2 0.153846

上面的代码中,pd.cut, pd.value_counts的用法之前都已经讲过了,不再重点多讲,唯一需要再提一点的是pd.value_counts方法中normalize=True时,输出的就是占比,否则是数量。

重点看看这一行

concat1 = pd.concat([ret1, nret1, ret2, nret2], axis=1)

pandas中的concat只是单纯地将两个表"连接"在一起。这个过程叫作绑定(binding)或堆叠(stacking)。上面的例子,因为我们要保持"行"不变,而将"列"追加到一起,所以设置axis=1。 如果没有指定axis参数,默认的是axis=0, 意思就是保持"列"不变,按行追加。看个简单的例子

def t2(): data1 = {"A": [1, 2, 3], "B": [4, 5, 6]} data2 = {"C": [7, 8, 9], "D": [10, 11, 12]} df1 = pd.DataFrame(data1) df2 = pd.DataFrame(data2) print(pd.concat([df1, df2])) t2()

结果为:

A B C D 0 1.0 4.0 NaN NaN 1 2.0 5.0 NaN NaN 2 3.0 6.0 NaN NaN 0 NaN NaN 7.0 10.0 1 NaN NaN 8.0 11.0 2 NaN NaN 9.0 12.0

concat方法中要有一个ignore_index参数。ignore_index 忽略需要连接的frame本身的index,当原本的index没有特别意义的时候可以使用该参数。

如果将上面的代码中稍作修改如下:

print(pd.concat([df1, df2], ignore_index=True))

则结果变为

A B C D 0 1.0 4.0 NaN NaN 1 2.0 5.0 NaN NaN 2 3.0 6.0 NaN NaN 3 NaN NaN 7.0 10.0 4 NaN NaN 8.0 11.0 5 NaN NaN 9.0 12.0 3.merge方法

实际处理数据过程中,我们还经常需要做的一件事情就是join,就是数据库中的join操作,将两个表中的数据根据某一列拼接到一起。

同样看一个很常见的例子:

def t3(): agedata = {"name": ["lucy", "lili", "xiaoming"], "age": [15, 18, 21]} citydata = {"name": ["lucy", "lili", "xiaohua"], "city": ["beijing", "shanghai", "guangzhou"]} df1 = pd.DataFrame(agedata) df2 = pd.DataFrame(citydata) ret = pd.merge(df1, df2, on="name", how="left") print(ret) t3()

输出结果如下

name age city 0 lucy 15 beijing 1 lili 18 shanghai 2 xiaoming 21 NaN

上面的例子,就是sql中的join。两个dataframe,根据name字段进行left join,得到最终的结果。

merge方法的签名如下

@Substitution("\nleft : DataFrame") @Appender(_merge_doc, indents=0) def merge( left, right, how: str = "inner", on=None, left_on=None, right_on=None, left_index: bool = False, right_index: bool = False, sort: bool = False, suffixes=("_x", "_y"), copy: bool = True, indicator: bool = False, validate=None, ) -> "DataFrame": op = _MergeOperation( left, right, how=how, on=on, left_on=left_on, right_on=right_on, left_index=left_index, right_index=right_index, sort=sort, suffixes=suffixes, copy=copy, indicator=indicator, validate=validate, ) return op.get_result() 4.merge各个参数的作用

参数left_index和right_index,最开始不明白这两参数的作用,后来经过尝试发现他们的作用如下。 上面的例子我们是用on来指定连接的主键。不光可以通过on来指定,我们还可以用索引作为拼接的主键,只需要将left_index与right_index参数设置为true就可以。

def t4(): agedata = {"name": ["lucy", "lili", "xiaoming"], "age": [15, 18, 21]} citydata = {"name": ["lucy", "lili", "xiaohua"], "city": ["beijing", "shanghai", "guangzhou"]} df1 = pd.DataFrame(agedata) df2 = pd.DataFrame(citydata) ret = pd.merge(df1, df2, left_index=True, right_index=True) print(ret) t4()

输出结果为

name_x age name_y city 0 lucy 15 lucy beijing 1 lili 18 lili shanghai 2 xiaoming 21 xiaohua guangzhou

df1与df2默认的索引均为0,1,2,所以根据索引进行连接时,就刚好将两个df完美地拼接到了一起。其中suffixes默认为("_x", “_y”),连接的时候如果有相同的字段名自动将其添加相应的后缀。

how参数控制拼接方式,默认内连接(inner)。连接方式与sql中一样,有left,right,inner,outer这几种。

5.join方法

通过上面的例子,我们不难看出,sql中传统的join,在pandas中实际上是通过merge方法实现的。但是pandas中也有join方法,那么pandas中的join方法实现的是啥功能?

先说结论: join方法拼接列主要是基于行索引上的合并。

看几个例子

def t5(): agedata = {"name": ["lucy", "lili", "xiaoming"], "age": [15, 18, 21]} citydata = {"name": ["lucy", "lili", "xiaohua"], "city": ["beijing", "shanghai", "guangzhou"]} df1 = pd.DataFrame(agedata) df2 = pd.DataFrame(citydata) df1.join(df2)

这个方法运行会报错

ValueError: columns overlap but no suffix specified: Index(['name'], dtype='object')

将上面的代码修改一下

def t5(): agedata = {"name": ["lucy", "lili", "xiaoming"], "age": [15, 18, 21]} citydata = {"name": ["lucy", "lili", "xiaohua"], "city": ["beijing", "shanghai", "guangzhou"]} df1 = pd.DataFrame(agedata) df2 = pd.DataFrame(citydata) ret = df1.join(df2, lsuffix="_x", rsuffix="_y") print(ret)

结果如下

name_x age name_y city 0 lucy 15 lucy beijing 1 lili 18 lili shanghai 2 xiaoming 21 xiaohua guangzhou

是不是与上面merge方法基于行索引合并的结果一致? 实际上我们查看join方法的源码

def join( self, other, on=None, how="left", lsuffix="", rsuffix="", sort=False ) -> "DataFrame": """ Join columns of another DataFrame. Join columns with `other` DataFrame either on index or on a key column. Efficiently join multiple DataFrame objects by index at once by passing a list. ....... return self._join_compat( other, on=on, how=how, lsuffix=lsuffix, rsuffix=rsuffix, sort=sort ) def _join_compat( self, other, on=None, how="left", lsuffix="", rsuffix="", sort=False ): from pandas.core.reshape.merge import merge from pandas.core.reshape.concat import concat if isinstance(other, Series): if other.name is None: raise ValueError("Other Series must have a name") other = DataFrame({other.name: other}) if isinstance(other, DataFrame): return merge( self, other, left_on=on, how=how, left_index=on is None, right_index=True, suffixes=(lsuffix, rsuffix), sort=sort, ) else: if on is not None: raise ValueError( "Joining multiple DataFrames only supported for joining on index" ) frames = [self] + list(other) can_concat = all(df.index.is_unique for df in frames) # join indexes only using concat if can_concat: if how == "left": res = concat( frames, axis=1, join="outer", verify_integrity=True, sort=sort ) return res.reindex(self.index, copy=False) else: return concat( frames, axis=1, join=how, verify_integrity=True, sort=sort ) joined = frames[0] for frame in frames[1:]: joined = merge( joined, frame, how=how, left_index=True, right_index=True ) return joined

通过上面代码,我们也不难看出,join方法,其实最终调用的,也是merge方法。或者说,join方法其实就是merge的一个特例而已。

6.结论

综上所述 1.要想实现sql中的join,需要使用merge方法。 2.pandas中的join方法,相比merge,只是个弟弟,使用场景有限。 3.concat实现的只是将两个df按行或者案列简单进行拼接的功能,并没有实现sql中的join功能。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3