在特定数据框列上使用R Apply()函数

101

我想在一个数据框上使用apply函数,但只想对最后的5列应用该函数。

B<- by(wifi,(wifi$Room),FUN=function(y){apply(y, 2, A)})

这适用于y的所有列

B<- by(wifi,(wifi$Room),FUN=function(y){apply(y[4:9], 2, A)})

这仅适用于 y 的4-9列,但B的总回报将剥离前3列...我仍然想要它们,只是不想对它们应用A。

wifi[,1:3]+B 

同样没有做到我期望/想要的。


2
“by”调用使得这个问题变得复杂。如果它是相关的,您应该重写问题以澄清(wifi$Room是什么?)。在下面的答案中,我忽略了“by”。 - leif
你可以使用cbind(y[1:3], ...)将其与你所得到的结果合并。 - IRTFM
6个回答

123

lapply在这里可能比apply更好,因为apply首先将您的data.frame强制转换为一个数组,这意味着所有列必须具有相同的类型。根据您的上下文,这可能会产生意想不到的后果。

模式是:

df[cols] <- lapply(df[cols], FUN)

“cols”向量可以是变量名称或索引。在可能的情况下,我更喜欢使用名称(这样可以使列重新排序更为稳健)。因此,在您的情况下,这可能是:

wifi[4:9] <- lapply(wifi[4:9], A)

使用列名的示例:
wifi <- data.frame(A=1:4, B=runif(4), C=5:8)
wifi[c("B", "C")] <- lapply(wifi[c("B", "C")], function(x) -1 * x)

2
一个小修正:wifi <- data.frame(A=1:4, B=runif(4), C=5:8) - jcfaria
你能更明确地说明一下你是如何创建 [cols] 向量的吗? - Mox
@Mox 你可以直接执行 cols <- c("var1", "var2") - cparmstrong
1
作为替代方案,使用dplyr避免重复列规范,您可以执行wifi[4:9] %<>% map_dbl(A) - Agile Bean
@AgileBean:map是一个不错的选择,但我建议使用%<>%运算符。请滚动到https://r4ds.had.co.nz/pipes.html的末尾。 - Kay

70
使用一个示例的数据框和示例函数(仅将所有值加1)。

使用一个示例的数据框和示例函数(仅将所有值加1)

A <- function(x) x + 1
wifi <- data.frame(replicate(9,1:4))
wifi

#  X1 X2 X3 X4 X5 X6 X7 X8 X9
#1  1  1  1  1  1  1  1  1  1
#2  2  2  2  2  2  2  2  2  2
#3  3  3  3  3  3  3  3  3  3
#4  4  4  4  4  4  4  4  4  4

data.frame(wifi[1:3], apply(wifi[4:9],2, A) )
#or
cbind(wifi[1:3], apply(wifi[4:9],2, A) )

#  X1 X2 X3 X4 X5 X6 X7 X8 X9
#1  1  1  1  2  2  2  2  2  2
#2  2  2  2  3  3  3  3  3  3
#3  3  3  3  4  4  4  4  4  4
#4  4  4  4  5  5  5  5  5  5

甚至更多选择:

data.frame(wifi[1:3], lapply(wifi[4:9], A) )
#or
cbind(wifi[1:3], lapply(wifi[4:9], A) )

#  X1 X2 X3 X4 X5 X6 X7 X8 X9
#1  1  1  1  2  2  2  2  2  2
#2  2  2  2  3  3  3  3  3  3
#3  3  3  3  4  4  4  4  4  4
#4  4  4  4  5  5  5  5  5  5

有没有一种方法可以使用 $ 按名称索引某个列,而不是使用 [:] 按列号进行索引?我尝试添加列名:colnames(wifi) = c("a", "b", "c", "d", "e", "f", "g", "h" ,"i") 但是任何尝试使用 lapply(wifi$e, 2, X) 都无法实现。 - santeko
9
你可以使用wifi[c("a","b","c")]这样的语法通过列名来同时索引多个列。 - thelatemail
@thelatemail,在 apply(wifi[4:9],2, A) 中,wifi[4:9] 是一个 data.frame。而 apply 只能用于数组或矩阵。为什么你的答案可行? - kittygirl
@kittygirl - 这是因为apply函数可以用于data.frame。在应用apply函数时,data.frame将被强制转换为矩阵。 - thelatemail
@thelatemail,会丢失行名或列名信息吗? - kittygirl

6

使用dplyr包的across功能很容易实现此任务。

借鉴thelatemail建议的数据结构

A <- function(x) x + 1
wifi <- data.frame(replicate(9,1:4))

我们可以通过索引来指定我们希望应用该函数的列,像这样:

library(dplyr)
wifi %>% 
   mutate(across(4:9, A))
#  X1 X2 X3 X4 X5 X6 X7 X8 X9
#1  1  1  1  2  2  2  2  2  2
#2  2  2  2  3  3  3  3  3  3
#3  3  3  3  4  4  4  4  4  4
#4  4  4  4  5  5  5  5  5  5

或者按名称:

wifi %>% 
   mutate(across(X4:X9, A))
#  X1 X2 X3 X4 X5 X6 X7 X8 X9
#1  1  1  1  2  2  2  2  2  2
#2  2  2  2  3  3  3  3  3  3
#3  3  3  3  4  4  4  4  4  4
#4  4  4  4  5  5  5  5  5  5

1

如前所述,您只需将标准的R apply函数应用于列(MARGIN=2):

wifi[,4:9] <- apply(wifi[,4:9], MARGIN=2, FUN=A)

或者,简短地说:
wifi[,4:9] <- apply(wifi[,4:9], 2, A)

这将使用A()函数原地更新列4:9。现在,假设na.rmA()的一个参数,它可能应该是这样的。我们可以传递na.rm=T来从计算中删除NA值,如下所示:
wifi[,4:9] <- apply(wifi[,4:9], MARGIN=2, FUN=A, na.rm=T)

对于您想要传递给自定义函数的任何其他参数,情况也是如此。


0
最简单的方法是使用 mutate 函数:
dataFunctionUsed <- data %>% 
  mutate(columnToUseFunctionOn = function(oldColumn ...))

-2

我认为你想要的是mapply。你可以将该函数应用于所有列,然后只需删除不需要的列。但是,如果你要对不同的列应用不同的函数,那么你可能需要使用dplyr包中的mutate函数。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接