R - 在多个数据帧上应用函数

R - Apply function on multiple data frames

我在 R 中加载了几个数据表作为数据帧:

temp = list.files(pattern="*.csv")
for (i in 1:length(temp)) assign(temp[i], read.csv(temp[i]))

现在我想对所有数据框应用一个函数。 我想到了类似的东西:

kappa1_mean_h_stem <- lapply(df.list, mean_h_stem)

其中 df.list 包含所有数据帧的列表。

    mean_h_stem <- function(x) {
  mean(x[1,3])
}

我想要函数 return 特定列的平均值。但它告诉我,我的维数有误。

您出错的原因是我认为您传递了 x[1,3],它只会从第三列的第一行获取值。我假设您想计算所有 data.frames 中同一列的平均值,因此我对您的函数进行了轻微修改,以便您可以传递数据和列的名称或位置:

mean_h_stem <- function(dat, col){ mean(dat[,col], na.rm=T)}

可以使用整数选择列:

lapply(df.list, mean_h_stem, 2)

或者列名,用字符串表示:

lapply(df.list, mean_h_stem, 'col_name')

像这样传递第二个参数感觉有点不直观,所以你可以用更清晰的方式来做:

lapply(df.list, function(x) mean_h_stem(dat = x, col ='col_name'))

根据您的问题,这一次仅适用于单列,但您可以轻松修改它以执行多列。

顺便说一句,要读取 csv 文件,您还可以使用 lapplyread.csv:

temp <- list.files(pattern='*.csv')
df.list <- lapply(temp, read.csv)

问题的一个不明确部分是,是应该在跨所有数据帧提取列后计算给定列的平均值,还是每个数据帧计算一次。

另一个答案中描述的技术将 return 每个文件计算一次的均值列表。如果问题的目的是在所有数据帧中提取一列,然后计算平均值,我们需要先提取 unlist() 结果,并将其用作 mean() 的输入。

我们用我在 Github 上维护的 Pokémon stats 数据来说明这一点。

download.file("https://raw.githubusercontent.com/lgreski/pokemonData/master/PokemonData.zip",
              "pokemonData.zip",
              method="curl",mode="wb")
unzip("pokemonData.zip",exdir="./pokemonData")

thePokemonFiles <- list.files("./pokemonData",pattern="*.csv",
                              full.names=TRUE)
dataList <- lapply(thePokemonFiles,read.csv) 

此时dataList是一个包含8个数据框的列表,每个数据框包含一代神奇宝贝的统计数据,我们可以使用RStudio Environment Viewer查看。

如果我们想按代查找Attack统计的mean(),我们可以使用lapply()如下。

lapply(dataList,function(x,y) mean(x[[y]]),"Attack")

这里我们使用一个带有两个参数的匿名函数,xy,其中x表示dataList和[=26=中的数据帧]代表列Attack.

这 return 是一个包含 8 个均值的列表。

> lapply(dataList,function(x,y) mean(x[[y]]),"Attack")
[[1]]
[1] 72.91391

[[2]]
[1] 68.26

[[3]]
[1] 73.93617

[[4]]
[1] 79.99138

[[5]]
[1] 82.44242

[[6]]
[1] 94.92366

[[7]]
[1] 86.5082

[[8]]
[1] 83.66387

如果想要的结果是所有 8 个数据帧中 Attack 的单一平均值怎么办?

在这种情况下,我们可以使用 lapply() 提取所需的列,unlist() 结果创建一个数值向量,并将其作为参数传递给 mean()

鉴于 R 如何嵌套函数,我们可以在一行代码中完成。

mean(unlist(lapply(dataList,function(x,y) x[,y],"Attack")))

...输出:

> mean(unlist(lapply(dataList,function(x,y) x[,y],"Attack")))
[1] 80.46699

扩充结果

鉴于对问题和答案的一些评论,我想我会说明我们如何生成比数字列表更容易使用的结果。

如果我们在创建初始列表时为每个数据框分配一个名称,我们就可以使用这些名称来驱动后续处理,包括输出数据框中的名称信息。

# assign names to dataList
df_names <- paste0("gen_",sprintf("%02d",1:8))
names(dataList) <- df_names

一旦分配了名称,我们就可以使用 df_names 向量通过匿名函数驱动 lapply()

resultList <- lapply(df_names,function(x,y) {
     df <- dataList[[x]] # use [[ to return a data frame vs. list()
     theMean <- mean(df[,y],na.rm=TRUE) # use [,] extract for variable substitution
     result_df <- data.frame(x,theMean)
     names(result_df) <- c("dataFrame",paste0("mean_",y))
     result_df 
 },"Attack")

此时,resultList 是一个包含 8 个数据框的列表,每个数据框包含一个观察值。我们使用 do.call()rbind() 的组合将数据帧组合成单个输出数据帧。

# combine into single dataframe
do.call(rbind,resultList)

生成的数据框让我们可以看到每个输入数据框属于哪个均值。

> do.call(rbind,resultList)
  dataFrame mean_Attack
1    gen_01    72.91391
2    gen_02    68.26000
3    gen_03    73.93617
4    gen_04    79.99138
5    gen_05    82.44242
6    gen_06    94.92366
7    gen_07    86.50820
8    gen_08    83.66387

注意:我根据我对内容的了解生成了数据框名称列表。也可以使用 list.files()full.names = FALSE 来生成名称。