5

大多数专业 R 用户都建议我不要在 R 中使用循环。改用应用函数。问题是,如果您不熟悉函数式编程,那么为每个 for/while 循环编写应用等效项并不是那么直观。以下面的例子为例。

F <- data.frame(name = c("a", "b", "c", "d"), var1 = c(1,0,0,1), var2 = c(0,0,1,1),  
var3 = c(1,1,1,1), clus = c("one", "two", "three", "four"))
F$ObjTrim <- ""
for (i in 1:nrow(F))
{
 for (j in 2:(ncol(F)-1))
{
 if(F[i, j] == 1) 
 {F$ObjTrim[i]  <- paste(F$ObjTrim[i], colnames(F)[j], sep = " ") }

 }
  print(i)
}

这里的目标是创建一个变量“ObjTrim”,它获取所有值 == 1 的列名的值。有人可以建议一个很好的应用程序吗?

例如,上面的代码将给出:

 name var1 var2 var3  clus         ObjTrim
1    a    1    0    1   one       var1 var3
2    b    0    0    1   two            var3
3    c    0    1    1 three       var2 var3
4    d    1    1    1  four  var1 var2 var3

谢谢!

4

3 回答 3

5

As your comment to @agstudy's answer says that you do want this for each row, maybe this helps you:

df <- F [, 2:4]
df
#   var1 var2 var3
# 1    1    0    1
# 2    0    0    1
# 3    0    1    1
# 4    1    1    1

ones <- which (df == 1, arr.ind=TRUE)
ones
#      row col
# [1,]   1   1
# [2,]   4   1
# [3,]   3   2
# [4,]   4   2
# [5,]   1   3
# [6,]   2   3
# [7,]   3   3
# [8,]   4   3

This you can aggregate by row:

aggregate (col ~ row, ones, paste)
#   row     col
# 1   1    1, 3
# 2   2       3
# 3   3    2, 3
# 4   4 1, 2, 3

If you insist on having the colnames instead of indices, replace the cols in ones first:

ones <- as.data.frame (ones) 
ones$col <- colnames (df)[ones$col]
aggregate (col ~ row, ones, paste)
#   row              col
# 1   1       var1, var3
# 2   2             var3
# 3   3       var2, var3
# 4   4 var1, var2, var3

Of course, you could also use apply along the rows:

apply (df, 1, function (x) paste (colnames (df) [x == 1], collapse = " "))
# [1] "var1 var3"       "var3"             "var2 var3"       "var1 var2 var3"

For your problem, vectorized functions exist so neither for loops nor apply are needed.

However, there are cases where for loops are the clearer (faster to read) and sometimes also the faster to compute alternative. This is particularly then the case when looping a few times allows to use vectorized functions and save applying some other function over a large margin.

于 2013-06-09T08:41:04.907 回答
5

在这里,您可以使用:来避免for循环,它是矢量化的,主要用于将矢量 c(TRUE,FALSE) 转换为 0 或 1。vectorizationcolSums

 colnames(F)[colSums(F==1) != 0] ## create 

这是使用我的可重现示例进行的测试:

set.seed(1234)
## create matrix 2*10
F <- matrix(sample(c(1:5),20,rep=TRUE),nrow=2,
            dimnames = list(c('row1','row2'),paste0('col',1:10)))

#        col1 col2 col3 col4 col5 col6 col7 col8 col9 col10
# row1    1    4    5    1    4    4    2    2    2     1
# row2    4    4    4    2    3    3    5    5    2     2
colnames(F)[colSums(F==1) != 0]
"col1"  "col4"  "col10"

PS:通常很容易用for“R风格的解决方案”替换循环,但在某些情况下,特别是在递归时很难/不可能做到这一点。

编辑

在OP的澄清之后,这是一个apply解决方案:

F$ObjTrim <- apply(F,1,function(x) paste(colnames(F)[x==1],collapse=' '))

 name var1 var2 var3  clus        ObjTrim
1    a    1    0    1   one      var1 var3
2    b    0    0    1   two           var3
3    c    0    1    1 three      var2 var3
4    d    1    1    1  four var1 var2 var3
于 2013-06-09T08:08:35.643 回答
4

要回答似乎是您的通用问题而不是您引用的示例 --- 如何将 for 循环转换为 apply 变体 --- 以下可能是一些有用的指针:

  1. 考虑您正在迭代的对象的结构。可能有不同的类型,例如:

    a) 向量/矩阵的元素。b) 矩阵的行/列。c) 更高维数组的维度。d) 列表的元素(其本身可能是上述对象之一)。e) 多个列表/向量的对应元素。

    在每种情况下,您使用的功能可能略有不同,但使用的策略是相同的。此外,学习应用家庭。各种 *pply 函数基于类似的抽象,但它们作为输入的内容和作为输出的内容有所不同。

  2. 例如,在上面的案例列表中。

    a) 向量的元素:寻找已经存在的向量化解决方案(如上所示),这是 R 的核心优势。除此之外,还要考虑矩阵代数。大多数似乎需要循环(或嵌套循环)的问题都可以写成矩阵代数中的方程。

    b) 矩阵的行/列:使用apply. 为参数使用正确的值MARGIN。c) 与更高维数组类似。

    d) 使用lapply. 如果您返回的输出是“简单”结构(标量或向量),您可以考虑 sapply ,它很简单simplify2array(lapply(...))并返回适当维度的数组。

    e) 使用mapply。'm' 可以代表多变量应用。

  3. 一旦你了解了你正在迭代的对象和相应的工具,就可以简化你的问题。不要想着你正在迭代的整个对象,而是它的一个实例。例如,当迭代矩阵的行时,忘记矩阵并只记住行。

    现在,编写一个函数(或 lambda),它只对你的 iterand 的一个实例(元素)进行操作,并使用 *pply 系列的正确成员简单地“应用”它。

现在,让我们以您的示例问题来使用此策略并复制@agstudy 给出的干净解决方案。

  1. 首先要确定的是您正在迭代矩阵的行。显然,您理解这一点,因为您的循环解决方案以for (i in 1:nrow(F)).

  2. 确定apply为您的朋友。

  3. 了解您需要对这一行做什么。首先,您要找出哪些值为 1。然后您需要找到这些值的列名。然后找到一种连接这些列名的方法。如果我可以冒昧地重写@agstudy 的解决方案来帮助解释:

    process.row <- function (arow) {
      ones <- arow == 1 # Returns logical vector.
      cnames <- colnames[ones] # Logical subsetting.
      cnames <- paste(cnames, collapse=' ') # Paste the names together.
      cnames # Return
    }
    

    你得到了解决方案:

    F$ObjTrim = apply(X=F, MARGIN=1, FUN=process.row)
    

    然后,当这样的想法变得本能时,您可以推出使用 R 编写密集表达式的能力,例如:

    F$ObjTrim = apply(F,1,function(x) paste(colnames(F)[x==1],collapse=' '))
    

它使用即时滚动的“lambda”来完成工作。

于 2013-06-09T13:31:40.133 回答