Pandas 是 python 的一个数据分析包,是基于 NumPy 的一种数据分析工具,其中纳入了大量库和一些标准的数据模型,提供了快速便捷地处理数据的函数和方法,是高效地操作结构化数据集所需的工具,也是使 Python 成为强大而高效的数据分析环境的重要因素之一。

但是相信经常使用 Pandas 的同学在处理结构化数据运算时也会遇到一些麻烦,这些问题要么使得问题解决很复杂(代码难写),要么使得运行极其缓慢(效率低下),下面总结整理了一些 Pandas 的困难问题进行吐槽,如有谬误欢迎指正,也欢迎大家参加到这次的“Pandas 吐槽大会”。

切片赋值

切片赋值,指取数据中的某个值或某一块值,修改其中的值,如把第 3 行第 5 列的 x 值修改为 y 值。

使用员工信息数据作为案例进行介绍,数据片段如下:

1png

问题一:将R&D 部门员工的工资改成 20000

Python 代码

2png

运行结果:

SettingWithCopyWarning:

A value is trying to be set on a copy of a slice from a DataFrame.

Try using .loc\[row\_indexer,col\_indexer\] = value instead

..

可以看到,报了这个 SettingWithCopyWarning,而且修改的值并没有起作用。相信这个问题对于大多数的 Pandas 用户并不陌生,那么怎么修改呢?

就像 SettingWithCopyWarning 中提示的那样,使用 df.loc\[row\_indexer,col\_indexer\] = value 进行修改,这样不仅可以得到正确的结果,而且也可以解决报警的问题。

代码修改如下:

3png

运行结果:

..

这才是 Pandas 解决问题的方案。

讨论:问题的实质是我们想通过修改视图修改源数据。而 data\[data\['DEPT'\]=='R&D'\]\['SALARY'\]=2000 是将两个索引操作链接在一起,即直接使用了两次方括号的链式索引。

  1. data\[data\['DEPT'\]=='R&D'\]
  2. \['SALARY'\]=20000

以上两个链式操作一个接一个地独立执行。第一次链式操作是为了 Get,返回一个 DataFrame,其中包含所有 DEPT 等于 'R&D' 的行;第二次链式操作是为了 Set,是在这个新返回的 DataFrame 上运行的,并没有修改原始的 DataFrame。而此时使用 loc 函数获得原 DataFrame 的视图,在视图上赋值就可以修改原始 DataFrame 的值。

这种问题还是比较容易发现的,下面再来看一种情况:

问题二:修改 R&D 部门 5 号员工的工资为 19950

问题分析:在实际的工作中,经常把视图赋值给某个变量进行后续的计算,直到某一步,又想修改其中的某行的值,此时再使用 loc 函数时也会出现 SettingWithCopyWarning

Python 代码:

4png

运行结果:

SettingWithCopyWarning:

A value is trying to be set on a copy of a slice from a DataFrame.

Try using .loc\[row\_indexer,col\_indexer\] = value instead

..

观察发现,即使使用了 loc 函数,当再次使用 loc 函数时,还是会出现 SettingWithCopyWarning 的报警,其中的原因还是将两个索引操作链接在一起,第一次为 get,第二次为 set。这次所不同的是赋值结果起作用了,得到了我们期望的结果。但我们也不应该忽略此 Warning,而是应该明确的告诉 Pandas 变量 r_d 是 data 中截取视图的副本,然后再使用 loc 函数修改 5 号员工的工资。

代码如下:

5png

运行结果:

..

讨论:var=df.copy() 是明确的告知此 var 是 DataFrame 的副本,此时再使用 loc 函数赋值时,就避免了两次链式索引,也就避免了 SettingWithCopyWarning 的警告。

Pandas 针对 df 的操作冷不防就会产生视图,赋值时会错位,同时也会浪费时间。

集合运算

常见的集合运算,包括交集,差集,并集,异或集和和集运算,下面来看下 Pandas 两个集合间的运算。

问题三:求销售部门的员工与女员工的交集,差集,并集,异或集和和集。

Python 代码:

6png

讨论:Pandas 集合运算时,只能对着索引进行,然后再从原始数据中按索引截取得到结果。DataFrame 不可以直接进行集合运算。而且当集合数多于两个时,需要通过循环两两计算得到结果,再从原始数据按索引截取。 当希望按照某列进行集合运算时,则还需要把该列转成索引,计算完成后还要重置索引,得到结果。对于简单的集合运算看起来就很麻烦,如果 Pandas 能支持集合 (set) 数据类型的集合运算,通过符号 (&-|^) 进行运算就好了。

聚合运算

Pandas 提供了很多聚合运算函数,比如求和 sum(),平均 mean(),计数 count(),方差 var(),标准差 std() 等等。但遇到稍微特殊一点聚合运算时就有点麻烦,请看以下两个问题。

问题四:查看所有工资最高的员工的信息

问题分析:首先找到最高工资,再筛选出等于最高工资的员工。

Python 代码:

7png

讨论:这种方式需要遍历两边数据,计算最大值时一遍,过滤时一遍,效率比较低。有一种方式可以只遍历一遍。即找最大值的同时记录下最大值员工的索引,然后直接利用索引取数就可以了。可是 Pandas 的 idxmax() 函数只返回一个最大值的索引,不可以返回全部最大值的索引,因此就只能用上边的笨方法来解决这个问题。

问题五:找到年龄最大的 5 位员工,即 TOPN 问题。

问题分析:最大值是相当于 TOP1,因此 TOPN 问题也相当于聚合运算。

Python 代码:

8png

讨论:TOPN 问题并不需要大排序,只需要维护一个 N 长度的序列即可,保持序列中的 N 个数总是遍历过的数据中的最大值或者最小值即可。大家都知道大排序的效率是很低的,而且当数据量很大时,大排序复杂度和效率又会进一步恶化。但 Pandas 并没有提供高效的计算函数。即使是 nlargest()和 nsmallest() 函数底层也是大排序后取前五。

定位计算

Pandas 提供了索引功能,用户可以使用索引进行切片等操作,但当遇到需要计算指定索引(即位置)比前一行的行就比较麻烦,如下面这个问题:

问题六:计算股价超过 100 的交易日的当日涨幅

问题分析:需要筛选出股价超过 100 的交易日的交易信息,将数据提前一天,使用相同的索引截取两份数据,计算两者的涨幅。

Python 代码:

9png

讨论:Python 并没有提供利用位置进行相关计算的函数,所以计算这类问题就略显麻烦。

分组运算

Pandas 提供了丰富的分组运算,既可以按照列名分组,也可以按照指定的数组分组,既可以对单列使用多种方式聚合,也可以对多列聚合,还可以循环各组,处理分组以后的集合。但有一些常见的分组运算使用 Pandas 做起来要么比较繁琐,要么效率低下。

比如按位置分组、值变化分组、条件变化分组都需要衍生出一个数组作为分组依据,对位分组则需要使用 left join 的方式来绕,枚举分组更是需要多次分组,筛选需要的分组再合并,这里有一篇文章详细介绍了 Pandas 分组运算的一些例子

Python 分组处理

大家可以通过具体的例子体会 Pandas 分组的不便之处。

并行运算

Pandas 并不提供并行计算的方法,这也是 Pandas 被诟病最多的一方面,而 Python 所谓的多线程对于 CPU 而言还是单线程。

大数据计算

Pandas 虽然可以使用分段读取的方式来获取数据,但想要实现一些复杂的运算,比如排序、分组、关联等等都会非常非常麻烦,而且对程序员的技术要求也会很高。详细论述可以查看另一篇文档。

Python 如何处理大文件


raqsoft
114 声望10 粉丝