长宽数据转换的特殊情况
Try to learn everything about something!
前面介绍了长宽数据转换的常见的几种情况,今天介绍下一些比较特殊的情况。
先变长,再变宽
有些数据单纯的一次变宽或变长是不能解决的,需要联合使用pivot_longer
和pivot_wider
。
使用world_bank_pop
数据集演示,这个数据集是2000年到2017年的每个国家的人口数据。
world_bank_pop
## # A tibble: 1,056 x 20
## country indicator `2000` `2001` `2002` `2003` `2004` `2005` `2006`
## <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 ABW SP.URB.TOTL 42444 4.30e4 4.37e4 4.42e4 4.47e+4 4.49e+4 4.49e+4
## 2 ABW SP.URB.GROW 1.18 1.41e0 1.43e0 1.31e0 9.51e-1 4.91e-1 -1.78e-2
## 3 ABW SP.POP.TOTL 90853 9.29e4 9.50e4 9.70e4 9.87e+4 1.00e+5 1.01e+5
## 4 ABW SP.POP.GROW 2.06 2.23e0 2.23e0 2.11e0 1.76e+0 1.30e+0 7.98e-1
## 5 AFG SP.URB.TOTL 4436299 4.65e6 4.89e6 5.16e6 5.43e+6 5.69e+6 5.93e+6
## 6 AFG SP.URB.GROW 3.91 4.66e0 5.13e0 5.23e0 5.12e+0 4.77e+0 4.12e+0
## 7 AFG SP.POP.TOTL 20093756 2.10e7 2.20e7 2.31e7 2.41e+7 2.51e+7 2.59e+7
## 8 AFG SP.POP.GROW 3.49 4.25e0 4.72e0 4.82e0 4.47e+0 3.87e+0 3.23e+0
## 9 AGO SP.URB.TOTL 8234766 8.71e6 9.22e6 9.77e6 1.03e+7 1.09e+7 1.15e+7
## 10 AGO SP.URB.GROW 5.44 5.59e0 5.70e0 5.76e0 5.75e+0 5.69e+0 4.92e+0
## # ... with 1,046 more rows, and 11 more variables: 2007 <dbl>, 2008 <dbl>,
## # 2009 <dbl>, 2010 <dbl>, 2011 <dbl>, 2012 <dbl>, 2013 <dbl>, 2014 <dbl>,
## # 2015 <dbl>, 2016 <dbl>, 2017 <dbl>
首先这个数据很明显不是整洁数据,所以我们先把它变成整洁数据:
pop2 <- world_bank_pop %>%
pivot_longer(
cols = "2000":"2017", # 因为是字符型需要加引号
names_to = "year",
values_to = "value"
)
pop2
## # A tibble: 19,008 x 4
## country indicator year value
## <chr> <chr> <chr> <dbl>
## 1 ABW SP.URB.TOTL 2000 42444
## 2 ABW SP.URB.TOTL 2001 43048
## 3 ABW SP.URB.TOTL 2002 43670
## 4 ABW SP.URB.TOTL 2003 44246
## 5 ABW SP.URB.TOTL 2004 44669
## 6 ABW SP.URB.TOTL 2005 44889
## 7 ABW SP.URB.TOTL 2006 44881
## 8 ABW SP.URB.TOTL 2007 44686
## 9 ABW SP.URB.TOTL 2008 44375
## 10 ABW SP.URB.TOTL 2009 44052
## # ... with 18,998 more rows
接下来看看indicator
这一列:
pop2 %>% count(indicator)
## # A tibble: 4 x 2
## indicator n
## <chr> <int>
## 1 SP.POP.GROW 4752
## 2 SP.POP.TOTL 4752
## 3 SP.URB.GROW 4752
## 4 SP.URB.TOTL 4752
这里SP.POP.GROW
是人口增长量,SP.POP.TOTL
是人口总量,SP.URB*
也是一样的,不过是城市人口。很明显这一列包含了多个信息,下面我们把这一列拆分为多列:
pop3 <- pop2 %>%
separate(indicator,
c(NA,"area","variable"),
sep = "\\."
)
pop3
## # A tibble: 19,008 x 5
## country area variable year value
## <chr> <chr> <chr> <chr> <dbl>
## 1 ABW URB TOTL 2000 42444
## 2 ABW URB TOTL 2001 43048
## 3 ABW URB TOTL 2002 43670
## 4 ABW URB TOTL 2003 44246
## 5 ABW URB TOTL 2004 44669
## 6 ABW URB TOTL 2005 44889
## 7 ABW URB TOTL 2006 44881
## 8 ABW URB TOTL 2007 44686
## 9 ABW URB TOTL 2008 44375
## 10 ABW URB TOTL 2009 44052
## # ... with 18,998 more rows
这样一个数据还不是很直观,我们可以把variable
这一列“变宽”:
pop3 %>%
pivot_wider(
names_from = variable,
values_from = value
)
## # A tibble: 9,504 x 5
## country area year TOTL GROW
## <chr> <chr> <chr> <dbl> <dbl>
## 1 ABW URB 2000 42444 1.18
## 2 ABW URB 2001 43048 1.41
## 3 ABW URB 2002 43670 1.43
## 4 ABW URB 2003 44246 1.31
## 5 ABW URB 2004 44669 0.951
## 6 ABW URB 2005 44889 0.491
## 7 ABW URB 2006 44881 -0.0178
## 8 ABW URB 2007 44686 -0.435
## 9 ABW URB 2008 44375 -0.698
## 10 ABW URB 2009 44052 -0.731
## # ... with 9,494 more rows
这样的数据看起来是不是更加一目了然呢?
问卷调查数据
在临床中常用到问卷调查,每一个问题都有1个或多个选择(比如ABCD)等,这样的数据都会变得非常宽,类似这样:
multi <- tribble(
~id, ~q1, ~q2, ~q3,
1, "A", "B", "C",
2, "C", "B", NA,
3, "D", NA, NA,
4, "B", "D", NA
)
multi
## # A tibble: 4 x 4
## id q1 q2 q3
## <dbl> <chr> <chr> <chr>
## 1 1 A B C
## 2 2 C B <NA>
## 3 3 D <NA> <NA>
## 4 4 B D <NA>
这样的数据id
就是id,没啥实际意义,q1/q2/q3
代表不同的问题,比如上面的数据表示第一个问题选择了ABCD四个选项。
现在我们想把ABCD变成单独的4列,然后每一个问题是一行,有没有选择用TRUE和FALSE表示。
可以通过2步实现:
multi2 <- multi %>%
pivot_longer(
cols = !id,
names_to = "choice", # 这种情况下这个和下面那个参数可以省略
values_to = "value", # 可省略
values_drop_na = T
) %>%
mutate(checked = TRUE)
multi2
## # A tibble: 8 x 4
## id choice value checked
## <dbl> <chr> <chr> <lgl>
## 1 1 q1 A TRUE
## 2 1 q2 B TRUE
## 3 1 q3 C TRUE
## 4 2 q1 C TRUE
## 5 2 q2 B TRUE
## 6 3 q1 D TRUE
## 7 4 q1 B TRUE
## 8 4 q2 D TRUE
接下来再变成宽数据,并且把缺失值用FALSE代表:
multi2 %>%
pivot_wider(
id_cols = id,
names_from = value,
values_from = checked,
values_fill = FALSE
)
## # A tibble: 4 x 5
## id A B C D
## <dbl> <lgl> <lgl> <lgl> <lgl>
## 1 1 TRUE TRUE TRUE FALSE
## 2 2 FALSE TRUE TRUE FALSE
## 3 3 FALSE FALSE FALSE TRUE
## 4 4 FALSE TRUE FALSE TRUE
这样就达到目的了,每一行是一个问题,列是不同的选项,如果选了就是TRUE,如果没选就是FALSE,很好看。
手动操作
长宽数据转换通过之前的示例讲解,基本覆盖了大多数情况,但是复杂的日常生活总是有很多意想不到的场景需求。因此作者开发了更为强大和自由的手动操作过程。
基本理念就是先创建一个类似模板的东西,然后根据这个模板进行转换。
变长
还是以relig_income
这个数据集为例。
relig_income
## # A tibble: 18 x 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 k~ 15 14 15 11 10 35 21
## 6 Evangel~ 575 869 1064 982 881 1486 949
## 7 Hindu 1 9 7 9 11 34 47
## 8 Histori~ 228 244 236 238 197 223 131
## 9 Jehovah~ 20 27 24 24 21 30 15
## 10 Jewish 19 19 25 25 30 95 69
## 11 Mainlin~ 289 495 619 655 651 1107 939
## 12 Mormon 29 40 48 51 56 112 85
## 13 Muslim 6 7 9 10 9 23 16
## 14 Orthodox 13 17 23 32 32 47 38
## 15 Other C~ 9 7 11 13 13 14 18
## 16 Other F~ 20 33 40 46 49 63 46
## 17 Other W~ 5 2 3 4 2 7 3
## 18 Unaffil~ 217 299 374 365 341 528 407
## # ... with 3 more variables: $100-150k <dbl>, >150k <dbl>,
## # Don't know/refused <dbl>
现在我们像把这个数据变为长数据,我们可以先建立一个模板,语法并没有什么不同:
spec <- relig_income %>%
build_longer_spec(
cols = !religion,
names_to = "income",
values_to = "count"
) # 建立模板
spec
## # A tibble: 10 x 3
## .name .value income
## <chr> <chr> <chr>
## 1 <$10k count <$10k
## 2 $10-20k count $10-20k
## 3 $20-30k count $20-30k
## 4 $30-40k count $30-40k
## 5 $40-50k count $40-50k
## 6 $50-75k count $50-75k
## 7 $75-100k count $75-100k
## 8 $100-150k count $100-150k
## 9 >150k count >150k
## 10 Don't know/refused count Don't know/refused
可以看到多了.name
和.value
列,这就是模板的作用,可以用在接下来的转换中。
# 使用模板进行转换
pivot_longer_spec(relig_income, spec)
## # A tibble: 180 x 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
上面的2步其实和正常的1步效果一样,只是为了演示模板的基础用法,在实际情况中大家可以自己设定更为精确的模板。
relig_income %>%
pivot_longer(
cols = !religion,
names_to = "income",
values_to = "count"
)
## # A tibble: 180 x 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
变宽
us_rent_income
## # A tibble: 104 x 5
## GEOID NAME variable estimate moe
## <chr> <chr> <chr> <dbl> <dbl>
## 1 01 Alabama income 24476 136
## 2 01 Alabama rent 747 3
## 3 02 Alaska income 32940 508
## 4 02 Alaska rent 1200 13
## 5 04 Arizona income 27517 148
## 6 04 Arizona rent 972 4
## 7 05 Arkansas income 23789 165
## 8 05 Arkansas rent 709 5
## 9 06 California income 29454 109
## 10 06 California rent 1358 3
## # ... with 94 more rows
# 简单的变宽
us_rent_income %>%
pivot_wider(
names_from = variable,
values_from = c(estimate, moe)
)
## # A tibble: 52 x 6
## GEOID NAME estimate_income estimate_rent moe_income moe_rent
## <chr> <chr> <dbl> <dbl> <dbl> <dbl>
## 1 01 Alabama 24476 747 136 3
## 2 02 Alaska 32940 1200 508 13
## 3 04 Arizona 27517 972 148 4
## 4 05 Arkansas 23789 709 165 5
## 5 06 California 29454 1358 109 3
## 6 08 Colorado 32401 1125 109 5
## 7 09 Connecticut 35326 1123 195 5
## 8 10 Delaware 31560 1076 247 10
## 9 11 District of Columbia 43198 1424 681 17
## 10 12 Florida 25952 1077 70 3
## # ... with 42 more rows
现在你不太喜欢这个列名,你想要income_moe/rent_moe
这样的列名,这可以通过创建模板来实现。
# 创建模板
spec1 <- us_rent_income %>%
build_wider_spec(names_from = variable, values_from = c(estimate, moe))
spec1
## # A tibble: 4 x 3
## .name .value variable
## <chr> <chr> <chr>
## 1 estimate_income estimate income
## 2 estimate_rent estimate rent
## 3 moe_income moe income
## 4 moe_rent moe rent
可以发现这个原始模板不是我们想要的,稍作修改:
spec2 <- spec1 %>%
mutate(.name = paste0(variable, ifelse(.value == "moe", "_moe", "")))
spec2
## # A tibble: 4 x 3
## .name .value variable
## <chr> <chr> <chr>
## 1 income estimate income
## 2 rent estimate rent
## 3 income_moe moe income
## 4 rent_moe moe rent
现在列名终于是我们想要的了,接下来就可以以此为模板进行转换了。
us_rent_income %>%
pivot_wider_spec(spec = spec2)
## # A tibble: 52 x 6
## GEOID NAME income rent income_moe rent_moe
## <chr> <chr> <dbl> <dbl> <dbl> <dbl>
## 1 01 Alabama 24476 747 136 3
## 2 02 Alaska 32940 1200 508 13
## 3 04 Arizona 27517 972 148 4
## 4 05 Arkansas 23789 709 165 5
## 5 06 California 29454 1358 109 3
## 6 08 Colorado 32401 1125 109 5
## 7 09 Connecticut 35326 1123 195 5
## 8 10 Delaware 31560 1076 247 10
## 9 11 District of Columbia 43198 1424 681 17
## 10 12 Florida 25952 1077 70 3
## # ... with 42 more rows
非常成功!
完全手动操作
有时候通过函数生成模板比较困难,还不如直接纯手动构建!
construction
## # A tibble: 9 x 9
## Year Month `1 unit` `2 to 4 units` `5 units or mor~ Northeast Midwest South
## <dbl> <chr> <dbl> <lgl> <dbl> <dbl> <dbl> <dbl>
## 1 2018 January 859 NA 348 114 169 596
## 2 2018 Februa~ 882 NA 400 138 160 655
## 3 2018 March 862 NA 356 150 154 595
## 4 2018 April 797 NA 447 144 196 613
## 5 2018 May 875 NA 364 90 169 673
## 6 2018 June 867 NA 342 76 170 610
## 7 2018 July 829 NA 360 108 183 594
## 8 2018 August 939 NA 286 90 205 649
## 9 2018 Septem~ 835 NA 304 117 175 560
## # ... with 1 more variable: West <dbl>
纯手动构建模板:
spec <- tribble(
~.name, ~.value, ~units, ~region,
"1 unit", "n", "1", NA,
"2 to 4 units", "n", "2-4", NA,
"5 units or more", "n", "5+", NA,
"Northeast", "n", NA, "Northeast",
"Midwest", "n", NA, "Midwest",
"South", "n", NA, "South",
"West", "n", NA, "West",
)
我们想要把上面这个数据集变长,下面就可以用模板了。
进行转换:
pivot_longer_spec(construction, spec)
## # A tibble: 63 x 5
## Year Month units region n
## <dbl> <chr> <chr> <chr> <dbl>
## 1 2018 January 1 <NA> 859
## 2 2018 January 2-4 <NA> NA
## 3 2018 January 5+ <NA> 348
## 4 2018 January <NA> Northeast 114
## 5 2018 January <NA> Midwest 169
## 6 2018 January <NA> South 596
## 7 2018 January <NA> West 339
## 8 2018 February 1 <NA> 882
## 9 2018 February 2-4 <NA> NA
## 10 2018 February 5+ <NA> 400
## # ... with 53 more rows
理念
使用spec
(模板)进行长宽转换是完全镜像的操作,只要用同一个spec
,结果是完全可以回复的。
# 和原数据一模一样
construction %>%
pivot_longer_spec(spec) %>%
pivot_wider_spec(spec)
## # A tibble: 9 x 9
## Year Month `1 unit` `2 to 4 units` `5 units or mor~ Northeast Midwest South
## <dbl> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 2018 January 859 NA 348 114 169 596
## 2 2018 Februa~ 882 NA 400 138 160 655
## 3 2018 March 862 NA 356 150 154 595
## 4 2018 April 797 NA 447 144 196 613
## 5 2018 May 875 NA 364 90 169 673
## 6 2018 June 867 NA 342 76 170 610
## 7 2018 July 829 NA 360 108 183 594
## 8 2018 August 939 NA 286 90 205 649
## 9 2018 Septem~ 835 NA 304 117 175 560
## # ... with 1 more variable: West <dbl>
使用模板可以帮我们精确控制数据转换的过程,在宽数据变为长数据后,将会有:nrow(df) * nrow(spec) rows, and ncol(df) - nrow(spec) + ncol(spec) - 2 列。
以上就是今天的内容,希望对你有帮助哦!欢迎点赞、在看、关注、转发!
欢迎在评论区留言或直接添加我的微信!
欢迎关注公众号:医学和生信笔记
“医学和生信笔记 公众号主要分享:1.医学小知识、肛肠科小知识;2.R语言和Python相关的数据分析、可视化、机器学习等;3.生物信息学学习资料和自己的学习笔记!
往期回顾
2022-03-13
2022-03-14
2022-03-02
2022-03-03
2022-02-28