一个列表的元素作为作用于另一个列表的函数的参数

时间:2016-04-20 23:47:34

标签: r list lapply

我有一个数据框列表,其中每个数据框都相似(具有相同名称的相同列)但包含有关不同的相关“事物”(例如,花的种类)的信息。我需要一种优雅的方法,使用函数cut()将所有这些数据框中的一列从连续分类到分类。问题是每个“东西”(花)都有不同的切点,并会使用不同的标签。

我把切割点和标签放在一个单独的列表中。如果我们遵循我的假例子,它基本上是这样的:

iris <- iris 
peony <- iris  #pretending that this is actually different data!
flowers <- list(iris = iris, peony = peony)

params <- list(iris_param = list(cutpoints = c(1, 4.5),
                             labels = c("low", "medium", "high")),

           peony_param = list(cutpoints = c(1.5, 2.5, 5),
                              labels = c("too_low", "kinda_low", "okay", "just_right")))

#And we want to cut 'Sepal.Width' on both peony and iris

我现在真的被卡住了。我尝试过使用lapply()do.call()的某些组合,但我只是在猜测(并且猜错了)。

更通用一点,我想知道:如何使用一组不断变化的参数将一个函数应用于列表中的不同数据框?

2 个答案:

答案 0 :(得分:3)

我认为这是for循环的好时机。编写和清除它很简单:

for (petal in seq_along(flowers)) {
    flowers[[petal]]$Sepal.Width.Cut = cut(
        x = flowers[[petal]]$Sepal.Width,
        breaks = c(-Inf, params[[petal]]$cutpoints, Inf),
        labels = params[[petal]]$labels
    )
}

请注意:(a)我不得不增加你的休息时间以使cut对标签的长度感到满意,(b)我真的只是迭代1,2。一个更强大的版本可能迭代列表的名称,作为安全检查,要求params列表具有相同的名称。由于列表的名称不同,我只是使用了索引。

这可能是使用mapply完成的。我认为没有任何优势 - 除非你已经对mapply感到满意,唯一真正的区别是mapply版本将花费你10倍的时间来写作。

答案 1 :(得分:3)

我喜欢Gregor的解决方案,但我可能会堆叠数据:

library(data.table)

# rearrange parameters
params0 = setNames(params, c("iris", "peony"))
my_params = c(list(.id = names(params0)), do.call(Map, c(list, params0)))

# stack
DT = rbindlist(flowers, id = TRUE)

# merge and make cuts
DT[my_params, Sepal.Width.Cut := 
  cut(Sepal.Width, breaks = c(-Inf,cutpoints[[1]],Inf), labels = labels[[1]])
, on=".id", by=.EACHI]

(我借用了格雷戈尔对分界点的翻译。)结果是:

       .id Sepal.Length Sepal.Width Petal.Length Petal.Width   Species Sepal.Width.Cut
  1:  iris          5.1         3.5          1.4         0.2    setosa       kinda_low
  2:  iris          4.9         3.0          1.4         0.2    setosa       kinda_low
  3:  iris          4.7         3.2          1.3         0.2    setosa       kinda_low
  4:  iris          4.6         3.1          1.5         0.2    setosa       kinda_low
  5:  iris          5.0         3.6          1.4         0.2    setosa       kinda_low
 ---                                                                                  
296: peony          6.7         3.0          5.2         2.3 virginica            okay
297: peony          6.3         2.5          5.0         1.9 virginica       kinda_low
298: peony          6.5         3.0          5.2         2.0 virginica            okay
299: peony          6.2         3.4          5.4         2.3 virginica            okay
300: peony          5.9         3.0          5.1         1.8 virginica            okay

我认为堆叠数据通常比data.frames列表更有意义。您不需要使用data.table来堆叠或进行剪切,但它的设计非常适合这些任务。

工作原理。

  1. 我猜rbindlist很清楚。

  2. 代码

    DT[my_params, on = ".id"]
    

    进行合并。要了解这意味着什么,请查看:

    as.data.table(my_params)
    #      .id   cutpoints                            labels
    # 1:  iris     1.0,4.5                   low,medium,high
    # 2: peony 1.5,2.5,5.0 too_low,kinda_low,okay,just_right
    

    因此,我们将此表与DT的公共.id列合并。

  3. 当我们进行合并时

    DT[my_params, j, on = ".id", by=.EACHI]
    

    这意味着

    • 进行合并,将my_params的每一行与DT的相关行进行匹配。
    • j的每一行执行my_params,使用两个表中任意一列中的列。
  4. 在这种情况下,
  5. j的格式为column_for_DT := cut(...),在DT中生成一个新列。