逐行向多列赋值

3

问题陈述:基于多列的值生成虚拟变量。

根据出现在其他“多个列”中的列来分配值(更像是虚拟变量)。以下代码使用数据框。

解释:

  • V2列代表值2。如果变量A1或A4中有一个具有值2,则V2=1且V1、V3:V12=0。
  • 类似地,如果A1=1且A2=4,则V1=1、V4=1,且V2、V3、V5:V12=0。

给出了代码以说明所需输出。

set.seed(12345)
df<- data.frame(A1=c(1L,2L),A2=LETTERS[1:3],A3=round(rnorm(4),4),A4=1:12)
df
names= paste0("V",c(1:12))
df[,c(names)]=0
for ( i in 1:nrow(df)){ df[i,c(names)]=match(c(1:12),df[i,c("A1","A4")])}
df[,c(names)][!is.na(df[,c(names)])]=1
df[,c(names)][is.na(df[,c(names)])]=0
df

我希望您能提供有关使用数据表:=运算符的代码建议,以使流程更快。谢谢。


你可以使用以下代码代替许多行:cbind(df, +(sapply(1:12, function(i) i==df['A1']|i==df['A4']))) - akrun
谢谢akrun。如果列数很多,我想使用列范围(数字)或列名称的向量,而不是“A1”,“A4”,是否有其他替代方法? - DaleSteyn
如果有很多列,我们可以将它们放在列表中。 - akrun
+(Reduce('|', lapply(df[c(1,4)], function(x) sapply(1:12, '==', x)))) - akrun
1个回答

3
我们可以使用lapply循环遍历df的列'A1'和'A4',并使用sapply将它们与值1:12进行比较。使用Reduce|,将list输出合并为单个矩阵。 +用于将逻辑矩阵转换为二进制格式。在最后一步中,我们使用cbind与原始数据集结合。
cbind(df, +(Reduce('|', lapply(df[c(1,4)], function(x) sapply(1:12, '==', x)))))

另一个使用基本R而无需循环的选项是table。我们将感兴趣的列即'A1'、'A4'进行unlist,然后用1:12值获取table,对取出的矩阵进行双重否定(!!),将'0'值转为FALSE,其他值转为TRUE,使用+将逻辑矩阵强制转换为二进制1/0,再与原始数据集cbind

subDF <- df[c('A1', 'A4')]
newdf <- cbind(df, +(!!table(rep(1:12, ncol(subDF)), unlist(subDF))))
colnames(newdf)[5:ncol(newdf)] <- paste0('V', 1:12)
newdf
#    A1 A2      A3 A4 V1 V2 V3 V4 V5 V6 V7 V8 V9 V10 V11 V12
#1   1  A  0.5855  1  1  0  0  0  0  0  0  0  0   0   0   0
#2   2  B  0.7095  2  0  1  0  0  0  0  0  0  0   0   0   0
#3   1  C -0.1093  3  1  0  1  0  0  0  0  0  0   0   0   0
#4   2  A -0.4535  4  0  1  0  1  0  0  0  0  0   0   0   0
#5   1  B  0.5855  5  1  0  0  0  1  0  0  0  0   0   0   0
#6   2  C  0.7095  6  0  1  0  0  0  1  0  0  0   0   0   0
#7   1  A -0.1093  7  1  0  0  0  0  0  1  0  0   0   0   0
#8   2  B -0.4535  8  0  1  0  0  0  0  0  1  0   0   0   0
#9   1  C  0.5855  9  1  0  0  0  0  0  0  0  1   0   0   0
#10  2  A  0.7095 10  0  1  0  0  0  0  0  0  0   1   0   0
#11  1  B -0.1093 11  1  0  0  0  0  0  0  0  0   0   1   0
#12  2  C -0.4535 12  0  1  0  0  0  0  0  0  0   0   0   1

我们也可以使用data.table。不确定这是否非常高效,因为我们在data.table内部执行了table操作。该方法的步骤是先将“data.frame”转换为“data.table”(setDT(df)),然后在.SDcols中指定的列上unlist,获取行数的seq_len.N),例如示例中的1:12,通过rep将其复制到“nm1”的长度,并获取table

我们从table类创建一个data.tablesplit(tbl..),通过使用for循环遍历列,我们将值设置为二进制的0/1set方法非常高效,因为它避免了[.data.table的开销。稍后,我们可以与原始数据集cbind

library(data.table)
nm1 <- c('A1', 'A4')
tbl <- setDT(df)[, table(rep(seq_len(.N),length(nm1)), unlist(.SD)), .SDcols=nm1]

dt1 <- setDT(split(tbl, col(tbl)))[]
for(j in seq_along(dt1)) {
       set(dt1, i=NULL, j=j, value=+(!!dt1[[j]]))
}

cbind(df, dt1)

2
在我的大型数据集中,使用data.table内部的表格非常有效。只需在cbind之前引入setnames(dt1,names(dt1),colnames(tbl))即可。谢谢。 - DaleSteyn
2
我们可以使用 as.data.frame.matrix(dt1) 来保留表格的值。 - DaleSteyn

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