R-Преобразование столбца списков в разные столбцы, используя их значения в качестве имен (фиктивные)

У меня есть таблица, содержащая данные фильмов, и в последнем столбце у нее есть категории, к которым принадлежит фильм.

  movieId                              title                   category
       1                   Toy Story (1995)  Animation|Children|Comedy
       2                     Jumanji (1995) Adventure|Children|Fantasy
       3            Grumpier Old Men (1995)             Comedy|Romance
       4           Waiting to Exhale (1995)               Comedy|Drama
       5 Father of the Bride Part II (1995)                     Comedy
       6                        Heat (1995)      Action|Crime|Thriller

Я хочу создать один столбец для каждой категории и поставить 1, если он был записан в списке для этого фильма, и ноль, если нет. Что-то вроде:

movieId title   animation   comedy  drama
1        xx        1           0      1
2        xy        1           0      0
3        yy        1           1      0

До сих пор я только преобразовал строку в список с помощью:

f<-function(x) {strsplit(x, split='|', fixed=TRUE)}
movies2$m<-lapply(movies2$category, f)

Но я не знаю, как сделать все остальное.

Я думал о словарях Python. Но я не знаю, как это сделать в R.

Данные

df1 <- read.table(header = TRUE, stringsAsFactors = FALSE,
                  text = " movieId                              title                   category
                  1                   'Toy Story (1995)'  Animation|Children|Comedy
                  2                     'Jumanji (1995)' Adventure|Children|Fantasy
                  3            'Grumpier Old Men (1995)'             Comedy|Romance
                  4           'Waiting to Exhale (1995)'               Comedy|Drama
                  5 'Father of the Bride Part II (1995)'                     Comedy
                  6                        'Heat (1995)'      Action|Crime|Thriller")

Ответ 1

Мы можем использовать mtabulate из qdapTools после расщепления

library(qdapTools)
cbind(df1[-3],mtabulate(strsplit(df1$category, "[|]")))
# movieId                              title Action Adventure Animation Children Comedy Crime Drama Fantasy Romance Thriller
#1       1                   Toy Story (1995)      0         0         1        1      1     0     0       0       0        0
#2       2                     Jumanji (1995)      0         1         0        1      0     0     0       1       0        0
#3       3            Grumpier Old Men (1995)      0         0         0        0      1     0     0       0       1        0
#4       4           Waiting to Exhale (1995)      0         0         0        0      1     0     1       0       0        0
#5       5 Father of the Bride Part II (1995)      0         0         0        0      1     0     0       0       0        0
#6       6                        Heat (1995)      1         0         0        0      0     1     0       0       0        1

Или используя base R

cbind(df1[-3], as.data.frame.matrix(table(stack(setNames(strsplit(df1$category,
                           "[|]"), df1$movieId))[2:1])))

Ответ 2

Здесь база R, которая использует strsplit() для разделения значений столбца, затем grepl(), чтобы соответствовать им в vapply(). Трюк здесь состоит в том, чтобы использовать FUN.VALUE = integer(.) в vapply(), чтобы результат grepl() был магически преобразован в целое число.

## split the 'category' column on '|'
s <- strsplit(df$category, "|", fixed = TRUE)
## run the unique sorted values through grepl(), getting integer result
newPart <- vapply(sort(unique(unlist(s))), grepl, integer(nrow(df)), df$category, fixed = TRUE)
## bind result to other columns
cbind(df[-3], newPart)

В результате получается следующий фрейм данных.

  movieId                              title Action Adventure Animation Children Comedy Crime Drama Fantasy Romance Thriller
1       1                   Toy Story (1995)      0         0         1        1      1     0     0       0       0        0
2       2                     Jumanji (1995)      0         1         0        1      0     0     0       1       0        0
3       3            Grumpier Old Men (1995)      0         0         0        0      1     0     0       0       1        0
4       4           Waiting to Exhale (1995)      0         0         0        0      1     0     1       0       0        0
5       5 Father of the Bride Part II (1995)      0         0         0        0      1     0     0       0       0        0
6       6                        Heat (1995)      1         0         0        0      0     1     0       0       0        1

Ответ 3

Экспериментальный подход:

library(dplyr)
library(tidyr)
library(reshape2)
library(stringr)

max.categories = max(str_count(df1$category, "\\|")) + 1

df1new = df1 %>% separate(category, into=letters[1:max.categories], sep="\\|") %>%
  melt(c("movieId","title")) %>%
  filter(!is.na(value)) %>%
  dcast(movieId + title ~ value, fun.aggregate=length) 
  movieId                              title Action Adventure Animation Children Comedy Crime Drama Fantasy Romance Thriller
1       1                   Toy Story (1995)      0         0         1        1      1     0     0       0       0        0
2       2                     Jumanji (1995)      0         1         0        1      0     0     0       1       0        0
3       3            Grumpier Old Men (1995)      0         0         0        0      1     0     0       0       1        0
4       4           Waiting to Exhale (1995)      0         0         0        0      1     0     1       0       0        0
5       5 Father of the Bride Part II (1995)      0         0         0        0      1     0     0       0       0        0
6       6                        Heat (1995)      1         0         0        0      0     1     0       0       0        1

max.categories - это всего лишь способ программно гарантировать, что вектор into не меньше, чем максимальное количество категорий для данного title. Если вы уже знаете, что это значение никогда больше, чем, скажем, 5, то вы можете просто сделать, например, into=letters[1:5].