在 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][]
我有一个包含三个相关列的 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][]