#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