这是所有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
P.S。我确实找到了几个线程,这些线程从头开始收集多个变量,但是没有一个线程处理收集不同类的问题并描述了最新的Tidyverse解决方案。
答案 0 :(得分:0)
可能对于您想要的内容来说太重复了,但是当处理大量变量时,可以使用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
答案 1 :(得分:0)
(我基本上重写了我以前的所有答案,但保留了此帖子以保留评论。)
您可以使用一些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"))
关于保持因子水平,很遗憾,您需要在之后将其强制退回。围绕它的可能方法还有其他疑问; here's one。
还值得注意的是,tidyr
github在完成实现multi_gather
类型的功能的工作中备有多个issues备案,很可能适用于像您这样的用例。不确定是否可以涵盖因子转换。