如何在数据表中使用mapply函数

5

我希望在数据表中使用mapply进行简单的应用。我想要将一系列数据表列乘以另一列中的值。这是我的函数。y是要将其他列的值乘以其值的单个列。xIn是要执行此操作的列名称。

f.xRatio <- function(xIn, y) {return(y * (xIn + 1)/(xIn - 1))}

我有一个数据表,其中有一列名为GDPratio,还有一些名字像x.food1、x.food2这样的列。我将这些列名存储在名为x的变量中。

x <- paste0("x.", foodNames)

我使用该函数创建的新列的名称创建另一个变量。

xRatio <- paste0("xRatio.", foodNames)

以下是我使用mapply创建xRatio列的两个版本。

dt[, (xRatio) := mapply(FUN = f.xRatio, xIn = .SD, y = GDPRatio), .SDcols = (x)]

dt[, (xRatio) := mapply(FUN = f.xRatio, xIn = .(x), y = GDPRatio)]

两者都不起作用。我认为第一个接近正确。希望有人能在我创建可重现示例之前指出我的逻辑缺陷。


2
(xRatio) := lapply(.SD, f.xRatio, y = GDPRatio), .SDcols = xRatio?我不会发布答案,因为我当然无法测试并验证这是否有效。 - Frank
1
你可以使用 y = list(GDPRatio) - akrun
弗兰克的帖子有效。如果您将其作为答案,我会将其标记为已回答。 - JerryN
2个回答

5

如果我们正在使用 Map/mapply,请确保将单列 'GDPRatio' 包含在 list 中,以便将其作为单个单位循环使用于 .SD 中的 list 列表中。

dt[, (xRatio) := Map(f.xRatio, .SD, list(GDPRatio)), .SDcols = x]

否则,该单元将成为 vector 中的单个元素,并且会与 .SD 的相应列一起循环使用,从而导致 length 问题,如 OP 代码中所述。
dt[, (xRatio) := Map(f.xRatio, .SD, GDPRatio), .SDcols = x]

警告信息:1: 在mapply(FUN = f, ..., SIMPLIFY = FALSE)中:
较长的参数不是较短的长度的倍数 2: 在[.data.table(dt, , :=((xRatio), Map(f.xRatio, .SD, GDPRatio)), 中: 提供了一个包含5个值的列表(未使用3个)来分配2列

数据

foodNames <- c("food1", "food2")
x <- paste0("x.", foodNames)
xRatio <- paste0("xRatio.", foodNames)

set.seed(24)
dt <- data.table(x.food1 = 2:6, x.food2 = 6:10, val = rnorm(5), 
                GDPRatio = c(0.5, 0.2, 0.3, 0.4, 0.1))

1
考虑不使用循环,并在列的子集上运行向量化算术:
dt[, xRatio] <- dt$GDPRatio * (dt[, foodNames, with=FALSE]  + 1) / 
                              (dt[, foodNames, with=FALSE]  - 1)

这将等同于@Frank的建议和@akrun的答案,使用以下随机数据:
foodNames <- c("apple", "banana", "orange")

set.seed(4252018)  # SEEDED FOR REPRODUCIBILITY

dt <- data.table(
  apple = abs(rnorm(50)) * 100,
  banana = abs(rnorm(50)) * 100,
  orange = abs(rnorm(50)) * 100,
  GDPRatio = abs(rnorm(50))
)

f.xRatio <- function(xIn, y) {return(y * (xIn + 1)/(xIn - 1))}
xRatio <- paste0("xRatio.", foodNames)

# @Parfait's NO LOOP FUNCTION
dt[, xRatio] <- dt$GDPRatio * (dt[, foodNames, with=FALSE]  + 1) / 
                              (dt[, foodNames, with=FALSE]  - 1)

# @Frank's COMMENT
frank_dt <- dt[, (xRatio) := lapply(.SD, f.xRatio, y = GDPRatio), .SDcols = xRatio]

all.equal(dt, frank_dt)
# [1] TRUE
identical(dt, newdt)
# [1] TRUE

# @akrun'S ANSWER
akrun_dt <- dt[, (xRatio) := Map(f.xRatio, .SD, list(GDPRatio)), .SDcols = xRatio]

all.equal(dt, akrun_dt)
# [1] TRUE
identical(dt, akrun_dt)
# [1] TRUE

这些都是很好的答案!Frank和Akrun的答案的优点在于它们展示了使用Map和apply方法的不同方式。它们也不会创建dt的新副本。Parfait的方法更容易阅读,因为它将公式与实际计算结合起来。有什么需要考虑的缺点吗? - JerryN
考虑阅读这篇关于应用循环和向量化的优秀文章:https://dev59.com/yV4b5IYBdhLWcg3wiSPV。如果您的情况允许向量化您的代码,许多人都会同意采用这种方法。 - Parfait

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接