merge
merge的参数
- on:列名,join用来对齐的那一列的名字,用到这个参数的时候一定要保证左表和右表用来对齐的那一列都有相同的列名。
- left_on:左表对齐的列,可以是列名,也可以是和dataframe同样长度的arrays。
- right_on:右表对齐的列,可以是列名,也可以是和dataframe同样长度的arrays。
- left_index/ right_index: 如果是True的haunted以index作为对齐的key
- how:数据融合的方法。没有指定how的话默认使用inner方法。
- v0.17.0 版本的pandas开始还支持一个indicator的参数,如果置True的时候,输出结果会增加一列 ’ _merge’。_merge列可以取三个值
- left_only 只在左表中
- right_only 只在右表中
- both 两个表中都有
- sort:根据dataframe合并的keys按字典顺序排序,默认是,如果置false可以提高表现。
- suffix后缀参数
- 如果和表合并的过程中遇到有一列两个表都同名,但是值不同,合并的时候又都想保留下来,就可以用suffixes给每个表的重复列名增加后缀。
result = pd.merge(left, right, on='k', suffixes=['_l', '_r'])
merge用于表内部基于 index-on-index 和 index-on-column(s) 的合并,但默认是基于index来合并
join方法
dataframe内置的join方法是一种快速合并的方法。它默认以index作为对齐的列。
how 参数
join中的how参数和merge中的how参数一样,用来指定表合并保留数据的规则。
on 参数
在实际应用中如果右表的索引值正是左表的某一列的值,这时可以通过将 右表的索引 和 左表的列 对齐合并这样灵活的方式进行合并
left1 = pd.DataFrame({'A': ['A0', 'A1', 'A2', 'A3'],'B': ['B0', 'B1', 'B2', 'B3'],'key': ['K0', 'K1', 'K0', 'K1']})
left1
right1 = pd.DataFrame({'C': ['C0', 'C1'],'D': ['D0', 'D1']},index=['K0', 'K1'])
right1
result = left1.join(right1, on='key')
result
组合join多个dataframe
一次组合多个dataframe的时候可以传入元素为dataframe的列表或者tuple。一次join多个。
result = left.join([right, right2])
更新表的nan值
combine_first
如果一个表的nan值,在另一个表相同位置(相同索引和相同列)可以找到,则可以通过combine_first来更新数据
import pandas as pd
df = pd.DataFrame(
{"A": ["001", None, "003", None, "005"],
"B": ["1", "2", "3", "4", "5"]}
)
print(df)
"""
A B
0 001 1
1 None 2
2 003 3
3 None 4
4 005 5
"""
# 我们现在需求如下,如果A列中的数据不为空,那么不做处理。
# 为空,则用B列中对应的数据进行替换
df["A"] = df["A"].combine_first(df["B"])
print(df)
"""
A B
0 001 1
1 2 2
2 003 3
3 4 4
4 005 5
"""
首先是两个Series对象,假设叫s1和s2,那么s1.combine_first(s2)就表示用s2替换掉s1中为空的数据,如果s1和s2的某个相同索引对应的数据都是空,那么结果只能是空。当然这个方法不是在原地操作,而是会返回一个新的Series对象
这个方法的理想前提是两个Series对象的索引是一致的,因为替换是根据索引来指定位置的
比如s1中index为1的数据为空,那么就会使用s2中index为1的数据进行替换。但如果s2中没有index为1数据,那么就不会替换了。并且,如果假设s2中存在index为100的数据,但是s1中没有,那么结果就会多出一个index为100的数据。
update
1.pd.Series.update
如果要用一张表中的数据来更新另一张表的数据则可以用update来实现
import pandas as pd
s1 = pd.Series([1, 2, 3, 4])
s2 = pd.Series([11, 22, 33, 44])
s1.update(s2)
print(s1)
"""
0 11
1 22
2 33
3 44
dtype: int64
"""
这个方法是在本地进行操作的,功能还是用s2的元素替换s1的元素,并且只要s2中的元素不为空,那么就进行替换。但如果s2中的元素为空,那么可以认为'新版本'没有出现,那么还是使用老版本。
import pandas as pd
s1 = pd.Series([1, 2, 3, 4])
s2 = pd.Series([11, 22, None, 44])
s1.update(s2)
print(s1)
"""
0 11
1 22
2 3
3 44
dtype: int64
"""
2.DataFrame.update(other, join='left', overwrite=True, filter_func=None, errors='ignore') [source]
使用来自另一个DataFrame的非NA值进行适当的修改。
参数: | other : 应该至少有一个与原始 如果传递了一个Series,则必须设置它的name属性, 并将其用作列名,以便与原始的DataFrame保持一致。 join : 只实现left join,保留原始对象的索引和列。 overwrite : 如何处理重复键的非NA值: 1) True:用其他 DataFrame的值覆盖原始数据的值。 2) False:仅更新原始DataFrame中NA的值。 filter_func :
可以选择替换NA以外的值。 对于应该更新的值返回True。 errors : 如果为 则当 将引发 在版本0.24.0中更改: 从 更改 为 |
返回值: | None : 方法直接更改调用对象 |
Raises: | ValueError 当 当错误不是
如果 |
df = pd.DataFrame({'A': [1, 2, 3],'B': [400, 500, 600]})
df
new_df = pd.DataFrame({'B': [4, 5, 6],'C': [7, 8, 9]})
new_df
df.update(new_df)
df
由于更新,DataFrame的长度不会增加,只会更新匹配的索引/列标签上的值
combine_first 和 update 的区别
combine_first:如果s1中的值为空,用s2的值替换,否则保留s1的值
update:如果s2中的值不为空,那么替换s1,否则保留s1的值
- 另外在combine_first的时候,我们反复强调了索引的问题,如果s1和s2索引不一样,那么生成的结果的元素个数会多。但是update不一样,因为它是在本地进行操作的,也就是直接本地修改s1。所以最终s1的元素个数是不为发生变化的。
使用combine_first会只更新左表的nan值。而update则会更新左表的所有能在右表中找到的值(两表位置相对应)
combine和combine_first类似,只是需要指定一个函数。
import pandas as pd
df = pd.DataFrame(
{"A": ["001", None, "003", None, "005"],
"B": ["1", "2", "3", "4", "5"]}
)
print(df)
"""
A B
0 001 1
1 None 2
2 003 3
3 None 4
4 005 5
"""
df["A"] = df["A"].combine(df["B"], lambda a, b: a if pd.notna(a) else b)
print(df)
"""
A B
0 001 1
1 2 2
2 003 3
3 4 4
4 005 5
"""
我们指定了一个匿名函数,参数a、b就代表df["A"]和df["B"]中对应的每一个数据。如果a不为空,那么返回a,否则返回b。
所以我们看到,我们使用combine实现了和combine_first的效果。combine_first是专门对空值进行替换的,但是combine则是可以让我们自己指定逻辑。我们可以实现combine_first的功能,也可以实现其它的功能
import pandas as pd
s1 = pd.Series([1, 22, 3, 44])
s2 = pd.Series([11, 2, 33, 4])
# 哪个元素大就保留哪一个
print(s1.combine(s2, lambda a, b: a if a > b else b))
"""
0 11
1 22
2 33
3 44
dtype: int64
"""
# 两个元素进行相乘
print(s1.combine(s2, lambda a, b: a * b))
"""
0 11
1 44
2 99
3 176
dtype: int64
"""
combine和combine_first内部会先对索引进行处理,如果两个Series对象的索引不一样,那么会先将它们索引变得一致。
import pandas as pd
s1 = pd.Series([1, 22, 3, 44], index=['a', 'b', 'c', 'd'])
s2 = pd.Series([11, 2, 33, 4], index=['c', 'd', 'e', 'f'])
# 先对两个索引取并集
index = s1.index.union(s2.index)
print(index) # Index(['a', 'b', 'c', 'd', 'e', 'f'], dtype='object')
# 然后通过reindex,获取指定索引的元素,当然索引不存在就用NaN代替
s1 = s1.reindex(index)
s2 = s2.reindex(index)
print(s1)
"""
a 1.0
b 22.0
c 3.0
d 44.0
e NaN
f NaN
dtype: float64
"""
print(s2)
"""
a NaN
b NaN
c 11.0
d 2.0
e 33.0
f 4.0
dtype: float64
"""
# 在将s1和s2的索引变得一致之后,依次进行操作。
再回过头看一下combine_first
import pandas as pd
s1 = pd.Series([1, 22, 3, 44], index=['a', 'b', 'c', 'd'])
s2 = pd.Series([11, 2, 33, 4], index=['a', 'b', 'c', 'e'])
print(s1.combine_first(s2))
"""
a 1.0
b 22.0
c 3.0
d 44.0
e 4.0
dtype: float64
"""
# 一开始的话可能有人会好奇为什么类型变了,但是现在显然不会有疑问了
# 因为s1和s2的索引不一致,index='e'在s1中不存在,index='d'在s2中不存在
# 而reindex如果指定不存在索引,则用NaN代替
# 而如果出现了NaN,那么类型就由整型变成了浮点型。
# 但两个Series对象的index如果一样,那么reindex的结果也还是和原来一样,由于没有NaN,那么类型就不会变化
# 所以我们可以自己实现一个combine_first,当然pandas内部也是这么做的
s1 = s1.reindex(['a', 'b', 'c', 'd', 'e'])
s2 = s2.reindex(['a', 'b', 'c', 'd', 'e'])
print(s1)
"""
a 1.0
b 22.0
c 3.0
d 44.0
e NaN
dtype: float64
"""
print(s2)
"""
a 11.0
b 2.0
c 33.0
d NaN
e 4.0
dtype: float64
"""
# s1不为空,否则用s2替换
print(s1.where(pd.notna(s1), s2))
"""
a 1.0
b 22.0
c 3.0
d 44.0
e 4.0
dtype: float64
"""
再重新回过头看一下combine
import pandas as pd
s1 = pd.Series([1, 22, 3, 44], index=['a', 'b', 'c', 'd'])
s2 = pd.Series([11, 2, 33, 4], index=['c', 'd', 'e', 'f'])
print(s1.combine(s2, lambda a, b: a if a > b else b))
"""
a NaN
b NaN
c 11.0
d 44.0
e 33.0
f 4.0
dtype: float64
"""
# 为什么出现这个结果,相信你很容易就分析出来
# reindex之后:
# s1的数据变成[1.0, 22.0, 33.0, 44.0, NaN, NaN]
# s2的数据变成[NaN, NaN, 11.0, 2.0, 33.0, 4.0]
# 然后依次比较,1.0 > NaN为False,那么保留b,而b为NaN, 所以结果的第一个元素为NaN,同理第二个也是如此。
# 同理最后两个元素,和NaN比较也是False,还是保留b,那么最后两个元素则是33.0和4.0
# 至于index为c、d的元素就没有必要分析了,显然是保留大的那个
对于combine和combine_first来说,它们是对相同索引的元素进行比较,如果两个Series对象的索引不一样,那么会先取并集,然后通过reindex,再进行比较
import pandas as pd
s1 = pd.Series([1, 2, 3, 4])
s2 = pd.Series([1, 2, 3, 4])
# 两个元素是否相等,相等返回True,否则返回False
print(s1.combine(s2, lambda a, b: True if a == b else False))
"""
0 True
1 True
2 True
3 True
dtype: bool
"""
s2.index = [0, 1, 3, 2]
print(s1.combine(s2, lambda a, b: True if a == b else False))
"""
0 True
1 True
2 False
3 False
dtype: bool
"""
# 当我们将s2的索引变成了[0, 1, 3, 2]结果就不对了
print(s1.index.union(s2.index)) # Int64Index([0, 1, 2, 3], dtype='int64')
# 此时reindex的结果,s1还是1、2、3、4,但是s2则变成了1、2、4、3
所以在使用combine和combine_first这两个方法的时候,一定要记住索引,否则可能会造成陷阱。事实上,包括pandas很多的其它操作也是,它们都是基于索引来的,并不是简单的依次从左到右或者从上到下
但还是那句话,我们很多时候都是对DataFrame中的两列进行操作,而它们索引是一样的,所以不需要想太多。
当然这两个方法除了针对Series对象,还可以针对DataFrame对象,比如:df1.combine(df2, func),对相同的column进行替换,但不是很常用,有兴趣可以自己研究。我们主要还是作用于Series对象
assign
assign的用途是增加新的一列,但是不会更改原df
df1.assign(C=pd.Series(list('def'))) # 等同于df1['B']=list('abc')
- 没有指定Series的index默认是从0开始的。
- 使用assign添加列时候,索引是对齐的,如果说添加列和原DataFrame不一致的时候就会出现NAN的情况。
- 所以为了不出现NAN,就要指定索引和原DataFrame一致。
concat
concat方法相当于数据库中的全连接(union all),与数据库不同的是,它不会去重,但是可以使用drop_duplicates方法达到去重的效果。
pd.concat(objs, axis=0, join='outer',join_axes=None,ignore_index=False,keys=None, levels=None, names=None, verify_integrity=False)
- objs:需要连接的对象集合,一般是列表或字典;
- axis:连接轴向;
- axis默认是0。
- 当axis=0时,pd.concat([obj1, obj2])的效果与obj1.append(obj2)是相同的
- 当axis=1时,pd.concat([obj1, obj2], axis=1)的效果与pd.merge(obj1, obj2, left_index=True, right_index=True, how='outer')是相同的。
- join:参数为‘outer’或‘inner’;
- join_axes=[]:指定自定义的索引;
- keys=[]:创建层次化索引;
- ignore_index=True:重建索引
dfs = [df.set_index(['profile', 'depth']) for df in [df1, df2, df3]]
print(pd.concat(dfs, axis=1).reset_index())
# profile depth VAR1 VAR2 VAR3
# 0 profile_1 0.5 38.198002 NaN NaN
# 1 profile_1 0.6 38.198002 0.20440 NaN
# 2 profile_1 1.1 NaN 0.20442 NaN
# 3 profile_1 1.2 NaN 0.20446 15.188
# 4 profile_1 1.3 38.200001 NaN 15.182
# 5 profile_1 1.4 NaN NaN 15.182
append
result = df1.append(df2)
>>> df1.append(df2).append(df3).sort_values('depth')
VAR1 VAR2 VAR3 depth profile
0 38.196202 NaN NaN 0.5 profile_1
1 38.198002 NaN NaN 0.6 profile_1
0 NaN 0.20440 NaN 0.6 profile_1
1 NaN 0.20442 NaN 1.1 profile_1
2 NaN 0.20446 NaN 1.2 profile_1
0 NaN NaN 15.188 1.2 profile_1
2 38.200001 NaN NaN 1.3 profile_1
1 NaN NaN 15.182 1.3 profile_1
2 NaN NaN 15.182 1.4 profile_1
result = df1.append(s2, ignore_index=True)
合并的同时增加区分数据组的键
可以直接用key参数实现
frames = [df1, df2, df3]
result = pd.concat(frames, keys=['x', 'y', 'z'])
传入字典来增加分组键
pieces = {'x': df1, 'y': df2, 'z': df3}
result = pd.concat(pieces)
表格列字段不同的表合并
如果遇到两张表的列字段本来就不一样,但又想将两个表合并,其中无效的值用nan来表示。那么可以使用ignore_index来实现
dicts = [{'A': 1, 'B': 2, 'C': 3, 'X': 4},{'A': 5, 'B': 6, 'C': 7, 'Y': 8}]
result = df1.append(dicts, ignore_index=True)