最后一个下划线后面的字符串分离

4
这确实是与此问题r-split-string-using-tidyrseparate重复的内容,但我无法将其用于我的目的,因为我不知道如何调整正则表达式。基本上,我想要同样的东西,但是在最后一个下划线之后拆分变量。
原因:我的数据中有些列对于同一因子/类型出现多次。我想到了可以熔化数据,在类型字符串之前单独分离值变量,然后再将其展开为宽格式,减少列数。我的问题是我的变量名有不同数量的下划线,我想学习如何在最后一个下划线之后分隔,而我事先添加了它。
MWE
library(tidyr)
library(data.table)
dt<-data.table(Name=c("A","B","C"),Var_1_EVU=c(2,NA,NA),Var_1_BdS=c(NA,3,4),Var_2_BdS=c(NA,3,4))
dt.long<-melt(dt, id.vars=c("Name"))
dt.long<-separate(dt.long,variable, c("test","type"), sep='/[^_]*$/')
dt.wide<-spread(dt.long,key=Name,value=value) 

我想要类似的东西

   Name type Var1 Var2
1:    A  BdS   NA   NA
2:    A  EVU    2   NA
3:    B  BdS    3    3
4:    B  EVU   NA   NA
5:    C  BdS    4    4
6:    C  EVU   NA   NA

目前,我采用“愚蠢”的解决方案,并使用“___”将类型添加到我的变量中,我确信它不会出现在我的数据名称中。但更好地理解正则表达式会更好。 - Max M
2个回答

5
library(tidyr)

df <- data.frame(Name = c("A","B","C"),
                 Var_1_EVU = c(2,NA,NA),
                 Var_1_BdS = c(NA,3,4),
                 Var_2_BdS = c(NA,3,4))

df %>% 
  gather("type", "value", -Name) %>% 
  separate(type, into = c("type", "type_num", "var")) %>% 
  unite(type, type, type_num, sep = "") %>% 
  spread(type, value)

#   Name var Var1 Var2
# 1    A BdS   NA   NA
# 2    A EVU    2   NA
# 3    B BdS    3    3
# 4    B EVU   NA   NA
# 5    C BdS    4    4
# 6    C EVU   NA   NA

以下是使用 tidyr::extract 处理具有任意数量下划线的变量名的示例...

library(dplyr)
library(tidyr)

df <- data.frame(Name = c("A","B","C"),
                 Var_x_1_EVU = c(2,NA,NA),
                 Var_x_1_BdS = c(NA,3,4),
                 Var_x_y_2_BdS = c(NA,3,4))

df %>% 
  gather("col_name", "value", -Name) %>% 
  extract(col_name, c("var", "type"), "(.*)_(.*)") %>% 
  spread(var, value)

#   Name type Var_x_1 Var_x_y_2
# 1    A  BdS      NA        NA
# 2    A  EVU       2        NA
# 3    B  BdS       3         3
# 4    B  EVU      NA        NA
# 5    C  BdS       4         4
# 6    C  EVU      NA        NA

您可以通过首先使用 mutate(n = row_number()) 添加行号列/变量使每个观测值独一无二,从而避免重复观测可能带来的问题,您可以通过显式地调用 tidyr::extract 来避免 tidyr::extractmagrittr 掩盖...

library(dplyr)
library(tidyr)
library(data.table)
library(magrittr)

dt <- data.table(Name = c("A", "A", "B", "C"),
                 Var_1_EVU = c(1, 2, NA, NA),
                 Var_1_BdS = c(1, NA, 3, 4),
                 Var_x_2_BdS = c(1, NA, 3, 4))

dt %>% 
  mutate(n = row_number()) %>% 
  gather("col_name", "value", -n, -Name) %>% 
  tidyr::extract(col_name, c("var", "type"), "(.*)_(.*)") %>% 
  spread(var, value)

#   Name n type Var_1 Var_x_2
# 1    A 1  BdS     1       1
# 2    A 1  EVU     1      NA
# 3    A 2  BdS    NA      NA
# 4    A 2  EVU     2      NA
# 5    B 3  BdS     3       3
# 6    B 3  EVU    NA      NA
# 7    C 4  BdS     4       4
# 8    C 4  EVU    NA      NA

这仅适用于我的 MWE。我的真实数据具有不同的名称,包括数字和下划线等。这就是为什么我想使用正则表达式的原因,因为它可以确保在我事先添加的最后一个下划线之后进行分隔。 - Max M
1
我添加了另一个使用 tidyr::extract 的示例,可以处理具有任意数量下划线的变量名。 - CJ Yetman
谢谢,我会在示例中将Var_x_2_BdS调整为Var_x_1_BdS,因为我的目的是获得Var_x_1的一个变量。老实说,我不太理解正则表达式的工作原理,但我想我需要努力学习。 - Max M
完成。第一个(.*)捕获下划线之前的任何内容(包括其他下划线)。 第二个(.*)捕获其后的任何内容。 - CJ Yetman

4

以下是使用 tstrsplit/melt/dcast 的另一种 data.table 解决方案。

个人认为在这种情况下应该坚持使用 data.table,因为 spread 没有 fun 参数,因此,如果再次扩展时存在重复,将会出现错误。

library(magrittr) # people like pipes these days
dt %>%
  # convert ot long format like you did
  melt(., id = "Name") %>% 
  # split by the last underscore
  .[, c("variable", "grp") := tstrsplit(variable, "_(?!.*_)", perl = TRUE)] %>% 
  # convert back to wide format
  dcast(., Name + grp ~ variable) 

#    Name grp Var_1 Var_2
# 1:    A BdS    NA    NA
# 2:    A EVU     2    NA
# 3:    B BdS     3     3
# 4:    B EVU    NA    NA
# 5:    C BdS     4     4
# 6:    C EVU    NA    NA

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接