不明白R的应用功能

时间:2013-11-19 21:20:27

标签: r apply

我有一个数据框,其中一列代表一个数值,我想在数据框中添加一列,这是该列的离散化版本。这是一个可重复的例子:

# create example data
smallData <- data.frame(name = as.character(c("IC","IC","IC","IC","IC","BC","BC","BC","BC","BC")), 
                        value = as.integer(c(29,29,29,29,29,29,29,29,43,26)))

这会在这里创建一个小例子:

 smallData
   name value
1    IC    29
2    IC    29
3    IC    29
4    IC    29
5    IC    29
6    BC    29
7    BC    29
8    BC    29
9    BC    43
10   BC    26

现在,我想在数据框中添加一列,根据“值”列对行进行离散化处理:

# add new column to data frame
smallData$category <- ""
# define function to categorize data frame objects
categorize <- function(r)
{
  target <- r[c("value")]

  if(target < 27)
  {
    r[c("category")] <- "A"
  } else if(target < 30) {
    r[c("category")] <- "B"
  } else {
    r[c("category")] <- "C"
  }
  return(r)
}
# call to apply
smallData <- apply(smallData,1,categorize)
smallData

此代码的输出为:

> smallData
         [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
name     "IC" "IC" "IC" "IC" "IC" "BC" "BC" "BC" "BC" "BC" 
value    "29" "29" "29" "29" "29" "29" "29" "29" "43" "26" 
category "B"  "B"  "B"  "B"  "B"  "B"  "B"  "B"  "C"  "A"  

这是smallData的str()函数的输出:

> str(smallData)
 chr [1:3, 1:10] "IC" "29" "B" "IC" "29" "B" "IC" "29" "B" "IC" "29" "B" ...
 - attr(*, "dimnames")=List of 2
  ..$ : chr [1:3] "name" "value" "category"
  ..$ : NULL

我不熟悉这种数据类型。 smallData现在是列表,向量还是其他什么?我认为,因为apply()返回一个向量或数组,当我从smallData数据框中连续输入它时,它将以相同的数据格式返回结果。为什么不是这样?我也看过sapply()和lapply(),但它们似乎明确地返回一个列表,这似乎不是我想要的。

我似乎对apply()函数有误解。我认为它本质上是'for'循环的矢量化替换,但将简单的for循环转换为使用apply()并不像它应该的那样简单。

3 个答案:

答案 0 :(得分:3)

smallData[ ,"category"] <- c("A","B","C")[ 
                   findInterval(smallData[, "value"], c(-Inf,27,30, Inf)

使用cut的建议也是有道理的。我的偏好是使用来自pkg Hmisc的cut2。你还使用了几个ifelse作业。你得到一个矩阵(以及一个字符矩阵)的原因是apply总是返回一个矩阵。它很容易使用,但通常会对您的数据结构造成极大的破坏。

进一步说明。当你使用cut时,你得到一个因子对象,而我上面概述的方法给你一个字符向量。在某些情况下,您需要一个因子,例如立即为回归函数准备数据,但我发现最好推迟构造因素。他们可能会有点痛苦。

答案 1 :(得分:3)

正如@Adrian所说,你可以使用cut()

smallData$category <- cut(smallData$value,breaks=c(0,27,30,Inf),
                          labels=c("A","B","C"))

(如果@DWin建议您想要as.character()而不是character结果,请对结果使用factor。)

有两个原因导致apply无法正常运作:

  • 它将结果强制转换为矩阵,这意味着所有元素都是character类型(包含矩阵中所有数据的最常见类型):来自?apply

      

    如果'X'不是数组,而是具有非null的类的对象   'dim'值(例如数据框),'apply'试图强制它   如果它是二维的(例如,数据,则通过'as.matrix'到一个数组   框架)或通过'as.array'。

  • 在这种情况下,
  • apply()有效地转换了您的数组:

      

    如果每次调用'FUN'都会返回长度为'n'的向量,那么'apply'   如果'n&gt;返回维度'c(n,dim(X)[MARGIN])'的数组1’ 。

答案 2 :(得分:2)

这里的另外两个答案很棒,它们是解决问题的更优雅的方法。我在这里添加自己的帖子,以便您可以看到apply语句如何实现您尝试做的事情:

smallData <- data.frame(name = as.character(c("IC","IC","IC","IC","IC","BC","BC","BC","BC","BC")), 
                        value = as.integer(c(29,29,29,29,29,29,29,29,43,26)))

# Create custom categorize function
categorize <- function(r)
{
  if(r < 27) {
    return("A")
  } else if(r < 30) {
    return("B")
  } else {
    return("C")
  }
}

# call to apply
smallData$category <- apply(smallData[match("value", names(smallData))],1,categorize)