#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
, отсюда и дополнительный столбец в конце.