tidyverse笔记——tidyr包


未完待续

tidry:Tidy Messy Data

观摩完 Hadley Wickham 大佬的 R for Data Science,我对有关 tidyr 的章节印象最深刻的是它的开场白,第一句话引用了列夫托尔斯泰的名言:幸福的家庭是相似的,不幸的家庭却各有各的不幸。第二句话是作者“拙劣”的模仿:整洁的数据集是相似的,脏的数据集却各有各的脏法!

  • Happy families are all alike; every unhappy family is unhappy in its own way.
    —Leo Tolstoy
  • Tidy datasets are all alike, but every messy dataset is messy in its own way.
    —Hadley Wickham

对于应对过“脏数据”的人来说可谓是深有同感,然后我们还不得不接受这样一个事实:当我们真正做一些自主性的数据分析,正准备处理一些生活中的数据和问题,绝大时候我们遇到的都是“脏数据”。
很多时候,我们其实并没有事先意识到它是脏的或者不知道脏在哪?做着做着就出现问题了。
数院有一位很厉害的老师跟我提及过:实际上数据预处理是非常难做的。我们今日尝试着用 tidyr 以较合理的方法解决其中的一部分问题。

常用函数及其功能

tidyr——Reshape

Gather

  • Gather columns into key-value pairs
    这是帮助文档的解释,实际上是把原来一群有相同性质的变量(列名)转换成新的列的值,实现了从 “键” 到 “值” 的转变。直接上例子,这里用的是 relig_income,它统计了不同区域不同薪资水平的人数。
> relig_income %>% print(n = 5)
# A tibble: 18 × 11
  religion   `<$10k` `$10-20k` `$20-30k` `$30-40k` `$40-50k` `$50-75k` `$75-100k`
  <chr>        <dbl>     <dbl>     <dbl>     <dbl>     <dbl>     <dbl>      <dbl>
1 Agnostic        27        34        60        81        76       137        122
2 Atheist         12        27        37        52        35        70         73
3 Buddhist        27        21        30        34        33        58         62
4 Catholic       418       617       732       670       638      1116        949
5 Don’t kno…      15        14        15        11        10        35         21
# … with 13 more rows, and 3 more variables: `$100-150k` <dbl>, `>150k` <dbl>,
#   `Don't know/refused` <dbl>

可以看到从第二列开始,变量都是关于薪资的,那么为何不直接把薪资作为新的变量,不同的薪资水平作为值呢?比如说第一行,就会被拆为10行,地区都是Agnostic ,薪资变量下的值是不一样的,用 gather() 实现如下:

relig_income %>% gather(2:11, key = "income", value = "count") %>% arrange(religion)

便会得到

# A tibble: 180 × 3
   religion income             count
   <chr>    <chr>              <dbl>
 1 Agnostic <$10k                 27
 2 Agnostic $10-20k               34
 3 Agnostic $20-30k               60
 4 Agnostic $30-40k               81
 5 Agnostic $40-50k               76
 6 Agnostic $50-75k              137
 7 Agnostic $75-100k             122
 8 Agnostic $100-150k            109
 9 Agnostic >150k                 84
10 Agnostic Don't know/refused    96
# … with 170 more rows

Spread

  • Spread a key-value pair across multiple columns
    这是帮助文档的解释,简单来讲,它是 gather() 的逆操作。它以某一列的不同值创建新的变量,将键值对分布在多个列上。
  • 所以正确使用 spread() 和 gather()的关键在于,思考清楚:何为键,何为值?
    还用上面的例子:
relig_income_ <- relig_income %>% gather(2:11, key = "income", value = "count")
relig_income_ %>% spread(key = income, value = count) %>% print(n = 5)

便可以得到原来的数据集。

pivot_longer & pivot_wider

简单认为,pivot_longer == spread pivot_wider == gather
早些版本的 tidyr 并没有 spreadgather,我们来看看作者本人是怎么想的Pivoting,他的大概意思是:原来的名字不够直观,减少列增加行、减少行增加列从直观上暗示该操作的意义。Many people don’t find the names intuitive and find it hard to remember which direction corresponds to spreading and which to gathering.
老实说,我是有些没必要的。不过,语法在进步。以spread的“精装版”为例,打开帮助文档,可以看到它的参数非常多,也变具有更灵活写法的可能。

pivot_longer(
  data,
  cols,
  names_to = "name",
  names_prefix = NULL,
  names_sep = NULL,
  names_pattern = NULL,
  names_ptypes = NULL,
  names_transform = NULL,
  names_repair = "check_unique",
  values_to = "value",
  values_drop_na = FALSE,
  values_ptypes = NULL,
  values_transform = NULL,
  ...
)

拿之前的例子,可以这么写:

relig_income %>% 
	pivot_longer(cols = c(2:11), names_to = "income", values_to = "value")

Spread & Gather等价写法

依旧用上面的例子relig_income,下面详细介绍如何构造一句话完成等价于spread的操作。

religion = as.vector(unlist(rep(relig_income[,1], dim(relig_income)[2] - 1)))

cbind的第一个参数利用了rep函数,因为按2到11列展开,所以,行数应该是原来的十倍(dim(relig_income)[2] - 1),由于cbind函数不能接受list类型的参数,便用unlist进行类型转换。

 stack(relig_income[, -1])

cbind的第二个参数利用了stack函数,它能以列序为主序展开列表或数据框
Stacking vectors concatenates multiple vectors into a single vector along with a factor indicating where each observation originated. Unstacking reverses this operation.

cbind(religion = as.vector(unlist(rep(relig_income[,1], dim(relig_income)[2] - 1))), stack(relig_income[, -1]))

其实也可以用reshape函数,不过参数列表令人眼花缭乱,这里便用更简明易懂的写法了。
gather函数的等价写法就有点麻烦了,我们还是按一种合理的思路想,我可以这么做:创建新列,确定行数,填充每一行。写起来非常麻烦,目前没有找到一种比较好的等价写法

Complete

  • Complete a data frame with missing combinations of data
  • 数学上的一个理解就是集合的 “笛卡尔积”

举个例子就懂,

> df = data.frame(x1=c(1, 2, 3), x2=c(2, 3, 3), value=c(11, 3, 5))
> df %>% complete(x1, x2)
# A tibble: 6 × 3
     x1    x2 value
  <dbl> <dbl> <dbl>
1     1     2    11
2     1     3    NA
3     2     2    NA
4     2     3     3
5     3     2    NA
6     3     3     5

等价的写法可以借助merge实现。如果两个都是向量的话,等价写法很简单

temp = unique(v2)
cbind(rep(unique(v1), each = length(temp)), temp)

如果包含data.frame等其他数据类型,最大的不同在于它的本质类型是list类型,而不是double,在使用rep函数时就会产生与预期不同的效果,需要借助unlist函数进行处理。

Separate

  • separate() pulls apart one column into multiple columns, by splitting wherever a separator character appears.
  • separate()通过在出现分隔符的位置拆分,将一列拆分为多列。

Example(from R for Data Science

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
table3 %>% 
  separate(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

可以看到它自动识别原本列rate里的分隔符——“/”,等价的操作可用 stringr 代替,它也是tidyverse系列中的一个包,当然,要是一些很复杂的,可以考虑正则表达式。

Unite

  • 可简单视为 separate() 的逆函数,将多列合并为一列
  • 默认的连接符是下划线“_”,但这往往不是我们想要的,可以利用参数 sep 选择合适的分隔符,如果想直接连接,可以置sep参数为空:sep=“”

tidyr——Handle Missing Value

Hadley Wickham 大佬对于缺失值又有骚话曰:

  • An explicit missing value is the presence of an absence; an implicit missing value is the absence of a presence.

非常有意思,他用了一个不同年份的各季度数据举例,详情请点击.

因为我想到一个更生动的例子,所以这里和大家分享以下。小学的时候,我们得做黄冈小状元,老实说,我从来没有完整完成过任何一本,我很懒不想写,我是怎么做的呢?有些麻烦的就空在那里了,这种老师一看就看到了,这就是“显式缺失值”(explicit missing value)。要是量很多,来不及补,我会偷偷把其中几页拿固体胶粘在一起,只粘边缘,如果不幸被老师发现了,我还能把责任推给出版社,是他们印刷有问题,我也没看到。也就是说,我把这几页偷偷藏起来,这就是“隐式缺失值”。是的,老师可能发现,也可能没发现。我们在对数据做一些处理的时候,对这些隐式缺失值有时候确实是没有反应过来,没有意识到有缺项,正如当年我的老师(笑了)。

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值