从包含许多二元分类列的 data.table 中观察的流行率估计

Posted

技术标签:

【中文标题】从包含许多二元分类列的 data.table 中观察的流行率估计【英文标题】:Prevalence Estimates from Observations in data.table Containing Many Binary Classification Columns 【发布时间】:2022-01-16 16:58:44 【问题描述】:

我正在通过蛮力从我的原始 data.table 进行流行率估计,我需要提高效率。你能帮忙吗?

我的 data.table 每行包含一个加权观察值。有许多列充当二进制虚拟变量,指示特定观察是否属于许多可能分类中的一个或多个。 (例如,一个故事可以是“惊人的”、“无聊的”或“迷人的”,或三者的任意组合。)

必须有一个data.table 的方式来替换我的forloop。我还怀疑我可能不需要生成queries 集。我很感激以全新的眼光看待这个问题。

library(data.table)

set.seed(42)
# I have many weighted observations that can be labeled as belonging to one of many categories
# in this example, I simulate 10 observations and only 3 categories
dt = data.table(
        weight = runif( n = 10 , min = 0, max = 1 ),
        a = sample( x = c(0,1) , size = 10 , replace = TRUE ),
        b = sample( x = c(0,1) , size = 10 , replace = TRUE ),
        c = sample( x = c(0,1) , size = 10 , replace = TRUE )
)

# Generate all combinations of categories
queries = as.data.table( expand.grid( rep( list(0:1) , length(names(dt))-1 ) ) )
names(queries) = names(dt)[ 2:length(names(dt)) ] # rename Var1, Var2, Var3 to a, b, c

# Brute force through each possible combination to calculate prevalence
prevalence = rep( NA, nrow(queries) )
for( q in 1:nrow(queries) )
    prevalence[q] = dt[ a == queries[q, a] & b == queries[q, b] & c == queries[q, c] , sum(weight) ] / dt[ , sum(weight) ]


results = copy(queries)
results$prevalence = prevalence

results

输出是:

#   a b c prevalence
#1: 0 0 0 0.09771385
#2: 1 0 0 0.10105192
#3: 0 1 0 0.36229784
#4: 1 1 0 0.00000000
#5: 0 0 1 0.00000000
#6: 1 0 1 0.05993197
#7: 0 1 1 0.00000000
#8: 1 1 1 0.37900443

更新:原始问题有 42 个模拟观察结果,数据涵盖了每个可能的类别组合(a、b、c)。该问题已修改为仅包含 10 个模拟观察结果,因此会有没有观察结果的组合(且流行率为零)。

【问题讨论】:

【参考方案1】:

更新答案

方法一:

    使用CJ创建a,b,c的完整组合,然后加入dt(如@TMo's answer) 将每组的weight相加,然后除以totoal_weight NA 的出现是理性的。如果需要,您也可以使用nafill 函数填充0
total_weight = sum(dt$weight)
dt[CJ(a, b, c, unique = TRUE),  
   on = .(a, b, c)][, 
                    .( prevalence = sum(weight)/total_weight), 
                    by = .(a,b,c)]

#      a     b     c prevalence
#   <num> <num> <num>      <num>
#1:     0     0     0 0.09771385
#2:     0     0     1         NA
#3:     0     1     0 0.36229784
#4:     0     1     1         NA
#5:     1     0     0 0.10105192
#6:     1     0     1 0.05993197
#7:     1     1     0         NA
#8:     1     1     1 0.37900443

方法二:

dt2 = dt[,.( prevalence = sum(weight) / total_weight ), by = .(a,b,c)]
dt2[queries, on = .(a,b,c)]
# or `queries[, prevalence := fcoalesce(dt2[queries, prevalence])]`
#       a     b     c prevalence
#   <int> <int> <int>      <num>
#1:     0     0     0 0.09771385
#2:     1     0     0 0.10105192
#3:     0     1     0 0.36229784
#4:     1     1     0         NA
#5:     0     0     1         NA
#6:     1     0     1 0.05993197
#7:     0     1     1         NA
#8:     1     1     1 0.37900443

原答案

可以按组计算

dt[,.( prevalence = sum(weight) / dt[,sum(weight)] ), by = .(a,b,c)]
每个组对应您的类别 将每组的weight相加,然后除以总权重

【讨论】:

【参考方案2】:

这里有一些解决方案(在这两种情况下,您都可以将keyby 参数替换为by

如果您的数据集 (dt) 已经包含不同类别的所有可能组合,那么您可以这样做(如 @Peace Wang solution)

dt[, .(prevalence = sum(weight)/sum(dt$weight)), keyby=.(a, b, c)]

#        a     b     c prevalence
# 1:     0     0     0 0.10876301
# 2:     0     0     1 0.02135357
# 3:     0     1     0 0.03775363
# 4:     0     1     1 0.12806864
# 5:     1     0     0 0.18204696
# 6:     1     0     1 0.15197811
# 7:     1     1     0 0.25629705
# 8:     1     1     1 0.11373903

相反,如果数据集不包含不同类别的所有可能组合,那么您可以如下解决它(CJ(a, b, c, unique=TRUE) 计算所有组合并删除重复项)

dt[CJ(a, b, c, unique=TRUE), .(prevalence = sum(weight)/sum(dt$weight)), keyby=.(a, b, c), on=.(a, b, c)]

#        a     b     c prevalence
# 1:     0     0     0 0.10876301
# 2:     0     0     1 0.02135357
# 3:     0     1     0 0.03775363
# 4:     0     1     1 0.12806864
# 5:     1     0     0 0.18204696
# 6:     1     0     1 0.15197811
# 7:     1     1     0 0.25629705
# 8:     1     1     1 0.11373903

【讨论】:

好点!我更改了问题中的模拟数据,使其不包括类别/分类的每种可能组合(这更能代表我的实际问题)。但是,当我运行您的解决方案时,我只得到非零流行行,加上一个 NA 行。 `,`` abc 流行 1:NA NA NA NA 2:0 0 0 0.09771385 3:0 1 0 0.36229784 4:1 0 0 0.10105192 5:1 0 1 0.05993197 6:1 1 1 0.37900443 ```我会将您的解决方案标记为最佳。

以上是关于从包含许多二元分类列的 data.table 中观察的流行率估计的主要内容,如果未能解决你的问题,请参考以下文章

Match()和data.table的列的动态选择

二元分类器过于自信,无法用 sklearn 绘制 ROC 曲线?

如何将图像输入 CNN 以进行二元分类

data.table 分组所有列的总和

在 R 中,自定义由 dcast.data.table 创建的列的名称

NN 模型输出一个类别进行二元分类