根据现有列的子字符串在 R data.table 中创建列

Create columns in R data.table based on sub-strings of existing column

我正在尝试使用以下 R data.table 从“参考”字段中创建多个列:

library(data.table)
(dt= data.table(Ref = c("R", "STOP", "STOP_TS", "P", "M", "STOP_P_R"),
               Qty= c(2,4,6,8,10,12)))

新列应仅基于单个引用(例如“STOP”和“TS”)而不是组合引用(例如“STOP_TS”)。使用“_”标识单个引用后" 分隔符,新列应采用 "Qty" 字段的值,否则应为零。所需的输出应如下所示:

#Desired Output  
  (desired=data.table(
  Ref= c("R", "STOP", "STOP_TS", "P", "M", "STOP_P_R"),
  Qty= c(2,4,6,8,10,12),
  R =  c(2,0,0,0,0,12),
  STOP= c (0,4,6,0,0,12),
  TS= c(0,0,6,0,0,0),
  P= c(0,0,0,8,0,12),
  M=c(0,0,0,0,10,0))) 

我的方法存在的问题是正则表达式部分在查看“STOP”时错误地匹配了“P”,因为它没有指定匹配完整 'words'。

library(foreach)
library(data.table)
ref<-unlist(unique(dt$Ref)) #extract unique combined ref
ref2<-strsplit(ref, "_")    #split ref by using "_"
ref3<-unique(unlist(ref2))  #extract unique single ref (columns to create)

dt2<-foreach(i=1:length(ref3), .combine='cbind')%do%{
  eval(parse(text=paste0("tmp<-ifelse( grepl(ref3[i], dt$Ref), dt$Qty,0)")))
  data.table(tmp)
}
names(dt2)<-ref3
(dt3=cbind(dt,dt2))

作为一种检查方式,“P”列的总和应为 20(Ref="P" 为 8,Ref="STOP_P_R" 为 12)。

如有任何意见或建议,我将不胜感激。

dl

一个选项是使用 separate_rows 拆分列,然后使用 pivot_wider 将其重塑为宽格式,并使用 bind_cols

绑定原始数据集
library(dplyr)
library(tidyr)
dt %>% 
   mutate(rn = row_number()) %>% 
   separate_rows(Ref) %>% 
   pivot_wider(names_from = Ref, values_from = Qty, 
       values_fill = list(Qty = 0)) %>%
   select(-rn) %>%
   bind_cols(dt, .)
#        Ref Qty  R STOP TS  P  M
#1:        R   2  2    0  0  0  0
#2:     STOP   4  0    4  0  0  0
#3:  STOP_TS   6  0    6  6  0  0
#4:        P   8  0    0  0  8  0
#5:        M  10  0    0  0  0 10
#6: STOP_P_R  12 12   12  0 12  0

或使用 data.table

中的 dcast
library(splitstackshape)
library(data.table)
cbind(dt, dcast(cSplit(dt[, rn := seq_len(.N)], 'Ref', '_', "long"), 
      rn ~ Ref, value.var = 'Qty', fill = 0)[, rn := NULL])

我们可以使用 splitstackshape 中的 cSplit_e 来获取在 "_" 上分隔的每一行的二进制格式数据。然后我们可以用相应的 Qty 值替换所有的 1。

data <- data.frame(splitstackshape::cSplit_e(dt, "Ref", sep = "_", 
                   type = "character", fill = 0))
cols <- grep('Ref_', names(data))
mat <- which(data[cols] == 1, arr.ind = TRUE)
data[cols][mat] <- data$Qty[mat[, 1]]
data

#       Ref Qty Ref_M Ref_P Ref_R Ref_STOP Ref_TS
#1        R   2     0     0     2        0      0
#2     STOP   4     0     0     0        4      0
#3  STOP_TS   6     0     0     0        6      6
#4        P   8     0     8     0        0      0
#5        M  10    10     0     0        0      0
#6 STOP_P_R  12     0    12    12       12      0