从 R 中的 data.table 列计算中位数
calculate median from data.table columns in R
我正在尝试计算多个列的中值,但我的数据有点古怪。看起来像下面的例子。
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
中的table列表示该值出现的次数。我想计算出现的中位数。
例如 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)
),这就是我所期望的。我只是在努力创建一个列表或向量,其中包含每列的重复项。
你需要一个字典来将列名翻译成相应的数字,然后就相当简单了:
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
这里有一个rowwise
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
这是另一种 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 的答案(我倾向于将其用作最后的手段)。
这里有一种方法可以避免按行操作和整形:
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)
而不是 names(.SD)
。
我正在尝试计算多个列的中值,但我的数据有点古怪。看起来像下面的例子。
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
中的table列表示该值出现的次数。我想计算出现的中位数。
例如 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)
),这就是我所期望的。我只是在努力创建一个列表或向量,其中包含每列的重复项。
你需要一个字典来将列名翻译成相应的数字,然后就相当简单了:
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
这里有一个rowwise
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
这是另一种 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 的答案(我倾向于将其用作最后的手段)。
这里有一种方法可以避免按行操作和整形:
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)
而不是 names(.SD)
。