【问题标题】:Tidy data with both multiple variables in column names and multiple observations per row整齐的数据,列名中有多个变量,每行有多个观察值
【发布时间】:2020-03-10 02:09:07
【问题描述】:

我有一个不整洁的数据集。

dat_untidy <- data.frame(AFDWSDF = c(10, 20),
                         AFDWSDFQUAL = c("a","b"),
                         TPFCE = c(1, 1.5),
                         TPFCEQUAL = c("c","d"))

变量名称包含 3 或 4 条信息,我想将它们分解并存储为单个变量:

dat_tidy <- data.frame(Var_X = c(rep("AFDW", 2),rep("TP", 2)),
                   Var_Y = c(rep("SD", 2), rep("FC", 2)),
                   Var_Z = c(rep("F", 2), rep("E", 2)),
                   Value = c(10, 20, 1, 1.5),
                   Qualifier = c("a","b","c","d"))

变量名称的格式为 xxxxyyz。 “xxxx”、“yy”和“z”都应该成为一个变量值。 'yy' 和 'z' 始终分别是 2 和 1 个字符,但 'xxxx' 可以是任意数量的字符。

最重要的是,在我的示例中,变量(大部分)成对出现,“xxxxyyz”与“xxxxyyzQUAL$”(基准值的限定符代码)相邻。 “...QUAL$”也需要收集到一个整洁的变量中。

vingette 中的 Anscombe 示例,感觉 pivot_longer() 可能是一个优雅的解决方案,但我需要帮助定义正则表达式模式。我怀疑将其分为两个步骤可能更简单,也许首先是 pivot_longer() 将变量名收集到所谓的 Var_XYZ 中,关联 ValueQualifier,然后是 mutate() 到分解Var_XYZ,但我也无法解决这个问题。

我是一个正则表达式的新手,并且最熟悉 dplyr 的操作。

提前谢谢你。

【问题讨论】:

  • 您是否能够使用 $ 登录列名创建第一个数据框。我认为 R 不会允许
  • 你是对的,我的错误是无法在列名中使用 $ 构建第一个数据框。这些字符包含在我从电子表格中读取的原始数据中。可以和这个问题分开处理,感谢编辑帖子。
  • 事实证明,我的真实数据中值和限定符列的不一致配对(我在我的问题中没有强调)打破了@Edward 和 Wimpel 原本不错的解决方案。

标签: r regex dplyr


【解决方案1】:

我使用了 stats 包中的 reshapetidyr 包中的 separate 的组合。

首先,将变量对存储到“Qualifiers”和“Vars”中。

Quals <- grep("QUAL$", names(dat_untidy), value=TRUE)
Vars <- sub("QUAL", "", Quals)

然后,重新整形为长格式并将Vars 分成_X_Y_Z 三列。

library(tidyr)

dat_tidy <- reshape(dat_untidy, direction="long", varying=list(Vars, Quals),
        v.names=c("Value","Qualifier"),
        timevar="Vars", times=Vars) %>%
  separate(col="Vars", into=c("Var_X","Var_Y","Var_Z"), 
           remove = TRUE, sep = -c(3,1)) %>%  # Reverse separator
  select(-id)

rownames(dat_tidy) <- NULL

dat_tidy

  Var_X Var_Y Var_Z Value Qualifier
1  AFDW    SD     F  10.0         a
2  AFDW    SD     F  20.0         b
3    TP    FC     E   1.0         c
4    TP    FC     E   1.5         d

【讨论】:

  • 优雅,谢谢。我通过将其分别应用于具有和不具有关联限定符列的列的选择来使其工作(修改 reshape()' call for the latter), and then bind_rows() 的参数生成的两个数据帧。
【解决方案2】:

这是一个应该可以工作的 data.table 解决方案...

library( data.table )
library( stringr )

#set untidy data to data.table format
setDT(dat_untidy)

#get names of columns not ending on QUAL
  cols <- names( dat_untidy )[!grepl( "QUAL$", names( dat_untidy ) ) ]
  #[1] "AFDWSDF" "TPFCE" 
  Var_X <- stringr::str_sub( cols,  1, -4 )
  #[1] "AFDW" "TP"
  Var_Y <- stringr::str_sub( cols, -3, -2 )
  #[1] "SD" "FC"
  Var_Z <- stringr::str_sub( cols, -1, -1 )  
  #[1] "F" "E"

#build regex patterns
  Var_X_pattern <- paste0( "(", paste0("^", Var_X, collapse = "|" ), ").*" )
  #[1] "(^AFDW|^TP).*"
  Var_Y_pattern <- paste0( ".*(", paste0(Var_Y, collapse = "|" ), 
                           ")(", paste0(Var_Z, "$", collapse = "|" ), ")" ) 
  #[1] ".*(SD|FC)(F$|E$)"
  Var_Z_pattern <- paste0( ".*(", paste0(Var_Z, "$", collapse = "|" ), ")" ) 
  #[1] ".*(F$|E$)"

#melt to long format (read, and then ignore, warning)
DT <- melt( dat_untidy, measure.vars = names(dat_untidy), variable.factor = FALSE )

#create columns
DT[, `:=`( Var_X = gsub( Var_X_pattern, "\\1", variable ),
           Var_Y = gsub( Var_Y_pattern, "\\1", variable ),
           Var_Z = gsub( Var_Z_pattern, "\\1", variable ) ) ][]

#create group_row_id
DT[, row_id := rowid( variable ) ][]
#create final output by joining, and then dropping the row_id column
DT[ !grepl("QUAL", variable ), .(Var_X, Var_Y, Var_Z, Value = value, row_id ) ][DT, Qualifier := i.value, on = .(Var_X, row_id) ][, row_id := NULL][]

输出

#    Var_X Var_Y Var_Z Value Qualifier
# 1:  AFDW    SD     F    10         a
# 2:  AFDW    SD     F    20         b
# 3:    TP    FC     E     1         c
# 4:    TP    FC     E   1.5         d

【讨论】:

  • 这会很好用,但我对 data.table 太不熟悉,无法修改它以处理我的真实数据,这比我的示例更复杂,结果证明这是一个重要的方式。
猜你喜欢
  • 1970-01-01
  • 2013-12-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-19
  • 2015-07-25
  • 1970-01-01
  • 2021-12-16
相关资源
最近更新 更多