【问题标题】:How to iterate a function along a single column across multiple data frames in R如何在 R 中跨多个数据帧沿单列迭代函数
【发布时间】:2021-07-17 16:07:28
【问题描述】:

编辑:我正在编辑我的原始问题,因为提供的解决方案可以完美运行,但不适用于我的数据框(在此示例和现实生活中),但我不知道为什么,所以我包括一个代表。

我最初的问题:我有许多列名相同的数据框(分光光度计数据),我想一次沿所有数据框的 单个 列应用一个函数,并附加每个数据框架与新列。该单列在数据帧中的名称相同。

我一直在尝试 lapply (并申请家庭),然后转到地图无济于事。这个解决方案看起来很有希望 (R - Apply function on multiple data frames),但是我在 lapply 函数中放置在位置 3 的所有内容都被忽略为未使用的参数。

我想通过对数据框列表中每个数据框的列 (colc) 应用计算来创建新列(冷列)。

这正是我需要的解决方案,但我无法让它在现实生活中发挥作用 - 我的代表如下

library(tidyverse)
sizes <- c(3, 3, 3)
dfs <- lapply(setNames(sizes, paste0("df", seq_along(sizes))),
              function(n) data.frame(cola = sample(1:3, n, replace = T),
                                     colb = sample(c("x", "y", "z"), n, replace = T),
                                     colc = runif(n, 2, 10)))
calculation <- function(x){
  b <- 20
  abs <- log10(x/b)
  return(abs)
}

dfs %>% map(~ .x %>% mutate(cold = calculation(colc)))
#> $df1
#>   cola colb     colc       cold
#> 1    3    x 7.849806 -0.4061711
#> 2    1    z 2.570162 -0.8910696
#> 3    3    y 4.787902 -0.6208847
#> 
#> $df2
#>   cola colb     colc       cold
#> 1    3    z 9.408709 -0.3275000
#> 2    1    z 8.979679 -0.3477692
#> 3    2    x 4.256270 -0.6720008
#> 
#> $df3
#>   cola colb     colc       cold
#> 1    2    x 7.283048 -0.4387168
#> 2    2    x 9.513528 -0.3226884
#> 3    2    z 7.552567 -0.4229354

lapply(dfs, function(df) df %>% mutate(cold = calculation(colc)))
#> $df1
#>   cola colb     colc       cold
#> 1    3    x 7.849806 -0.4061711
#> 2    1    z 2.570162 -0.8910696
#> 3    3    y 4.787902 -0.6208847
#> 
#> $df2
#>   cola colb     colc       cold
#> 1    3    z 9.408709 -0.3275000
#> 2    1    z 8.979679 -0.3477692
#> 3    2    x 4.256270 -0.6720008
#> 
#> $df3
#>   cola colb     colc       cold
#> 1    2    x 7.283048 -0.4387168
#> 2    2    x 9.513528 -0.3226884
#> 3    2    z 7.552567 -0.4229354

我的(缓慢的)数据框:

library(tidyverse)
cola <- c(1,2,3)
colb <- c("x","y","z")
colc <- c(1.4,1.2,2.5)
mydf1 <- as.data.frame(colb %>% cbind(cola, colc))
colc <- 1.1*colc # just to change content of same column name for df2
mydf2 <- as.data.frame(colb %>% cbind(cola, colc))
mydfs <- list(mydf1, mydf2)

calculation <- function(x){
  b <- 20
  abs <- log10(x/b)
  return(abs)
}

mydfs %>% map(~ .x %>% mutate(cold = calculation(colc)))
#> Error: Problem with `mutate()` column `cold`.
#> ℹ `cold = calculation(colc)`.
#> x non-numeric argument to binary operator
lapply(mydfs, function(df) df %>% mutate(cold = calculation(colc)))
#> Error: Problem with `mutate()` column `cold`.
#> ℹ `cold = calculation(colc)`.
#> x non-numeric argument to binary operator

我知道这是一种创建数据帧的可怕方式,但它在现实生活中会产生与从 csv 文件导入的数据帧相同的错误。

这里有什么区别/问题?

【问题讨论】:

  • 你想要一个函数,它需要一个 data.frame 和一个列名,并将这个函数应用于列表中的多个 data.frames?你的calculation-function 只接受一个参数...

标签: r dataframe


【解决方案1】:

只需将lapply 一个函数添加到您的数据帧列表中,依次修改每个数据帧。

# First, build a few random datasets for testing purposes.

sizes <- c(10, 20, 30)
dfs <- lapply(setNames(sizes, paste0("df", seq_along(sizes))),
              function(n) data.frame(cola = sample(1:3, n, replace = T),
                                     colb = sample(c("x", "y", "z"), n, replace = T),
                                     colc = runif(n, 2, 10)))

# Define your computation function.
calculation <- function(x) log10(x - 2)
# Note the function has to be vectorized.
# Wrap it with Vectorize if necessary, for instance:
# calculation <- Vectorize(function(x) log10(x - 2))

如果您的colc 是一个保存数字的字符变量,您必须先将其转换为数字。例如:

library(tidyverse)
library(magrittr)

dfs %<>% mutate(colc = as.numeric(colc))

关键是,您不能将计算直接应用于数据框,而是应用于向量。以下是返回已修改数据框列表的方法:

library(tidyverse)

dfs %>% map(~ .x %>% mutate(cold = calculation(colc)))

lapply(dfs, function(df) df %>% mutate(cold = calculation(colc)))

lapply(dfs, function(df) within(df, cold <- calculation(colc)))

lapply(dfs, function(df) { df$cold <- calculation(df$colc); df })

使用示例数据框

spec_tbl_df <- data.frame(Wavelength = c(187, 187, 188, 188, 188),
                          Intensity = c(-79.398, -80.068, 1.602, -2.068, 0.602))

# List of dataframes, with only one dataframe.
dfs <- list(spec_tbl_df)

calculation <- function(x) log10(x / 20)

library(tidyverse)

# Say you want to apply the calculation to Wavelength
dfs %>% map(~ .x %>% mutate(Wavelength2 = calculation(Wavelength)))

【讨论】:

  • 非常感谢您。它适用于您设置数据框的方式,但是当我以相同的方式使用实际数据时,我得到: UseMethod("mutate") 中的错误:没有适用于 'mutate' 的方法应用于类“字符”的对象或错误:mutate()cold 有问题。 ℹcold = calculation(colc)。 x 二元运算符的非数字参数。我无法弄清楚为什么它以不同的方式感知相同的输入。你能帮忙澄清一下吗?谢谢!
  • 是的,即使您使用我创建 2 个数据帧并将它们放在原始帖子中的列表中的缓慢方法,我也会收到该错误。实际上,我让它工作的唯一方法是使用您创建数据框和列表的方法。我已经通过两种方法查看了数据框和列表之间是否存在某种格式差异的各个角度。我唯一确定的是我的数据框列表为我的示例中的 2 个数据框返回了这种表示法 [[1]] 和 [[2]],而您的方法使用 $df1、$df2。除此之外,我找不到区别
  • @user16469993 问题发生得更早:当您在向量上调用cbind 时,R 会构建一个矩阵。由于矩阵的所有列必须具有相同的类型,并且其中之一是字符向量,因此整个矩阵都将转换为字符,并且最终会得到一个带有字符变量的数据帧,在该数据帧上计算失败。解决方案:以通常的方式构建数据框。如果您将as.data.frame 应用于向量列表,它也将起作用,因为列表可能具有异构条目。或者你也可以调用cbind,但是在一个数据框上。
  • 我确实申请了as.data.frame,但这些丑陋的数据框构建所遇到的错误与现实生活中的尝试相同,它们是从 csv 文件导入的数据框。我一直试图弄清楚为什么它在您提供的数据帧上完美运行,但不适用于我的或我的实际数据。我添加了一个表示希望澄清。
  • @user16469993 你还是不明白。 colb %&gt;% cbind(cola, colc) 如果 cola、colb、colc 之一是字符,则将所有内容转换为字符。错误在 your 部分,如果您将字符 verctor 提供给需要数字向量的函数,它会失败,句号。如果您坚持以不好的方式这样做,那么您将不得不在某个时候转换您的数据。它甚至可以在calculation 内部:如果您愿意,可以添加x &lt;- as.numeric(x) 作为该函数的第一行。或者在致电lapply 之前执行此操作。只是在某个地方做。如果您仍有疑问,请尝试str(mydf1)
猜你喜欢
  • 2022-01-04
  • 2020-10-11
  • 2020-09-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-05-23
  • 2023-01-04
相关资源
最近更新 更多