趁着今天有时间,想着是不是该记录点东西了,没错是该记录一下了。今天的重头戏是tidyr package。
Tidy datasets are all alike, but every messy dataset is messy in its own way 。这是 Hadley Wickham(牛人)的总结。
数据预处理对于后续的分析很关键,处理过后的Tidy data 只要有三类特征:
- 每一列代表一个变量
- 每一行代表一个观测值
-
每一单元格代表一个value
在Tidyverse(它是由RStudio选出多个资料科学应用套件的集合)中其他一些如dplyr、ggplot2等都是基于Tidy tada,下面这张图片很好的说明了tidy data的数据结构。这个内容也是我最近在学习的内容。
1. spreading and gathering
拿到一个数据首先要分辨出观测值和变量,然后就是解决两个很常见的问题:
- 一个变量分散在多列
- 一个观测分散在多行
因此,就要进行长宽数据结构的转换,因此你需要用到tidyr package 中两个重要的函数: spread 和 gather
1.1 gather
让数据变成长窄型
> table4a
# A tibble: 3 x 3
country `1999` `2000`
* <chr> <int> <int>
1 Afghanistan 745 2666
2 Brazil 37737 80488
3 China 212258 213766
对于上面的数据,可以看出1999和2000两列可以合并成一个新变量year,要完成这个转换:
Usage : gather(key= , value= , ...)
1. 为需要合并的多列定义新的变量名,key = "year"
2. 为分布在单元格变量定义一个变量名,value = "cases"
3. 设置需要合并成一个变量的列名,`1999`,`2000`,若需要合并成多列可直接指定列的索引值
tidy4a <- table4a %>% gahter (key = "year", value = "cases", `1999`,`2000`)
> tidy4a
# 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
1.2 spread
让数据变成宽短型
> 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
对于上面的数据可以看出一个观测值是每个国家每一年的数据,然而每一个观测值确被分成两行排列,要完成这个转换:
Usage: spread(key= ,value = )
1. 包含变量名的列,即key列,key = "type"
2. 包含value的列,即value列,value = "count"
对于这个函数的理解,将type列和count列每一行数据看成key-value pair ,从而利用spread对每一个key-value进行列的转换
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
2. Separating and uniting
对于一些数据,如果每一列的每一个单元格是由多个变量组成,那么我们需要将这一列分割成两列,这时候就要用到separate ; 又或者将多列合并成一列,就要用到unite
2.1 separate
> 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
Usage: separate (col = , into = , sep = )
1. 定义需要分割的列名 col = "rate"
2. 定义分割后的多个列名 into = c("cases", "population")
3. 指定分隔符,默认以非字母、数字为分割符,也可用数字指定从字符串的那个字符分割
table3 %>% separate(col = rate,into = c("cases","population"))
# 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
从返回的结果可以看出新生成的cases 和population两列数据类型为数值型,而结果是字符型,因此需要指定参数convert = TRUE
table3 %>% separate(col = rate,into = c("cases","population"),convert = TRUE)
table3 %>% separate(year, into = c("century", "year"), sep = 2) # 表示从year列中第二个字符后进行分割
# 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
2.2 unite
对上面的数据century和year 两列进行合并
Usage : unite(col = , ... , sep = ,)
1. 定义合并后的列名
2. 指定需要合并的多列(列名或索引)
3. 指定合并后来自不同列数据的连接符,默认是"_"
unite (col = "new" , "century", "year", sep = "")
# 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
3. missing value
改变数据结构的同时,会出现一些缺失值,缺失值主要有以下两种方式: 1、原数据集中有NA 标记 ; 2、 有些观测值不存在数据集中
> stocks
# A tibble: 7 x 3
year qtr return
<dbl> <dbl> <dbl>
1 2015 1 1.88
2 2015 2 0.59
3 2015 3 0.35
4 2015 4 NA
5 2016 2 0.92
6 2016 3 0.17
7 2016 4 2.66
观测数据可以看出: 有一个明确的缺失值NA, 还有一个隐藏的观测值缺失
为了让隐藏的缺失值变成NA,可以spread 将year列展开
stocks %>% spread(key = year,value = return)
>stocks
# A tibble: 4 x 3
qtr `2015` `2016`
<dbl> <dbl> <dbl>
1 1 1.88 NA
2 2 0.59 0.92
3 3 0.35 0.17
4 4 NA 2.66
一般原本存在的NA可能不是很重要,因此你可以利用gather将NA变成隐藏的缺失。
stocks %>% spread(key = year,value = return) %>% gather (key = "year" , value = "return" ,`2015`,`2016`, na.rm = TRUE)
# A tibble: 6 x 3
qtr year count
<dbl> <chr> <dbl>
1 1 2015 1.88
2 2 2015 0.59
3 3 2015 0.35
4 2 2016 0.92
5 3 2016 0.17
6 4 2016 2.66
另一个让隐藏的缺失值变成NA的函数: complete()
stocks %>% complete(year, qtr) #列出year和qtr所有的观测组合
# A tibble: 8 x 3
year qtr return
<dbl> <dbl> <dbl>
1 2015 1 1.88
2 2015 2 0.59
3 2015 3 0.35
4 2015 4 NA
5 2016 1 NA
6 2016 2 0.92
7 2016 3 0.17
8 2016 4 2.66
还有一种情况就是对于数据录入员来说,有时候缺失值表示与之前值相同故而省略
> treament
# A tibble: 4 x 3
person treament response
<chr> <dbl> <dbl>
1 Derrick Whitmore 1 7
2 NA 2 10
3 NA 3 7
4 Katherine Burke 1 4
treatment %>% fill(person)
# A tibble: 4 x 3
person treament response
<chr> <dbl> <dbl>
1 Derrick Whitmore 1 7
2 Derrick Whitmore 2 10
3 Derrick Whitmore 3 7
4 Katherine Burke 1 4
有了Tidy data,我们就可以开展后续数据整理和分析,这就要介绍到Tidyverse家族另一个重要的dplyr package,可以实现类似SQL的功能,虽然一些R基础包中函数也可以完成目标,但是当你熟悉了dplyr的操作,你会感到很High!!!
附上原文链接: https://r4ds.had.co.nz/