Объединение/слияние строк, но сохранение «доминирующих» значений

#r #dataframe #dplyr #character #aggregate

Вопрос:

Моя проблема кажется довольно тривиальной, но, похоже, я не придумал подходящих поисковых терминов.

Мои данные таковы:

 data lt;- data.frame(ID = c(1,1,2,3,3),  V1 = c("A","B","A","B","C"),  V2 = c("C","B",NA,"B","A"),  V3 = c("A","B","C","B",NA))  

Я хочу объединить или объединить строки по идентификатору и сохранить только одну строку на идентификатор с «самым высоким» значением в каждом столбце. В моем примере я хотел бы расставить приоритеты C над B над A.

После желаемой операции мои данные будут выглядеть следующим образом:

 | ID | V1 | V2 | V3 | | -- | -- | -- | -- | | 1 | B | C | B | | 2 | A | NA | C | | 3 | C | B | B |  

Любые намеки будут высоко оценены! Dplyr предпочтительнее, но в этом нет необходимости. Спасибо!

Правка: Все решения (спасибо вам за это!) воспользовались тем фактом, что буквы «упорядочены» в R.

Давайте возьмем этот пример данных:

 data lt;- data.frame(ID = c(1,1,2,3,3),  V1 = c("yes","no","yes","no","unsure"),  V2 = c("unsure","no",NA,"no","yes"),  V3 = c("yes","no","unsure","no",NA))  

Где желаемый результат заключается в том, что приоритет «да» над «нет» над «не уверен».

Комментарии:

1. Похоже, вы могли бы поочередно сформулировать это так: «Я хотел бы расставить приоритеты B над C над A.»

Ответ №1:

ИЗМЕНИТЬ: добавлено более простое только для dplyr

 library(dplyr) data %gt;%  group_by(ID) %gt;%  summarize(across(V1:V3, max))  # A tibble: 3 × 4  ID V1 V2 V3   lt;dblgt; lt;chrgt; lt;chrgt; lt;chrgt; 1 1 B C B  2 2 A NA C  3 3 C B NA   

Если вам нужны упорядоченные факторы, вот подход, при котором мы указываем порядок, применяем его к данным в V1:V3, а затем действуем, как и раньше.

 data lt;- data.frame(ID = c(1,1,2,3,3),  V1 = c("yes","no","yes","no","unsure"),  V2 = c("unsure","no",NA,"no","yes"),  V3 = c("yes","no","unsure","no",NA))  var_order lt;- c("yes", "no", "unsure")   # Note addition of `ordered = TRUE` to make the `min` work data %gt;%  mutate(across(V1:V3, ~factor(.x, levels = var_order, ordered = TRUE))) %gt;%  group_by(ID) %gt;%  summarize(across(V1:V3, ~min(., na.rm = TRUE)))   # A tibble: 3 × 4  ID V1 V2 V3   lt;dblgt; lt;ordgt; lt;ordgt; lt;ordgt;  1 1 yes no yes  2 2 yes NA unsure 3 3 no yes no   

Более раннее решение с использованием изменения формы из tidyr. Это работало без ordered = TRUE установленного флага, но было бы неэффективно для больших наборов данных.

 library(dplyr); library(tidyr) data %gt;%  mutate(across(V1:V3, ~factor(.x, levels = var_order))) %gt;%  pivot_longer(-ID) %gt;%  group_by(ID, name) %gt;%  slice_min(value) %gt;%  ungroup() %gt;%   pivot_wider(names_from = name)   # A tibble: 3 × 4  ID V1 V2 V3   lt;dblgt; lt;fctgt; lt;fctgt; lt;fctgt;  1 1 yes no yes  2 2 yes NA unsure 3 3 no yes no   

Комментарии:

1. Большое вам за это спасибо. Ваш подход работает, потому что буквы «упорядочены» в R. Я должен был уточнить свой вопрос: я хочу иметь возможность настраивать приоритет, например, B над C над A, так как в моих реальных данных строки, с которыми я имею дело, расположены не в алфавитном порядке.

2. Вероятно, тогда будет проще всего преобразовать ваши значения в V1:V3 в упорядоченные коэффициенты, которые упорядочены так, как вы хотите.

3. Это звучит вполне разумно. Большое спасибо!

4. Пример использования упорядоченных коэффициентов см. в разделе Редактирование.

5. Еще раз большое вам спасибо за ваше самое полезное решение. Могу я спросить, почему вы выбрали здесь поворотный подход? По моему опыту, при больших наборах данных они становятся довольно ресурсоемкими. Теперь я просто преобразовал строки в коэффициенты, упорядочил их, преобразовал их в числовые значения с помощью первой функции-мутации, затем применил ваше решение dplyr и преобразовал цифры обратно в символы со второй функцией-мутацией (case_when()).

Ответ №2:

Поскольку мы можем получить максимум для буквы из алфавита, мы могли бы использовать:

 library(tidyverse)  data %gt;%  group_by(ID) %gt;%  summarize(across(everything(), ~ max(., na.rm = TRUE)))  

Что дает:

 # A tibble: 3 x 4  ID V1 V2 V3   lt;dblgt; lt;chrgt; lt;chrgt; lt;chrgt; 1 1 B C B  2 2 A lt;NAgt; C  3 3 C B B  

Комментарии:

1. %gt;% summarise_all(max, na.rm = T) было бы немного более лаконично

2. Нет. С выпуском dplyr 1.0.0 (IIRC) ..._all версии глаголов были заменены в пользу across() . См ?summarize_all .: «Глаголы с областью действия (_if, _at, _all) были заменены использованием across() в существующем глаголе. Подробности см. в виньетке(«colwise»).»

3. Круто, я этого не знал. Хотя не уверен, что нахожу это лучше. В любом случае 1

4. mutate_if(is.factor, as.character) нам это тоже нужно

5. ЕСЛИ данные содержат столбцы факторов, то да. В предоставленных образцах данных из TO этого нет.

Ответ №3:

Вот решение в base :

 aggregate(data[,-1], by = list(ID=data$ID), FUN = max, na.rm = T)  # ID V1 V2 V3 # 1 1 B C B # 2 2 A lt;NAgt; C # 3 3 C B B