Как объединить / перечислить значения нескольких столбцов по группам?

#r #dataframe #grouping

#r #фрейм данных #группировка

Вопрос:

У меня есть фрейм данных, описывающий уровни владения компаний, который выглядит следующим образом:

 Company   Subsidiary1    Subsidiary2    Subsidiary3
DE5930      DE5931           NA             NA
GB3489      GB3490           NA             NA
GB3489      GB3490         GB3491           NA
US2036      US2037           NA             NA
US2036      US2037         US2038           NA
US2036      US2037         US2038         GB3491
....# and so on
  

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

 Company   Subsidiaries
DE5930     DE5931          
GB3489     GB3490
GB3489     GB3491
US2036     US2037
US2036     US2038       
US2036     GB3491
  

Набор данных действительно большой (более 100 000 строк), и я не смог найти никаких решений с использованием функции group_by or aggregate , поскольку большинство примеров относятся к числовым переменным (например, average).

Одной из идей было бы удалить дубликаты с помощью df[ !duplicated(df$Subsidiary1), ] , чтобы сохранить первое появление каждого дочернего предприятия, а затем сдвинуть значения влево, но проблема в том, что одно дочернее предприятие может принадлежать нескольким компаниям (например, «GB3491»), и я не хочу терять эти наблюдения. Есть ли какое-либо элегантное решение этой проблемы?

Заранее благодарю вас!

Ответ №1:

Я бы предложил следующий tidyverse подход:

 library(tidyverse)
#Data
df <- structure(list(Company = c("DE5930", "GB3489", "GB3489", "US2036", 
"US2036", "US2036"), Subsidiary1 = c("DE5931", "GB3490", "GB3490", 
"US2037", "US2037", "US2037"), Subsidiary2 = c(NA, NA, "GB3491", 
NA, "US2038", "US2038"), Subsidiary3 = c(NA, NA, NA, NA, NA, 
"GB3491")), class = "data.frame", row.names = c(NA, -6L))
  

Код:

 df %>% pivot_longer(cols = -Company) %>% select(-name) %>%
  filter(!is.na(value)) %>%
  filter(!duplicated(paste(Company,value)))
  

Вывод:

 # A tibble: 6 x 2
  Company value 
  <chr>   <chr> 
1 DE5930  DE5931
2 GB3489  GB3490
3 GB3489  GB3491
4 US2036  US2037
5 US2036  US2038
6 US2036  GB3491
  

Ответ №2:

Мы можем использовать coalesce

 library(dplyr)
df1 %>%
    transmute(Company, Subsidiaries = 
        coalesce(!!! rlang::syms(rev(names(df1)[-1]))))
#  Company Subsidiaries
#1  DE5930       DE5931
#2  GB3489       GB3490
#3  GB3489       GB3491
#4  US2036       US2037
#5  US2036       US2038
#6  US2036       GB3491
  

Или с base R помощью max.col

 cbind(df1[1], Subsidiaries =  df1[-1][cbind(seq_len(nrow(df1)), 
         max.col(!is.na(df1[-1]), "last"))])
  

данные

 df1 <- structure(list(Company = c("DE5930", "GB3489", "GB3489", "US2036", 
"US2036", "US2036"), Subsidiary1 = c("DE5931", "GB3490", "GB3490", 
"US2037", "US2037", "US2037"), Subsidiary2 = c(NA, NA, "GB3491", 
NA, "US2038", "US2038"), Subsidiary3 = c(NA, NA, NA, NA, NA, 
"GB3491")), class = "data.frame", row.names = c(NA, -6L))