在 R 中的 data.table 中有条件地跨行计算

Compute conditionally across rows in data.table in R

我有一个包含三个相关列的 data.table:id、timepoint 和 metric(实际大小要大得多)。

我正在尝试计算时间点 A 和 D 的指标值之间的百分比变化,并使用它来创建标签(良好指标、半合格指标、低于标准指标)。

情况变得更加复杂,因为如果指标小于或等于 2,那么新列应该报告“Super metric!”。如果不是,则应计算百分比差异。根据百分比变化,id 将被报告为“低于标准”(< 30%)、“一般指标”(30 - 50%)、“良好指标”(> 50%)。 如果在时间点 A 或 D 有一个 NA 值,那么 returning NA 是可以的。如果缺少时间点 A 或 D,也 return NA。

我最初的想法是我可以在 data.table 中计算它而无需创建不必要的列,但我什至无法获得更简单的解决方案,即我分别进行计算然后再加入它们.

# Example data

library(data.table)
dat <- data.table(id = c(1,1,1,1,2,2,3,3,3,3,4,4,4,6,6,10,10,10,11,11,12,12,14,14),
                  timepoint = c("A","B","C","D","A","D","A","B","C","D","A","B","C","A","D","A","B","D", "A","D","A","D", "A","D"),
                  metric = c(NA, 3, 3, 4, 4, 2, 3, 3, 2, 1, 4, 3, NA, NA, 4, 1, 5, 2, 5,3, 5,5,6,3))

部分解决方案:首先确定“超级指标”ID,但我希望 class 所有“超级指标”ID 的实例都这样(现在 returns “Super指标”仅适用于时间点 D.

# Inefficient solution
# Step 1: Identify id's that need to be computed

dat1 <- dat[, `:=` (Metric_score = if (metric <= 2 & timepoint == "D")
            Metric_score = "Super metric"
            else Metric_score = "Calc PC"),
            by = 'id,timepoint']


# id timepoint metric Metric_score
# 1:  1         A     NA      Calc PC
# 2:  1         B      3      Calc PC
# 3:  1         C      3      Calc PC
# 4:  1         D      4      Calc PC
# 5:  2         A      4      Calc PC # Should be Super metric
# 6:  2         D      2 Super metric


执行计算: 这会计算所有 ID 的百分比变化,无论是否需要计算

# Step 2: Calculate percent change between timepoint D and A

dat[ , `:=`(col = (metric[timepoint == "A"] - metric[timepoint == "D"])/metric[timepoint == "A"]*100), by = 'id'] 
    

期望输出:Class当最终得分(时间点 D)<= 2 时,每个指标作为“超级指标”,否则,计算百分比变化((指标@timeD-指标@timeA)/指标@ timeA)*100) 和 class 根据结果进行验证(“次等指标”(< 30%)、“一般指标”(30 - 50%)、“良好指标”(>50%)

id 时间点 公制 metric_class
1 一个 不适用 不适用
1 B 3 不适用
1 C 3 不适用
1 D 4 不适用
2 一个 4 超级指标
2 D 2 超级指标
3 一个 3 超级指标
3 B 3 超级指标
3 C 2 超级指标
3 D 1 超级指标
4 一个 4 不适用
4 B 3 不适用
4 C 不适用 不适用
6 一个 不适用 不适用
6 D 4 不适用
10 一个 1 超级指标
10 B 5 超级指标
10 D 2 超级指标
11 一个 5 半标准
11 D 3 半标准
12 一个 5 指标不达标
12 D 5 指标不达标
14 一个 6 良好的指标
14 D 3 良好的指标

使用 fcase 应该会给你一个理想的结果。

由于 0.5 介于 0.3-0.5 和 >= 0.5 之间,因此将采用列表中的第一种情况,在这种情况下是“良好指标”,如果您想要更改它,只需更改顺序即可。

metrics <- dcast.data.table(dat, id~timepoint)
metrics[, metric_class := fcase(D <= 2, "Super metric",
                                abs(D-A)/A < 0.3, "Subpar metric",
                                abs(D-A)/A >= 0.5, "Good metric",
                                between(abs(D-A)/A, 0.3, 0.5), "Half-decent metric")]

dat <- merge(dat, metrics[, .(id, metric_class)], by = "id")

这是另一种不需要 dcast.

的方法
metric_class <- function(t,m) {
  if("D" %in% t && m[t=="D"]<=2) return(rep("Super metric", length(t)))
  mvals = c("a"= m[t=="A"], "d" = m[t=="D"])
  val = abs((mvals["d"]-mvals["a"])/mvals["a"])
  return(rep(fcase(val<0.3, "Subpar metric", val>=0.5, "Good metric", val>=0.3 & val<0.5, "Half-decent metric"), length(t)))
}

setDT(dat)[, metric_class:=metric_class(timepoint, metric), by=id][]