在R中从data.table列计算中位数

15
我正在尝试计算多列的中位数,但是我的数据有些问题。它看起来像以下示例。

我正在尝试计算多列的中位数,但是我的数据有些问题。它看起来像以下示例。

library(data.table)

dt <- data.table("ID" = c(1,2,3,4),"none" = c(0,5,5,3), 
                 "ten" = c(3,2,5,4),"twenty" = c(0,2,3,1))


   ID none ten twenty
1:  1    0   3      0
2:  2    5   2      2
3:  3    5   5      3
4:  4    3   4      1
在表格中,每一列代表该值出现的次数。我想要计算中位数出现次数。
例如,对于ID = 1.
median(c(10, 10, 10))

我想要创建的计算公式。

对于ID = 2

median(c(0, 0, 0, 0, 0, 10, 10, 20, 20))

我尝试过使用rep()lapply(),但成功有限,并且需要关于如何实现的清晰指导。我知道像rep()这样的函数需要硬编码来重复值(例如rep(0,2)rep(10,2)),这也是我期望的。我只是不知道如何创建一个包含每列重复次数的列表或向量。


在您的示例中创建“dt”的代码与问题描述不匹配。这只是一个笔误,最初使我感到困惑,因为所有答案都与您的期望结果不同。对于ID = 2,“none”和“twenty”的数字被交换了。使用您的代码,您将获得ID = 2的中值 median(0,0,0,0,0,10,10,20,20) - Uwe
@Uwe 好的,发现得不错。我已经尝试修复它,以便显示的内容与示例输入匹配。如果有问题,我相信 OP 不会介意编辑;如果我们弄错了,他们总是可以回滚。 - Frank
4个回答

16

这是另一种使用 data.table 的方式(假设有唯一的 ID):

dt[, median(rep(c(0, 10, 20), c(none, ten, twenty))), by=ID]
#    ID V1
# 1:  1 10
# 2:  2  0
# 3:  3 10
# 4:  4 10

这只是一个试图获取 @eddi 答案的尝试,而不需要进行重塑(这是我倾向于使用作为最后手段的方法)。


很不错的干净的单行代码,这正是我试图实现的目标。我必须硬编码我的值,所以这是最好的答案,尽管我也可以看到如果我的值也在一列中,它将是合适的。 - Dan

12

你需要一本词典来将列名翻译为相应的数字,然后就相当容易了:

dict = data.table(name = c('none', 'ten', 'twenty'), number = c(0, 10, 20))

melt(dt, id.var = 'ID')[
  dict, on = c(variable = 'name')][, median(rep(number, value)), by = ID]
#   ID V1
#1:  1 10
#2:  2  0
#3:  3 10
#4:  4 10

6

以下是一种避免逐行操作和重塑的方法:

dt[, m := {
    cSD  = Reduce(`+`, .SD, accumulate=TRUE)
    k    = floor(cSD[[length(.SD)]]/2)

    m    = integer(.N)
    for(i in seq_along(cSD)) {
        left = m == 0L
        if(!any(left)) break
        m[left] = i * (cSD[[i]][left] >= k[left])
    }
    names(.SD)[m]
}, .SDcols=none:twenty]

这提供了

   ID none ten twenty    m
1:  1    0   3      0  ten
2:  2    5   2      2 none
3:  3    5   5      3  ten
4:  4    3   4      1  ten

对于循环,我借鉴了@alexis_laz的风格,例如https://dev59.com/3l0a5IYBdhLWcg3wTHBt#30513197/

我跳过了列名的翻译,但那很简单。您可以在末尾使用c(0,10,20)而不是names(.SD)


.SDcols = none:twenty 很巧妙。我不知道你可以那样做。此外,什么是 .N - CJB
2
@Bazz 是的,.SDcols 的快捷方式是最近才添加的。.N 指的是表中的行数,或者如果有 by= 子句(就像在 Arun 的答案中一样),它指的是按组分组后的行数。 - Frank

3

以下是dplyr的逐行方式:

dt %>% rowwise %>% 
       do(med = median(c(rep(0, .$none), rep(10, .$ten), rep(20, .$twenty)))) %>%  
       as.data.frame
  med
1  10
2   0
3  10
4  10

受@Arun回答的启发,以下方法也可行:

dt %>% group_by(ID) %>% 
       summarise(med = median(rep(c(0, 10, 20), c(none, ten, twenty))))

Source: local data table [4 x 2]

     ID   med
  (dbl) (dbl)
1     1    10
2     2     0
3     3    10
4     4    10

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接