[英]Gather multiple variables of different classes at the same time using Tidyverse
这是所有Tidyverse专家都面临的一个问题。 我有一个包含许多不同类(日期时间,整数,因子等)的数据集,并希望使用tidyr同时收集多个变量 。 在下面的可复制示例中,我想一次收集time_,factor_和integer_,而id和gender保持不变。
我正在寻找使用任何Tidyverse函数的当前最佳实践解决方案。
(我希望解决方案不是太“ hacky”,因为我有一个包含数十个不同关键变量和大约五十万行的数据集)。
示例数据:
library("tidyverse")
data <- tibble(
id = c(1, 2, 3),
gender = factor(c("Male", "Female", "Female")),
time1 = as.POSIXct(c("2014-03-03 20:19:42", "2014-03-03 21:53:17", "2014-02-21 12:13:06")),
time2 = as.POSIXct(c("2014-05-28 15:26:49 UTC", NA, "2014-05-24 10:53:01 UTC")),
time3 = as.POSIXct(c(NA, "2014-09-26 00:52:40 UTC", "2014-09-27 07:08:47 UTC")),
factor1 = factor(c("A", "B", "C")),
factor2 = factor(c("B", NA, "C")),
factor3 = factor(c(NA, "A", "B")),
integer1 = c(1, 3, 2),
integer2 = c(1, NA, 4),
integer3 = c(NA, 5, 2)
)
期望的结果:
# A tibble: 9 x 5
id gender Time Integer Factor
<dbl> <fct> <dttm> <dbl> <fct>
1 1 Male 2014-03-03 20:19:42 1 A
2 2 Female 2014-03-03 21:53:17 3 B
3 3 Female 2014-02-21 12:13:06 2 C
4 1 Male 2014-05-28 15:26:49 1 B
5 2 Female NA NA NA
6 3 Female 2014-05-24 10:53:01 4 C
7 1 Male NA NA NA
8 2 Female 2014-09-26 00:52:40 5 A
9 3 Female 2014-09-27 07:08:47 2 B
PS我确实找到了几个线程,这些线程从头开始收集多个变量,但是没有一个线程处理收集不同类的问题并描述了Tidyverse解决方案的当前状态。
可能对于您想要的内容来说太重复了,但是当处理大量变量时,可以使用mutate_at
在最后重新编码多个变量
一开始将它们全部更改为字符会保留time
数据,然后需要在结束时将其转换回日期时间
data %>%
mutate_all(funs(as.character)) %>%
gather(key = variable, value = value, -id, -gender, convert = T) %>%
mutate(wave = readr::parse_number(variable),
variable = gsub("\\d","", x = variable)) %>%
spread(variable, value, convert = T) %>%
mutate(time = as.POSIXct(time),
factor = factor(factor),
gender = factor(gender)) %>%
select(1, 2, 6, 5, 4)
# A tibble: 9 x 5
id gender time integer factor
<chr> <fct> <dttm> <int> <fct>
1 1 Male 2014-03-03 20:19:42 1 A
2 1 Male 2014-05-28 15:26:49 1 B
3 1 Male NA NA NA
4 2 Female 2014-03-03 21:53:17 3 B
5 2 Female NA NA NA
6 2 Female 2014-09-26 00:52:40 5 A
7 3 Female 2014-02-21 12:13:06 2 C
8 3 Female 2014-05-24 10:53:01 4 C
9 3 Female 2014-09-27 07:08:47 2 B
(我基本上重写了我以前的所有答案,但保留了此帖子以保留评论。)
您可以使用一些tidyselect
辅助函数,即starts_with
,选择要收集的一批列,然后删除多余的列。 这可以通过收集来处理(某些)数据类型的问题,因为您是一起收集同一类型的列集,但是由于收集时存在不同的因子级别,因此仍然需要将Factor
重新强制为一个因子(请参见警告消息)。
我很难理解的是,在保持ID和性别列的某种模式的同时,收集的列将如何“移动”。 进行一系列的gather
调用不会保持您想要的模式,但是您可以执行每个 gather
调用并将它们重新组合在一起。
这是一个:
library(tidyverse)
data %>%
select(id, gender, starts_with("time")) %>%
gather(key = key_time, value = Time, starts_with("time"))
#> # A tibble: 9 x 4
#> id gender key_time Time
#> <dbl> <fct> <chr> <dttm>
#> 1 1 Male time1 2014-03-03 20:19:42
#> 2 2 Female time1 2014-03-03 21:53:17
#> 3 3 Female time1 2014-02-21 12:13:06
#> 4 1 Male time2 2014-05-28 15:26:49
#> 5 2 Female time2 NA
#> 6 3 Female time2 2014-05-24 10:53:01
#> 7 1 Male time3 NA
#> 8 2 Female time3 2014-09-26 00:52:40
#> 9 3 Female time3 2014-09-27 07:08:47
要完成所有这些操作,您可以映射前缀-“时间”,“因数”和“整数”,并将它们归约连接在一起。 诀窍是您需要为每行添加一些唯一的标识符,以便正确连接。 为此,我添加了具有row_number
的列,将其用作连接列,然后将其删除。
map(c("time", "factor", "integer"), function(p) {
val_name <- str_to_title(p)
data %>%
select(id, gender, starts_with(p)) %>%
gather(key = key, value = !!val_name, starts_with(p)) %>%
select(-key) %>%
mutate(row = row_number())
}) %>%
reduce(left_join) %>%
select(-row)
#> Warning: attributes are not identical across measure variables;
#> they will be dropped
#> Joining, by = c("id", "gender", "row")
#> Joining, by = c("id", "gender", "row")
#> # A tibble: 9 x 5
#> id gender Time Factor Integer
#> <dbl> <fct> <dttm> <chr> <dbl>
#> 1 1 Male 2014-03-03 20:19:42 A 1
#> 2 2 Female 2014-03-03 21:53:17 B 3
#> 3 3 Female 2014-02-21 12:13:06 C 2
#> 4 1 Male 2014-05-28 15:26:49 B 1
#> 5 2 Female NA <NA> NA
#> 6 3 Female 2014-05-24 10:53:01 C 4
#> 7 1 Male NA <NA> NA
#> 8 2 Female 2014-09-26 00:52:40 A 5
#> 9 3 Female 2014-09-27 07:08:47 B 2
这有点丑陋,无法很好地适应正在进行的管道工作流程,但是您可以轻松地将其包装到函数中:
gather_by_prefix <- function(.data, prefix) {
map(prefix, function(p) {
val_name <- str_to_title(p)
data %>%
select(id, gender, starts_with(p)) %>%
gather(key = key, value = !!val_name, starts_with(p)) %>%
select(-key) %>%
mutate(row = row_number())
}) %>%
reduce(left_join) %>%
select(-row)
}
这样调用即可获得与上述相同的输出:
data %>%
gather_by_prefix(c("time", "factor", "integer"))
至于保持因子水平,我认为很遗憾,之后需要将其强制退回。 围绕它的可能方法还有其他疑问; 这是一个 。
值得注意的是, tidyr
github在实现multi_gather
函数类型的工作方面存在一些问题 ,很可能适用于像您这样的用例。 不知道这些内容是否涵盖了因子转换。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.