遍历data.table并根据某些条件创建新列

use*_*412 5 r data.table

我有一个有很多列的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)

可以帮助简化这段代码,以便

  1. 我不必为异常值编写单独的代码行。在这个例子中,我们只有8个变量,但是如果我们有100个变量,最终会写100行代码吗?可以使用for循环吗?怎么样?

  2. 通常,对于data.table,我们如何添加保留原始列的新列。因此,例如在下面的示例中,我记录第3列到第10列。如果不创建新的DTlog,它将覆盖DT中的原始列。如何在DT中保留原始列并在DT中保留新列。

    DTlog <- DT[,(lapply(.SD,log)),by = .(town,tc),.SDcols=3:10]

期待一些专家的建议。

akr*_*run 5

我们可以使用 来做到这一点:=。我们对不是分组变量(“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 forset这会更有效)

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)