将函数应用于 data.frame 生成 NA,同时将其应用于列有效

Applying function to data.frame generates NAs while applying it to columns works

我想对 R 中的数据框应用权重,该数据框由数字变量和因子组成。当我创建一个将因子转换为数字然后对变量加权并将其应用于任何给定列的函数时,它运行良好。但是,当我使用 apply 函数将它应用于 data.frame 时,它会生成 NA。例如:

set.seed(123)
frame <- data.frame(x = sample(1:100,10), y = c(rep("1",5), rep("2",5)))

weights <- 10
weight.fun <- function(x){
    x <- if(class(x) == "numeric" | class(x) == "integer"){x} else {as.numeric(levels(x))[x]}
    x*weights
}

weight.fun(frame$x)
# [1] 290 790 410 860 910  50 500 830 510 420
weight.fun(frame$y)
# [1] 10 10 10 10 10 20 20 20 20 20
apply(frame,2,weight.fun)
#        x  y
#  [1,] NA NA
#  [2,] NA NA
#  [3,] NA NA
#  [4,] NA NA
#  [5,] NA NA
#  [6,] NA NA
#  [7,] NA NA
#  [8,] NA NA
#  [9,] NA NA
# [10,] NA NA

知道为什么会这样吗?

如果您使用 sapply 而不是 apply,操作将按预期进行:

sapply(frame, weight.fun)
#         x  y
#  [1,] 290 10
#  [2,] 790 10
#  [3,] 410 10
#  [4,] 860 10
#  [5,] 910 10
#  [6,]  50 20
#  [7,] 500 20
#  [8,] 830 20
#  [9,] 510 20
# [10,] 420 20

造成这种差异的原因是 apply 对矩阵(或数组)进行运算。来自 ?apply:

Returns a vector or array or list of values obtained by applying a function to margins of an array or matrix.

因此,当使用 apply 时,您的数据框 frame 将被转换为矩阵,这意味着所有列的数据类型将被强制相同(在您的情况下为字符串):

as.matrix(frame)
#        x    y  
#  [1,] "29" "1"
#  [2,] "79" "1"
#  [3,] "41" "1"
#  [4,] "86" "1"
#  [5,] "91" "1"
#  [6,] " 5" "2"
#  [7,] "50" "2"
#  [8,] "83" "2"
#  [9,] "51" "2"
# [10,] "42" "2"

这解释了 apply 的意外行为 -- weight.fun 正在传递字符向量。

同时,sapply 对列表进行操作,这正是您想要的,因为数据框是列表。使用 sapply,每列的类型都从数据框中保留下来,因此 weight.fun 首先使用整数向量调用,然后使用因子调用。

看起来问题出在您的函数中。您的 if 语句返回 NA,因此,这不是一个应用问题,如果 x 是一个字符,它将失败。像这样编写函数似乎适用于 apply.

set.seed(123)
frame <- data.frame(x = sample(1:100,10), y = c(rep("1",5), rep("2",5)))

weight.fun <- function(x, w = 10){ 
  if(!class(x) == "numeric" & !class(x) == "integer") {
    if(class(x) == "factor") { x <- as.numeric(as.character(x)) }
    else if(class(x) == "character") { x <- as.numeric(x) }  
  } 
  return(x * w)
}

apply(frame, MARGIN = 2, FUN = weight.fun)