根据R中的data.table列计算中值

根据R中的data.table列计算中值,r,data.table,lapply,rep,R,Data.table,Lapply,Rep,我试图计算一个跨多个列的中值,但是我的数据有点古怪。它看起来像下面的示例 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

我试图计算一个跨多个列的中值,但是我的数据有点古怪。它看起来像下面的示例

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=2

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

我曾经尝试过使用
rep()
lappy()
,但收效甚微,我正在寻找一些关于如何实现这一目标的明确指导。我知道对于
rep()
这样的人,我必须硬编码我的值以重复(例如
rep(0,2)
rep(10,2)
),这就是我所期望的。我正在努力创建一个列表或向量,每个列都有重复。

您需要一个字典来将列名翻译成相应的数字,然后它就相当简单了:

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

以下是一种按行
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

下面是另一个
数据。表
方式(假设唯一的
ID
):


这只是一种尝试,目的是在不改变形状的情况下获得@eddi的答案(我倾向于将其作为最后手段)。

以下是一种避免行操作和改变形状的方法:

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的风格,例如


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

示例中创建
dt
的代码与问题描述不匹配。这只是一个输入错误,但一开始让我感到困惑,因为所有的答案都与您预期的结果不同。对于ID=2,
“无”
“二十”
的数字互换。根据你的代码,你将得到ID=2的中位数(0,0,0,0,0,10,10,20,20)@Uwe好的,抓到了。我试图修复它,以便显示的内容与示例输入匹配。如果有问题,我怀疑OP会介意编辑;如果我们错了,他们总是可以把它们回滚。这个
.SDcols=none:twenty
很漂亮。我不知道你能做到。还有,什么是
.N
?@Bazz是的,.SDcols的快捷方式是最近才添加的
.N
指的是表中的行数,或者,如果有一个
by=
子句(就像在Arun的回答中),它指的是by-group.Nice-clean单行代码中的行数,这正是我试图实现的。我必须对我的价值观进行硬编码,这样才能最好地回答这个问题,尽管我也可以看到,如果我的价值观也在一个专栏中,它将如何适用。
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