Чтобы помочь тем, кто может пройти мимо этого поста в будущем, я использовал ответ @Cole, чтобы найти первое не пропущенное значение для каждой переменной для каждого идентификатора группировки:
## Character Vars ##
vars_char <- names(dt)[sapply(dt, is.character)]
dt_char <- melt(dt,
id.vars = "id",
measure.vars = vars_char,
na.rm = T)
dt_char <- dt_char[, .SD[1], by = .(id, variable)]
dt_char <- dcast(dt_char, id ~ variable)
## Integer Vars ##
vars_int <- names(dt)[sapply(dt, is.integer)]
vars_int <- vars_int[vars_int != "id"]
dt_int <- melt(dt,
id.vars = "id",
measure.vars = vars_int,
na.rm = T)
dt_int <- dt_int[, .SD[1], by = .(id, variable)]
dt_int <- dcast(dt_int, id ~ variable)
## Double Vars ##
vars_doub <- names(dt)[sapply(dt, is.double)]
dt <- melt(dt,
id.vars = "id",
measure.vars = vars_doub,
na.rm = T)
dt <- dt[, .SD[1], by = .(id, variable)]
dt <- dcast(dt, id ~ variable)
## Combine Variables Types ##
dt <- Reduce(function(x, y){merge(x, y, by = "id", all = T)}, list(dt_int, dt, dt_char))
Выше разделен на три, чтобы избежать проблем с памятью, связанных со всеми значениями, приведенными к типу символа.Если это не проблема для вашего набора данных, то будет работать следующее:
dt <- melt(dt,
id.vars = "id",
na.rm = T)
dt <- dt[, .SD[1], by = .(id, variable)]
dt <- dcast(dt, id ~ variable)
Для начального примера набора данных для его выполнения требуется значительно больше времени, чем для любой из функций firstnonmiss()
.
### Benchmarking ###
t <- microbenchmark(
"which()[1]" = dt[, lapply(.SD, firstnonmiss_1), by = id],
"first()" = dt[, lapply(.SD, firstnonmiss_2), by = id],
"[1]" = dt[, lapply(.SD, firstnonmiss_3), by = id],
"reshape" = dcast(melt(dt, id.vars = "id", na.rm = T)[, .SD[1], by = .(id, variable)], id ~ variable),
times = 1e4
)
t
## Unit: microseconds
## expr min lq mean median uq max neval
## which()[1] 416.199 434.8970 497.6187 447.8205 471.3300 19577.46 10000
## first() 400.774 421.4570 472.8580 434.2320 458.2420 31315.78 10000
## [1] 389.710 408.6455 464.6562 421.2085 442.8305 17822.18 10000
## reshape 2052.353 2120.1925 2400.9130 2178.8150 2285.6500 96451.59 10000
units <- attributes(print(t))[["unit"]]
autoplot(t) +
labs(x = "Function", y = paste0("Timings, (", units, ")")) +
scale_x_discrete() +
scale_y_log10() +
geom_violin(fill = "skyblue", alpha = 0.5) +
theme_light() +
theme(axis.text.y = element_text(family = "Monaco", angle = 90, hjust = 0.5))
Однако он работает намного быстрее, чем функции firstnonmiss()
в очень больших наборах данных (60 секунд против 11 минут).