Я часто работаю с фреймами данных, и мне приходится выполнять некоторые сложные преобразования / манипуляции с данными по подгруппам, определенным в одном из столбцов. Я знаю о dplyr и group_by и знаю, что многие вещи можно решить с помощью group_by. Однако часто мне приходится делать довольно сложные вычисления и в конечном итоге просто использовать цикл for.
Мне было интересно узнать о существовании какого-то другого общего подхода или парадигмы, которая была бы более быстрой / более элегантной. Может быть, карта (с которой я не очень знаком)?
Ниже приведен пример. Обратите внимание - это подделка и бессмысленность. Итак, давайте проигнорируем, почему мне нужно делать эти вещи или тот факт, что в столбце может быть 2 последовательных NA и т. Д. Это не главное в моем вопросе. Дело в том, что часто мне приходится работать «в рамках ограничений подгруппы», а затем - внутри этой подгруппы - я должен выполнять операции по столбцам, по строкам, а иногда даже по клеткам.
Я также понимаю, что мог бы, вероятно,поместите большую часть этого кода в функцию, разбейте мой фрейм данных на список, основанный на 'group', примените эту функцию к каждому элементу этого списка и затем сделайте do.call (rbind ...) в конце. Но так ли это?
Большое спасибо за любые подсказки!
library(dplyr)
library(forcats)
set.seed(123)
x <- tibble(group = c(rep('a', 10), rep('b', 10), rep('c', 10)),
attrib = c(sample(c("one", "two", "three", "four"), 10, replace = T),
sample(c("one", "two", "three"), 10, replace = T),
sample(c("one", "three", "four"), 10, replace = T)),
v1 = sample(c(1:5, NA), 30, replace = T),
v2 = sample(c(1:5, NA), 30, replace = T),
v3 = sample(c(1:5, NA), 30, replace = T),
n1 = abs(rnorm(30)), n2 = abs(rnorm(30)), n3 = abs(rnorm(30)))
v_vars = paste0("v", 1:3)
n_vars = paste0("n", 1:3)
results <- NULL # Placeholder for final results
for(i in seq(length(unique(x$group)))) { # loop through groups
mygroup <- unique(x$group)[i]
mysubtable <- x %>% filter(group == mygroup)
# IMPUTE NAs in v columns
# Replace every NA with a mean of values above and below it; and if it's the first or
# the last value, with the mean of 2 values below or above it.
for (v in v_vars){ # loop through v columns
which_nas <- which(is.na(mysubtable[[v]])) # create index of NAs for column v
if (length(which_nas) == 0) next else {
for (na in which_nas) { # loop through indexes of column values that are NAs
if (na == 1) {
mysubtable[[v]][na] <- mean(c(mysubtable[[v]][na + 1],
mysubtable[[v]][na + 2]), na.rm = TRUE)
} else if (na == nrow(mysubtable)) {
mysubtable[[v]][na] <- mean(c(mysubtable[[v]][na - 2],
mysubtable[[v]][na - 1]), na.rm = TRUE)
} else {
mysubtable[[v]][na] <- mean(c(mysubtable[[v]][na - 1],
mysubtable[[v]][na + 1]), na.rm = TRUE)
}
} # end of loop through NA indexes
} # end of else
} # end of loop through v vars
# Aggregate v columns (mean) for each value of column 'attrib'
result1 <- mysubtable %>% group_by(attrib) %>%
summarize_at(v_vars, mean)
# Aggregate n columns (sum) for each value of column 'attrib'
result2 <- mysubtable %>% group_by(attrib) %>%
summarize_at(n_vars, sum)
# final result should contain the name of the group
results[[i]] <- cbind(mygroup, result1, result2[-1])
}
results <- do.call(rbind, results)