本篇内容为整理,博主使用代码为 Python3,部分内容和书本有出入。
在前几篇中我们介绍了 NumPy、pandas、matplotlib 三个库的基本操作,本篇介绍对数据的一些操作。
- 数据规整化:清理、转换、合并、重塑
数据规整化:清理、转换、合并、重塑
-
pile
自己编译一个regex
,以得到一个可重用的regex
对象,如上所示。如果打算对许多字符串应用同一条正则表达式,强烈建议通过这种方法,可以节省大量的 CPU 时间。
得到匹配
regex
的所有模式:-
findall
:返回字符串中所有的匹配项。 -
search
:只返回第一个匹配项。 -
match
:只匹配字符串的首部。
sub
方法:将匹配到的模式替换为指定字符串,并返回所得到的新字符串。不仅想找出电子邮件地址,还想将各个地址分为 3 个部分,只需将待分段的模式的各部分用圆括号包起来:
通过
groups
方法返回一个由模式各段组成的元组。
对于带有分组功能的模式,
findall
会返回一个元组列表:sub
还能通过诸如\1
,\2
之类的特殊符号访问各匹配项中的分组:为各个匹配分组加上一个名称,由这种正则表达式所产生的匹配对象可以得到一个简单易用的带有分组名称的字典:
pandas 中矢量化的字符串函数
通过
data.map
,所有字符串和正则表达式方法都能被应用于各个值,但如存在NA
就会报错,为了解决这个问题,Series
有一些能够跳过NA
值的字符串操作方法,通过Series
的str
属性即可访问这些方法:实现矢量化的元素获取操作,对
str.get/str
属性上使用索引:对字符串进行子串截取:
对数据集进行分组并对各组应用一个函数。
在将数据集准备好之后,通常的任务就是计算分组统计或生成透视表。
pandas
提供了一个灵活高效的gruopby
功能,对数据集进行切片、切块、摘要等操作。用
python
和pandas
强大的表达能力可以执行复杂的多的分组运算:利用任何可以接受pandas
对象或NumPy
数组的函数。
- 列表或数组,其长度与待分组的轴一样。
-
- 字典或
Series
,给出待分组轴上的值与分组名之间的对应关系。 - 函数,用于处理轴索引或索引中的各个标签。
变量grouped
是一个GroupBy对象
,它实际上还没有进行任何计算,只是含有一些有关分组键df['key1']
的中间数据。
例如,调用GroupBy
的mean
方法来计算分组平均值:
Series
根据分组键进行了聚合,产生了一个新的Series
,其索引为key1
列中的唯一值。
通过两个键对数据进行了分组后,得到的Series
具有一个层次化索引:
分组键可以是任何长度适当的数组:
GroupBy
对象支持迭代,可以产生一组二元元组(由分组名和数据块组成)。
对于多重键,元组的第一个元素将会是由键值组成的元组。
对数据片段进行操作,如将这些数据片段做成一个字典:
groupby
默认在axis=0
上进行分组,通过设置可以在其它任何轴上进行分组,如可以根据dtype
对列进行分组:
对于由DataFrame
产生的GroupBy对象
,用一个或一组(单个字符串或字符串数组)列名对其进行索引,就能实现选取部分列进行聚合的目的:
例如,对部分列进行聚合:计算data2
列的平均值并以DataFrame
形式得到结果:
返回一个已分组的DataFrame
(传入的是列表或数组)或Series
(传入的是标量形式的单个列名):
通过字典或 Series 进行分组
除数组以外,分组信息还可以其他形式存在
根据分组计算列的sum
:
用Series
作为分组键:
这里Series
可以被看做一个固定大小的映射。pandas
会检查Series
以确保其索引根分组轴是对齐的。
任何被当做分组键的函数都会在各个索引值上被调用一次,其返回值就会被用作分组名称。
将函数根数组、列表、字典、Series
混合使用(任何东西最终都会被转换为数组):
Key_list
和人名对应,再在相同长度的对应一列里选min
的值。
层次化索引数据集通过level
关键字传入级别编号或名称:
可以使用经过优化的GroupBy
的方法,还可以使用自己发明的聚合运算,还可以调用分组对象上已经定义好的任何方法,如 quantile
可以计算Series
或DataFrame
列的样本分位数:
GroupBy
会高效地对Series
进行切片,然后对各片调用piece.quantile(0.9)
,最后将这些结果组装成最终结果。
使用自己的聚合函数,传入aggregate
或agg方法
即可:
有些方法如describe
也可以用,但严格来讲它们并非聚合运算。
自定义聚合函数比表中的经过优化的函数慢得多,这是因为在构造中间分组数据块时存在非常大的开销(函数调用、数据重排等)。
传入一组函数或函数名,得到的DataFrame
的列就会以相应的函数命名:
传入一个由(name, function )
元组组成的列表,各元组的第一个元素会被用作DataFrame
的列名:
对于DataFrame
,定义一组应用于全部列的函数,或不同的列应用不同的函数。
传入带有自定义名称的元组列表:
对不同的列应用不同的函数:向agg
传入一个从列名映射到函数的字典
只有将多个函数应用到至少一列时,DataFrame
才会拥有层次化的列:
以“无索引”的形式返回聚合数据
聚合只是分组运算的其中一种,它接受能够将一维数据简化为标量值的函数。
接下来介绍 transform
和 apply
方法,执行更多其他的分组运算。
为一个DataFrame
添加一个用于存放各索引分组平均值的列:先聚合再合并。
transform
会将一个函数应用到各个分组,然后将结果放置到适当的位置上。
检查demeaned
现在的分组平均值是否为 0:
跟aggregate
一样,transform
也是一个有着严格条件的特殊函数,传入的函数只能产生两种结果,一个可以广播的标量值(如 np.mean) 或一个相同大小的结果数组。
apply:一般性的“拆分-应用-合并”
最一般化的GroupBy
方法是apply
:apply
会将待处理的对象拆分成多个片段,然后对各片段调用传入的函数,最后尝试将各片段组合到一起。
根据分组选出最高的 5 个tip_pct
值:编写一个函数,在指定列找出最大值,然后把这个值所在的行选取出来。
对smoker
分组并用该分组函数调用apply
,得到:
top
函数在DataFrame
的各个片段上调用,然后结果由pandas
.concat
组装到一起,并以分组名称进行了标记。
最后结果就有了一个层次化索引,其内层索引值来自原DataFrame
。
如果传给apply
的函数能够接受其他参数或关键字,可以将这些内容放在函数名后面一并传入:
在GroupBy
中,当调用如describe
之类的方法时,实际上只是应用了下面两条代码的快捷方式:
除这些基本用法之外,能否充分发挥apply
的威力很大程度上取决于你的创造力,传入的哪个函数能做什么全由你说了算,它只需返回一个pandas 对象或标量值即可。
分组键会跟原始对象的索引共同构成结果对象中的层次化索引,将group_keys=False
传入groupby
即可禁止该效果:
pandas
有一些能根据指定面元或样本分位数将数据拆分成多块的工具(cut 和 qcut),将这些函数跟groupby
结合起来,就能非常轻松地实现对数据集的桶或分位数分析了。
“长度相等的桶”指的是“区间大小相等”,“大小相等的桶”指的是“数据点数量相等”。
利用cut
将其装入长度相等的桶中:
由cut
返回的Factor
对象可直接用于groupby
,可以对data2
做一些统计计算:
要根据样本分位数得到大小相等的桶,使用qcut
:
传入labels=False
,即可只获取分位数的编号。否则那段还是区间而不是编号:
示例:用特定于分组的值填充缺失值
对于缺失数据的清理工作,有时用dropna
将其滤除,有时则希望用一个固定值或由数据集本身所衍生出来的值去填充NA值
,用fillna
这个工具。
如用平均值去填充NA值
:
对不同的分组填充不同的值:将数据分组,并使用apply
和一个能够对各数据块调用fillna
的函数即可。
用这个分组平均值去填充NA值
:
也可以在代码中预定义各组的填充值,由于分组具有一个name 属性
:
从一个大数据集中随机抽取样本以进行蒙特卡罗模拟(Monte Carlo simulation)或其他分析工作。抽取的方式很多,其中的一些效率会比其他的高很多
一个办法是:选取np.random.permutation(N)
的前K
个元素,其中N
为完整数据的大小,K
为期望的样本大小。
从整副牌中抽出 5 张:
从每种花色中随机抽取两张牌,由于花色是牌名的最后一个字符,可以据此进行分组,并使用apply
:
示例:分组加权平均数和相关系数
例如对这个数据集利用category
计算分组加权平均数:
计算一个由日收益率(通过百分数变化计算)与 SPX 之间的年度相关系数组成的DataFrame
:
计算列于列之间的相关系数:(苹果和微软的年度相关系数)
示例:面向分组的线性回归
按年计算 AAPL 对 SPX 收益率的线性回归:
是各种电子表格程序和其他数据分析软件中一种常见的数据汇总工具。它根据一个或多个键对数据进行聚合,并根据行和列上的分组键将数据分配到各个矩形区域中。
在小费数据集中,根据day
和smoker
计算分组平均数(pivot_table 的默认聚合类型):
传入margins=True
添加分项小计,将会添加标签为All
的行和列,其值对应于单个等级中所有数据的分组统计。
这里All
值为平均数。
要使用其他的聚合函数,将其传给aggfunc
即可。例如使用count
或len
得到有关分组大小的交叉表:
是一种用于计算分组频率的特殊透视表。
crosstab
的前两个参数可以是数组、Series
、数组列表:
示例:2012联邦选举委员会数据库
抽取有关赞助人和赞助模式的统计信息。
通过unique
,可以获取全部的候选人名单:
利用字典说明党派关系:
通过这个映射以及 Series
对象的map
方法,可以根据候选人姓名得到一组党派信息:
注意,该数据集既包括赞助也包括退款(负的出资额),为了简化分析过程,限定该数据集只能有正的出资额:
由于 Barack Obama 和 Mitt Romney 是最主要的两名候选人,专门准备了一个子集,只包含针对他们两人的竞选活动的赞助信息:
根据职业和雇主统计赞助信息
首先,根据职业计算出资总额:
这里只列出了前10个,注意到 许多职业都涉及相同的基本工作类型或同一样东西有多种变体,清理一些这样的数据:将一个职业信息映射到另一个。
对雇主信息也进行了同样的处理。
这里利用了dict.get
,它允许没有映射关系的职业也能“通过”。
现在,可以通过pivot_table
根据党派和职业对数据进行聚合,然后过滤掉总出资额不足 200 万美元对数据:
对 Obama 和 Romney 总出资额最高的职业和企业:先对候选人进行分组,然后求取最大值:
利用cut
函数根据出资额的大小将数据离散化到多个面元中:
根据候选人姓名以及面元标签对数据进行分组:
对出资额求和并在面元内规格化,以便图形化显示两位候选人各种赞助额度的比例:
首先,根据候选人和州对数据进行聚合:
对各行除以总赞助额,就得到各候选人在各州的总赞助额比例: