重塑数据:提取“一些”行并将它们转换为 R 中的新列

时间:2021-03-13 23:03:22

标签: r reshape

(已编辑)我有一个很长的数据集,其中包含多列长格式。以下是数据示例:

Groups duration response  value      trial
------ -----    -------- --------- --------
C       525      ID       5578        ID
C       525      1-1      676|342     C3
C       525      1-2      676|342     C3
C       525      1-3      676|342     C3
C       525      1-4      676|342     C3
C       525      1-5      676|342     C3
C       521      ID       6331        ID
C       521      1-1      643|461     C3

在此数据框中,每个参与者的 ID 与响应和值位于同一列。我需要的是将对应于“ID”的行放入一个单独的重复测量列中,使其看起来像这样:

Groups duration  ID     response   value      trial
------ -----   ------   --------  --------- --------
C       525     5578       1-1      676|342    C3 
C       525     5578       1-2      676|342    C3
C       525     5578       1-3      676|342    C3
C       525     5578       1-4      676|342    C3
C       525     5578       1-5      676|342    C3
C       525     5578       1-6      676|342    C3
C       521     6331       1-1      643|461    C3
C       521     6331       1-2      643|461    C3
C       521     6331       1-3      643|461    C3
C       521     6331       1-4      643|461    C3
C       521     6331       1-5      643|461    C3
C       521     6331       1-6      643|461    C3

我最初的尝试是将数据框转换为宽格式,以便 ID 和其他响应各有自己的列,然后再次将其设为长,但仅适用于示例中的 1-1 到 1-6 列,使用此代码:

df <- spread(df, response, value)

#fill in the whole column with corresponding values
df<-fill(df, ID, .direction="down")

df <- gather(df, name, coordinates, 9:1417, factor_key=TRUE)


第一部分有效。我得到一个带有 ID 列的数据框。但是,当我尝试将其他列转换回长格式时,出现错误:“错误:向量内存已耗尽(达到限制?)”

我想这是因为列太多了,我的记忆力受不了。 有没有更简单的方法来做到这一点?

3 个答案:

答案 0 :(得分:1)

一个选项是通过执行逻辑向量 (response == 'ID') 的累积和来基于 'ID' 的出现创建一个分组,然后创建 'ID' 列作为 first 中的元素'value',然后用 slice 删除第一行并删除 'grp' 列

library(dplyr)
df %>%
   group_by(grp = cumsum(response == 'ID'), Groups) %>%
   mutate(ID = first(value)) %>%
   slice(-1) %>%
   ungroup %>%
   select(-grp)

-输出

# A tibble: 6 x 6
#  Groups duration response value   trial ID   
#  <chr>     <int> <chr>    <chr>   <chr> <chr>
#1 C           525 1-1      676|342 C3    5578 
#2 C           525 1-2      676|342 C3    5578 
#3 C           525 1-3      676|342 C3    5578 
#4 C           525 1-4      676|342 C3    5578 
#5 C           525 1-5      676|342 C3    5578 
#6 C           521 1-1      643|461 C3    6331 

如果我们需要通过将“响应”从“1-1”变为“1-6”来扩展行

library(stringr)
library(tidyr)
df %>%
    group_by(grp = cumsum(response == 'ID'), Groups) %>%
    mutate(ID = first(value)) %>%
    slice(-1) %>%
    group_by(Groups, duration, value, trial, ID, .add = TRUE) %>% 
    complete(response = str_c('1-', 1:6)) %>% 
    ungroup  %>%
    select(-grp)

-输出

# A tibble: 12 x 6
#   Groups duration value   trial ID    response
#   <chr>     <int> <chr>   <chr> <chr> <chr>   
# 1 C           525 676|342 C3    5578  1-1     
# 2 C           525 676|342 C3    5578  1-2     
# 3 C           525 676|342 C3    5578  1-3     
# 4 C           525 676|342 C3    5578  1-4     
# 5 C           525 676|342 C3    5578  1-5     
# 6 C           525 676|342 C3    5578  1-6     
# 7 C           521 643|461 C3    6331  1-1     
# 8 C           521 643|461 C3    6331  1-2     
# 9 C           521 643|461 C3    6331  1-3     
#10 C           521 643|461 C3    6331  1-4     
#11 C           521 643|461 C3    6331  1-5     
#12 C           521 643|461 C3    6331  1-6     

优点是我们不需要做任何整形,而是在相同的数据上创建列并在末尾删除一些行

数据

df <- structure(list(Groups = c("C", "C", "C", "C", "C", "C", "C", 
"C"), duration = c(525L, 525L, 525L, 525L, 525L, 525L, 521L, 
521L), response = c("ID", "1-1", "1-2", "1-3", "1-4", "1-5", 
"ID", "1-1"), value = c("5578", "676|342", "676|342", "676|342", 
"676|342", "676|342", "6331", "643|461"), trial = c("ID", "C3", 
"C3", "C3", "C3", "C3", "ID", "C3")), class = "data.frame", 
row.names = c(NA, 
-8L))

答案 1 :(得分:0)

使用 ID 创建一个新列 (value) 并使用 replace NA 创建一个新列 (response != 'ID'fill NA } 值并删除带有 response = 'ID' 的行。

library(dplyr)
library(tidyr)

df %>%
  mutate(ID  = replace(value, response != 'ID', NA)) %>%
  fill(ID) %>%
  filter(response != 'ID')

#  Groups duration response   value trial   ID
#1      C      525      1-1 676|342    C3 5578
#2      C      525      1-2 676|342    C3 5578
#3      C      525      1-3 676|342    C3 5578
#4      C      525      1-4 676|342    C3 5578
#5      C      525      1-5 676|342    C3 5578
#6      C      521      1-1 643|461    C3 6331

答案 2 :(得分:0)

基本的 R 方法可能是在 cumsum 上拆分,然后重新组合(并重新排列列以获得预期输出):

df <- split(df, cumsum(df$response =="ID"))
do.call(rbind, lapply(df, function(x) {
    y=x[-1,]; y$ID <- x$value[1]; y[, c(1:2,6,3:5)]}))