打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
Python 数据分析——利用Pandas进行数据整理

有统计表明数据科学家80%的时间都是用于数据清洗和准备的过程。数据清洗的第一步目标可以总结为“完全合一”。

· 完整性:单条数据是否存在空值,统计的字段是否完善。

· 全面性:观察某一列的全部数值,可以看到该列的平均值、最大值、最小值。我们可以通过常识来判断该列是否有问题,如数据定义、单位标识、数值本身等方面。

· 合法性:数据的类型、内容、大小的合法性,如销售额为负、年龄超过了200岁等。

· 唯一性:数据是否存在重复记录,因为数据通常来自不同渠道的汇总,重复的情况是常见的。行数据、列数据都需要是唯一的,例如一个人不能重复记录多次,而相同数据不能多列重复。

关于数据空值、异常值、重复值处理,前面已经有详细讨论。本章的目标是重点讨论数据清洗的第二步目标:数据整理(Tidy Data)。Tidy Data得名于著名数据科学家Hadley Wickham于2014年在Journal of Statistical Software发表的同名论文。在该论文中,Hadley给出了Tidy Data的定义,同时对5种典型的混乱数据格式进行了讨论。基于该理念,Hadley还开发了基于R语言的软件包:reshape、reshape2、plyr和dplyr,本章将完全按照Hadley的论文用Pandas软件包的功能来完成他提出的5种混乱数据的整理。

一、什么是数据整理

1.1数据的语义

如Hadley的论文中所指出,现实中大多数统计数据集是矩形的表格,由行和列构成。各列几乎总带有标签,而各行有时也带有标签。表1展示了关于一个假想实验的数据,其格式很常见。表格有2列3行,行列都有标签。

表1 假想实验数据

这是其中一种数据的展现方式,同样,数据也可以用表2的方式展示。表2展示的数据和表1相同,但是行列被转置。

表2 转置表示

数据相同,但是展示的布局不同。显然,除了数据的外观,需要一种统一方式来描述表格所展示数值的语义或含义。因此,需要对数据进行下列定义。

· 一个数据集是一组“值”的集合,通常不是数字(定量的)就是字符串(定性的)。

· 每个“值”属于一个变量和一个观察对象。

· 一个变量包含测量各个观察单元同一内在属性的所有值(如高度、温度、时长)。

· 一个观察对象包含测量该对象各不同属性的所有值(如一个人、某一天或一场比赛)。

基于此,表3将前面的数据重新排列,使各个值、变量和观察值更加清晰。

表3 数据重新排列

这个数据集包含3个变量、6个观察对象的18个变量,具体包括:

· 人:有3个可能的值(John Smith、Jane Doe和Mary Johnson);

· 治疗方案:有两个可能的值(a和b);

· 结果:有5个或6个可能的值(-,16,3,2,11,1),当然这取决于如何看待缺失值。

1.2整齐的数据

整齐的数据是指数据含义和其结构的标准化匹配方式。一个数据集是混乱还是整齐,取决于行、列、表格与观察对象、变量和类型如何匹配。在整齐的数据中:

· 每个变量组成一列;

· 每个观察对象的所有属性组成一行;

· 每个观察单元的类型组成一个表格。

显然,表3是表1和表2的整齐版本。每行代表一个观察,即某一治疗方案对某个人的效果,每列是一个变量。整齐的数据能让分析师或计算机更容易地提取所需变量,因为它提供了一个构成数据集的标准方式。不幸的是,真实的数据集将会而且经常会以各种可能的方式违反整齐数据的3个原则。典型的混乱数据通常会具有以下5个常见的问题。

· 列标题是值,而非变量名。

· 多个变量存储在一列中。

· 变量既在列中存储,又在行中存储。

· 多个观测单元存储在同一表中。

· 一个观测单元存储在多个表中。

二、数据整理实战

本节将针对上面列出的5个常见混乱数据的问题,提出解决方案。

6.2.1列标题是值,而非变量名

第一种常见的混乱数据集是为展示而设计的表格数据,变量既构成列,又构成行,列标题是值,而非变量名。图1展示了这类典型数据集的一个子集。这个数据集记录的是在美国部分居民收入和宗教信仰的关系。数据来源于皮尤研究中心(Pew Research Center)所做的一个报告。

图6.1 宗教信仰与收入关系

这个数据表将“$10k”“$10-20k”这些收入变量的值作为了列名,而表中的数值应该是对应另一变量——人数。因此,该数据集本来应该有3个变量:宗教、收入和人数,对其进行整理,需要把它融合(又称融化)或堆叠(有的数据分析师也把这一过程称为将宽的数据集变长或变高,本书后面统一用融合来代表这一含义)。所谓融合是指将已经是变量的各列的列表(简称为id_vars)进行参数化,其他的列转换为两个变量:一个被称作列(var_name)的新变量,它包含重复的列标题;另一个被称作值(value_name)的新变量,它包含从之前分离的列中提供联系的数据值。图2通过一个简单的数据集对这一过程进行了说明,这里以row变量作为id_vars,将原来的列名融合后成为新列column中的值(对应var_name),原来列中的值则融合到了另一列(对应value_name)。

图2 数据的融合

融合的结果是一个“熔化”的数据集,下面用如下数据来做一个示例,如图3所示。

图3 数据示例

上面的数据中,列标题实际应该是变量income的取值,因此需要将religion作为id_vars,原来的列名将变成新变量income,由参数var_name指定,对其进行融合操作,代码如下。

图4 melt()函数操作结果

结果如图4所示。这里采用了melt()函数来完成这一功能,该函数有5个参数,各参数含义如下。

· frame:需要处理的数据框。

· id_vars:保持原样的数据列。

· value_vars:需要被转换成变量值的数据列。

· var_name:转换后变量的列名。

· value_name:数值变量的列名。

在本例中通过融合这一操作,指定religion列不变,所有列转换为对应income(参数var_name指定)变量的变量值,原表中的数值变量列名为freq(参数value_name指定)。除了采用融合操作,也可以利用堆叠(stack)功能来完成数据变换,代码如下。

结果如图5所示。上述代码中,首先利用set_index()将religion列设置为了行索引,之后利用stack()函数将所有的列堆叠,即将其列作为二级索引,得到的数据如图6所示。

图5 stack()函数操作结果

图6 堆叠后的数据

之后利用.index.rename()函数,将第二级索引名修改为income,这里的第一级索引是前面提到的religion,堆叠操作将原来的所有列名作为了第二层索引的值。接下来将formatted_df.name修改为freq,最后再将所有的索引还原到列,就得到了和融合操作一样的数据,显然采用melt()函数实现起来更简单。下面再来看一个更复杂一点的例子来加深对融合操作的理解,这里采用的是一个75周的歌曲排行榜数据集,数据内容如图7所示。

图7 歌曲排行榜数据集

与前一数据表类似,查看前5行数据,很容易发现所有这些类似x72nd.week的列名实际应该对应变量week的取值,而原来这些列中对应的值其实是对应变量rank的取值。因此,需要做如下变换,结果如图8所示。

图8 变换结果

虽然从数据整理的角度已经完成了列标题是值而非变量名的问题处理,但是从数据分析角度来看,数据整理的工作远远未完成。week变量值应该对应数值1,2,3,…而不是字符串,需要将其转化为数值,代码如下。

结果如图9所示。上述代码通过字符串函数extract(),采用'(\d+)'正则表达式提取了所有的数值,之后转换为整数类型,至此得到的数据中week已经转换为了数值。

图9 将week对应变量转化为数值

2.2多个变量存储在一列中

如图10所示,下列数据集来源于国际卫生组织,该数据按照国家、年和人口统计学分组记录了确诊的肺结核(Tuberculosis)病例数。人口统计学分组根据性别(m,f)、年龄(0-14,15-25,25-34,35-44,45-54,55-64,unknown)划分。具体数据内容如下。

图10 肺结核病例数据集

此数据将性别和年龄这两个变量都放入了列名中,那么数据整理工作的第一步就是利用melt()将列名转换为变量,之后利用正则表达式通过函数extract('(\D)(\d+)(\d{2})',expand=False)分别提取性别和年龄下限和上限信息。有的情况下,可能需要保留原来的年龄区间记录方式,那么还需要将提前出来的数据再还原为之前的记录方式,代码如下。

图11 性别、年龄提取结果

提取结果如图11所示。

2.3变量既在列中存储,又在行中存储

当变量既在列中存储,又在行中存储时,就会出现最复杂形式的混乱数据。读入要分析的数据,如图12所示。

图12 天气数据集

上面的数据来源于全球历史气象网,提供了墨西哥MX17004气象站2010年5个月的每日天气数据。其中的日期被作为了列名(d1~d8),该问题可以用melt()函数处理,而tmin和tmax代表每日最低和最高温度,显然应该是两个变量,在本例中它们代表了观测对象“天”的属性。因此,需要用pivot_table()函数将其拆分为两列。利用融合操作处理列名实际是变量的值的问题,代码如下。

图13 天气数据融合结果

融合结果如图13所示。经过melt()处理,日期数据已经转换为了列day_raw的取值,进一步需要处理day_raw以取出具体的第几天。此外,日期信息需要合并year、month和day_raw 3列才能得到,因此需要用如下代码整理数据,结果如图14所示。

图14 合并日期信息

上述代码首先使用extract()函数提取了原始日期中的具体第几天信息,之后利用apply()函数将year、month、day列转换为了数字(转换函数为lambda函数),最后再次通过apply()函数将
create_date_from_year_month_day()函数作用于df来建立新的date列。现在已经解决了数据的第一个问题,下一步要做的就是拆分tmin、tmax到列,这里使用pivot_table()函数,在进行这一操作前,因为value列有大量缺失数据,所以需要使用dropna()函数将缺失数据丢弃,代码如下。

图15 丢弃缺失数据

结果如图15所示。上述代码中,pivot_table()函数将原来的id、date列变换成为行的索引,因此还需要利用reset_index()函数将多级索引id、date还原到列,即可完成全部的数据处理,结果如图16所示。

图16 将多级索引还原到列

2.4多个观测单元存储在同一表中

再次回到上节中处理过的歌曲排行榜数据,仔细观察一下,该数据实际上包含了两个观测对象,一个是排行榜,一个是歌曲信息。排行榜提供了每年每首歌在每周的榜单中的名次,而歌曲信息提供了每首歌的风格、长度等信息。显然,这一数据集是把多个观测单元存储在了同一表中。回到数据分析的本源,数据分析人员需要关注的是使用此数据的目的是什么。显然目的是想知道某一天某一首歌在榜单上的排名,而这样保存数据不利于数据分析人员快速做出回答。要解决此问题,需要将数据拆分到两张表中,首先用如下代码来获取榜单信息,如图17所示。

图17 获取榜单信息

上述代码利用上榜日期date.entered与week变量,就可以获得新的当前日期date,基于此可以构建整理后的数据集,该表提供与每周的歌曲排行榜信息。接下来利用如下代码再建立一个关于歌曲的数据表,如图18所示。

图18 歌曲数据表

最后将歌曲数据表和原来的排行榜数据表进行合并,就可以很快找出某首歌在某个时间的排名了,代码如下。

合并结果如图19所示。

图19 合并结果

2.5一个观测单元存储在多个表中

实际的数据分析中经常会遇到一个观测单元的数据被存放在不同的表里面的情况。典型的拆分方式有两种,一种是按照某个变量拆分,如按年拆分为2016年、2017年,或者按地理位置、渠道等进行拆分;另一种则是按度量的不同属性拆分,例如前面的天气数据就可能用多个表保存,一张表是来源于温度传感器的数据,另一张是来源于湿度传感器的数据。对于第二种情况,只要各数据集的记录中有一个唯一标识,如日期、身份证号等,那么后续分析中可以通过pd.merge将各个数据集连接起来。对这类问题的处理也很简单,可以先编写一个读取数据的函数,遍历目录中的文件,并将文件名作为单独的列加入到DataFrame,最后使用pd.concat 进行合并。例如,现有如图20所示的数据,该数据是对美国伊利诺伊州每年的小孩取名的统计,每年的数据作为一个数据集,实际上所有这些数据集应该合并为一个。

图20 姓名数据集

现在的数据整理问题就变成了如何将不同年份的数据读取并合并,首先来解决读取问题,代码如下。

上述代码利用了Python中的标准库glob。glob提供了文件名模式匹配,不用遍历整个目录判断每个文件是不是符合通配符。利用201*可以完成对2014和2015的匹配。之后利用extract_year()函数可以将年的信息从文件名中提取出来,将其作为year列的信息。完成上述工作后,每年的数据保存到了列表df_list中,此时只需要用pd.concat将列表中的数据合并就可以了,代码如下。

合并结果如图21所示。

图21 合并不同年份的数据

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
统计学基础之总体与样本
R语言非参数PDF和CDF估计、非参数分位数回归分析间歇泉、GDP增长数据
SAS-EM 决策树案例
核对数据眼睛疼,Excel函数帮到你,轻松解决得表扬
自学R语言(十四)-tidyr包的学习
《R数据科学》第3章——dplyr是什么
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服