本篇内容为整理《利用Python进行数据分析》,博主使用代码为 Python3,部份内容和书本有出入。python
在前几篇中咱们介绍了 NumPy、pandas、matplotlib 三个库的基本操做,本篇介绍对数据的一些操做。正则表达式
pandas.merge
:可根据一个或多个键将不一样DataFrame
中的行连接起来。pandas.concat
:可沿着一条轴将多个对象堆叠到一块儿。combine_first
:可将重复数据编接在一块儿,用一个对象中的值填充另外一个对象中的缺失值。数据库风格的 DataFrame 合并数据库
数据集的合并或链接运算:经过一个或多个键将行连接起来。数组
多对一的合并:app
若没有指定用哪一个列进行链接,merge
会将重叠列名当作键,指定以下:dom
若两个对象的列名不一样,可分别进行指定:函数
默认状况下,merge
作inner
链接,结果中的键是交集。外链接求取的是键的并集:工具
多对多的合并操做:大数据
链接方式只影响出如今结果中的键。优化
根据多个键进行合并,传入一个由列名组成的列表:
在进行列-列链接时,DataFrame
对象中的索引会被丢弃。
suffixes
选项:指定附加到左右两个DataFrame
对象的重叠列名上的字符串。
索引上的合并
当DataFrame
中的链接键位于其索引中时,传入left_index=True
、 right_index=True
,以说明索引应该被用做链接键:
对于层次化索引的数据:
必须以列表的形式指明用做合并键的列(注意对重复索引值的处理):
使用合并双方的索引:
DataFrame
的 join
实例方法:
更方便的实现按索引合并,无论有没有重叠的列。在链接键上做左链接。
支持参数DataFrame
的索引跟调用者DataFrame
的某个列之间的链接:
对于简单的索引合并,能够向join
传入一组DataFrame
(concat 函数也是这个功能):
轴向链接
数据合并运算:
NumPy
有一个用于合并原始NumPy
数组的concatenation
函数:
pandas
的concat
函数:
默认状况下,concat
在 axis=0
上工做,产生一个新Series
。传入 axis=1
,产生一个DataFrame
:
这种状况下,另一条轴上没有重叠,传入 join = 'inner'
获得它们的交集:
使用 key
参数,在链接轴上建立一个层次化索引:
沿着 axis=1
对Series
进行合并,keys
就会成为DataFrame
的列头:
对DataFrame
对象也是如此:
传入一个字典,则字典的键会被当作keys
选项的值:
用于管理层次化索引建立方式的参数:
跟当前分析工做无关的DataFrame
行索引:
传入 ignore_index = True
:
合并重叠数据
关于有索引所有或部分重叠的两个数据集。
NumPy
的where
函数,用于表达一种矢量化的if-else
:
Series
的combine_first
方法,实现与上面同样的功能,并会进行数据对齐:
对于DataFrame
同样:
能够看做用参数对象中的数据为调用者对象的缺失数据“打补丁”。
用于从新排列表格型数据的基础运算:重塑(reshape)或轴向旋转(pivot)。
重塑层次化索引
stark
:将数据的列“旋转”为行unstark
:将数据的行“旋转”为列用stack
方法将行转为列,获得一个Series
:
对层次化索引的Series
,能够用unstack
将其从新排为一个DataFrame
:
默认状况下,unstack
操做最内层。
传入分层级别的编号或名称可对其余级别进行unstack
操做:
若是不是全部的级别值都能在各分组找到的话,unstack
操做可能会引入缺失数据:
stack
默认会滤除缺失数据,所以该运算是可逆的:
对DataFrame
进行unstack
操做时,做为旋转轴的级别将会成为结果中的最低级别:
将“长格式”旋转为“宽格式”
时间序列数据一般以 “长格式(long)”或“堆叠格式(stacked)”存储在数据库和 CSV 中。
转成DataFrame
,用 pivot
方法:
获得的DataFrame
带有层次化的列:
假设有两个须要参与重塑的数据列:
pivot
其实只是一个快捷方式:用set_index
建立层次化索引,再用unstack
重塑。
以上是数据的重排,下面是过滤、清理及其余转换工做。
移除重复数据
DataFrame
中出现的重复行:
DataFrame
的duplicated
方法返回一个布尔型Series
,表示各行是不是重复行,drop_duplicates
方法返回一个移除了重复行的DataFrame
:
指定部分列进行重复项判断,如只但愿根据k1
列过滤重复项:
duplicated
和drop_duplicates
默认保留重复数值里第一次出现的组合,传入keep = last
则保留最后一个:
利用函数或映射进行数据转换
根据数组、Series
或DataFrame
列中的值来实现转换。
编写一个肉类到动物的映射:
Series
的map
方法:能够接受一个函数或含有映射关系的字典型对象,用于修改对象的数据子集。
也能够传入一个可以完成所有这些工做的函数:
替换值
replace
方法 :替换
利用fillna
方法填充缺失数据能够看做替换的一种特殊状况。
替换一个值和一次性替换多个值:
对不一样的值进行不一样的替换:
传入的参数也能够是字典:
重命名轴索引
轴标签有一个map
方法:
对函数或映射进行转换,从而获得一个新对象。
将其值赋给index
,就能够对DataFrame
进行就地修改了:
要建立数据集的转换版,而不是修改原始数据,用rename
:
rename
结合字典型对象能够实现对部分轴标签的更新:
rename
实现了复制DataFrame
并对其索引和列标签进行赋值,就地修改某个数据集,传入inplace=True
:
离散化和面元划分
为了便于分析,连续数据经常被离散化或拆分为“面元(bin)”。
用pandas
的cut
函数:
pandas
返回的是一个特殊的Categorical
对象,它含有一个表示不一样分类名称的数组和一个为年龄数据进行标号的属性:
哪边是闭端能够经过right=False
进行修改:
设置本身的面元名称:
将labels
选项设置为一个列表或数组便可。
若是向cut
传入的是面元的数量而不是确切的面元边界,则它会根据数据的最小值和最大值计算等长面元:
将一些均匀分布的数据分红了四组。
qcut
函数:根据样本分位数对数据进行面元划分。
因为qcut
使用的是样本分位数,能够获得大小基本相等的面元(而 cut 根据数据的分布状况,可能没法使各个面元中含有相同数量的数据点)。
设置自定义的分位数:
在聚合和分组运算时会再次用到cut
和 qcut
这两个离散化函数。
检测和过滤异常值
判断是否存在异常值(outlier ):
找出某列中绝对值大小超过 3 的值:
选出所有含有“超过 3 或 -3 的值”的行:
将值限制在区间 -3 到 3 之内:
np.sign
这个ufunc
返回的是一个由 1 和 -1 组成的数组,表示原始值的符号。
排列和随机采样
numpy.random.permutation
函数:对Series
和DataFrame
的列排列。
Permutation(5)
:须要排列的轴的长度。
而后就能够在基于ix
的索引操做或take
函数中使用该数组了:
选取随机子集(非替换):
用替换的方式产生样本:
计算指标/哑变量
将分类变量(Categorical)转换为“哑变量矩阵(dummy matrix)”或“指标矩阵(indicator matrix)”。
给DataFrame
的列加上一个前缀,以便可以跟其余数据进行合并:
用get_dummies
的prefix
参数。
DataFrame
中的某行同属于多个分类的状况,举个例子:
要为每一个genre
添加指标变量就须要作一些数据规整操做,构建多成员指标变量:
对于很大的数据,这种方式会变得很是慢,须要编写一个可以利用DataFrame
内部机制的更低级的函数:
用get_dummies
和cut
之类的离散化函数。
字符串对象方法
Python 字符串对象的内置方法:
find
找不到返回 -1,index
找不到引起一个异常
传入空字符串经常用于删除模式:
正则表达式(regex)
提供了一种灵活的在文本中搜索或匹配字符串模式的方式。python 内置的re
模块负责对字符串应用正则表达式。
re
模块的函数分为三个大类:模式匹配、替换、拆分。
描述一个或多个空白符的regex
是 \s+
。
调用re.split('\s+', text)
时,正则表达式会先被编译,而后再在text
上调用其split
方法。
能够用re.compile
本身编译一个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
数组的函数。
分组运算:split
(拆分)--apply
(应用)--combine
(合并)。
分组键的形式:
DataFrame
某个列名的值。Series
,给出待分组轴上的值与分组名之间的对应关系。访问data1
,并根据key1
调用groupby
。
变量grouped
是一个GroupBy对象
,它实际上尚未进行任何计算,只是含有一些有关分组键df['key1']
的中间数据。
例如,调用GroupBy
的mean
方法来计算分组平均值:
Series
根据分组键进行了聚合,产生了一个新的Series
,其索引为key1
列中的惟一值。
经过两个键对数据进行了分组后,获得的Series
具备一个层次化索引:
分组键能够是任何长度适当的数组:
将列名用做分组键:
GroupBy
的size
方法返回一个含有分组大小的Series
:
对分组进行迭代
GroupBy
对象支持迭代,能够产生一组二元元组(由分组名和数据块组成)。
对于多重键,元组的第一个元素将会是由键值组成的元组。
对数据片断进行操做,如将这些数据片断作成一个字典:
groupby
默认在axis=0
上进行分组,经过设置能够在其它任何轴上进行分组,如能够根据dtype
对列进行分组:
选取一个或一组列
对于由DataFrame
产生的GroupBy对象
,用一个或一组(单个字符串或字符串数组)列名对其进行索引,就能实现选取部分列进行聚合的目的:
例如,对部分列进行聚合:计算data2
列的平均值并以DataFrame
形式获得结果:
返回一个已分组的DataFrame
(传入的是列表或数组)或Series
(传入的是标量形式的单个列名):
经过字典或 Series 进行分组
除数组之外,分组信息还能够其余形式存在
根据分组计算列的sum
:
将mapping
这个字典传给groupby
便可。
用Series
做为分组键:
这里Series
能够被看作一个固定大小的映射。pandas
会检查Series
以确保其索引根分组轴是对齐的。
经过函数进行分组
任何被当作分组键的函数都会在各个索引值上被调用一次,其返回值就会被用做分组名称。
将函数根数组、列表、字典、Series
混合使用(任何东西最终都会被转换为数组):
Key_list
和人名对应,再在相同长度的对应一列里选min
的值。
根据索引级别分组
层次化索引数据集经过level
关键字传入级别编号或名称:
可使用通过优化的GroupBy
的方法,还可使用本身发明的聚合运算,还能够调用分组对象上已经定义好的任何方法,如 quantile
能够计算Series
或DataFrame
列的样本分位数:
GroupBy
会高效地对Series
进行切片,而后对各片调用piece.quantile(0.9)
,最后将这些结果组装成最终结果。
使用本身的聚合函数,传入aggregate
或agg方法
便可:
有些方法如describe
也能够用,但严格来说它们并不是聚合运算。
自定义聚合函数比表中的通过优化的函数慢得多,这是由于在构造中间分组数据块时存在很是大的开销(函数调用、数据重排等)。
面向列的多函数应用
根据 'smoker'
和 'size'
对 tips
进行分组:
传入一组函数或函数名,获得的DataFrame
的列就会以相应的函数命名:
传入一个由(name, function )
元组组成的列表,各元组的第一个元素会被用做DataFrame
的列名:
对于DataFrame
,定义一组应用于所有列的函数,或不一样的列应用不一样的函数。
结果的DataFrame
拥有层次化的列。至关于分别对列['tip_pct']
和列['total_bill']
进行聚合,而后用concat
将结果组装到一块儿(列名用做 keys 参数)。
传入带有自定义名称的元组列表:
对不一样的列应用不一样的函数:向agg
传入一个从列名映射到函数的字典
只有将多个函数应用到至少一列时,DataFrame
才会拥有层次化的列:
以“无索引”的形式返回聚合数据
向groupby
传入as_index=False,禁用功能由惟一的分组键组成索引:
聚合只是分组运算的其中一种,它接受可以将一维数据简化为标量值的函数。
接下来介绍 transform
和 apply
方法,执行更多其余的分组运算。
为一个DataFrame
添加一个用于存放各索引分组平均值的列:先聚合再合并。
下面在GroupBy
上使用transform
方法:
transform
会将一个函数应用到各个分组,而后将结果放置到适当的位置上。
从各组中减去平均值:先建立一个距平化函数(demeaning function),而后将其传给transform
。
检查demeaned
如今的分组平均值是否为 0:
跟aggregate
同样,transform
也是一个有着严格条件的特殊函数,传入的函数只能产生两种结果,一个能够广播的标量值(如 np.mean) 或一个相同大小的结果数组。
apply:通常性的“拆分-应用-合并”
最通常化的GroupBy
方法是apply
:apply
会将待处理的对象拆分红多个片断,而后对各片断调用传入的函数,最后尝试将各片断组合到一块儿。
根据分组选出最高的 5 个tip_pct
值:编写一个函数,在指定列找出最大值,而后把这个值所在的行选取出来。
对smoker
分组并用该分组函数调用apply
,获得:
top
函数在DataFrame
的各个片断上调用,而后结果由pandas
.concat
组装到一块儿,并以分组名称进行了标记。
最后结果就有了一个层次化索引,其内层索引值来自原DataFrame
。
若是传给apply
的函数可以接受其余参数或关键字,能够将这些内容放在函数名后面一并传入:
在GroupBy
对象上调用describe
:
在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
计算分组加权平均数:
来自 Yahoo! Finance 的数据集:
计算一个由日收益率(经过百分数变化计算)与 SPX 之间的年度相关系数组成的DataFrame
:
计算列于列之间的相关系数:(苹果和微软的年度相关系数)
示例:面向分组的线性回归
仍是上个例子,定义下面这个regress
函数(利用 statsmodels 库)对各数据块执行普通最小二乘法回归(Ordinary Least Squares, OLS)。
按年计算 AAPL 对 SPX 收益率的线性回归:
透视表(pivot table)
是各类电子表格程序和其余数据分析软件中一种常见的数据汇总工具。它根据一个或多个键对数据进行聚合,并根据行和列上的分组键将数据分配到各个矩形区域中。
在小费数据集中,根据day
和smoker
计算分组平均数(pivot_table 的默认聚合类型):
只想聚合tip_pct
和size
,并根据day
进行分组:
传入margins=True
添加分项小计,将会添加标签为All
的行和列,其值对应于单个等级中全部数据的分组统计。
这里All
值为平均数。
要使用其余的聚合函数,将其传给aggfunc
便可。例如使用count
或len
获得有关分组大小的交叉表:
存在NA值
,就设置一个fill_value
:
交叉表(crosstab)
是一种用于计算分组频率的特殊透视表。
用pandas.crosstab
函数(pivot_table 也能实现该功能:根据 Nationality 和 Handedness 对这段数据进行汇总):
crosstab
的前两个参数能够是数组、Series
、数组列表:
加载数据
抽取有关赞助人和赞助模式的统计信息。
经过unique
,能够获取所有的候选人名单:
利用字典说明党派关系:
经过这个映射以及 Series
对象的map
方法,能够根据候选人姓名获得一组党派信息:
注意,该数据集既包括赞助也包括退款(负的出资额),为了简化分析过程,限定该数据集只能有正的出资额:
因为 Barack Obama 和 Mitt Romney 是最主要的两名候选人,专门准备了一个子集,只包含针对他们两人的竞选活动的赞助信息:
根据职业和雇主统计赞助信息
首先,根据职业计算出资总额:
这里只列出了前10个,注意到 许多职业都涉及相同的基本工做类型或同同样东西有多种变体,清理一些这样的数据:将一个职业信息映射到另外一个。
对雇主信息也进行了一样的处理。
这里利用了dict.get
,它容许没有映射关系的职业也能“经过”。
如今,能够经过pivot_table
根据党派和职业对数据进行聚合,而后过滤掉总出资额不足 200 万美圆对数据:
作成柱状图:
对 Obama 和 Romney 总出资额最高的职业和企业:先对候选人进行分组,而后求取最大值:
对出资额分组
利用cut
函数根据出资额的大小将数据离散化到多个面元中:
根据候选人姓名以及面元标签对数据进行分组:
对出资额求和并在面元内规格化,以便图形化显示两位候选人各类赞助额度的比例:
根据州统计赞助信息
首先,根据候选人和州对数据进行聚合:
对各行除以总赞助额,就获得各候选人在各州的总赞助额比例:
不足之处,欢迎指正。