购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

2.3 tidyr
——数据清道夫

2.3.1 为什么使用tidyr

tidyr包作为整个tidy系列里的支柱之一,可以称为目前最容易上手的数据清理和数据操控工具。开发者Hadley汲取了前作reshape和reshape2包中的精华,并最大限度地考虑到新用户的使用习惯,用精简的人类语言创造了tidyr包。根据笔者的使用经验,使用tidyr包进行数据清理的优势在于以下四点。

1)简洁直观的函数名称,可读性极强——易上手。

2)默认设置可以满足大部分使用需求,无须时刻参考帮助文档——易使用。

3)不同函数中的参数设置结构清晰——易于记忆。

4)处理数据过程中完整保留了变量属性及数据格式——不易出现未知错误。

本节将通过介绍tidyr包中最重要的几个函数来给为读者展示tidyr包在数据清理和数据操控上的优势,并将通过代码演示来介绍其基本的使用方法。

2.3.2 gather/spread——“长”“宽”数据转换

1.gather——“宽”变“长”

2.1节讨论了“宽”数据格式的弊端,所以推荐将数据转换成“长”数据——全部变量名称为一列,相关数值为一列。gather函数因此而生。图2-1所示的示例用箭头标识出了数据由“宽”变“长”的具体路线。在理想情况下,整洁的数据框应为如图2-1a所示的格式,因子水平一列(性别),变量(或指标)一列,剩余所有数值型数据一列。

读者可以使用2.2.2节中介绍的tribble函数来构建如图2-1b所示的“脏”数据框,然后使用以下代码实现从“脏”和“宽”的形式到“干净”和“长”的转换。在代码清单2-1中,笔者将“脏”数据保存在名为df的数据框中(此处略去创建数据集的代码),然后使用管道函数“%>%”,将df传递给gather函数(中文释义见表2-13),因为管道函数的存在,所以无须重新引用df,而以“.”来代替,指定指标列为key,数值列为value,保留序号列(保留列需要使用负号加列名的形式进行设置),并移除默认值。之后会得到一个中间产物数据框,该数据框指标列中的“性别”和指标虽然以空格分隔开,但仍然在一列中,不满足“干净”数据的原则。所以再次使用管道函数将中间产物的数据框,传递给函数separate(详见2.3.3节),将key列拆分成两列,分别为性别和key,此时的数据库便如图2-1a所示。

图2-1 “宽”数据变“长”数据示意

代码清单2-1 gather和separate函数基本使用示例


  > df %>% 
gather(data = .,key = key, value = value, ... = -序号, na.rm = T) %>% 
separate(data = .,key, into = c("性别","key"))

小知识

上述代码中的“%>%”为'magrittr'包中的forward-pipe operator,中文可以理解为管道函数。该这个函数能够与'tidyverse'内的所有函数完美结合使用,且易于理解记忆。有兴趣的读者可以尝试运行指令“?'%>%'”来查看具体的英文帮助。

因为tidy系列中各个的函数结构非常简洁清晰,因此当读者熟悉各种参数的位置情况之后,完全可以省略各种参数名称,而只依靠位置来进行传参,具体代码如下:


  > df %>% 
gather(key, value, -序号, na.rm = T) %>% 
separate(key, c("性别","key"))

表2-13 gather函数中参数中的及功能说明

2.spread——“长”数据变“宽”

函数spread是gather函数的逆向函数,即将“长”数据转换成“宽”数据。图2-2简要展示了函数的执行规则,将key列中的变量单独拆分成新列,value列中与变量中对应的数值同样会按规则进行放置。读者可以参考表2-14中的中文释义自行练习代码。

图2-2 “长”数据变“宽”数据示意图

表2-14 函数spread参数及功能说明对照表

2.3.3 separate/unite——拆分合并列

2.3.2节中展示了函数separate的具体用法,该函数完全可以理解为是Excel中的拆分列,该函数无法对一个单独的数值位置进行操作。表2-15介绍了其所包含的参数及中文释义。unite函数则是其相对的逆向函数。

表2-15 函数separate参数及功能说明对照表

2.3.4 replace_na/drop_na/——默认值处理工具

一旦明确了默认值的替代方式,replace_na和drop_na两个函数就可以通过对指定列的查询来将NA替换成需要的数值,例如,去掉所有存在默认值的观察值。表2-16中列出了函数的功能简介及使用时应注意的事项。读者可以参照帮助文档中的例子结合表2-16中的提示来自行练习这两个函数的功能。

表2-16 函数replace_na和drop_na对比

下面的代码列出了如何使用两个函数:


  > df %>%
gather(key, value, -序号) %>% 
separate(key, c("性别","key")) %>%
replace_na(list(value = "missing"))
  > df %>% 
gather(key, value, -序号) %>% 
separate(key, c("性别","key")) %>%
drop_na()

这里必须提醒一下读者关于默认值全部替换的情况,将所有默认值替换成0是很危险的行为,不推荐使用这种做法,因为0代表该数据是存在的,只是数值为0,而默认值则可能代表数据不存在和存在两种情况,只是因为某些原因而导致数据采集失败。因此对默认值的处理一定要视具体情况而定。

2.3.5 fill/complete——填坑神器

在处理日期或者计算累积值的时候,如果中间有一个默认数值,则意味着值不完整或累积值无法计算。fill函数可以自动填补默认的日期或者日期,类似于Excel中拖动鼠标来完成单元格数值的复制或序列填充功能。complete函数是将三个函数揉在一起,这三个函数分别为:expand、dplyr::left_join和replace_na。主要功能是将变量和因子的各种组合可能性全部罗列出来,并用指定的数值替代默认值部分。complete函数在日常练习中并不常用,所以这里不做过多介绍,感兴趣的读者可以参考帮助文档进行练习。

fill函数的参考及功能说明详见见表2-17。

表2-17 函数fill参数及功能说明对照

2.3.6 separate_rows/nest/unest——行数据处理

1.separate_rows——拆分“单元格”

当遇到一个数据单位中出现多个数值的情况时,separate_rows函数就会显得非常有用。图2-3中展示了最基本的函数逻辑,将一个数据单位中的不同数值按照参数进行sep中给出的参数拆分,然后将拆分之后的结果顺序地放在同一列的不同行中,并自动增加行数。

图2-3 separate_rows函数的工作原理示意图

separate_rows函数的参数及功能说明详见表2-18。

表2-18 separate_rows函数的参数及功能说明对照

2.nest/unest——“压缩”和“解压缩”行数据

nest/unest是两个互逆函数,它们最重要的功能是将一个数据框,按照用户自定义的规则,将其压缩成一个新的数据框,新的数据框中包含列表型数据。Jenny Bryan认为这是目前最有实际操作意义的数据框形式,因为它比较符合人们对数据集形式的一般主观印象,而且数据框同时还保留了列表格式的灵活性。下面就来通过代码具体介绍nest函数的实现机制。

将2.3.4节中清理后的数据保存为df_tidy,然后再将该数据框传递给nest函数,并设定压缩除性别列以外的变量。函数运行的结果是生成了一个只有两列的新数据框,变量为性别和data,其中,data列包含了原数据框中其他三个变量的数据(具体见表2-19)。变量data列中的列表格式将三个变量存储为三个独立的元素,如果读者对两个列表中任意一个进行as_tibble运算,都会得到一个完整的数据框。示例代码如下:


  > df_tidy %>% 
nest(-性别)

上述代码运行结果如表2-19所示。

表2-19 函数nest运行结果一

将序号列排除在外,压缩其余变量列,代码如下:


  > df_tidy %>% 
nest(-序号)

上述代码运行结果如表2-20所示。

表2-20 函数nest运行结果二

单独使用nest函数没有任何实际价值,但是当配合循环(第4章)和purrr包(第5章)中的map函数家族时,nest函数就会显示出强大的功能性。对dplyr包有一定了解的读者可以跳过第3章,直接查看nest与其他具有循环功能的函数结合使用的例子。

表2-21和表2-22中列举了nest/unest这一对函数的参数及功能说明。需要提醒读者一点的是,如果需要使用unnest函数“解压缩”两列及以上时,那么每一列中数据框的行数都必须相等,否则无法成功“解压”。

表2-21 函数nest参数及功能说明对照

表2-22 函数unnest参数中英文对照 Nh5YQEc8t9GR35nTzkRF7BoKarsWizDGHbrcD3vH7AOC5Koxv+KEmIWQz7rJ1D8h

点击中间区域
呼出菜单
上一章
目录
下一章
×