这个 R 语法是如何工作的?通过 [apply() with match()] 在 for 循环中选择性地使用 gsub()

How does this R syntax work? Selective use of gsub() by [apply() with match()] inside a for loop

前段时间我从Whosebug改编了一些代码,但我一直无法找到原来的post。该代码适用于我的应用程序,但我并没有真正 'get' 语法。 (代码通过将数值数据值与 'new_labels' 向量中的相应值进行匹配,用特定字符串覆盖数据中的数值。匹配是特定于列的。)

参考下面的代码,有人可以解释一下 [apply] 子句是如何被用来指定 gsub() 在哪里进行替换的吗?或者换句话说,这种语法是如何工作的?为什么 apply 在这里影响 gsub() ?更好的是,是否有更简单的方法来执行此操作?

# reproducible example
# fake data           
    dat <- tibble(var1 = c(1,NA, 1,1,1,2,1,NA,1,1),
                  var2 = c(NA,1,NA,NA,NA,NA,NA,1,NA,NA),
                  var3 = c(2,2,NA,2,NA,1,2,NA,NA,NA),
                  var4 = c(NA,NA,2,NA,2,NA,NA,2,2,2)) 
# names of columns in fake data
variables <- names(dat)
# names of new labels to replace the numeric values
new_labels <- c("elf", "hobbit", "wizard", "ranger")      
# empty list
    llist <- list(ones = data.frame(matrix(ncol=4,nrow=10)),
                  twos = data.frame(matrix(ncol=4,nrow=10)))
    names(llist[[1]]) <- letters[1:length(new_labels)]
    names(llist[[2]]) <- letters[1:length(new_labels)]
# for loops
    for(j in 1:2){   
        for(i in 1:length(new_labels)){
            llist[[j]][, letters[i]] <- gsub(variables[i], new_labels[i], names(dat[,variables[i]]))[apply(dat[,variables[i]], 1, match, x= j)]
    }
        llist[[j]][is.na(llist[[j]])] <- ""
}

让我们把它从循环中拉出来,看看到底发生了什么。

gsub(variables[1], new_labels[1], names(dat[,variables[1]]))[apply(dat[,variables[1]], 1, match, x = 1)]
[1] "elf" NA    "elf" "elf" "elf" NA    "elf" NA    "elf" "elf"

查看 ?apply 的帮助:apply(X, MARGIN, FUN, ...),其中 x 是我们首先循环到的 dat 的列,1 是边距,?match 是它应用的函数,x = jmatch 循环的一部分,在 12 之间(来自 for(j in 1:2)).例如,如果它找到 1 的匹配项,则替换为 1.

apply(dat[,variables[1]], 1, match, x = 1)
 [1]  1 NA  1  1  1 NA  1 NA  1  1

并且 gsub 将第一个 new_labels 名称 (elf) 替换为第一个 variables 名称 (var1),并将其写入匹配元素letters[i]letters[1]a)。因此它在匹配 a.

的列中找到 llist 的第一个 list 元素(名为 ones

它为每个标签的所有 1 值重复此操作,然后为每个列表元素重复此操作,然后为所有 2 值重复此操作。第一行变成第二行。

[1]  1    NA   1     1     1    NA     1    NA     1     1
[1] "elf" NA  "elf" "elf" "elf" NA    "elf" NA    "elf" "elf"

这是一个相当优雅的过程。

这是一个没有循环的分步说明,因此您可以从本质上了解循环的作用。

dat2 <- as.data.frame(dat)
names(dat2) <- new_labels
dat2 <- list(dat2, dat2)
dat2[[1]][dat2[[1]] == 2] <- NA
dat2[[2]][dat2[[2]] == 1] <- NA
w1 <- which(dat2[[1]] == TRUE, arr.ind = TRUE)
w2 <- which(dat2[[2]] == 2, arr.ind = TRUE)
dat2[[1]][w1] <- colnames(dat2[[1]])[w1[,"col"]]
dat2[[2]][w2] <- colnames(dat2[[2]])[w2[,"col"]]
dat2 <- lapply(dat2, function(x) { x[is.na(x)] <- "" ; x})

[[1]]
   elf hobbit wizard ranger
1  elf                     
2      hobbit              
3  elf                     
4  elf                     
5  elf                     
6             wizard       
7  elf                     
8      hobbit              
9  elf                     
10 elf                     

[[2]]
   elf hobbit wizard ranger
1             wizard       
2             wizard       
3                    ranger
4             wizard       
5                    ranger
6  elf                     
7             wizard       
8                    ranger
9                    ranger
10                   ranger

这里有很多嵌套,但是要理解功能,最好的方法是运行控制台中嵌套最多的部分并向外工作。

假设 i = 1 且 j = 1

apply(dat[,variables[i]], 1, match, x= j) apply 正在输入数据的子集,即 "var1" 列。然后将匹配应用于该列的每个索引,return如果索引中的值与 x 参数匹配则为 1,否则 returns NA。

这个向量,我们称之为 vec,然后传递给 names(dat[,variables[i]]))[vec]

names(dat[,variables[i]]) 似乎只是 return variable[i] 所以在这个例子中看起来有点毫无意义。

names(dat[,variables[i]]))[vec]variables[i]

中的值替换 vec 中的每个值 1

最后,在gsub(pattern = variables[i], new_labels[i], new.vec) 其中 new.vecnames(dat[,variables[i]]))[vec]