我的问题与Subset by group with data.table有关,但有所不同。
想象一个这样的数据集:
tmp <- data.table(x = 1:10, y = c(27, 70, 54, 18, 50, 44, 22, 73, 6, 5))
对于数据的每一行,我想计算一个新值z,对于所有具有较大x值的行,它都是min(y)
。例如,对于x为3的数据的第三行,我希望min(y)
在x> 3(即值为5)的行中。出于我们的目的和目的,您可以假定数据已经按x排序。
起初我想到使用这样的函数:
min.y <- function(val, dt) {
dt[x > val, min(y)]
}
但是致电tmp[, z:= fun(x, tmp)]
会导致警告消息:
In min(y) : no non-missing arguments to min; returning Inf
执行此操作的正确方法是什么?
PS:显然,对于最后一行,我希望得到NA作为结果
答案 0 :(得分:5)
方法1:
由于您说过我们可以假设数据按y
排序,因此可以使用从>
末尾开始的累计最小值。我们切出第一个观察值,以便我们进行>=
而不是tmp$min_y <- c(rev(cummin(rev(tmp$y[-1]))), NA)
的搜索:
>=
更新:旧方法有效地进行了>
搜索,而不是>
。更新为执行data.table
。
方法2:Data.table
如果您想使用J
,则可以尝试按每行分组,然后在tmp[, "min_y" := {curr_x <- x
tmp_subs <- tmp[x > curr_x]
ifelse(nrow(tmp_subs)>0, min(tmp[x > curr_x][["y"]]), NA_real_)},
by = 1:nrow(tmp)]
tmp
# x y min_y
# 1: 1 27 5
# 2: 2 70 5
# 3: 3 54 5
# 4: 4 18 5
# 5: 5 50 5
# 6: 6 44 5
# 7: 7 22 5
# 8: 8 73 5
# 9: 9 6 5
#10: 10 5 NA
中进行分组。需要ifelse,以便当我们在最后一行时,不要取无值的最小值:
5
由于5
是最小值,最终所有值都是tmp <- data.table(x = 1:10, y = c(27, 70, 54, 18, 50, 44, 22, 73, 47, 58))
。让我们做些更有趣的事情:
# x y min_y
# 1: 1 27 18
# 2: 2 70 18
# 3: 3 54 18
# 4: 4 18 22
# 5: 5 50 22
# 6: 6 44 22
# 7: 7 22 47
# 8: 8 73 47
# 9: 9 47 58
#10: 10 58 NA
我们的结果将是:
while
答案 1 :(得分:3)
一种选择是自我非股权联接
tmp[, min_y := .SD[.SD, min(y, na.rm = TRUE), on = .(x > x),
by = .EACHI]$V1][is.infinite(min_y), min_y := NA_real_][]
# x y min_y
# 1: 1 27 5
# 2: 2 70 5
# 3: 3 54 5
# 4: 4 18 5
# 5: 5 50 5
# 6: 6 44 5
# 7: 7 22 5
# 8: 8 73 5
# 9: 9 6 5
#10: 10 5 NA
答案 2 :(得分:2)
对于它的价值,还有另一种可能的方法(不确定它是否比其他方法更好或更坏):
tmp[, z := min(tmp$y[(.I+1):NROW(tmp)]), by = 1:NROW(tmp)]
答案 3 :(得分:2)
以下是一些解决方案:
1)滚动应用假设tmp
已排序(如果未排序,则排序),我们可以使用rollapply
来获得一个紧凑的解决方案。请注意,当width
的{{1}}自变量是一个列表时,其元素被视为偏移量向量,将在其上应用rollapply
。
min
给予:
library(data.table)
library(zoo)
tmp[, min := rollapply(y, lapply(pmax(.N:1-1, 1), seq), min, fill = NA)]
2)sqldf 使用SQL按照指示的条件将 x y min
1: 1 27 5
2: 2 70 5
3: 3 54 5
4: 4 18 5
5: 5 50 5
6: 6 44 5
7: 7 22 5
8: 8 73 5
9: 9 6 5
10: 10 5 NA
与其自身连接,并像这样对组进行最小化。 tmp
不需要排序。
tmp
给予:
library(data.table)
library(sqldf)
sqldf("select a.*, min(b.y) min
from tmp a left join tmp b on b.x > a.x group by a.rowid")