Как свернуть несколько записей в одну при удалении значений NA

Скажем, у меня есть следующий кадр данных df

name <- c("Bill", "Rob", "Joe", "Joe")
address <- c("123 Main St", "234 Broad St", NA, "456 North Ave")
favteam <- c("Dodgers", "Mets", "Pirates", NA)

df <- data.frame(name = name, 
                 address = address,
                 favteam = favteam)
df

Что выглядит так:

  name       address favteam
1 Bill   123 Main St Dodgers
2  Rob  234 Broad St    Mets
3  Joe          <NA> Pirates
4  Joe 456 North Ave    <NA>

Что я хочу сделать, так это свернуть (объединить) строки по имени (или, вообще, любому количеству группирующих переменных) и любое другое значение, кроме NA, заменить значение NA в окончательных данных, например так:

df_collapse <- foo(df)

  name   address        favteam
1 Bill   123 Main St    Dodgers
2  Rob   234 Broad St      Mets
3  Joe   456 North Ave  Pirates

person mcjudd    schedule 13.02.2015    source источник
comment
Может ли Джо изменить свое мнение о своей команде или исправить свой адрес во второй или последующих записях?   -  person vpipkt    schedule 14.02.2015
comment
Джо живет вне сети и активно избегает сборщиков данных. Нам удалось разыскать его только дважды, и он был категорически против того, чтобы рассказывать нам что-либо о своей жизни, но, к счастью, он любит говорить о бейсболе, так что...   -  person mcjudd    schedule 14.02.2015
comment
Шутки в сторону, разные ответы, не относящиеся к АН, добавляют еще одну морщинку к моей проблеме, но я решил, что буду делать это шаг за шагом. Любые мысли о реализации иерархии на основе порядка уровней в факторной переменной? На такой вопрос, похоже, не ответили на SO...   -  person mcjudd    schedule 14.02.2015
comment
Что вы подразумеваете под реализацией иерархии, основанной на порядке уровней в факторной переменной? Можете ли вы объяснить немного подробнее? Что вы ожидаете в случае разных ответов, не относящихся к АН, на одно имя?   -  person talat    schedule 14.02.2015


Ответы (1)


Вот вариант с dplyr:

library(dplyr)

df %>%
  group_by(name) %>%
  summarise_each(funs(first(.[!is.na(.)]))) # or summarise_each(funs(first(na.omit(.))))

#Source: local data frame [3 x 3]
#
#  name       address favteam
#1 Bill   123 Main St Dodgers
#2  Joe 456 North Ave Pirates
#3  Rob  234 Broad St    Mets

И с data.table:

library(data.table)
setDT(df)[, lapply(.SD, function(x) x[!is.na(x)][1L]), by = name]
#   name       address favteam
#1: Bill   123 Main St Dodgers
#2:  Rob  234 Broad St    Mets
#3:  Joe 456 North Ave Pirates

Or

setDT(df)[, lapply(.SD, function(x) head(na.omit(x), 1L)), by = name]

Редактировать:

Вы говорите, что в ваших фактических данных у вас разное количество ответов, не относящихся к АН, на одно имя. В этом случае может помочь следующий подход.

Рассмотрим этот измененный образец данных (посмотрите на последнюю строку):

name <- c("Bill", "Rob", "Joe", "Joe", "Joe")
address <- c("123 Main St", "234 Broad St", NA, "456 North Ave", "123 Boulevard")
favteam <- c("Dodgers", "Mets", "Pirates", NA, NA)

df <- data.frame(name = name, 
                 address = address,
                 favteam = favteam)

df
#  name       address favteam
#1 Bill   123 Main St Dodgers
#2  Rob  234 Broad St    Mets
#3  Joe          <NA> Pirates
#4  Joe 456 North Ave    <NA>
#5  Joe 123 Boulevard    <NA>

Затем вы можете использовать этот подход data.table для получения ответов, не относящихся к NA, число которых может различаться по имени:

setDT(df)[, lapply(.SD, function(x) unique(na.omit(x))), by = name]
#   name       address favteam
#1: Bill   123 Main St Dodgers
#2:  Rob  234 Broad St    Mets
#3:  Joe 456 North Ave Pirates
#4:  Joe 123 Boulevard Pirates
person talat    schedule 13.02.2015
comment
Отличный ответ - спасибо за внимание к деталям, @docendo. Меня особенно привлекает решение dplyr, так как оно было в моем списке, чтобы лучше ознакомиться с синтаксисом этого пакета. Для этой части: summarise_each(funs(first(.[!is.na(.)]))), является ли период сокращенным обозначением df, сгруппированного по name? Я не знал, что dplyr хорошо играется с индексами. Кроме того, было бы очень признательно, если бы вы могли порекомендовать мне подробное руководство по тонкостям dplyr. - person mcjudd; 14.02.2015
comment
@mcjudd, рад, что это сработало :) . в summarise_each относится к текущим данным, которые а) сгруппированы и б) по столбцам. Таким образом, first(.[!is.na(.)]) означает: в каждом столбце, который мы суммируем, и в каждой группе name в этом столбце берем первую точку данных, которая не является NA, и возвращаем ее как суммарное значение в этом столбце для этой группы. К сожалению, я не могу рассказать вам много об уроках dplyr. Вы найдете много, если просто погуглите, например это от Хэдли. - person talat; 14.02.2015
comment
@mcjudd, я не уверен, что точно понимаю, что вы имеете в виду, но вы можете попробовать расширить канал dplyr с помощью ... %>% mutate_each(funs(replace(., which(. == 0), 1)). Вы также можете использовать ifelse, но заменить быстрее. Технически вы также можете сделать это внутри сводки для каждого, но это сделает ее менее читаемой, и, что более важно, вам не нужно делать это по группам (и после суммирования данные больше не группируются, поэтому будет лучше с мутировать каждый потом ). - person talat; 17.02.2015
comment
Извините, глупый вопрос. Догадаться. summarise_each(funs(first(.[!is.na(.)]), max)) :) - person mcjudd; 17.02.2015
comment
Интересно, однако - когда вы применяете более одной функции, столбцы, к которым вы применили функции, принимают имя функции в качестве суффикса... например, var1_max, var2_first. Можно ли отключить это поведение? - person mcjudd; 18.02.2015
comment
Что бы вы хотели вместо этого? Это предполагаемое поведение, чтобы избежать дублирования имен столбцов, что обычно имеет большой смысл. - person talat; 18.02.2015
comment
@mcjudd, также см. stackoverflow.com/questions/27027347/ для связанного вопроса - person talat; 18.02.2015