很多数据一拿来并不是整齐的,不适合让计算机来作数据分析,因此需要对数据进行各种处理,来让数据变得“tidy”。

下面,基于R语言的tidyr包的一些函数来对原始数据进行整理。

*(以下所有操作依照The University of Auckland课程自己整理,均为原创)

dt包 r语言 r语言tidyverse包总结_数据集

一、需要安装的工具集tidyverse

tidyverse是集合了dplyr,tidyr,ggplot2,stringr等R语言包的一款工具集,在RStudio中执行以下代码,安装tidyverse,tidyr包就自然一并下载了。

install.packages("tidyverse")

然后导入该工具集

library(tidyverse)



二、数据整理的一些标准

这里有点像数据库中的范式标准一样,要求消除冗余的数据等,主要有以下几个方面:

1、每一个属性(也叫变量)占一列,即所有属性组成一个数据集的最上面那一行。
2、每一组观测值占一行
3、每一个数据(观测值)独占一个格子

比较官方的说法是这样的:
Tidy data has one row per observation and one column per variable and one table per type of observational unit.



三、数据整理要用到的一些操作函数

下面我们以tidyr包里给出的几个简单的数据集(table2、table3等)为例,介绍这些数据整理操作函数的使用。
首先我们看下符合我们“tidy data”的数据集table1长什么样:

> table1
# A tibble: 6 x 4
  country      year  cases population
  <chr>       <int>  <int>      <int>
1 Afghanistan  1999    745   19987071
2 Afghanistan  2000   2666   20595360
3 Brazil       1999  37737  172006362
4 Brazil       2000  80488  174504898
5 China        1999 212258 1272915272
6 China        2000 213766 1280428583

此后我们的任务就是将整齐的数据集整理成table1这种格式。



1、gather(合并):将多个属性合并为观测值,并归于一个新属性

当一个属性占了两列或更多列的时候,将这几列合并为一个属性。
比如,我们看下table4a和table4b这两个表:

> table4a
# A tibble: 3 x 3
  country     `1999` `2000`
* <chr>        <int>  <int>
1 Afghanistan    745   2666
2 Brazil       37737  80488
3 China       212258 213766
> table4b
# A tibble: 3 x 3
  country         `1999`     `2000`
* <chr>            <int>      <int>
1 Afghanistan   19987071   20595360
2 Brazil       172006362  174504898
3 China       1272915272 1280428583

发现`1999`和`2000`这两个应该为一个观测值而不是一个属性,他们应归属于“year”这个属性,而`1999`和`2000`当前对应的观测值应分别对应于一个新的属性“cases”和“population”,于是我们使用gather()函数。

> table4a %>% gather(`1999`, `2000`, key = "year", value = "cases")

# A tibble: 6 x 3
  country     year   cases
  <chr>       <chr>  <int>
1 Afghanistan 1999     745
2 Brazil      1999   37737
3 China       1999  212258
4 Afghanistan 2000    2666
5 Brazil      2000   80488
6 China       2000  213766
> table4b %>% gather(`1999`, `2000`, key = "year", value = "population")

# A tibble: 6 x 3
  country     year  population
  <chr>       <chr>      <int>
1 Afghanistan 1999    19987071
2 Brazil      1999   172006362
3 China       1999  1272915272
4 Afghanistan 2000    20595360
5 Brazil      2000   174504898
6 China       2000  1280428583



2、spread(展开):将某个属性下的观测值展开为多个属性

与gather相反,如果某个属性下的观测值应当归于一个新的属性,我们使用gather,如下table2表:

> table2
# A tibble: 12 x 4
   country      year type            count
   <chr>       <int> <chr>           <int>
 1 Afghanistan  1999 cases             745
 2 Afghanistan  1999 population   19987071
 3 Afghanistan  2000 cases            2666
 4 Afghanistan  2000 population   20595360
 5 Brazil       1999 cases           37737
 6 Brazil       1999 population  172006362
 7 Brazil       2000 cases           80488
 8 Brazil       2000 population  174504898
 9 China        1999 cases          212258
10 China        1999 population 1272915272
11 China        2000 cases          213766
12 China        2000 population 1280428583

发现属性type下的cases和population应当各自单独作为一个属性(列名)而不是观测值,而count下的观测值才应当为cases和population下的观测值,故我们将type属性展开。

> table2 %>% spread(key = type, value = count)
# A tibble: 6 x 4
  country      year  cases population
  <chr>       <int>  <int>      <int>
1 Afghanistan  1999    745   19987071
2 Afghanistan  2000   2666   20595360
3 Brazil       1999  37737  172006362
4 Brazil       2000  80488  174504898
5 China        1999 212258 1272915272
6 China        2000 213766 1280428583



3、separate(分割):将一个属性分割为多个,即一列分多列

我们看下table3的内容:

> table3
# A tibble: 6 x 3
  country      year rate             
* <chr>       <int> <chr>            
1 Afghanistan  1999 745/19987071     
2 Afghanistan  2000 2666/20595360    
3 Brazil       1999 37737/172006362  
4 Brazil       2000 80488/174504898  
5 China        1999 212258/1272915272
6 China        2000 213766/1280428583

我们发现,rate这一列看起来很别扭,因为它并不是个真正的“比率”,而且中间还有个“/”分割符,因此我们想把它分成两列。

> table3 %>% separate(rate, into = c("cases", "population"), sep = "/")
# A tibble: 6 x 4
  country      year cases  population
  <chr>       <int> <chr>  <chr>     
1 Afghanistan  1999 745    19987071  
2 Afghanistan  2000 2666   20595360  
3 Brazil       1999 37737  172006362 
4 Brazil       2000 80488  174504898 
5 China        1999 212258 1272915272
6 China        2000 213766 1280428583

函数里使用“sep = ”来指明分割标志符,这里可以不加,因为默认分割符就是“/”。如果原数据集中没有明确的分割标志符,可以使用数字标明分割位置,如“sep = 3”表示从第4个字符前面开始分割(下标从0开始计算)。下面我们尝试分割属性year(仅仅用来说明问题,事实上并不需要这么分割):

> table3 %>% separate(year, into = c("century", "year"), sep = 2)
# A tibble: 6 x 4
  country     century year  rate             
  <chr>       <chr>   <chr> <chr>            
1 Afghanistan 19      99    745/19987071     
2 Afghanistan 20      00    2666/20595360    
3 Brazil      19      99    37737/172006362  
4 Brazil      20      00    80488/174504898  
5 China       19      99    212258/1272915272
6 China       20      00    213766/1280428583



4、unite(联合):将多个属性合并为一个,即多列合一列

与separate相反,unite将多个属性合并为一个属性,我们以table5为例:

> table5
# A tibble: 6 x 4
  country     century year  rate             
* <chr>       <chr>   <chr> <chr>            
1 Afghanistan 19      99    745/19987071     
2 Afghanistan 20      00    2666/20595360    
3 Brazil      19      99    37737/172006362  
4 Brazil      20      00    80488/174504898  
5 China       19      99    212258/1272915272
6 China       20      00    213766/1280428583

这个就是我们在第3步对table3测试的结果,现在我们把它变回来。

> table5 %>% unite(year, century, year, sep = "")
# A tibble: 6 x 3
  country     year  rate             
  <chr>       <chr> <chr>            
1 Afghanistan 1999  745/19987071     
2 Afghanistan 2000  2666/20595360    
3 Brazil      1999  37737/172006362  
4 Brazil      2000  80488/174504898  
5 China       1999  212258/1272915272
6 China       2000  213766/1280428583

这里依然使用“sep = ”确定合并后的分隔符,默认为下划线”_“。

> table5 %>% unite(year, century, year)
# A tibble: 6 x 3
  country     year  rate             
  <chr>       <chr> <chr>            
1 Afghanistan 19_99 745/19987071     
2 Afghanistan 20_00 2666/20595360    
3 Brazil      19_99 37737/172006362  
4 Brazil      20_00 80488/174504898  
5 China       19_99 212258/1272915272
6 China       20_00 213766/1280428583



5、mutate(转变):转变某属性下观测值的数据类型

在第3步我们使用separate函数操作table3后,生成的新属性casespopulation数据类型是,我们需要将他们转换为整型,使用mutate函数:

> table3 %>% separate(rate, into = c("cases", "population"), sep = "/") %>% mutate(cases = as.integer(cases), population = as.integer(population))
# A tibble: 6 x 4
  country      year  cases population
  <chr>       <int>  <int>      <int>
1 Afghanistan  1999    745   19987071
2 Afghanistan  2000   2666   20595360
3 Brazil       1999  37737  172006362
4 Brazil       2000  80488  174504898
5 China        1999 212258 1272915272
6 China        2000 213766 1280428583

这样看起来稍显繁琐,我们还可以在separate的同时就将数据类型转换掉:

table3 %>% separate(rate, into = c("cases", "population"), convert = TRUE, sep = "/")
# A tibble: 6 x 4
  country      year  cases population
  <chr>       <int>  <int>      <int>
1 Afghanistan  1999    745   19987071
2 Afghanistan  2000   2666   20595360
3 Brazil       1999  37737  172006362
4 Brazil       2000  80488  174504898
5 China        1999 212258 1272915272
6 China        2000 213766 1280428583

使用convert参数自动转换为数值类型。



四、数据整理结束后需要用到的操作

我们整理数据得到“tidy data”后,可以使用下面这些操作更清晰地观察数据的状态和更进一步的分析数据。



1、summarise(总结):对某些属性进行求平均、最值等操作并返回一个新的表

我们以数据集table1为例,假设我们希望得出year属性的最大值,cases属性的最小值和population属性的平均值,我们可以使用summarise()函数生成一个新的观察表。

> table1 %>% summarise(year_max = max(.$year), cases_min = min(.$cases), population_mean = mean(.$population))
# A tibble: 1 x 3
  year_max cases_min population_mean
     <int>     <int>           <dbl>
1     2000       745      490072924.



2、group_by(分组):类似SQL的grouy_by操作,一般与summarise()配合使用

以table1为例,如果我们想知道对应每个country的population最大值和cases的最大值,就需要借助group_by函数,对country属性进行分组后再summarise()。

> table1 %>% group_by(country) %>% summarise(cases_max = max(.$cases), population_max = max(.$population))
# A tibble: 3 x 3
  country     cases_max population_max
  <chr>           <int>          <int>
1 Afghanistan    213766     1280428583
2 Brazil         213766     1280428583
3 China          213766     1280428583



3、join(连接):类似SQL的连接操作,将两个表连成一个

之前在操作table4a和table4b的时候,发现可以把table4a的cases和table4b的population合在一个表内,即类似于数据库的join操作,我们以table4a为基准,使用左连接:

left_join(table4a %>% gather(`1999`, `2000`, key = "year", value = "cases"), table4b %>% gather(`1999`, `2000`, key = "year", value = "population"))

# Joining, by = c("country", "year")
# A tibble: 6 x 4
  country     year   cases population
  <chr>       <chr>  <int>      <int>
1 Afghanistan 1999     745   19987071
2 Brazil      1999   37737  172006362
3 China       1999  212258 1272915272
4 Afghanistan 2000    2666   20595360
5 Brazil      2000   80488  174504898
6 China       2000  213766 1280428583

或以table4b为基准,使用右连接:

> right_join(table4a %>% gather(`1999`, `2000`, key = "year", value = "cases"), table4b %>% gather(`1999`, `2000`, key = "year", value = "population"))
Joining, by = c("country", "year")
# A tibble: 6 x 4
  country     year   cases population
  <chr>       <chr>  <int>      <int>
1 Afghanistan 1999     745   19987071
2 Brazil      1999   37737  172006362
3 China       1999  212258 1272915272
4 Afghanistan 2000    2666   20595360
5 Brazil      2000   80488  174504898
6 China       2000  213766 1280428583

甚至可以直接使用内连接,因为table4a和table4b规格一样。

> inner_join(table4a %>% gather(`1999`, `2000`, key = "year", value = "cases"), table4b %>% gather(`1999`, `2000`, key = "year", value = "population"))
Joining, by = c("country", "year")
# A tibble: 6 x 4
  country     year   cases population
  <chr>       <chr>  <int>      <int>
1 Afghanistan 1999     745   19987071
2 Brazil      1999   37737  172006362
3 China       1999  212258 1272915272
4 Afghanistan 2000    2666   20595360
5 Brazil      2000   80488  174504898
6 China       2000  213766 1280428583

一句话总结内连接、外连接的区别:
① 内连接显示左右两个表共有部分
② 左外连接以左边表为基准,除显示共有部分外,左边有而右边没有的显示为空。
③ 右外连接以右边表为基准,除显示共有部分外,右边有而左边没有的显示为空。