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 文件,您还可以使用 lapply
和 read.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")
这里我们使用一个带有两个参数的匿名函数,x
和y
,其中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
来生成名称。
我在 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 文件,您还可以使用 lapply
和 read.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")
这里我们使用一个带有两个参数的匿名函数,x
和y
,其中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
来生成名称。