Функция ниже вычисляет среднее значение вектора. Однако сначала он проверяет долю NA
, присутствующих в векторе, и, если она выше заданного порога, возвращает NA вместо среднего значения.
Моя проблема в том, что моя текущая реализация довольно неэффективна. Это занимает в 7 раз больше времени, чем просто запуск mean(vec, na.rm=TRUE)
Я попробовал альтернативный метод с использованием na.omit
, но он еще медленнее.
Учитывая размер моих данных, выполнение одного lapply
занимает более 40 минут.
Любые предложения о том, как выполнить ту же задачу быстрее?
ОБНОВЛЕНИЕ - RE: решение @thelatemail и комментарий @Arun:
- Я выполняю эту функцию для нескольких сотен групп, каждая из которых имеет разный размер. Образцы данных (первоначально), предоставленные в этом вопросе, были предоставлены в виде аккуратного фрейма данных просто для простоты создания искусственных данных.
Альтернативные образцы данных, чтобы избежать путаницы
# Sample Data
# ------------
set.seed(1)
# slightly different sizes for each group
N1 <- 5e3
N2 <- N1 + as.integer(rnorm(1, 0, 100))
# One group has only a moderate amount of NA's
SAMP1 <- rnorm(N1)
SAMP1[sample(N1, .25 * N1, FALSE)] <- NA # add in NA's
# Another group has many NA's
SAMP2 <- rnorm(N2)
SAMP2[sample(N2, .95 * N2, FALSE)] <- NA # add in large number of NA's
# put them all in a list
SAMP.NEW <- list(SAMP1, SAMP2)
# keep it clean
rm(SAMP1, SAMP2)
# Execute
# -------
lapply(SAMP.NEW, meanIfThresh)
Исходные образцы данных, функции и т. д.
# Sample Data
# ------------
set.seed(1)
rows <- 20000 # actual data has more than 7M rows
cols <- 1000
SAMP <- replicate(cols, rnorm(rows))
SAMP[sample(length(SAMP), .25 * length(SAMP), FALSE)] <- NA # add in NA's
# Select 5 random rows, and have them be 90% NA
tooSparse <- sample(rows, 5)
for (r in tooSparse)
SAMP[r, sample(cols, cols * .9, FALSE)] <- NA
# Function
# ------------
meanIfThresh <- function(vec, thresh=12/15) {
# Calculates the mean of vec, however,
# if the number of non-NA values of vec is less than thresh, returns NA
# thresh : represents how much data must be PRSENT.
# ie, if thresh is 80%, then there must be at least
len <- length(vec)
if( (sum(is.na(vec)) / len) > thresh)
return(NA_real_)
# if the proportion of NA's is greater than the threshold, return NA
# example: if I'm looking at 14 days, and I have 12 NA's,
# my proportion is 85.7 % = (12 / 14)
# default thesh is 80.0 % = (12 / 15)
# Thus, 12 NAs in a group of 14 would be rejected
# else, calculate the mean, removing NA's
return(mean(vec, na.rm=TRUE))
}
# Execute
# -----------------
apply(SAMP, 1, meanIfThresh)
# Compare with `mean`
#----------------
plain <- apply(SAMP, 1, mean, na.rm=TRUE)
modified <- apply(SAMP, 1, meanIfThresh)
# obviously different
identical(plain, modified)
plain[tooSparse]
modified[tooSparse]
microbenchmark( "meanIfThresh" = apply(SAMP, 1, meanIfThresh)
, "mean (regular)" = apply(SAMP, 1, mean, na.rm=TRUE)
, times = 15L)
# With the actual data, the penalty is sevenfold
# Unit: seconds
# expr min lq median uq max neval
# meanIfThresh 1.658600 1.677472 1.690460 1.751913 2.110871 15
# mean (regular) 1.422478 1.485320 1.503468 1.532175 1.547450 15
SAMP[sample(SAMP, .25 * length(SAMP), FALSE)] <- NA
выдает мне ошибку в R3.0.1only 0's may be mixed with negative subscripts
- person thelatemail   schedule 13.06.2013SAMP[sample(seq_along(SAMP), .25 * length(SAMP))] <- NA
? - person thelatemail   schedule 13.06.2013SAMP[sample(length(SAMP), .25 * length(SAMP), FALSE)] <- NA
(исправлено в редактировании) - person Ricardo Saporta   schedule 13.06.2013