文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

详解Pandas如何高效对比处理DataFrame的两列数据

2024-04-02 19:55

关注

楔子

我们在用 pandas 处理数据的时候,经常会遇到用其中一列数据替换另一列数据的场景。比如 A 列和 B 列,对 A 列中不为空的数据不作处理,对 A 列中为空的数据使用 B 列对应索引的数据进行替换。这一类的需求估计很多人都遇到,当然还有其它更复杂的。

解决这类需求的办法有很多,这里我们来推荐几个。

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 对象的索引是一致的,因为替换是根据索引来指定位置的,举个例子。

import pandas as pd

s1 = pd.Series(["001", None, None, "004"], 
               index=['a', 'b', 'c', 'd'])
s2 = pd.Series(["2", "3", "4"], 
               index=['b', 'd', "e"])

print(s1)
"""
a     001
b    None
c    None
d     004
dtype: object
"""
print(s2)
"""
b    2
d    3
e    4
dtype: object
"""

print(s1.combine_first(s2))
"""
a    001
b      2
c    NaN
d    004
e      4
dtype: object
"""

解释一下,首先替换的都是 s1 中值为空的数据,如果不为空那么不做任何处理。s1 中值为空的数据有两个,索引分别为 b、c,那么会用 s2 中索引为 b、c 的数据进行替换。但 s2 中只存在索引为 b、不存在索引为 c 的数据,那么就只能替换一个值。

另外我们看到结尾还多了个索引为 e 的数据,是的,如果 s2 中的数据,s1 没有,那么会直接加上去。

注意:pandas 的很多操作都是基于自带的索引进行的,并不是简单的从上往下一一对应。即便是很多 pandas 老手,偶尔也会犯这个错误。

当然大部分情况下我们处理的都是同一个 DataFrame 的两列,对于同一个 DataFrame 中的两列,它们的索引显然是一致的,所以就是简单的从上到下,不会有太多花里胡哨的。

combine

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
"""

# 两个元素进行相乘
# 当然,对于目前这个需求,最好的办法是 s1 * s2
print(s1.combine(s2, lambda a, b: a * b))
"""
0     11
1     44
2     99
3    176
dtype: int64
"""

combine 用起来还是很方便的,当然它同样是针对索引来操作的。此外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
"""

combine 和 combine_first 都是先让 s1 和 s2 的索引变得一致之后,再进行操作。

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_first(s2))
"""
a     1.0
b    22.0
c     3.0
d    44.0
e    33.0
f     4.0
dtype: float64
"""

所以你会发现,s1 和 s2 里面都没有空值,返回的结果也没有空值,但是类型却从整型变成了浮点型。就是因为 s1 和 s2 在 reindex 的过程中出现了 NaN,所以类型变成了浮点型。

所以在使用 combine 和 combine_first 这两个方法的时候,一定要记住索引,否则可能会造成陷阱。事实上,包括 pandas 很多的其它操作也是,它们都是基于索引来的,并不是简单的依次从左到右或者从上到下。

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 中的元素不为空,那么就进行替换。

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
"""

所以这个函数叫 update,意思就是更新。用 s2 中的元素换掉 s1 中的元素。但如果 s2 中的元素为空,那么可以认为新版本还没出来,那么还是使用老版本,所以 s1 中的 3 没有被换掉。

因此 update 和 combine_first 比较类似,但它们的区别在于:

另外在 combine_first 的时候,我们反复强调了索引的问题,如果 s1 和 s2 索引不一样,那么生成的结果的元素个数会增多。但是 update 不同,因为它是在本地进行操作的,也就是直接本地修改 s1,所以最终 s1 的元素个数是不会发生变化的。

import pandas as pd

s1 = pd.Series([1, 2, 3, 4], 
               index=['a', 'b', 'c', 'd'])
s2 = pd.Series([11, 22, 33, 44], 
               index=['c', 'd', 'e', 'f'])

s1.update(s2)
print(s1)
"""
a     1
b     2
c    11
d    22
dtype: int64
"""

s2 中不存在 index 为 a、b 的元素,那么可以认为新版本没有出现,因此不更新、保留原来的值。但 s2 中存在 index 为 c、d 的元素,所以有新版本,那么就更新。所以 s1 由 [1 2 3 4] 变成了 [1 2 11 22]。

至于 s2 中 index 为 e、f 的元素,它们和 s1 没有关系,因为 s1 中压根没有 index 为 e、f 的元素,s2 提供了新版本也是没用的。所以使用 update,是在 s1 本地操作的,操作前后 s1 的索引以及元素个数不会改变。

当然 update 也适用于对两个 DataFrame 进行操作,有兴趣可以自己去了解,但大部分时候我们都用在 Series 上面。

到此这篇关于详解Pandas如何高效对比处理DataFrame的两列数据的文章就介绍到这了,更多相关Pandas处理DataFrame数据内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯