Как изменить все значения, ожидаемые для верхних значений (по частоте) из категориальной переменной в R - PullRequest
0 голосов
/ 17 июня 2020

У меня есть фрейм данных в R, который похож на тот, что показан ниже, с факторной переменной «Жанр»:

|Genre|Listening Time|
|Rock |1:05          |
|Pop  |3:10          |
|RnB  |4:12          |
|Rock |2:34          |
|Pop  |5:01          |
|RnB  |4:01          |
|Rock |1:34          |
|Pop  |2:04          |

Я хочу оставить 15 лучших жанров (по количеству) такими, какие они есть, и переименуйте только все остальные жанры, которые входят в число , а не среди первых 15. Их следует переименовать в слово «Other».

Другими словами - если, например, жанр «RnB» не входит в число 15 лучших жанров, то его следует заменить словом «Other».

Таблица, которую я хотел бы Тогда get будет выглядеть так:

|Genre|Listening Time|
|Rock |1:05          |
|Pop  |3:10          |
|Other|4:12          |
|Rock |2:34          |
|Pop  |5:01          |
|Other|4:01          |
|Rock |1:34          |
|Pop  |2:04          |

Как мне подойти к этому? Спасибо!

Ответы [ 5 ]

1 голос
/ 17 июня 2020

Если вы хотите изучить tidyverse, вы можете сделать что-то вроде этого. Я попытался скопировать c ваш фрейм данных, но добавил еще несколько строк.

Вы начинаете с data> group_by Genre> order> выбираете топ 5


library(tidyverse)

set.seed(1)
Data <- data.frame(
  listen = format(as.POSIXlt(paste0(
      as.character(sample(1:5)),
      ':',
      as.character(sample(0:59))), format = '%H:%M'),format = '%H:%M'),
  Genre = sample(c("Rock", "Pop", 'RnB'), 120, replace = TRUE)
)


Data %>%
  group_by(Genre ) %>%
  arrange(desc(listen)) %>% 
  select(listen) %>% 
  top_n(5) %>% 
  arrange(Genre)
#> Adding missing grouping variables: `Genre`
#> Selecting by listen
#> # A tibble: 15 x 2
#> # Groups:   Genre [3]
#>    Genre listen
#>    <chr> <chr> 
#>  1 Pop   05:47 
#>  2 Pop   05:47 
#>  3 Pop   05:43 
#>  4 Pop   05:41 
#>  5 Pop   05:28 
#>  6 RnB   05:54 
#>  7 RnB   05:44 
#>  8 RnB   05:43 
#>  9 RnB   05:29 
#> 10 RnB   05:28 
#> 11 Rock  05:54 
#> 12 Rock  05:44 
#> 13 Rock  05:41 
#> 14 Rock  05:29 
#> 15 Rock  05:26

Извините, если я неправильно понял то, что вы хотели. Если вы назначите код новому data.frame и сделаете anti_join исходному DF, а затем измените Genre на others , это должно быть то, что вы хотите - я думаю.

df <- Data %>%
  group_by(Genre ) %>%
  arrange(desc(listen)) %>% 
  select(listen) %>% 
  top_n(5) %>% 
  arrange(Genre) 

# make an anti_join and assign 'other' to Genre

anti_join(Data, df) %>% 
  mutate(Genre = 'others')

Следующее редактирование

Надеюсь, теперь я понял ваш вопрос. Вам нужно просто подсчитать, как часто жанры встречаются в ваших данных, и дать тем, которые не входят в топ-15, имя Другие . Может быть, меня ввел в заблуждение предложенный вами фрейм данных, в котором показаны только 3 жанра. Итак, я поискал в Википедию и добавил несколько, придумал несколько собственных жанров и использовал БУКВЫ для создания DF с достаточным количеством жанров.

С count(Genre) жанры встречаются. посчитали, а затем расположили в порядке убывания. Затем я ввел новый столбец с номерами строк. Вы можете удалить это, если хотите, так как он нужен только для следующего шага, который вводит другой столбец - я решил создать новый столбец вместо переименования всех имен в Genre - с name Top15 , присваивая каждому жанру, который находится на месте (в строке) 16 или более поздних, имя Others и оставляя остальные без изменений.

head(20) просто печатает первые 20 рядов этого DF.


library(tidyverse)

set.seed(1)
Data <- data.frame(
  listen = format(as.POSIXlt(paste0(
      as.character(sample(1:5)),
      ':',
      as.character(sample(0:59))), format = '%H:%M'),format = '%H:%M'),
  Genre = sample(c("Rock", "Pop", 'RnB', 'Opera',
                   'Birthday Songs', 'HipHop',
                   'Chinese Songs', 'Napoli Lovesongs',
                   'Benga', 'Bongo', 'Kawito', 'Noise',
                   'County Blues','Mambo', 'Reggae',
                   LETTERS[0:24]), 300, replace = TRUE)
)

Data %>% count(Genre) %>% 
  arrange(desc(n)) %>% 
  mutate(place = row_number()) %>% 
  mutate(Top15 = ifelse(place > 15, 'Others', Genre)) %>% 
  head(20)
#> # A tibble: 20 x 4
#>    Genre            n place Top15       
#>    <chr>        <int> <int> <chr>       
#>  1 N               15     1 N           
#>  2 T               13     2 T           
#>  3 V               13     3 V           
#>  4 K               12     4 K           
#>  5 Rock            11     5 Rock        
#>  6 X               11     6 X           
#>  7 E               10     7 E           
#>  8 W               10     8 W           
#>  9 Benga            9     9 Benga       
#> 10 County Blues     9    10 County Blues
#> 11 G                9    11 G           
#> 12 J                9    12 J           
#> 13 M                9    13 M           
#> 14 Reggae           9    14 Reggae      
#> 15 B                8    15 B           
#> 16 D                8    16 Others      
#> 17 I                8    17 Others      
#> 18 P                8    18 Others      
#> 19 R                8    19 Others      
#> 20 S                8    20 Others

Надеюсь, это было то, что вы искали

0 голосов
/ 18 июня 2020

Существует довольно изящное решение с пакетом forcats, примененным здесь к набору данных diamonds, чтобы назвать только верхние 5 clarity значений и объединить остальные как «Другое»

library(dplyr)
library(forcats)

diamonds %>%
  mutate(clarity2 = fct_lump(fct_infreq(clarity), n = 5))

Результат:

# A tibble: 53,940 x 11
   carat cut       color clarity depth table price     x     y     z clarity2
   <dbl> <ord>     <ord> <ord>   <dbl> <dbl> <int> <dbl> <dbl> <dbl> <ord>   
 1 0.23  Ideal     E     SI2      61.5    55   326  3.95  3.98  2.43 SI2     
 2 0.21  Premium   E     SI1      59.8    61   326  3.89  3.84  2.31 SI1     
 3 0.23  Good      E     VS1      56.9    65   327  4.05  4.07  2.31 VS1     
 4 0.290 Premium   I     VS2      62.4    58   334  4.2   4.23  2.63 VS2     
 5 0.31  Good      J     SI2      63.3    58   335  4.34  4.35  2.75 SI2     
 6 0.24  Very Good J     VVS2     62.8    57   336  3.94  3.96  2.48 VVS2    
 7 0.24  Very Good I     VVS1     62.3    57   336  3.95  3.98  2.47 Other   
 8 0.26  Very Good H     SI1      61.9    55   337  4.07  4.11  2.53 SI1     
 9 0.22  Fair      E     VS2      65.1    61   337  3.87  3.78  2.49 VS2     
10 0.23  Very Good H     VS1      59.4    61   338  4     4.05  2.39 VS1     
# … with 53,930 more rows
0 голосов
/ 17 июня 2020

Я могу придумать решение data.table. Предположим, ваш data.frame называется music, затем:

library(data.table)
setDT(music)

other_genres <- music[, .N, by = genre][order(-N)][16:.N, genre]

music[genre %chin% other_genres, genre := "other"]

Первая строка эффективного кода подсчитывает появления по жанрам, сортирует их от наибольшего к наименьшему и выбирает от 16 до последнего один, присвоив результат переменной с именем other_genres. Вторая строка проверит, какие жанры есть в этом списке, и обновит их название до "other".

0 голосов
/ 17 июня 2020
library(dplyr)

set.seed(123)
compute_listen_time <- function(n.songs) {
  min <- sample(1:15, n.songs, replace = TRUE)
  sec <- sample(0:59, n.songs, replace = TRUE)
  sec <- ifelse(sec > 10, sec, paste0("0", sec))
  paste0(min, ":", sec)
}



df <- data.frame(
  Genre = sample(c("Rock", "Pop", "RnB", "Rock", "Pop"), 100, replace = TRUE),
  Listen_Time = compute_listen_time(100)
)


df <- add_count(df, Genre, name = "count") %>%
  mutate(
    rank = dense_rank(desc(count)),
    group = ifelse(rank <= 15, Genre, "other")
  )
df

0 голосов
/ 17 июня 2020

Попробуйте заменить df своим data.frame, чтобы проверить, получили ли вы желаемый результат:

df <- data.frame(Genre=sample(letters, 1000, replace=TRUE),
                 ListeningTime=runif(1000, 3, 5))
> head(df)
  Genre ListeningTime
1     j      3.437013
2     n      4.151121
3     p      3.109044
4     z      4.529619
5     h      4.043982
6     i      3.590463
freq <- table(df$Genre)
sorted <- sort(freq, decreasing=TRUE)  # Sorted by frequency of df$Genre
> sorted
 d  x  o  q  r  u  g  i  j  f  a  p  b  e  v  n  w  c  k  m  z  l  h  t  y  s 
53 50 46 45 45 42 41 41 40 39 38 38 37 37 37 36 36 35 35 35 35 34 33 33 30 29
not_top_15 <- names(sorted[-1*1:15])  # The Genres not in the top 15
pos <- which(df$Genre %in% not_top_15)  # Their position in df
> head(df[pos, ])  # The original data, without the top 15 Genres
   Genre ListeningTime
2      n      4.151121
4      z      4.529619
5      h      4.043982
7      s      3.521054
16     w      3.528091
18     h      4.588815
...