Использование str_split и удаление повторяющихся значений из одной и той же записи в R - PullRequest
0 голосов
/ 29 июня 2018

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

titles <- c("Harry Potter 1", "To Kill A Mockingbird", "The Hunger Games 1")
genres <- c("Fantasy, Young Adult, Fantasy, Magic", "Classics, Fiction, Historical, Historical Fiction, Academic", "Young Adult, Fiction, Science Fiction, Dystopia, Science Fiction")
books <- tibble(
  title = titles,
  genre = genres)
books

# A tibble: 3 x 2
  title                 genre                                                           
  <chr>                 <chr>                                                           
1 Harry Potter 1        Fantasy, Young Adult, Fantasy, Magic                            
2 To Kill A Mockingbird Classics, Fiction, Historical, Historical Fiction, Academic     
3 The Hunger Games 1    Young Adult, Fiction, Science Fiction, Dystopia, Science Fiction  

В настоящее время жанры расположены в порядке того, сколько людей классифицировали их как этот жанр. Я хотел бы разбить строку жанров на несколько столбцов, указывающих основной жанр, дополнительный жанр и т. Д., Но удалите дубликаты. Разделить жанры на несколько столбцов достаточно просто, и я уверен, что есть какой-то способ заставить функцию, подобную unique (), работать по строкам и опускать дубликаты, но я застрял. Желаемый результат будет выглядеть так:

# A tibble: 3 x 6
  title                                genre1      genre2      genre3          genre4             genre5  
  <chr>                                <chr>       <chr>       <chr>           <chr>              <chr>   
1 Harry Potter and the Sorcerors Stone Fantasy     Young Adult Magic           NA                 NA      
2 To Kill A Mockingbird                Classics    Fiction     Historical      Historical Fiction Academic
3 The Hunger Games                     Young Adult Fiction     Science Fiction Dystopia           NA  

Ответы [ 5 ]

0 голосов
/ 01 июля 2018

Мы можем вставить столбцы вместе и использовать магию data.table::fread, а затем переименовать наши поля.

library(data.table)
dt <- fread(paste(books$title, books$genre, sep=", ",collapse="\n"),header = FALSE,fill=TRUE,sep=",")
setNames(as.data.frame(dt),c("title",paste0("genre",seq(ncol(dt)-1))))
#                   title      genre1      genre2          genre3             genre4          genre5
# 1        Harry Potter 1     Fantasy Young Adult         Fantasy              Magic                
# 2 To Kill A Mockingbird    Classics     Fiction      Historical Historical Fiction        Academic
# 3    The Hunger Games 1 Young Adult     Fiction Science Fiction           Dystopia Science Fiction
0 голосов
/ 30 июня 2018

Всегда лучше работать с данными в длинном формате. Следовательно, одним из вариантов является изменение данных в длинном формате с использованием tidyr::gather, а затем удаление дубликатов перед преобразованием данных обратно в wide-format.

library(tidyverse)
library(splitstackshape)

books %>% cSplit("genre") %>% mutate_if(is.factor, as.character) %>%
  gather(key, value, - title) %>% distinct(title, value) %>%
  group_by(title) %>%
  mutate(key = paste0("genre",row_number())) %>%
  spread(key, value) %>% as.data.frame()

#                   title      genre1      genre2          genre3             genre4   genre5
# 1        Harry Potter 1     Fantasy Young Adult           Magic               <NA>     <NA>
# 2    The Hunger Games 1 Young Adult     Fiction Science Fiction           Dystopia     <NA>
# 3 To Kill A Mockingbird    Classics     Fiction      Historical Historical Fiction Academic
0 голосов
/ 29 июня 2018

Вы можете использовать separate с шагом для удаления неуникальных жанров перед использованием separate.

library(tidyverse)
library(magrittr)

#remove non-unique genres
books %<>% mutate(genre = map(str_split(genre, ', '), ~ paste(unique(.x), collapse = ','))) 

#separate into columns
books %>% 
  separate(col = 2, into = paste0('genre', seq(max(str_count(books$genre, ',')) + 1L))
           , sep = ',')


# # A tibble: 3 x 6
#   title                 genre1      genre2      genre3          genre4             genre5  
#   <chr>                 <chr>       <chr>       <chr>           <chr>              <chr>   
# 1 Harry Potter 1        Fantasy     Young Adult Magic           NA                 NA      
# 2 To Kill A Mockingbird Classics    Fiction     Historical      Historical Fiction Academic
# 3 The Hunger Games 1    Young Adult Fiction     Science Fiction Dystopia           NA  
0 голосов
/ 29 июня 2018

Здесь решение с использованием data.table и базы R.

library(data.table)
setDT(books)

books = unique(books[, strsplit(genre, ", "), by = title])
books[, genre:= paste0("genre_", seq_along(V1)), by = title]
dcast(books, title ~ genre, value.var = "V1")
#                    title     genre_1     genre_2         genre_3            genre_4  genre_5
# 1:        Harry Potter 1     Fantasy Young Adult           Magic               <NA>     <NA>
# 2:    The Hunger Games 1 Young Adult     Fiction Science Fiction           Dystopia     <NA>
# 3: To Kill A Mockingbird    Classics     Fiction      Historical Historical Fiction Academic
0 голосов
/ 29 июня 2018

Вы можете сделать это с помощью stringr::str_split, чтобы создать список-столбец жанров. genre станет списком символьных векторов, которые вы можете затем откатить, а затем провести различные наблюдения.

library(tidyverse)

books %>%
  mutate(genre = str_split(genre, ", ")) %>%
  unnest(genre) %>%
  distinct()
#> # A tibble: 12 x 2
#>    title                 genre             
#>    <chr>                 <chr>             
#>  1 Harry Potter 1        Fantasy           
#>  2 Harry Potter 1        Young Adult       
#>  3 Harry Potter 1        Magic             
#>  4 To Kill A Mockingbird Classics          
#>  5 To Kill A Mockingbird Fiction           
#>  6 To Kill A Mockingbird Historical        
#>  7 To Kill A Mockingbird Historical Fiction
#>  8 To Kill A Mockingbird Academic          
#>  9 The Hunger Games 1    Young Adult       
#> 10 The Hunger Games 1    Fiction           
#> 11 The Hunger Games 1    Science Fiction   
#> 12 The Hunger Games 1    Dystopia

Ярлык, о котором я всегда забываю, это separate_rows, который выполняет разбиение и удаление в один шаг:

books %>%
  separate_rows(genre, sep = ", ") %>%
  distinct()

эквивалентно предыдущему блоку.

Чтобы получить это в широком формате, вы можете использовать tidyr::spread. Для динамического создания имен столбцов "genre1", "genre2" и т. Д. Я сгруппировал по заголовкам, а затем пронумеровал уникальные жанры для каждого заголовка. Таким образом, вам не нужно знать, сколько столбцов жанра вам нужно, как если бы вы использовали tidyr::separate для разделения столбца.

books %>%
  mutate(genre = str_split(genre, ", ")) %>%
  unnest(genre) %>%
  distinct() %>%
  group_by(title) %>%
  mutate(num = row_number() %>% paste0("genre", .)) %>%
  spread(key = num, value = genre)
#> # A tibble: 3 x 6
#> # Groups:   title [3]
#>   title                 genre1      genre2      genre3    genre4    genre5
#>   <chr>                 <chr>       <chr>       <chr>     <chr>     <chr> 
#> 1 Harry Potter 1        Fantasy     Young Adult Magic     <NA>      <NA>  
#> 2 The Hunger Games 1    Young Adult Fiction     Science … Dystopia  <NA>  
#> 3 To Kill A Mockingbird Classics    Fiction     Historic… Historic… Acade…

Создан в 2018-06-29 пакетом Представ (v0.2.0).

...