【问题标题】:Count all observed factor levels, also those that aren't observed计算所有观察到的因子水平,以及未观察到的因子水平
【发布时间】:2026-01-12 15:05:01
【问题描述】:

我们有 DF

df <- data.frame(group=as.factor(rep(c("UP","DOWN"),6)),variables=(rep(c("sex","smoke","sport"),each=4))
             ,values=as.factor(c(1,1,1,0  ,1,1,0,0, 1,1,1,1)))

   group variables values
1     UP       sex      1
2   DOWN       sex      1
3     UP       sex      1
4   DOWN       sex      0
5     UP     smoke      1
6   DOWN     smoke      1
7     UP     smoke      0
8   DOWN     smoke      0
9     UP     sport      1
10  DOWN     sport      1
11    UP     sport      1
12  DOWN     sport      1
> 

现在我想知道所有级别的所有计数

library(plyr)

这个命令几乎完全符合我的要求

count(df, c("variables", "group", "values"))

 variables group values freq
1      sex  DOWN     0    1
2      sex  DOWN     1    1
3      sex    UP     1    2
4    smoke  DOWN     0    1
5    smoke  DOWN     1    1
6    smoke    UP     0    1
7    smoke    UP     1    1
8    sport  DOWN     1    2
9    sport    UP     1    2

我还想计算未观察到的因子水平。就像我在以下输出中所做的那样。

 variables group values freq
1      sex  DOWN     0    1
2      sex  DOWN     1    1
3      sex    UP     0    0  <-- 
4      sex    UP     1    2
5    smoke  DOWN     0    1
6    smoke  DOWN     1    1
7    smoke    UP     0    1  
8    smoke    UP     1    1
9    sport  DOWN     0    0  <--
10   sport  DOWN     1    2
11   sport    UP     0    0  <--
12   sport    UP     1    2

如何才能实现上述输出?

【问题讨论】:

  • 一种选择是使用expand.grid 生成所有可能性的df,然后使用join 这个count 的结果
  • 有人可以为此示例预编码吗?

标签: r count aggregate plyr r-factor


【解决方案1】:

你也可以用更少的代码行 data.table 做到这一点:

library(data.table)
dt <- setDT(df)
cj <- CJ(dt$variables, dt$group, dt$values, unique = TRUE)
dt[, .N, keyby = c("variables", "group", "values")][cj][is.na(N), N := 0]

print(dt)    
    variables group values N
 1:       sex  DOWN      0 1
 2:       sex  DOWN      1 1
 3:       sex    UP      0 0
 4:       sex    UP      1 2
 5:     smoke  DOWN      0 1
 6:     smoke  DOWN      1 1
 7:     smoke    UP      0 1
 8:     smoke    UP      1 1
 9:     sport  DOWN      0 0
10:     sport  DOWN      1 2
11:     sport    UP      0 0
12:     sport    UP      1 2

说明

setDT()data.frame 转换为data.table 通过引用,即,无需复制。

CJ() 是一个交叉连接。它从向量的叉积形成data.table。因此,它是expand.griddata.table 版本。 参数unique = TRUE 是一种方便的替代方法,可以将每个参数包装在level()unique() 中。

按组计数使用dt[, .N, keyby = c("variables", "group", "values")]

   variables group values N
1:       sex  DOWN      0 1
2:       sex  DOWN      1 1
3:       sex    UP      1 2
4:     smoke  DOWN      0 1
5:     smoke  DOWN      1 1
6:     smoke    UP      0 1
7:     smoke    UP      1 1
8:     sport  DOWN      1 2
9:     sport    UP      1 2

现在,dt[, .N, keyby = c("variables", "group", "values")][cj](右)以所有可能的组合加入 CJ() 结果。

最后,[is.na(N), N := 0]N 列中的所有NAs 替换为0

【讨论】:

    【解决方案2】:

    你也可以这样做:

    library(plyr)
    d1 <- count(df, c("variables", "group", "values"))
    d2 <- expand.grid(list(levels(df$variables), levels(df$group), levels(df$values)))
    d2$freq <- 0
    colnames(d2) <- colnames(d1)
    m <- merge(d1, d2, by = c("variables", "group", "values"), all.y  = T)[,-5]
    m[is.na(m)] <- 0
    
       # variables group values freq.x
    # 1        sex  DOWN      0      1
    # 2        sex  DOWN      1      1
    # 3        sex    UP      0      0
    # 4        sex    UP      1      2
    # 5      smoke  DOWN      0      1
    # 6      smoke  DOWN      1      1
    # 7      smoke    UP      0      1
    # 8      smoke    UP      1      1
    # 9      sport  DOWN      0      0
    # 10     sport  DOWN      1      2
    # 11     sport    UP      0      0
    # 12     sport    UP      1      2
    

    这个想法是制作一个数据框(命名为d2),在其中生成variablesgroupvalues的所有可能组合,然后将其与d1合并。

    【讨论】:

    • 我像你一样尝试了expand.grid方法+合并。 Expand.grid 非常强大。谢谢。
    • 如果有更快的方法我很想知道。但是 expand.grid 已经可以正常工作了。
    【解决方案3】:

    这是一个想法。您可以根据freq 变量复制行。如果频率为 2,则复制的行将具有唯一的行名,我们可以将其定位并将其 freqvalues 更改为 0。

    df1 <- plyr::count(df, c("variables", "group", "values"))
    df2 <- df1[rep(row.names(df1), df1$freq),]
    df2$freq[grep('.', row.names(df2), fixed = TRUE)] <- 0
    df2$values[df2$freq == 0] <- 0
    
    df2
    #     variables group values freq
    #1         sex  DOWN      0    1
    #2         sex  DOWN      1    1
    #3         sex    UP      1    2
    #3.1       sex    UP      0    0
    #4       smoke  DOWN      0    1
    #5       smoke  DOWN      1    1
    #6       smoke    UP      0    1
    #7       smoke    UP      1    1
    #8       sport  DOWN      1    2
    #8.1     sport  DOWN      0    0
    #9       sport    UP      1    2
    #9.1     sport    UP      0    0
    

    如果你想重置你的行名,那么, row.names(df2) &lt;- NULL

    【讨论】:

    • 还有其他方法吗?
    • 我认为这种方式不适用于我的大数据集。组不平衡,有时频率数高达 130。这将导致许多“新”生成行”
    • 那么 expand.grid 在 cmets 中提到的可能是您最好的选择