资讯专栏INFORMATION COLUMN

【数据科学系统学习】Python # 数据分析基本操作[四] 数据规整化和数据聚合与分组运算

The question / 3948人阅读

摘要:数据规整化清理转换合并重塑数据聚合与分组运算数据规整化清理转换合并重塑合并数据集可根据一个或多个键将不同中的行链接起来。函数根据样本分位数对数据进行面元划分。字典或,给出待分组轴上的值与分组名之间的对应关系。

本篇内容为整理《利用Python进行数据分析》,博主使用代码为 Python3,部分内容和书本有出入。

在前几篇中我们介绍了 NumPy、pandas、matplotlib 三个库的基本操作,本篇介绍对数据的一些操作。

数据规整化:清理、转换、合并、重塑

数据聚合与分组运算


数据规整化:清理、转换、合并、重塑 合并数据集

pandas.merge:可根据一个或多个键将不同DataFrame中的行链接起来。

pandas.concat:可沿着一条轴将多个对象堆叠到一起。

combine_first:可将重复数据编接在一起,用一个对象中的值填充另一个对象中的缺失值。


数据库风格的 DataFrame 合并


数据集的合并或连接运算:通过一个或多个键将行链接起来。

多对一的合并:

若没有指定用哪个列进行连接,merge会将重叠列名当做键,指定如下:

若两个对象的列名不同,可分别进行指定:

默认情况下,mergeinner连接,结果中的键是交集。外连接求取的是键的并集:

多对多的合并操作:

连接方式只影响出现在结果中的键。

根据多个键进行合并,传入一个由列名组成的列表:

在进行列-列连接时,DataFrame对象中的索引会被丢弃。

suffixes选项:指定附加到左右两个DataFrame对象的重叠列名上的字符串。


索引上的合并


DataFrame中的连接键位于其索引中时,传入left_index=Trueright_index=True,以说明索引应该被用作连接键:


对于层次化索引的数据:

必须以列表的形式指明用作合并键的列(注意对重复索引值的处理):

使用合并双方的索引:


DataFramejoin 实例方法:

更方便的实现按索引合并,不管有没有重叠的列。在连接键上作左连接。


支持参数DataFrame的索引跟调用者DataFrame的某个列之间的连接:

对于简单的索引合并,可以向join传入一组DataFrame(concat 函数也是这个功能):


轴向连接


数据合并运算:

连接(concatenation)

绑定(binding)

堆叠(stacking)

NumPy有一个用于合并原始NumPy数组的concatenation函数:

pandasconcat函数:

默认情况下,concataxis=0 上工作,产生一个新Series。传入 axis=1,产生一个DataFrame

这种情况下,另外一条轴上没有重叠,传入 join = "inner" 得到它们的交集:

使用 key 参数,在连接轴上创建一个层次化索引:

沿着 axis=1Series进行合并,keys 就会成为DataFrame的列头:

DataFrame对象也是如此:

传入一个字典,则字典的键会被当做keys选项的值:

用于管理层次化索引创建方式的参数:

跟当前分析工作无关的DataFrame行索引:

传入 ignore_index = True


合并重叠数据


关于有索引全部或部分重叠的两个数据集。

NumPywhere函数,用于表达一种矢量化的if-else

Seriescombine_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中出现的重复行:

DataFrameduplicated方法返回一个布尔型Series,表示各行是否是重复行,drop_duplicates方法返回一个移除了重复行的DataFrame

指定部分列进行重复项判断,如只希望根据k1列过滤重复项:

duplicateddrop_duplicates默认保留重复数值里第一次出现的组合,传入keep = last则保留最后一个:


利用函数或映射进行数据转换


根据数组、SeriesDataFrame列中的值来实现转换。

编写一个肉类到动物的映射:

Seriesmap方法:可以接受一个函数或含有映射关系的字典型对象,用于修改对象的数据子集。

也可以传入一个能够完成全部这些工作的函数:


替换值


replace方法 :替换

利用fillna方法填充缺失数据可以看作替换的一种特殊情况。

替换一个值和一次性替换多个值:

对不同的值进行不同的替换:

传入的参数也可以是字典:


重命名轴索引


轴标签有一个map方法:

对函数或映射进行转换,从而得到一个新对象。


将其值赋给index,就可以对DataFrame进行就地修改了:


要创建数据集的转换版,而不是修改原始数据,用rename

rename结合字典型对象可以实现对部分轴标签的更新:

rename实现了复制DataFrame并对其索引和列标签进行赋值,就地修改某个数据集,传入inplace=True


离散化和面元划分


为了便于分析,连续数据常常被离散化或拆分为“面元(bin)”。

pandascut函数:

pandas返回的是一个特殊的Categorical对象,它含有一个表示不同分类名称的数组和一个为年龄数据进行标号的属性:

哪边是闭端可以通过right=False进行修改:

设置自己的面元名称:

labels选项设置为一个列表或数组即可。


如果向cut传入的是面元的数量而不是确切的面元边界,则它会根据数据的最小值和最大值计算等长面元:

将一些均匀分布的数据分成了四组。


qcut函数:根据样本分位数对数据进行面元划分。

由于qcut使用的是样本分位数,可以得到大小基本相等的面元(而 cut 根据数据的分布情况,可能无法使各个面元中含有相同数量的数据点)。

设置自定义的分位数:

在聚合和分组运算时会再次用到cutqcut 这两个离散化函数。


检测和过滤异常值


判断是否存在异常值(outlier ):

找出某列中绝对值大小超过 3 的值:

选出全部含有“超过 3 或 -3 的值”的行:

将值限制在区间 -3 到 3 以内:

np.sign这个ufunc返回的是一个由 1 和 -1 组成的数组,表示原始值的符号。


排列和随机采样


numpy.random.permutation函数:对SeriesDataFrame的列排列。

Permutation(5):需要排列的轴的长度。

然后就可以在基于ix的索引操作或take函数中使用该数组了:

选取随机子集(非替换):

用替换的方式产生样本:


计算指标/哑变量


将分类变量(Categorical)转换为“哑变量矩阵(dummy matrix)”或“指标矩阵(indicator matrix)”。

DataFrame的列加上一个前缀,以便能够跟其他数据进行合并:

get_dummiesprefix参数。


DataFrame中的某行同属于多个分类的情况,举个例子:

要为每个genre添加指标变量就需要做一些数据规整操作,构建多成员指标变量:

对于很大的数据,这种方式会变得非常慢,需要编写一个能够利用DataFrame内部机制的更低级的函数:

get_dummiescut之类的离散化函数。


字符串操作

字符串对象方法


Python 字符串对象的内置方法:

find找不到返回 -1,index找不到引发一个异常


传入空字符串常常用于删除模式:


正则表达式(regex)


提供了一种灵活的在文本中搜索或匹配字符串模式的方式。python 内置的re模块负责对字符串应用正则表达式。

re模块的函数分为三个大类:模式匹配、替换、拆分。

描述一个或多个空白符的regexs+

调用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值的字符串操作方法,通过Seriesstr属性即可访问这些方法:

也可以用正则表达式:

实现矢量化的元素获取操作,对str.get/str属性上使用索引:

对字符串进行子串截取:


数据聚合与分组运算

对数据集进行分组并对各组应用一个函数。

在将数据集准备好之后,通常的任务就是计算分组统计生成透视表pandas提供了一个灵活高效的gruopby功能,对数据集进行切片、切块、摘要等操作。

pythonpandas强大的表达能力可以执行复杂的多的分组运算:利用任何可以接受pandas对象或NumPy数组的函数。


GroupBy技术

分组运算:split(拆分)--apply(应用)--combine(合并)。

分组键的形式:

列表或数组,其长度与待分组的轴一样。

表示DataFrame某个列名的值。

字典或Series,给出待分组轴上的值与分组名之间的对应关系。

函数,用于处理轴索引或索引中的各个标签。


访问data1,并根据key1调用groupby

变量grouped是一个GroupBy对象,它实际上还没有进行任何计算,只是含有一些有关分组键df["key1"]的中间数据。

例如,调用GroupBymean方法来计算分组平均值:

Series根据分组键进行了聚合,产生了一个新的Series,其索引为key1列中的唯一值。

通过两个键对数据进行了分组后,得到的Series具有一个层次化索引:

分组键可以是任何长度适当的数组:

将列名用作分组键:

GroupBysize方法返回一个含有分组大小的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可以计算SeriesDataFrame列的样本分位数:

GroupBy会高效地对Series进行切片,然后对各片调用piece.quantile(0.9),最后将这些结果组装成最终结果。

使用自己的聚合函数,传入aggregateagg方法即可:

有些方法如describe也可以用,但严格来讲它们并非聚合运算。


自定义聚合函数比表中的经过优化的函数慢得多,这是因为在构造中间分组数据块时存在非常大的开销(函数调用、数据重排等)。


面向列的多函数应用


根据 "smoker""size"tips 进行分组:

传入一组函数或函数名,得到的DataFrame的列就会以相应的函数命名:

传入一个由(name, function )元组组成的列表,各元组的第一个元素会被用作DataFrame的列名:

对于DataFrame,定义一组应用于全部列的函数,或不同的列应用不同的函数。

结果的DataFrame拥有层次化的列。相当于分别对列["tip_pct"]和列["total_bill"]进行聚合,然后用concat将结果组装到一起(列名用作 keys 参数)。

传入带有自定义名称的元组列表:

对不同的列应用不同的函数:向agg传入一个从列名映射到函数的字典

只有将多个函数应用到至少一列时,DataFrame才会拥有层次化的列:


以“无索引”的形式返回聚合数据


groupby传入as_index=False,禁用功能由唯一的分组键组成索引:


分组级运算和转换

聚合只是分组运算的其中一种,它接受能够将一维数据简化为标量值的函数。

接下来介绍 transformapply 方法,执行更多其他的分组运算。

为一个DataFrame添加一个用于存放各索引分组平均值的列:先聚合再合并。

下面在GroupBy上使用transform方法:

transform会将一个函数应用到各个分组,然后将结果放置到适当的位置上。

从各组中减去平均值:先创建一个距平化函数(demeaning function),然后将其传给transform

检查demeaned现在的分组平均值是否为 0:

aggregate一样,transform也是一个有着严格条件的特殊函数,传入的函数只能产生两种结果,一个可以广播的标量值(如 np.mean) 或一个相同大小的结果数组。


apply:一般性的“拆分-应用-合并”


最一般化的GroupBy方法是applyapply会将待处理的对象拆分成多个片段,然后对各片段调用传入的函数,最后尝试将各片段组合到一起。

根据分组选出最高的 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结合起来,就能非常轻松地实现对数据集的桶或分位数分析了。

桶:bucket

分位数:quantile


“长度相等的桶”指的是“区间大小相等”,“大小相等的桶”指的是“数据点数量相等”。

利用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)


是各种电子表格程序和其他数据分析软件中一种常见的数据汇总工具。它根据一个或多个键对数据进行聚合,并根据行和列上的分组键将数据分配到各个矩形区域中。

在小费数据集中,根据daysmoker计算分组平均数(pivot_table 的默认聚合类型):

只想聚合tip_pctsize,并根据day进行分组:

传入margins=True添加分项小计,将会添加标签为All的行和列,其值对应于单个等级中所有数据的分组统计。

这里All值为平均数。

要使用其他的聚合函数,将其传给aggfunc即可。例如使用countlen得到有关分组大小的交叉表:

存在NA值,就设置一个fill_value


交叉表(crosstab)


是一种用于计算分组频率的特殊透视表。

pandas.crosstab函数(pivot_table 也能实现该功能:根据 Nationality 和 Handedness 对这段数据进行汇总):

crosstab的前两个参数可以是数组、Series、数组列表:

示例:2012联邦选举委员会数据库

加载数据

抽取有关赞助人和赞助模式的统计信息。

通过unique,可以获取全部的候选人名单:

利用字典说明党派关系:

通过这个映射以及 Series对象的map方法,可以根据候选人姓名得到一组党派信息:

注意,该数据集既包括赞助也包括退款(负的出资额),为了简化分析过程,限定该数据集只能有正的出资额:

由于 Barack Obama 和 Mitt Romney 是最主要的两名候选人,专门准备了一个子集,只包含针对他们两人的竞选活动的赞助信息:

根据职业和雇主统计赞助信息

首先,根据职业计算出资总额:

这里只列出了前10个,注意到 许多职业都涉及相同的基本工作类型或同一样东西有多种变体,清理一些这样的数据:将一个职业信息映射到另一个。

对雇主信息也进行了同样的处理。

这里利用了dict.get,它允许没有映射关系的职业也能“通过”。

现在,可以通过pivot_table根据党派和职业对数据进行聚合,然后过滤掉总出资额不足 200 万美元对数据:

做成柱状图:

对 Obama 和 Romney 总出资额最高的职业和企业:先对候选人进行分组,然后求取最大值:

对出资额分组

利用cut函数根据出资额的大小将数据离散化到多个面元中:

根据候选人姓名以及面元标签对数据进行分组:

对出资额求和并在面元内规格化,以便图形化显示两位候选人各种赞助额度的比例:

根据州统计赞助信息

首先,根据候选人和州对数据进行聚合:

对各行除以总赞助额,就得到各候选人在各州的总赞助额比例:


不足之处,欢迎指正。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/44765.html

相关文章

  • 数据学系学习Python # 数据分析基本操作[二] pandas

    摘要:中面向行和面向列的操作基本是平衡的。用层次化索引,将其表示为更高维度的数据。使用浮点值表示浮点和非浮点数组中的缺失数据。索引的的格式化输出形式选取数据子集在内层中进行选取层次化索引在数据重塑和基于分组的操作中很重要。 我们在上一篇介绍了 NumPy,本篇介绍 pandas。 pandas入门 Pandas 是基于Numpy构建的,让以NumPy为中心的应用变的更加简单。 pandas...

    jayzou 评论0 收藏0
  • 数据学系学习Python # 数据分析基本操作[一] numpy

    摘要:提供了使我们能够快速便捷地处理结构化数据的大量数据结构和函数。结构化数据,例如多维数据矩阵表格行数据,其中各列可能是不同的类型字符串数值日期等。基础数组和矢量计算高性能科学计算和数据分析的基础包。 本篇内容为整理《利用Python进行数据分析》,博主使用代码为 Python3,部分内容和书本有出入。 利用 Python 进行科学计算的实用指南。本书重点介绍了用于高效解决各种数据分析问...

    wushuiyong 评论0 收藏0
  • 数据学系学习Python # 数据分析基本操作[三] matplotlib

    摘要:有一些表示常见图形的对象称为块,完整的集合位于。中的绘图函数在中,有行标签列标签分组信息。密度图通过计算可能会产生观测数据的连续概率分布的估计而产生的。在探索式数据分析工作中,同时观察一组变量的散布图是很有意义的。 我们在上一篇介绍了 pandas,本篇介绍 matplotlib。 绘图和可视化 一个用于创建出版质量图表的桌面绘图包。 Matplotlib API入门 Figure ...

    BDEEFE 评论0 收藏0
  • 新书《全栈数据之门》完整目录

    摘要:全栈数据之门前言自强不息,厚德载物,自由之光,你是我的眼基础,从零开始之门文件操作权限管理软件安装实战经验与,文本处理文本工具的使用家族的使用综合案例数据工程,必备分析文件探索内容探索交差并补其他常用的命令批量操作结语快捷键,之门提高效率光 showImg(https://segmentfault.com/img/bVK0aK?w=350&h=350); 全栈数据之门 前言 自强不息,...

    yibinnn 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<