Распределить объединенную переменную, разделенную запятой и двоеточием, в отдельные логические столбцы

#r #dplyr

#r #dplyr

Вопрос:

У меня есть a df с одним столбцом feature_service , который содержит одну или несколько пар функций (a — d) со службами (A или B). функции и службы разделяются с помощью : , а пары разделяются usign , .

df Выглядит так:

 df <- data.frame(feature_service = c("a:A", "a:A, b:A", "a:A, a:B", "a:B, b:B, c:B", "d:A, d:B"), stringsAsFactors = FALSE)
df
  feature_service
1             a:A
2        a:A, b:A
3        a:A, a:B
4   a:B, b:B, c:B
5        d:A, d:B
  

Теперь я хочу разделить сервисы и функции на отдельные логические столбцы. Цель состоит в том, чтобы иметь data.frame значение, которое выглядит как:

 df_goal <- data.frame(feature_a = c(TRUE, TRUE, TRUE, TRUE, FALSE), feature_b = c(FALSE, TRUE, FALSE, TRUE, FALSE), feature_c = c(FALSE, FALSE, FALSE, TRUE, FALSE)
                 , feature_d = c(FALSE, FALSE, FALSE, FALSE, TRUE), service_A = c(TRUE, TRUE, TRUE, FALSE, TRUE), service_B = c(FALSE, FALSE, TRUE, TRUE, TRUE))
df_goal
  feature_a feature_b feature_c feature_d service_A service_B
1      TRUE     FALSE     FALSE     FALSE      TRUE     FALSE
2      TRUE      TRUE     FALSE     FALSE      TRUE     FALSE
3      TRUE     FALSE     FALSE     FALSE      TRUE      TRUE
4      TRUE      TRUE      TRUE     FALSE     FALSE      TRUE
5     FALSE     FALSE     FALSE      TRUE      TRUE      TRUE
  

Как я могу этого добиться?

Ответ №1:

Базовое решение R:

 foo <- strsplit(df$feature_service, ",")
# Get all possible features
feature <- unique(unlist(lapply(foo, function(x) trimws(sub(":.*", ":", x)))))
# Get all possible services
service <- unique(unlist(lapply(foo, function(x) trimws(sub(".*:", ":", x)))))

# Generate occurrence table
result <- sapply(c(feature, service), grepl, df$feature_service)
# Name final result
colnames(result) <- c(paste0("feature_", sub(":", "", feature)),
                      paste0("service_", sub(":", "", service)))
  

Если у вас уже есть все возможные функции и сервисы, тогда необходима только sapply часть.

Ответ №2:

Один вариант с mtabulate после разделения столбца feature_service разделителями

 library(qdapTools)
out <- mtabulate(strsplit(df$feature_service, "[:, ]"))[-1] > 0
  

Или с помощью tidyverse создайте столбец имен строк, разделите ‘feature_service’ разделителями в separate_rows , получите уникальные строки ( distinct ), создайте логический столбец TRUE и spread в ‘широком’ формате

 library(tidyverse)
df %>% 
  rownames_to_column('rn') %>% 
  separate_rows(feature_service) %>%
  distinct(rn, feature_service) %>%
  mutate(n = TRUE) %>%
  spread(feature_service, n, fill = FALSE)
  

Если нам нужны имена столбцов, как указано, то после выполнения разделения в , , затем separate на два столбца (‘key’, ‘val’), gather от ‘wide’ до ‘long’, unite столбец ‘key / val’ в один, и spread как указано выше

 df %>%
  rownames_to_column('rn') %>% 
  separate_rows(feature_service, sep = ", ") %>% 
  separate(feature_service, into = c('feature', 'service')) %>% 
  gather(key, val, feature:service) %>% 
  distinct() %>% 
  unite(keyval, key, val) %>% 
  mutate(n = TRUE) %>% 
  spread(keyval, n, fill =  FALSE) %>%
  select(-rn)
#  feature_a feature_b feature_c feature_d service_A service_B
#1      TRUE     FALSE     FALSE     FALSE      TRUE     FALSE
#2      TRUE      TRUE     FALSE     FALSE      TRUE     FALSE
#3      TRUE     FALSE     FALSE     FALSE      TRUE      TRUE
#4      TRUE      TRUE      TRUE     FALSE     FALSE      TRUE
#5     FALSE     FALSE     FALSE      TRUE      TRUE      TRUE
  

Ответ №3:

Чтобы завершить трио ( baseR , tidyverse , data.table ), вот моя попытка с data.table ,

 library(data.table)
dt1 <- setDT(df)[, tstrsplit(feature_service, ", |:")]
dcast(melt(dt1, measure = names(dt1)), rowid(variable) ~ value, length)[,variable := NULL][] > 0
  

что дает,

          A     B     a     b     c     d    NA
[1,]  TRUE FALSE  TRUE FALSE FALSE FALSE  TRUE
[2,]  TRUE FALSE  TRUE  TRUE FALSE FALSE  TRUE
[3,]  TRUE  TRUE  TRUE FALSE FALSE FALSE  TRUE
[4,] FALSE  TRUE  TRUE  TRUE  TRUE FALSE FALSE
[5,]  TRUE  TRUE FALSE FALSE FALSE  TRUE  TRUE
  

ПРИМЕЧАНИЕ: Это также вводит в заблуждение NA , отсюда и дополнительный столбец в конце.