我有一个数据框列表,其中每个数据框都相似(具有相同名称的相同列)但包含有关不同的相关“事物”(例如,花的种类)的信息。我需要一种优雅的方法,使用函数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()
的某些组合,但我只是在猜测(并且猜错了)。
更通用一点,我想知道:如何使用一组不断变化的参数将一个函数应用于列表中的不同数据框?
答案 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来堆叠或进行剪切,但它的设计非常适合这些任务。
工作原理。
我猜rbindlist
很清楚。
代码
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
列合并。
当我们进行合并时
DT[my_params, j, on = ".id", by=.EACHI]
这意味着
my_params
的每一行与DT
的相关行进行匹配。j
的每一行执行my_params
,使用两个表中任意一列中的列。 j
的格式为column_for_DT := cut(...)
,在DT
中生成一个新列。