我有一个有很多列的data.table。我需要遍历它们并使用某种条件创建新列。目前,我正在为每列写单独的条件行。让我举例说明。让我们考虑一个样本数据为-
set.seed(71)
DT <- data.table(town = rep(c('A','B'), each=10),
tc = rep(c('C','D'), 10),
one = rnorm(20,1,1),
two = rnorm(20,2,1),
three = rnorm(20,3,1),
four = rnorm(20,4,1),
five = rnorm(20,5,2),
six = rnorm(20,6,2),
seven = rnorm(20,7,2),
total = rnorm(20,28,3))
Run Code Online (Sandbox Code Playgroud)
对于从一列到总数的每一列,我需要创建4个新列,即用于2 sigma离群值计算的均值,sd,上限,下限。我正在这样做-
DTnew <- DT[, as.list(unlist(lapply(.SD, function(x) list(mean = mean(x), sd = sd(x), uplimit = mean(x)+1.96*sd(x), lowlimit = mean(x)-1.96*sd(x))))), by = .(town,tc)]
Run Code Online (Sandbox Code Playgroud)
然后将这个DTnew data.table与DT合并
DTmerge <- merge(DT, DTnew, by= c('town','tc'))
Run Code Online (Sandbox Code Playgroud)
现在提出异常值,我为每个变量编写了单独的代码集-
DTAoutlier <- DTmerge[ ,one.Aoutlier := ifelse (one >= one.lowlimit & one <= one.uplimit,0,1)]
DTAoutlier <- DTmerge[ ,two.Aoutlier := ifelse (two >= two.lowlimit & two <= two.uplimit,0,1)]
DTAoutlier <- DTmerge[ ,three.Aoutlier := ifelse (three >= three.lowlimit & three <= three.uplimit,0,1)]
Run Code Online (Sandbox Code Playgroud)
可以帮助简化这段代码,以便
我不必为异常值编写单独的代码行。在这个例子中,我们只有8个变量,但是如果我们有100个变量,最终会写100行代码吗?可以使用for循环吗?怎么样?
通常,对于data.table,我们如何添加保留原始列的新列。因此,例如在下面的示例中,我记录第3列到第10列。如果不创建新的DTlog,它将覆盖DT中的原始列。如何在DT中保留原始列并在DT中保留新列。
DTlog <- DT[,(lapply(.SD,log)),by = .(town,tc),.SDcols=3:10]
期待一些专家的建议。
我们可以使用 来做到这一点:=。我们对不是分组变量(“nm”)的列名称进行子集化。使用('nm1')创建vector要为新列分配的名称。outer然后,我们使用 OP 的代码、unlist输出并将:=其分配 () 给“nm1”来创建新列。
nm <- names(DT)[-(1:2)]
nm1 <- c(t(outer(c("Mean", "SD", "uplimit", "lowlimit"), nm, paste, sep="_")))
DT[, (nm1):= unlist(lapply(.SD, function(x) { Mean = mean(x)
SD = sd(x)
uplimit = Mean + 1.96*SD
lowlimit = Mean - 1.96*SD
list(Mean, SD, uplimit, lowlimit) }), recursive=FALSE) ,
.(town, tc)]
Run Code Online (Sandbox Code Playgroud)
问题的第二部分涉及在列之间进行逻辑比较。一种选择是分别对初始列、“lowlimit”和“uplimit”列进行子集化,并进行比较(因为它们具有相同的维度)以获得逻辑输出,该逻辑输出可以使用 强制转换为二进制+。然后将其分配给原始数据集以创建离群值列。
m1 <- +(DT[, nm, with = FALSE] >= DT[, paste("lowlimit", nm, sep="_"),
with = FALSE] & DT[, nm, with = FALSE] <= DT[,
paste("uplimit", nm, sep="_"), with = FALSE])
DT[,paste(nm, "Aoutlier", sep=".") := as.data.frame(m1)]
Run Code Online (Sandbox Code Playgroud)
或者,我们也可以使用循环来代替比较 data.tables for(set这会更有效)
nm2 <- paste(nm, "Aoutlier", sep=".")
DT[, (nm2) := NA_integer_]
for(j in nm){
set(DT, i = NULL, j = paste(j, "Aoutlier", sep="."),
value = as.integer(DT[[j]] >= DT[[paste("lowlimit", j, sep="_")]] &
DT[[j]] <= DT[[paste("uplimit", j, sep="_")]]))
}
Run Code Online (Sandbox Code Playgroud)
“日志”列也可以通过以下方式创建:=
DT[,paste(nm, "log", sep=".") := lapply(.SD,log),by = .(town,tc),.SDcols=nm]
Run Code Online (Sandbox Code Playgroud)