很多数据一拿来并不是整齐的,不适合让计算机来作数据分析,因此需要对数据进行各种处理,来让数据变得“tidy”。
下面,基于R语言的tidyr包的一些函数来对原始数据进行整理。
*(以下所有操作依照The University of Auckland课程自己整理,均为原创)
一、需要安装的工具集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后,生成的新属性cases
和population
数据类型是,我们需要将他们转换为整型,使用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
一句话总结内连接、外连接的区别:
① 内连接显示左右两个表共有部分
② 左外连接以左边表为基准,除显示共有部分外,左边有而右边没有的显示为空。
③ 右外连接以右边表为基准,除显示共有部分外,右边有而左边没有的显示为空。