Скажем, у меня есть таблица данных.
prueba <- data.table(aa=1:7,
bb=c(1,2,NA,NA,3,1,1),
cc=c(1,2,NA,NA,3,1,1),
YEAR=c(1,1,1,2,2,2,2))
и я хочу получить таблицу пропущенных в каждом столбце заданного набора, например aa, bb и cc.
Результат должен быть таким:
aa bb cc 1: 0 2 2 0: 7 0 0
или его транспонированная форма или с другими этикетками.
Я пробовал с
prueba[,lapply(.SD, function(x) as.list( table(
factor(is.na(x), levels=c("0","1"))))),
.SDcols=c("aa","bb", "cc")]
но вместо этого я получаю это:
aa bb cc 1: 7 5 5 0: 7 2 2
Я думаю, это связано с тем, что в таблице выпадают неиспользуемые уровни. Но я безуспешно пробовал с xtabs и всякими взломами.
Я мог бы получить что-нибудь уродливое с
sapply(c("aa","bb","cc"), function(x) prueba[,as.list(
table(is.na(get(x))))])
Вот подход с базовым R:
rbind(tmp <- colSums(is.na(prueba[ , -"YEAR"])), nrow(prueba) - tmp)
# aa bb cc
# [1,] 0 2 2
# [2,] 7 5 5
Это быстро, но мне нужно решение с table (), которое можно было бы обобщить с большим количеством факторов.
Хорошо, я нашел решение, немного запутанное:
prueba[, lapply(.SD, function(x) as.list( table(factor(
is.na(x), levels=c(F,T)))) ), .SDcols=c("aa","bb", "cc")]
Должен быть способ попроще.
Может быть, использовать table
:
prueba[, table(is.na(.SD), names(.SD)[col(.SD)]), .SDcols=aa:cc]
aa bb cc
FALSE 7 5 5
TRUE 0 2 2
По сути, это рассматривается как матрица.
Некоторые альтернативы:
prueba[, table(is.na(.SD), rep(names(.SD), each=.N)), .SDcols=aa:cc]
melt(prueba[, aa:cc])[, table(is.na(value), variable)]
Что означают имена (.SD) [col (.SD)]? Я не знаю, почему ваш метод занимает в два раза больше времени, чем мой, на большом фрейме данных. Метод Свена в 10 раз быстрее, хотя мне нужно было решение, использующее команду «table ()».
@skan col(.SD)
- это матрица того же размера, что и .SD, с единицами в первом столбце, 2 во втором столбце и так далее. Думаю, имеет смысл, что это будет медленно. Я добавил еще пару вариантов, но не уверен, что они будут работать лучше.
Вот еще одно предложение, которое должно быть достаточно общим. Во-первых, составьте таблицу непредвиденных обстоятельств на основе требований. Затем преобразуйте вывод таблицы в список и rbindlist
все результаты вместе. Наконец, замените NA на 0 counts.
output <- prueba[, rbindlist(
lapply(.SD, function(x) as.list(table(is.na(x)))),
fill=TRUE,
idcol=TRUE),
.SDcols=aa:cc]
output[, lapply(.SD, function(x) replace(x, is.na(x), 0L))]
вывод:
.id FALSE TRUE
1: aa 7 0
2: bb 5 2
3: cc 5 2
изменить: добавить еще один общий подход:
#build and flatten contingency table
tab <- prueba[, as.list(unlist(lapply(.SD, function(x) table(is.na(x))))),
.SDcols=aa:cc]
#melt, split original column names and then pivot
dcast(
melt(tab, measure.vars=names(tab))[,
c("V1","Factor") := tstrsplit(variable, split = "\\.")],
Factor ~ V1,
function(x) x[1L],
fill=0L)
вывод:
Factor aa bb cc
1: FALSE 7 5 5
2: TRUE 0 2 2
изменить: добавить тайминги
set.seed(0L)
sz <- 1e6
nc <- 10
DT <- as.data.table(matrix(sample(c(NA_integer_, 1L:10L), sz*nc, TRUE), ncol=nc))
setnames(DT, paste0("C", 1L:nc))
cols <- names(DT)
mtd1 <- function() {
DT[, table(is.na(.SD), names(.SD)[col(.SD)]), .SDcols=cols]
}
mtd2 <- function() {
DT[, table(is.na(.SD), rep(names(.SD), each=.N)), .SDcols=cols]
}
mtd3 <- function() {
melt(DT[, ..cols], measure.vars=cols)[, table(is.na(value), variable)]
}
mtd4 <- function() {
tab <- DT[, as.list(unlist(lapply(.SD, function(x) table(is.na(x))))),
.SDcols=cols]
dcast(melt(tab, measure.vars=names(tab))[, c("V1","Factor") := tstrsplit(variable, split = "\\.")],
Factor ~ V1, function(x) x[1L], fill=0L)
}
mtd5 <- function() {
output <- DT[, rbindlist(lapply(.SD, function(x) as.list(table(is.na(x)))), fill=TRUE, idcol=TRUE),
.SDcols=cols]
output[, lapply(.SD, function(x) replace(x, is.na(x), 0L))]
}
library(microbenchmark)
microbenchmark(mtd1(), mtd2(), mtd3(), mtd4(), mtd5(), times=3L)
тайминги:
Unit: seconds
expr min lq mean median uq max neval cld
mtd1() 5.044369 5.049252 5.086534 5.054135 5.107617 5.161100 3 b
mtd2() 5.106796 5.110014 5.474269 5.113232 5.658005 6.202778 3 b
mtd3() 2.395127 2.461463 2.509938 2.527799 2.567344 2.606888 3 a
mtd4() 2.138672 2.142300 2.145895 2.145927 2.149506 2.153084 3 a
mtd5() 2.113367 2.175346 2.228162 2.237325 2.285560 2.333794 3 a
Я думаю, почти с базовым R, поскольку - "ГОД" - это функциональность data.table