R:将 lapply 与作用于拆分数据帧的一部分的函数一起使用

R: Use lapply with a function acting on part of a split dataframe

此 R 代码正在设置我试图解决的问题的示例。该数据集测量粒子在非均匀时间间隔内的释放。使用梯形法则随时间对粒子释放进行积分。

library(caTools)

test.data.frame <- data.frame( 
    sample = c('sample 1','sample 1','sample 1','sample 1',
               'sample 2','sample 2','sample 2','sample 2'))
test.data.frame$time <- c(1,2,4,6,1,4,5,6)
test.data.frame$material.released.g <- c(5,3,2,1,2,4,5,1)

split.test <- split(test.data.frame, test.data.frame$sample)

integrate.test <- function(x){
   dataframe.segment <- do.call(rbind.data.frame,x)
   return(trapz(dataframe.segment$time,dataframe.segment$material.released.g))
}

到目前为止,integrate.test 函数似乎只作用于列表的单个元素。

   > integrate.test(split.test[1])
   [1] 12

   > integrate.test(split.test[2])
   [1] 16.5

lapply 函数在输出中给出零。

  > lapply(split.test, integrate.test)
  $`sample 1`
  [1] 0

  $`sample 2`
  [1] 0

我正在寻找的输出是一个数据框,相当于:

expected.output <- data.frame(
    sample = c('sample 1','sample 2'), 
    total.material.released = c(12 , 16.5))

有谁能帮忙解决错误代码。谢谢!

这是包含数据框的单元素列表split.test[1]和存储在列表元素[[1]]中的数据框split.test[[1]]的区别。

您的函数通过调用 do.call(rbind.data.frame, x),期望 x 是一个列表。但是 lapply(split.test, integrate.test) 实际上为它提供了一个数据框。以下是当您输入 integrate.test 数据框而不是(通用)列表时会发生的情况:

x = do.call(rbind.data.frame, split.test[[1]])
x
                    c.1..1..5. c.1..2..3. c.1..4..2. c.1..6..1.
sample                       1          1          1          1
time                         1          2          4          6
material.released.g          5          3          2          1

do.call 对列表进行操作。如果您为它提供一个通用列表(如 split.test[1],这是一个单元素列表),它会尝试 rbind 每个列表元素。如果列表包含多个数据框,它将把它们堆叠成一个数据框。但是只有一个元素——split.test 的元素 1 中包含的数据框——所以这就是返回的内容。

但是,当您 运行 do.call(rbind, split.test[[1]]) 时,您是在给 do.call 一个要操作的数据框。数据框是一种特殊的列表,其中每一列都是一个列表元素。所以 do.call 获取原始数据框的列,将它们转置为行并堆叠它们。积分returns0,因为它要操作的列已经不存在了。当您引用那些不存在的列时,将返回 NULL 的值而不是您期望的数据,并且 trapz(NULL, NULL) 为零。

如果您直接使用数据框并跳过 do.call 步骤,该函数将起作用:

integrate.test <- function(x){
  #dataframe.segment <- do.call(rbind.data.frame,x)
  dataframe.segment = x
  return(trapz(dataframe.segment$time,dataframe.segment$material.released.g))
}

lapply(split.test, integrate.test)
$`sample 1`
[1] 12

$`sample 2`
[1] 16.5

当然可以缩短为:

integrate.test <- function(x){
  return(trapz(x$time,x$material.released.g))
}

或者您可以直接使用 trapz,而不用将其包装在函数中。