У меня есть данные от субъектов с уникальными идентификаторами, которые появляются при нескольких посещениях, каждый из которых находится в отдельной строке фрейма данных. Некоторая информация, такая как пол или год рождения, может быть собрана только за одно посещение, но она имеет отношение к любому из посещений. Для посещений, где информация не была собрана, это поле будет NA. Итак, я создал функцию, которая будет копировать информацию субъекта для данного поля во все посещения, заменив NA. Это сработало, но код неуклюжий, и теперь, когда я изучаю аккуратную обработку данных, я хотел включить это, чтобы сделать код чище. Я также ожидал, что это ускорит процесс, но этого не произошло.
Во-первых, вот некоторые данные о игрушках:
data <- tibble(record_id = c(rep(LETTERS[1:4], 3)),
year1 = c(NA, NA, 2000, 2001, 2002, rep(NA, 7)),
year2 = c(rep(NA, 5), 2003, 2004, 2005, 2006, rep(NA, 3)))
Следующее даст желаемый результат:
data %>%
group_by(id) %>%
arrange(year1, .by_group = T) %>%
fill(year1) %>%
arrange(year2) %>%
fill(year2)
Перед тем, как привести в порядок, я создал этот код, и он отлично работал.
mash.old <- function(data, variable){
x <- data[!is.na(data[,variable]),] %>%
distinct(record_id, .keep_all = T)
x <- as.data.frame(x)
for(i in 1:nrow(data)){
if(is.na(data[i,variable]) &
data[i, "record_id"] %in% x$record_id){
id <- data[i, "record_id"]
data[i,variable] <- x[x$record_id == as.character(id),
variable]
}else{
next
}
}
rm(x, id, i)
return(data)
}
Я мог бы бежать
data <- mash.old(data, 'year1')
data <- mash.old(data, 'year2')
и получите желаемый результат.
Я хотел улучшить его, позволив ему принимать вектор переменных для выполнения функции, иметь возможность выбрать группирующую переменную (имя переменной субъектного идентификатора) и использовать dplyr / tidyr. Итак, я создал это:
mash.new <- function(data, variables, grouping.var = record_id){
for(i in variables){
data <- data %>%
group_by(!!enquo(grouping.var)) %>%
arrange((!!sym(i)), .by_group = T) %>%
fill(!!sym(i)) %>%
ungroup()
}
return(data)
}
Теперь mash.new(data, c('year1, 'year2'))
вернет ожидаемые результаты. Нет проблем для этого небольшого фрейма данных.
Мой фактический фрейм данных содержит 15762 строки, и я хотел запустить функцию для двенадцати переменных. mash.old()
На это ушло около четырех минут. mash.new()
сказал, что это займет около трех часов, поэтому я остановил это примерно через пять минут или около того.
Мой вопрос, почему огромная разница? Я смотрел на свою первую функцию как на любительскую и неуклюжую, я думал, что улучшаюсь Это просто, что процесс группировки, упорядочения, заполнения и группировки требует гораздо больше вычислительной мощности? Есть ли лучший способ написать это? Я самоучка и просто пытаюсь улучшить свои навыки.
Редактировать
Спасибо за помощь. Вот функция, которую я в конечном итоге использовал. Хотя версия Коул data.table
быстрее, я решил придерживаться метода dplyr
, просто потому, что это то, что я знаю.
mash <- function(data, variables, grouping.var = record_id){
data <- data %>%
arrange(!!enquo(grouping.var)) %>%
group_by(!!enquo(grouping.var)) %>%
mutate_at(vars(!!!variables),
function(x) zoo::na.locf(x[order(x)], na.rm = F)) %>%
ungroup()
return(data)
}
#Note that if there are two different entries for a given subject in a
#variable, this will fill with the data that comes last in the sort order