在 R 中将凌乱的宽数据转换为长数据

transforming messy wide data to long in R

我是 R 的新手,我在尝试将我的 df 从宽转换为长时遇到了一些问题。目前看起来如下:

ppt_num w.1.rt w.1.vrt w.2.rt w.2.vrt n.1.rt n.1.vrt n.2.rt n.2.vrt
1 0.90 2.00 1.25 2.05 1.01 1.85 2.06 1.76
2 1.02 2.04 3.02 2.45 1.07 1.95 2.54 1.60

有两个实验条件和两个难度级别。对于实验条件,“w”代表 want 启动,“n”代表 need 启动。对于难度级别,1 表示困难的试验,2 表示简单的试验。最后,rt(反应时间)和 vrt(反应时间方差)是我感兴趣的测量值。我正在尝试重塑我的数据,使其如下所示:

ppt_num type difficulty rt vrt
1 w 1 0.90 2.00
1 w 2 1.25 2.05
1 n 1 1.01 1.85
2 n 2 2.06 1.76
2 w 1 1.02 2.04
2 w 2 3.02 2.45
2 n 1 1.07 1.95
2 n 2 2.54 1.60

到目前为止,我已经尝试使用 melt() 但这并没有产生预期的结果。

new_df <- melt(df, id.vars = c("ppt_num"))

如有任何关于此处采用的方法的建议,我们将不胜感激。

这里有一个带有 pivot_longer 的选项,我们指定 names_sep 来匹配列名中数字后的 .,然后 separate 'grp' 列变成 'type' 和 'difficulty'

library(dplyr)
library(tidyr)
df %>%
   pivot_longer(cols = -ppt_num, names_to = c('grp', '.value'), 
      names_sep = '(?<=\d)\.') %>% 
   separate(grp, into = c('type', 'difficulty'))

-输出

# A tibble: 8 x 5
#  ppt_num type  difficulty    rt   vrt
#    <int> <chr> <chr>      <dbl> <dbl>
#1       1 w     1           0.9   2   
#2       1 w     2           1.25  2.05
#3       1 n     1           1.01  1.85
#4       1 n     2           2.06  1.76
#5       2 w     1           1.02  2.04
#6       2 w     2           3.02  2.45
#7       2 n     1           1.07  1.95
#8       2 n     2           2.54  1.6 

names_sep中使用的模式匹配..是正则表达式中的一个元字符,可以匹配任何字符,所以我们转义得到字面值),后面是一个数字((?<=\d) - 匹配数字的正则表达式查找)在列名称

数据

df <- structure(list(ppt_num = 1:2, w.1.rt = c(0.9, 1.02), w.1.vrt = c(2, 
2.04), w.2.rt = c(1.25, 3.02), w.2.vrt = c(2.05, 2.45), n.1.rt = c(1.01, 
1.07), n.1.vrt = c(1.85, 1.95), n.2.rt = c(2.06, 2.54), n.2.vrt = c(1.76, 
1.6)), class = "data.frame", row.names = c(NA, -2L))

这是一个基本的 R 选项

reshape(
  reshape(
    setNames(df, sub("(.*)\.(.*)", "\2-\1", names(df))),
    direction = "long",
    idvar = "ppt_num",
    varying = -1,
    timevar = "type"
  ),
  direction = "long",
  idvar = c("ppt_num", "type"),
  varying = -(1:2),
  timevar = "difficulty",
  sep = "-"
)

这给出了

      ppt_num type difficulty   rt  vrt
1.1.w       1    1          w 0.90 2.00
2.1.w       2    1          w 1.02 2.04
1.2.w       1    2          w 1.25 2.05
2.2.w       2    2          w 3.02 2.45
1.1.n       1    1          n 1.01 1.85
2.1.n       2    1          n 1.07 1.95
1.2.n       1    2          n 2.06 1.76
2.2.n       2    2          n 2.54 1.60

这里我的版本使用data.table

library(data.table)
dt <- read.table(header = TRUE, text ="ppt_num  w.1.rt  w.1.vrt w.2.rt  w.2.vrt n.1.rt  n.1.vrt n.2.rt  n.2.vrt
1   0.90    2.00    1.25    2.05    1.01    1.85    2.06    1.76
2   1.02    2.04    3.02    2.45    1.07    1.95    2.54    1.60")
setDT(dt)
dt2 <- melt(dt,id.vars = "ppt_num") #First melt your initial data frame

dt2[,c("type","difficult", "varbl" ):= tstrsplit(variable, "\.")] #Split the new column "variable" containing your ex-column names 
dt2$variable<-NULL # I don't need column variable anymore
dcast(dt2,ppt_num+type+difficult~varbl,value.var = "value") #casting your result fixing ppt_num, type, and difficult but sending varbl to columns 

   ppt_num type difficult   rt  vrt
1:       1    n         1 1.01 1.85
2:       1    n         2 2.06 1.76
3:       1    w         1 0.90 2.00
4:       1    w         2 1.25 2.05
5:       2    n         1 1.07 1.95
6:       2    n         2 2.54 1.60
7:       2    w         1 1.02 2.04
8:       2    w         2 3.02 2.45

您只能使用 pivot_longer 作为 :

tidyr::pivot_longer(df, cols = -ppt_num, 
                    names_to = c('type', 'difficulty', '.value'), 
                    names_sep = '\.')

#  ppt_num type  difficulty    rt   vrt
#    <int> <chr> <chr>      <dbl> <dbl>
#1       1 w     1           0.9   2   
#2       1 w     2           1.25  2.05
#3       1 n     1           1.01  1.85
#4       1 n     2           2.06  1.76
#5       2 w     1           1.02  2.04
#6       2 w     2           3.02  2.45
#7       2 n     1           1.07  1.95
#8       2 n     2           2.54  1.6