1 Načtení balíčků

Kromě balíčku tidyverse použijeme několik nových balíčků:

  • skimr ke snadnému výpočtu deskriptivních statistik.
  • dlookr k exploraci a diagnostice dat.
  • ggmosaic k tvorbě mozaikových grafů.

Rovněž využijeme několik custom funkcí ze souboru r101_funs.R. Pomocí příkazu source můžeme soubor načíst i rovnou z internetu.

library(tidyverse)
library(haven)
library(dlookr)
library(skimr)
library(ggmosaic)
library(corrplot)
source("https://is.muni.cz/el/fss/podzim2022/PSYn5320/um/r101_funs.R")

2 Načtení dat

Jako cvičná data použijeme dataset s údaji o prodeji videoher, který obsahuje tyto proměnné:

  • name: název videohry.
  • platform: pro kterou herní platformu je hra určena.
  • year_of_release: rok vydání.
  • genre: žánr (např. FPS, RPG apod.).
  • publisher: herní vydavatel, který hru vydal.
  • na_sales: tržby z prodeje hry v Severní america (v milionech dolarů).
  • eu_sales: tržby z prodeje hry v Evropě (v milionech dolarů).
  • jp_sales: tržby z prodeje hry v Japonsku (v milionech dolarů).
  • other_sales: tržby z prodeje hry v ostatních částech světa (v milionech dolarů).
  • total_sales: celkové tržby (v milionech dolarů).
  • critic_score: průměrné hodnocení profesionálních recenzentů od 0 do 100.
  • user_score: průměrné hodnocení běžných hráčů
  • user_count: počet hráčů, kteří si hru zakoupili (v milionech).
  • developer: developer (herní vývojář/studio), který hru vytvořil.
  • rating: rating pro hodnocení obsahu videoher.
    • E/10+ čili Everyone nebo 10+ let,
    • EC/KA čili Early Childhood nebo Kids to Adults,
    • T: Teens,
    • M/A: Mature or Adult,
    • RP: Rating Pending,
    • NA: Not Available.
  • post_2010: zda byla hra vydáva po roce 2010.
  • decade: v které dekádě byla hra vydána (od 1980s po 2010s).
  • platform_type: typ herní platformy, např. Nintendo, Playstation, PC atd.
  • generation: “generace” herní platformy.
  • handheld: jedná se o přenosnou herní platformu?
df <- read_rds("https://is.muni.cz/el/fss/podzim2022/PSYn5320/um/datasets/games_sales.rds")
glimpse(df)
#> Rows: 16,719
#> Columns: 21
#> $ name            <chr> "Wii Sports", "Super Mario Bros.", "Mario Kart Wii", "…
#> $ platform        <chr> "Wii", "NES", "Wii", "Wii", "GB", "GB", "DS", "Wii", "…
#> $ year_of_release <dbl> 2006, 1985, 2008, 2009, 1996, 1989, 2006, 2006, 2009, …
#> $ genre           <fct> Sports, Platform, Racing, Sports, Role-Playing, Puzzle…
#> $ publisher       <chr> "Nintendo", "Nintendo", "Nintendo", "Nintendo", "Ninte…
#> $ na_sales        <dbl> 41.36, 29.08, 15.68, 15.61, 11.27, 23.20, 11.28, 13.96…
#> $ eu_sales        <dbl> 28.96, 3.58, 12.76, 10.93, 8.89, 2.26, 9.14, 9.18, 6.9…
#> $ jp_sales        <dbl> 3.77, 6.81, 3.79, 3.28, 10.22, 4.22, 6.50, 2.93, 4.70,…
#> $ other_sales     <dbl> 8.45, 0.77, 3.29, 2.95, 1.00, 0.58, 2.88, 2.84, 2.24, …
#> $ global_sales    <dbl> 82.53, 40.24, 35.52, 32.77, 31.37, 30.26, 29.80, 28.92…
#> $ critic_score    <dbl> 76, NA, 82, 80, NA, NA, 89, 58, 87, NA, NA, 91, NA, 80…
#> $ critic_count    <dbl> 51, NA, 73, 73, NA, NA, 65, 41, 80, NA, NA, 64, NA, 63…
#> $ user_score      <dbl> 8.0, NA, 8.3, 8.0, NA, NA, 8.5, 6.6, 8.4, NA, NA, 8.6,…
#> $ user_count      <dbl> 322, NA, 709, 192, NA, NA, 431, 129, 594, NA, NA, 464,…
#> $ developer       <chr> "Nintendo", NA, "Nintendo", "Nintendo", NA, NA, "Ninte…
#> $ rating          <fct> E/10+, NA, E/10+, E/10+, NA, NA, E/10+, E/10+, E/10+, …
#> $ post_2010       <fct> No, No, No, No, No, No, No, No, No, No, No, No, No, No…
#> $ decade          <fct> 2000s, 1980s, 2000s, 2000s, 1990s, 1980s, 2000s, 2000s…
#> $ platform_type   <fct> Nintendo, Nintendo, Nintendo, Nintendo, Nintendo, Nint…
#> $ generation      <fct> 7th, 3rd, 7th, 7th, 4th, 4th, 7th, 7th, 7th, 3rd, 7th,…
#> $ handheld        <fct> No, No, No, No, Yes, Yes, Yes, No, No, No, Yes, Yes, Y…

3 Descriptives statistiky a balíček skimr

Hlavní funkcí balíčku skimr je funkce skim(), která funguje s dataframy včetně tibbles a dodržuje i rozdělení do skupin pomocí funkce group_by().

Podobně jako funkce summary() poskytuje deskriptivní statistiky všech proměnných a typ statistik závisí na typu proměnné, protože by např. nemělo smysl počítat průměry pro kategorické proměnné, ale jen pro numerické.

data_summary <- skim(df)
data_summary
#> ── Data Summary ────────────────────────
#>                            Values
#> Name                       df    
#> Number of rows             16719 
#> Number of columns          21    
#> _______________________          
#> Column type frequency:           
#>   factor                   7     
#>   character                4     
#>   numeric                  10    
#> ________________________         
#> Group variables            None  
#> 
#> ── Variable type: factor ───────────────────────────────────────────────────────
#>   skim_variable n_missing complete_rate ordered n_unique
#> 1 genre                 2         1.00  FALSE         12
#> 2 rating             6769         0.595 FALSE          5
#> 3 post_2010             0         1     FALSE          2
#> 4 decade              269         0.984 FALSE          4
#> 5 platform_type         0         1     FALSE          5
#> 6 generation            0         1     FALSE          8
#> 7 handheld              0         1     FALSE          2
#>   top_counts                                
#> 1 Act: 3370, Spo: 2348, Mis: 1750, Rol: 1500
#> 2 E/1: 5411, T: 2961, M/A: 1564, EC/: 11    
#> 3 No: 11169, Yes: 5550                      
#> 4 200: 9193, 201: 5281, 199: 1771, 198: 205 
#> 5 Pla: 6723, Nin: 6271, XBo: 2333, PC: 974  
#> 6 7th: 7274, 6th: 4415, 8th: 1739, 5th: 1699
#> 7 No: 10923, Yes: 5796                      
#> 
#> ── Variable type: character ────────────────────────────────────────────────────
#>   skim_variable n_missing complete_rate min max empty n_unique whitespace
#> 1 name                  2         1.00    1 132     0    11562          0
#> 2 platform              0         1       2   4     0       31          0
#> 3 publisher            54         0.997   3  38     0      580          0
#> 4 developer          6623         0.604   2  80     0     1696          0
#> 
#> ── Variable type: numeric ──────────────────────────────────────────────────────
#>    skim_variable   n_missing complete_rate      mean      sd      p0     p25
#>  1 year_of_release       269         0.984 2006.       5.88  1980    2003   
#>  2 na_sales                0         1        0.263    0.814    0       0   
#>  3 eu_sales                0         1        0.145    0.503    0       0   
#>  4 jp_sales                0         1        0.0776   0.309    0       0   
#>  5 other_sales             0         1        0.0473   0.187    0       0   
#>  6 global_sales            0         1        0.534    1.55     0.01    0.06
#>  7 critic_score         8582         0.487   69.0     13.9     13      60   
#>  8 critic_count         8582         0.487   26.4     19.0      3      12   
#>  9 user_score           9129         0.454    7.13     1.50     0       6.4 
#> 10 user_count           9129         0.454  162.     561.       4      10   
#>        p50     p75    p100 hist 
#>  1 2007    2010     2020   ▁▁▃▇▂
#>  2    0.08    0.24    41.4 ▇▁▁▁▁
#>  3    0.02    0.11    29.0 ▇▁▁▁▁
#>  4    0       0.04    10.2 ▇▁▁▁▁
#>  5    0.01    0.03    10.6 ▇▁▁▁▁
#>  6    0.17    0.47    82.5 ▇▁▁▁▁
#>  7   71      79       98   ▁▁▅▇▃
#>  8   21      36      113   ▇▃▂▁▁
#>  9    7.5     8.2      9.7 ▁▁▂▇▇
#> 10   24      81    10665   ▇▁▁▁▁
str(data_summary)
#> skim_df [21 × 20] (S3: skim_df/tbl_df/tbl/data.frame)
#>  $ skim_type           : chr [1:21] "factor" "factor" "factor" "factor" ...
#>  $ skim_variable       : chr [1:21] "genre" "rating" "post_2010" "decade" ...
#>  $ n_missing           : int [1:21] 2 6769 0 269 0 0 0 2 0 54 ...
#>  $ complete_rate       : num [1:21] 1 0.595 1 0.984 1 ...
#>  $ factor.ordered      : logi [1:21] FALSE FALSE FALSE FALSE FALSE FALSE ...
#>  $ factor.n_unique     : int [1:21] 12 5 2 4 5 8 2 NA NA NA ...
#>  $ factor.top_counts   : chr [1:21] "Act: 3370, Spo: 2348, Mis: 1750, Rol: 1500" "E/1: 5411, T: 2961, M/A: 1564, EC/: 11" "No: 11169, Yes: 5550" "200: 9193, 201: 5281, 199: 1771, 198: 205" ...
#>  $ character.min       : int [1:21] NA NA NA NA NA NA NA 1 2 3 ...
#>  $ character.max       : int [1:21] NA NA NA NA NA NA NA 132 4 38 ...
#>  $ character.empty     : int [1:21] NA NA NA NA NA NA NA 0 0 0 ...
#>  $ character.n_unique  : int [1:21] NA NA NA NA NA NA NA 11562 31 580 ...
#>  $ character.whitespace: int [1:21] NA NA NA NA NA NA NA 0 0 0 ...
#>  $ numeric.mean        : num [1:21] NA NA NA NA NA NA NA NA NA NA ...
#>  $ numeric.sd          : num [1:21] NA NA NA NA NA NA NA NA NA NA ...
#>  $ numeric.p0          : num [1:21] NA NA NA NA NA NA NA NA NA NA ...
#>  $ numeric.p25         : num [1:21] NA NA NA NA NA NA NA NA NA NA ...
#>  $ numeric.p50         : num [1:21] NA NA NA NA NA NA NA NA NA NA ...
#>  $ numeric.p75         : num [1:21] NA NA NA NA NA NA NA NA NA NA ...
#>  $ numeric.p100        : num [1:21] NA NA NA NA NA NA NA NA NA NA ...
#>  $ numeric.hist        : chr [1:21] NA NA NA NA ...
#>  - attr(*, "data_rows")= int 16719
#>  - attr(*, "data_cols")= int 21
#>  - attr(*, "df_name")= chr "`df`"
#>  - attr(*, "dt_key")= logi NA
#>  - attr(*, "groups")= list()
#>  - attr(*, "base_skimmers")= chr [1:2] "n_missing" "complete_rate"
#>  - attr(*, "skimmers_used")=List of 3
#>   ..$ character: chr [1:5] "min" "max" "empty" "n_unique" ...
#>   ..$ numeric  : chr [1:8] "mean" "sd" "p0" "p25" ...
#>   ..$ factor   : chr [1:3] "ordered" "n_unique" "top_counts"

Kdybychom htěli deskriptivní statistiky jen pro některé proměnné, můžeme si je dopředu vybrat pomocí funkce select() anebo je uvést, až v samotné funkci skim(). V ní můžeme totiž provádět výběr proměnných (sloupců) podobně jako ve funkci select().

 skim(df, ends_with("sales"))
#> ── Data Summary ────────────────────────
#>                            Values
#> Name                       df    
#> Number of rows             16719 
#> Number of columns          21    
#> _______________________          
#> Column type frequency:           
#>   numeric                  5     
#> ________________________         
#> Group variables            None  
#> 
#> ── Variable type: numeric ──────────────────────────────────────────────────────
#>   skim_variable n_missing complete_rate   mean    sd   p0  p25  p50  p75 p100
#> 1 na_sales              0             1 0.263  0.814 0    0    0.08 0.24 41.4
#> 2 eu_sales              0             1 0.145  0.503 0    0    0.02 0.11 29.0
#> 3 jp_sales              0             1 0.0776 0.309 0    0    0    0.04 10.2
#> 4 other_sales           0             1 0.0473 0.187 0    0    0.01 0.03 10.6
#> 5 global_sales          0             1 0.534  1.55  0.01 0.06 0.17 0.47 82.5
#>   hist 
#> 1 ▇▁▁▁▁
#> 2 ▇▁▁▁▁
#> 3 ▇▁▁▁▁
#> 4 ▇▁▁▁▁
#> 5 ▇▁▁▁▁

Výstup s výsledky má podobu dataframu s dodatečnými atributy (např. počtem řádků a sloupců) a dvěma sloupci obsahující metadata o proměnných:

  • skim_variable obsahuje názvy původních proměnných,
  • skim_type uvádí typ proměnné.

Objekt vytvořený funkcí skim (který jsme si pojmenovali data_summary) má “třídu” skim_df (obecně mu můžeme říkat skim_df objekt) a můžeme jej pomocí pipe operátoru snadno poslat do další funkce, např. filter, a vybrat pouze informace o proměnných, které mají v názvu “sales”.

Dejte si pozor na to, že proměnné jsou ve skim_df objektu reprezentovány řádky, zatímco typ informace či statistik je reprezentován sloupci, proto k výběru proměnných používáme filter() a k výběru typu informací/statistik select(). V příkladu níže jsme si navíc pomohli funkcí str_detect(), která vrací hodnotu TRUE, pokud prvek dané proměnné obsahuje zadaný řetězec znaků.

class(data_summary)
#> [1] "skim_df"    "tbl_df"     "tbl"        "data.frame"
data_summary %>% 
  filter(str_detect(skim_variable, "sales"))
#> ── Data Summary ────────────────────────
#>                            Values
#> Name                       df    
#> Number of rows             16719 
#> Number of columns          21    
#> _______________________          
#> Column type frequency:           
#>   numeric                  5     
#> ________________________         
#> Group variables            None  
#> 
#> ── Variable type: numeric ──────────────────────────────────────────────────────
#>   skim_variable n_missing complete_rate   mean    sd   p0  p25  p50  p75 p100
#> 1 na_sales              0             1 0.263  0.814 0    0    0.08 0.24 41.4
#> 2 eu_sales              0             1 0.145  0.503 0    0    0.02 0.11 29.0
#> 3 jp_sales              0             1 0.0776 0.309 0    0    0    0.04 10.2
#> 4 other_sales           0             1 0.0473 0.187 0    0    0.01 0.03 10.6
#> 5 global_sales          0             1 0.534  1.55  0.01 0.06 0.17 0.47 82.5
#>   hist 
#> 1 ▇▁▁▁▁
#> 2 ▇▁▁▁▁
#> 3 ▇▁▁▁▁
#> 4 ▇▁▁▁▁
#> 5 ▇▁▁▁▁

Podobně můžeme vybrat např. pouze numerické proměnné a ze statistik pak pouze průměry a směrodatné odchylky

data_summary %>% 
  filter(skim_type == "numeric") %>% 
  select(skim_variable, numeric.mean, numeric.sd)
#> # A tibble: 10 × 3
#>    skim_variable   numeric.mean numeric.sd
#>    <chr>                  <dbl>      <dbl>
#>  1 year_of_release    2006.          5.88 
#>  2 na_sales              0.263       0.814
#>  3 eu_sales              0.145       0.503
#>  4 jp_sales              0.0776      0.309
#>  5 other_sales           0.0473      0.187
#>  6 global_sales          0.534       1.55 
#>  7 critic_score         69.0        13.9  
#>  8 critic_count         26.4        19.0  
#>  9 user_score            7.13        1.50 
#> 10 user_count          162.        561.

Do funkce skim také můžeme vložit dataset rozdělený do skupin pomocí funkce group_by(). V takovém případě se nám vypočtou statistiky pro jednotlivé skupiny a do výstupního skim_df objektu přibude sloupec udávající skupinu (groping variable). V příkladu níže přibyl sloupec genre.

df %>% 
  group_by(genre) %>% 
  select(genre, critic_score) %>% 
  skim() %>% 
  arrange(desc(numeric.mean))
#> ── Data Summary ────────────────────────
#>                            Values    
#> Name                       Piped data
#> Number of rows             16719     
#> Number of columns          2         
#> _______________________              
#> Column type frequency:               
#>   numeric                  1         
#> ________________________             
#> Group variables            genre     
#> 
#> ── Variable type: numeric ──────────────────────────────────────────────────────
#>    skim_variable genre        n_missing complete_rate  mean   sd p0  p25 p50 p75
#>  1 critic_score  Role-Playing       763         0.491  72.7 12.0 35 65    74  82
#>  2 critic_score  Strategy           381         0.442  72.1 11.8 35 65    73  80
#>  3 critic_score  Sports            1154         0.509  72.0 13.9 19 64    75  82
#>  4 critic_score  Shooter            379         0.714  70.2 14.6 22 61    73  81
#>  5 critic_score  Fighting           440         0.482  69.2 14.3 23 61    72  80
#>  6 critic_score  Simulation         522         0.403  68.6 12.4 31 62    70  77
#>  7 critic_score  Platform           391         0.560  68.1 13.2 19 59    69  77
#>  8 critic_score  Racing             507         0.594  68.0 14.2 13 59    69  79
#>  9 critic_score  Puzzle             356         0.386  67.4 12.7 25 60.8  70  76
#> 10 critic_score  Action            1480         0.561  66.6 14.2 19 57    68  77
#> 11 critic_score  Misc              1227         0.299  66.6 14.2 19 58.5  69  77
#> 12 critic_score  Adventure          980         0.248  65.3 13.9 17 57    66  76
#> 13 critic_score  <NA>                 2         0     NaN   NA   NA NA    NA  NA
#>    p100 hist   
#>  1   96 "▁▂▆▇▅"
#>  2   94 "▁▂▆▇▃"
#>  3   98 "▁▁▃▇▃"
#>  4   97 "▁▂▃▇▃"
#>  5   98 "▁▂▅▇▂"
#>  6   92 "▁▂▆▇▅"
#>  7   97 "▁▂▆▇▃"
#>  8   96 "▁▁▅▇▅"
#>  9   90 "▁▂▅▇▃"
#> 10   98 "▁▂▆▇▂"
#> 11   93 "▁▂▅▇▃"
#> 12   93 "▁▂▅▇▅"
#> 13   NA " "

Dále balíček skimr nabízí funkci skim_tee(). Ta funguje velmi podobně jako skim(), ale s tím rozdílem, že jejím skutečným výstupem je původní dataframe, nikoli tabulka s deskriptivními statistikami. Ta putuje jen do konzole. Toho můžeme použít v případě, kdy deskriptivní statistiky nepotřebujeme uložit, ale jen vytisknout do konzole, a zároveň chceme původní dataset využít v jakékoli další funkci, např. pro tvorbu grafu.

df %>% 
  skim_tee(critic_score, user_score) %>% 
  ggplot(aes(critic_score, user_score)) +
  geom_point()
#> ── Data Summary ────────────────────────
#>                            Values
#> Name                       data  
#> Number of rows             16719 
#> Number of columns          21    
#> _______________________          
#> Column type frequency:           
#>   numeric                  2     
#> ________________________         
#> Group variables            None  
#> 
#> ── Variable type: numeric ──────────────────────────────────────────────────────
#>   skim_variable n_missing complete_rate  mean    sd p0  p25  p50  p75 p100 hist 
#> 1 critic_score       8582         0.487 69.0  13.9  13 60   71   79   98   ▁▁▅▇▃
#> 2 user_score         9129         0.454  7.13  1.50  0  6.4  7.5  8.2  9.7 ▁▁▂▇▇

K podrobnější úpravě výstupu z funkce skim slouží další dvě funce. Nejprve si ukážeme funkci partition(). Ta vrátí objekt typu list, jehož prvky tvoří několik dataframes (podle typu proměnné: "factor", "character", "numeric") se statistikami.

data_summary <- df %>%
  skim() %>%
  partition()
glimpse(data_summary)
#> List of 3
#>  $ factor   : one_skim_df [7 × 6] (S3: one_skim_df/tbl_df/tbl/data.frame)
#>   ..$ skim_variable: chr [1:7] "genre" "rating" "post_2010" "decade" ...
#>   ..$ n_missing    : int [1:7] 2 6769 0 269 0 0 0
#>   ..$ complete_rate: num [1:7] 1 0.595 1 0.984 1 ...
#>   ..$ ordered      : logi [1:7] FALSE FALSE FALSE FALSE FALSE FALSE ...
#>   ..$ n_unique     : int [1:7] 12 5 2 4 5 8 2
#>   ..$ top_counts   : chr [1:7] "Act: 3370, Spo: 2348, Mis: 1750, Rol: 1500" "E/1: 5411, T: 2961, M/A: 1564, EC/: 11" "No: 11169, Yes: 5550" "200: 9193, 201: 5281, 199: 1771, 198: 205" ...
#>   ..- attr(*, "data_rows")= int 16719
#>   ..- attr(*, "data_cols")= int 21
#>   ..- attr(*, "df_name")= chr "`.`"
#>   ..- attr(*, "dt_key")= logi NA
#>   ..- attr(*, "groups")= list()
#>   ..- attr(*, "base_skimmers")= chr [1:2] "n_missing" "complete_rate"
#>   ..- attr(*, "skimmers_used")=List of 3
#>   ..- attr(*, "skim_type")= chr "factor"
#>  $ character: one_skim_df [4 × 8] (S3: one_skim_df/tbl_df/tbl/data.frame)
#>   ..$ skim_variable: chr [1:4] "name" "platform" "publisher" "developer"
#>   ..$ n_missing    : int [1:4] 2 0 54 6623
#>   ..$ complete_rate: num [1:4] 1 1 0.997 0.604
#>   ..$ min          : int [1:4] 1 2 3 2
#>   ..$ max          : int [1:4] 132 4 38 80
#>   ..$ empty        : int [1:4] 0 0 0 0
#>   ..$ n_unique     : int [1:4] 11562 31 580 1696
#>   ..$ whitespace   : int [1:4] 0 0 0 0
#>   ..- attr(*, "data_rows")= int 16719
#>   ..- attr(*, "data_cols")= int 21
#>   ..- attr(*, "df_name")= chr "`.`"
#>   ..- attr(*, "dt_key")= logi NA
#>   ..- attr(*, "groups")= list()
#>   ..- attr(*, "base_skimmers")= chr [1:2] "n_missing" "complete_rate"
#>   ..- attr(*, "skimmers_used")=List of 3
#>   ..- attr(*, "skim_type")= chr "character"
#>  $ numeric  : one_skim_df [10 × 11] (S3: one_skim_df/tbl_df/tbl/data.frame)
#>   ..$ skim_variable: chr [1:10] "year_of_release" "na_sales" "eu_sales" "jp_sales" ...
#>   ..$ n_missing    : int [1:10] 269 0 0 0 0 0 8582 8582 9129 9129
#>   ..$ complete_rate: num [1:10] 0.984 1 1 1 1 ...
#>   ..$ mean         : num [1:10] 2.01e+03 2.63e-01 1.45e-01 7.76e-02 4.73e-02 ...
#>   ..$ sd           : num [1:10] 5.879 0.814 0.503 0.309 0.187 ...
#>   ..$ p0           : num [1:10] 1980 0 0 0 0 0.01 13 3 0 4
#>   ..$ p25          : num [1:10] 2003 0 0 0 0 ...
#>   ..$ p50          : num [1:10] 2007 0.08 0.02 0 0.01 ...
#>   ..$ p75          : num [1:10] 2010 0.24 0.11 0.04 0.03 0.47 79 36 8.2 81
#>   ..$ p100         : num [1:10] 2020 41.4 29 10.2 10.6 ...
#>   ..$ hist         : chr [1:10] "▁▁▃▇▂" "▇▁▁▁▁" "▇▁▁▁▁" "▇▁▁▁▁" ...
#>   ..- attr(*, "data_rows")= int 16719
#>   ..- attr(*, "data_cols")= int 21
#>   ..- attr(*, "df_name")= chr "`.`"
#>   ..- attr(*, "dt_key")= logi NA
#>   ..- attr(*, "groups")= list()
#>   ..- attr(*, "base_skimmers")= chr [1:2] "n_missing" "complete_rate"
#>   ..- attr(*, "skimmers_used")=List of 3
#>   ..- attr(*, "skim_type")= chr "numeric"
#>  - attr(*, "class")= chr [1:2] "skim_list" "list"
#>  - attr(*, "data_rows")= int 16719
#>  - attr(*, "data_cols")= int 21
#>  - attr(*, "df_name")= chr "`.`"
#>  - attr(*, "dt_key")= logi NA
#>  - attr(*, "groups")= list()
#>  - attr(*, "base_skimmers")= chr [1:2] "n_missing" "complete_rate"
#>  - attr(*, "skimmers_used")=List of 3
#>   ..$ character: chr [1:5] "min" "max" "empty" "n_unique" ...
#>   ..$ numeric  : chr [1:8] "mean" "sd" "p0" "p25" ...
#>   ..$ factor   : chr [1:3] "ordered" "n_unique" "top_counts"
data_summary$factor
#> 
#> ── Variable type: factor ───────────────────────────────────────────────────────
#>   skim_variable n_missing complete_rate ordered n_unique top_counts             
#> 1 genre                 2         1.00  FALSE         12 Act: 3370, Spo: 2348, …
#> 2 rating             6769         0.595 FALSE          5 E/1: 5411, T: 2961, M/…
#> 3 post_2010             0         1     FALSE          2 No: 11169, Yes: 5550   
#> 4 decade              269         0.984 FALSE          4 200: 9193, 201: 5281, …
#> 5 platform_type         0         1     FALSE          5 Pla: 6723, Nin: 6271, …
#> 6 generation            0         1     FALSE          8 7th: 7274, 6th: 4415, …
#> 7 handheld              0         1     FALSE          2 No: 10923, Yes: 5796
data_summary$character
#> 
#> ── Variable type: character ────────────────────────────────────────────────────
#>   skim_variable n_missing complete_rate min max empty n_unique whitespace
#> 1 name                  2         1.00    1 132     0    11562          0
#> 2 platform              0         1       2   4     0       31          0
#> 3 publisher            54         0.997   3  38     0      580          0
#> 4 developer          6623         0.604   2  80     0     1696          0
data_summary$numeric
#> 
#> ── Variable type: numeric ──────────────────────────────────────────────────────
#>    skim_variable n_mis…¹ compl…²    mean      sd      p0     p25     p50     p75
#>  1 year_of_rele…     269   0.984 2.01e+3   5.88  1980    2003    2007    2010   
#>  2 na_sales            0   1     2.63e-1   0.814    0       0       0.08    0.24
#>  3 eu_sales            0   1     1.45e-1   0.503    0       0       0.02    0.11
#>  4 jp_sales            0   1     7.76e-2   0.309    0       0       0       0.04
#>  5 other_sales         0   1     4.73e-2   0.187    0       0       0.01    0.03
#>  6 global_sales        0   1     5.34e-1   1.55     0.01    0.06    0.17    0.47
#>  7 critic_score     8582   0.487 6.90e+1  13.9     13      60      71      79   
#>  8 critic_count     8582   0.487 2.64e+1  19.0      3      12      21      36   
#>  9 user_score       9129   0.454 7.13e+0   1.50     0       6.4     7.5     8.2 
#> 10 user_count       9129   0.454 1.62e+2 561.       4      10      24      81   
#> # … with 2 more variables: p100 <dbl>, hist <chr>, and abbreviated variable
#> #   names ¹​n_missing, ²​complete_rate

Alternativně můžeme použít funkci yank() k výběru proměnných pouze určitého typu. Ostatní typy proměnných jsou vyřazeny.

data_summary <- df %>%
  skim() %>%
  yank("factor")
data_summary
#> 
#> ── Variable type: factor ───────────────────────────────────────────────────────
#>   skim_variable n_missing complete_rate ordered n_unique top_counts             
#> 1 genre                 2         1.00  FALSE         12 Act: 3370, Spo: 2348, …
#> 2 rating             6769         0.595 FALSE          5 E/1: 5411, T: 2961, M/…
#> 3 post_2010             0         1     FALSE          2 No: 11169, Yes: 5550   
#> 4 decade              269         0.984 FALSE          4 200: 9193, 201: 5281, …
#> 5 platform_type         0         1     FALSE          5 Pla: 6723, Nin: 6271, …
#> 6 generation            0         1     FALSE          8 7th: 7274, 6th: 4415, …
#> 7 handheld              0         1     FALSE          2 No: 10923, Yes: 5796

I kdybychom k výběru statistik mohli použít funkci select(), lepší je použít funkci focus(), protože funkce select() vede k odstranění užitečných metadat z objejtu skim_df, zatímco focus() vždy tato metadata zachová.

data_summary <- df %>%
  skim() %>%
  focus(n_missing, numeric.mean) %>% 
  yank("numeric")
data_summary
#> 
#> ── Variable type: numeric ──────────────────────────────────────────────────────
#>    skim_variable   n_missing      mean
#>  1 year_of_release       269 2006.    
#>  2 na_sales                0    0.263 
#>  3 eu_sales                0    0.145 
#>  4 jp_sales                0    0.0776
#>  5 other_sales             0    0.0473
#>  6 global_sales            0    0.534 
#>  7 critic_score         8582   69.0   
#>  8 critic_count         8582   26.4   
#>  9 user_score           9129    7.13  
#> 10 user_count           9129  162.

Samozřejmě se také může stát, že chceme vypočíst jiné než defaultní statistiky. K tomu se používá funkce skim_with() do které specifikujeme, jaké dodatečné statistiky chceme vypočíst, pro které typy proměnných a jak se mají jmenovat. Typ proměnné může být base (statistika se počítá pro všechny typy), factor, character a numeric.

my_skim <- skim_with(
  # Pro všechny typy proměnných vypočítáme počet validních hodnot
  base = sfl(
    n_valid = ~sum(!is.na(.)) 
    ),
  # Pro numerické proměnné vypočteme navíc šikmost a špičatost
  numeric = sfl(
    skew = ~skewness(., na.rm = TRUE),
    kurt = ~kurtosis(., na.rm = TRUE)
    )
  )
 # ~NAZEV_FUNKCE(TEČKA_JAKO_PLACEHOLDER_PRO_PROMĚNNÉ, DODATEČNÉ_ARGUMENTY)

# Tím jsme definovali svou vlastní funkci my_skim() a můžeme ji použít
# stejně jako skim()
df %>% 
  my_skim()
#> ── Data Summary ────────────────────────
#>                            Values    
#> Name                       Piped data
#> Number of rows             16719     
#> Number of columns          21        
#> _______________________              
#> Column type frequency:               
#>   factor                   7         
#>   character                4         
#>   numeric                  10        
#> ________________________             
#> Group variables            None      
#> 
#> ── Variable type: factor ───────────────────────────────────────────────────────
#>   skim_variable n_valid ordered n_unique
#> 1 genre           16717 FALSE         12
#> 2 rating           9950 FALSE          5
#> 3 post_2010       16719 FALSE          2
#> 4 decade          16450 FALSE          4
#> 5 platform_type   16719 FALSE          5
#> 6 generation      16719 FALSE          8
#> 7 handheld        16719 FALSE          2
#>   top_counts                                
#> 1 Act: 3370, Spo: 2348, Mis: 1750, Rol: 1500
#> 2 E/1: 5411, T: 2961, M/A: 1564, EC/: 11    
#> 3 No: 11169, Yes: 5550                      
#> 4 200: 9193, 201: 5281, 199: 1771, 198: 205 
#> 5 Pla: 6723, Nin: 6271, XBo: 2333, PC: 974  
#> 6 7th: 7274, 6th: 4415, 8th: 1739, 5th: 1699
#> 7 No: 10923, Yes: 5796                      
#> 
#> ── Variable type: character ────────────────────────────────────────────────────
#>   skim_variable n_valid min max empty n_unique whitespace
#> 1 name            16717   1 132     0    11562          0
#> 2 platform        16719   2   4     0       31          0
#> 3 publisher       16665   3  38     0      580          0
#> 4 developer       10096   2  80     0     1696          0
#> 
#> ── Variable type: numeric ──────────────────────────────────────────────────────
#>    skim_variable   n_valid      mean      sd      p0     p25     p50     p75
#>  1 year_of_release   16450 2006.       5.88  1980    2003    2007    2010   
#>  2 na_sales          16719    0.263    0.814    0       0       0.08    0.24
#>  3 eu_sales          16719    0.145    0.503    0       0       0.02    0.11
#>  4 jp_sales          16719    0.0776   0.309    0       0       0       0.04
#>  5 other_sales       16719    0.0473   0.187    0       0       0.01    0.03
#>  6 global_sales      16719    0.534    1.55     0.01    0.06    0.17    0.47
#>  7 critic_score       8137   69.0     13.9     13      60      71      79   
#>  8 critic_count       8137   26.4     19.0      3      12      21      36   
#>  9 user_score         7590    7.13     1.50     0       6.4     7.5     8.2 
#> 10 user_count         7590  162.     561.       4      10      24      81   
#>       p100 hist    skew    kurt
#>  1  2020   ▁▁▃▇▂ -0.973    4.77
#>  2    41.4 ▇▁▁▁▁ 18.8    652.  
#>  3    29.0 ▇▁▁▁▁ 18.9    758.  
#>  4    10.2 ▇▁▁▁▁ 11.2    197.  
#>  5    10.6 ▇▁▁▁▁ 24.6   1058.  
#>  6    82.5 ▇▁▁▁▁ 17.4    607.  
#>  7    98   ▁▁▅▇▃ -0.614    3.14
#>  8   113   ▇▃▂▁▁  1.15     4.03
#>  9     9.7 ▁▁▂▇▇ -1.25     4.72
#> 10 10665   ▇▁▁▁▁  9.03   115.

Defaultní chování skim_with() je takové, že nové statistiky jsou doplněny k těm původním. Kdyby nám to nevyhovovalo, můžeme to změnit pomocí argumentu append = FALSE. Původní statistiky jsou tím odstraněny, ale jen u toho typu proměnných, u něhož explicitně specifikujeme statistiky nové (tj. kromě base), jak si můžeme všimnout níže.

my_skim <- skim_with(
  base = sfl(
    n_valid = ~sum(!is.na(.))
  ),
  numeric = sfl(
    median = ~median(., na.rm = TRUE),
    IQR = ~IQR(., na.rm = TRUE)),
  append = FALSE)
my_skim(df)
#> ── Data Summary ────────────────────────
#>                            Values
#> Name                       df    
#> Number of rows             16719 
#> Number of columns          21    
#> _______________________          
#> Column type frequency:           
#>   factor                   7     
#>   character                4     
#>   numeric                  10    
#> ________________________         
#> Group variables            None  
#> 
#> ── Variable type: factor ───────────────────────────────────────────────────────
#>   skim_variable n_valid ordered n_unique
#> 1 genre           16717 FALSE         12
#> 2 rating           9950 FALSE          5
#> 3 post_2010       16719 FALSE          2
#> 4 decade          16450 FALSE          4
#> 5 platform_type   16719 FALSE          5
#> 6 generation      16719 FALSE          8
#> 7 handheld        16719 FALSE          2
#>   top_counts                                
#> 1 Act: 3370, Spo: 2348, Mis: 1750, Rol: 1500
#> 2 E/1: 5411, T: 2961, M/A: 1564, EC/: 11    
#> 3 No: 11169, Yes: 5550                      
#> 4 200: 9193, 201: 5281, 199: 1771, 198: 205 
#> 5 Pla: 6723, Nin: 6271, XBo: 2333, PC: 974  
#> 6 7th: 7274, 6th: 4415, 8th: 1739, 5th: 1699
#> 7 No: 10923, Yes: 5796                      
#> 
#> ── Variable type: character ────────────────────────────────────────────────────
#>   skim_variable n_valid min max empty n_unique whitespace
#> 1 name            16717   1 132     0    11562          0
#> 2 platform        16719   2   4     0       31          0
#> 3 publisher       16665   3  38     0      580          0
#> 4 developer       10096   2  80     0     1696          0
#> 
#> ── Variable type: numeric ──────────────────────────────────────────────────────
#>    skim_variable   n_valid  median   IQR
#>  1 year_of_release   16450 2007     7   
#>  2 na_sales          16719    0.08  0.24
#>  3 eu_sales          16719    0.02  0.11
#>  4 jp_sales          16719    0     0.04
#>  5 other_sales       16719    0.01  0.03
#>  6 global_sales      16719    0.17  0.41
#>  7 critic_score       8137   71    19   
#>  8 critic_count       8137   21    24   
#>  9 user_score         7590    7.5   1.80
#> 10 user_count         7590   24    71

4 Explorace dat pomocí balíčku dlookr

Balíček dlookr slouží k diagnostice dat. Začít můžeme např. funkcí diagnose(), která slouží k přehledu chybějících a jedinečných hodnot. Prvním argumentem je použitý dataframe a dalšími argumenty si můžeme vybrat proměnné podobně jako ve funkci select().

diagnose(df)
#> # A tibble: 21 × 6
#>    variables       types     missing_count missing_percent unique_count unique…¹
#>    <chr>           <chr>             <int>           <dbl>        <int>    <dbl>
#>  1 name            character             2          0.0120        11563 0.692   
#>  2 platform        character             0          0                31 0.00185 
#>  3 year_of_release numeric             269          1.61             40 0.00239 
#>  4 genre           factor                2          0.0120           13 0.000778
#>  5 publisher       character            54          0.323           581 0.0348  
#>  6 na_sales        numeric               0          0               402 0.0240  
#>  7 eu_sales        numeric               0          0               307 0.0184  
#>  8 jp_sales        numeric               0          0               244 0.0146  
#>  9 other_sales     numeric               0          0               155 0.00927 
#> 10 global_sales    numeric               0          0               629 0.0376  
#> # … with 11 more rows, and abbreviated variable name ¹​unique_rate
diagnose(df, ends_with("sales"))
#> # A tibble: 5 × 6
#>   variables    types   missing_count missing_percent unique_count unique_rate
#>   <chr>        <chr>           <int>           <dbl>        <int>       <dbl>
#> 1 na_sales     numeric             0               0          402     0.0240 
#> 2 eu_sales     numeric             0               0          307     0.0184 
#> 3 jp_sales     numeric             0               0          244     0.0146 
#> 4 other_sales  numeric             0               0          155     0.00927
#> 5 global_sales numeric             0               0          629     0.0376
diagnose(df, rating, platform, name)
#> # A tibble: 3 × 6
#>   variables types     missing_count missing_percent unique_count unique_rate
#>   <chr>     <chr>             <int>           <dbl>        <int>       <dbl>
#> 1 rating    factor             6769         40.5               6    0.000359
#> 2 platform  character             0          0                31    0.00185 
#> 3 name      character             2          0.0120        11563    0.692

Funkce balíčku dlookr můžeme pipovat do dalších funkcí a tím s výstupem dále pracovat. Např. vybrat pouze proměnné s chybějícími hodnotami a seřadit je sestupně právě podle počtu chybějících hodnot

df %>% 
  diagnose() %>% 
  filter(missing_count > 0) %>%
  arrange(desc(missing_count))
#> # A tibble: 11 × 6
#>    variables       types     missing_count missing_percent unique_count unique…¹
#>    <chr>           <chr>             <int>           <dbl>        <int>    <dbl>
#>  1 user_score      numeric            9129         54.6              96 0.00574 
#>  2 user_count      numeric            9129         54.6             889 0.0532  
#>  3 critic_score    numeric            8582         51.3              83 0.00496 
#>  4 critic_count    numeric            8582         51.3             107 0.00640 
#>  5 rating          factor             6769         40.5               6 0.000359
#>  6 developer       character          6623         39.6            1697 0.102   
#>  7 year_of_release numeric             269          1.61             40 0.00239 
#>  8 decade          factor              269          1.61              5 0.000299
#>  9 publisher       character            54          0.323           581 0.0348  
#> 10 name            character             2          0.0120        11563 0.692   
#> 11 genre           factor                2          0.0120           13 0.000778
#> # … with abbreviated variable name ¹​unique_rate

Funkce diagnose_numeric() diagnostikuje numerické proměnné (tj. proměnné typu integer a double) v daném datasetu, ale na rozdíl od funkce diagnose() poskytuje více informací.

diagnose_numeric(df)
#> # A tibble: 10 × 10
#>    variables      min      Q1    mean  median      Q3    max  zero minus outlier
#>    <chr>        <dbl>   <dbl>   <dbl>   <dbl>   <dbl>  <dbl> <int> <int>   <int>
#>  1 year_of_r… 1980    2003    2.01e+3 2007    2010    2.02e3     0     0     305
#>  2 na_sales      0       0    2.63e-1    0.08    0.24 4.14e1  4511     0    1687
#>  3 eu_sales      0       0    1.45e-1    0.02    0.11 2.90e1  5874     0    2061
#>  4 jp_sales      0       0    7.76e-2    0       0.04 1.02e1 10515     0    2434
#>  5 other_sal…    0       0    4.73e-2    0.01    0.03 1.06e1  6604     0    2243
#>  6 global_sa…    0.01    0.06 5.34e-1    0.17    0.47 8.25e1     0     0    1892
#>  7 critic_sc…   13      60    6.90e+1   71      79    9.8 e1     0     0      83
#>  8 critic_co…    3      12    2.64e+1   21      36    1.13e2     0     0     251
#>  9 user_score    0       6.4  7.13e+0    7.5     8.2  9.7 e0     1     0     305
#> 10 user_count    4      10    1.62e+2   24      81    1.07e4     0     0    1080

Funkce diagnose_category() sloužít slouží k diagnostice kategorických proměnných (včetně factor a character). Pokud je počet úrovní velký, můžeme pomocí argumentu top specifikovat, ať se nám zobrazí pouze četnosti pro nejvíce zastoupené kategorie, např. prvních pět:

diagnose_category(df, top = 5)
#> # A tibble: 49 × 6
#>    variables levels                          N  freq   ratio  rank
#>    <chr>     <chr>                       <int> <int>   <dbl> <int>
#>  1 name      Need for Speed: Most Wanted 16719    12  0.0718     1
#>  2 name      FIFA 14                     16719     9  0.0538     2
#>  3 name      LEGO Marvel Super Heroes    16719     9  0.0538     2
#>  4 name      Madden NFL 07               16719     9  0.0538     2
#>  5 name      Ratatouille                 16719     9  0.0538     2
#>  6 platform  PS2                         16719  2161 12.9        1
#>  7 platform  DS                          16719  2152 12.9        2
#>  8 platform  PS3                         16719  1331  7.96       3
#>  9 platform  Wii                         16719  1320  7.90       4
#> 10 platform  X360                        16719  1262  7.55       5
#> # … with 39 more rows

Funkce diagnose_outlier() slouží k diagnostice extrémních případů, identifikovaných na základě pravidla boxplotu (hodnot odchylujících se více než 1,5násobek mezikvartilového rozpětí od 1. a 3. kvartilu směrem k bližšímu konci distribuce). Zobrazí nám počet a podíl outlierů, jejich průměr a průměr proměnných bez outlierů a s nimi.

diagnose_outlier(df)
#> # A tibble: 10 × 6
#>    variables       outliers_cnt outliers_ratio outliers_mean with_mean without…¹
#>    <chr>                  <int>          <dbl>         <dbl>     <dbl>     <dbl>
#>  1 year_of_release          305          1.82       1986.    2006.     2007.    
#>  2 na_sales                1687         10.1           1.62     0.263     0.111 
#>  3 eu_sales                2061         12.3           0.873    0.145     0.0427
#>  4 jp_sales                2434         14.6           0.471    0.0776    0.0106
#>  5 other_sales             2243         13.4           0.267    0.0473    0.0133
#>  6 global_sales            1892         11.3           2.92     0.534     0.229 
#>  7 critic_score              83          0.496        26.2     69.0      69.4   
#>  8 critic_count             251          1.50         82.1     26.4      24.6   
#>  9 user_score               305          1.82          2.75     7.13      7.31  
#> 10 user_count              1080          6.46        929.     162.       35.1   
#> # … with abbreviated variable name ¹​without_mean

Funkce plot_outlier() pak slouží k zobrazení distribuce s outliery a bez nich pomocí boxplotu a histogramu.

plot_outlier(df, global_sales)

Bohužel funkce diagnose_numeric() nedodržuje rozdělení datasetu pomocí funkce group_by(), ale dlookr má také funkci describe(), která to dovede.

df %>% 
  group_by(post_2010) %>% 
  describe() %>% 
  mutate(across(where(is.numeric), round, digits = 2))
#> # A tibble: 20 × 27
#>    described…¹ post_…²     n    na    mean     sd se_mean    IQR skewn…³ kurto…⁴
#>    <chr>       <fct>   <dbl> <dbl>   <dbl>  <dbl>   <dbl>  <dbl>   <dbl>   <dbl>
#>  1 critic_cou… No       5671  5498   24.3   16.8     0.22  22       1.19    1.3 
#>  2 critic_cou… Yes      2466  3084   31.0   22.6     0.45  34       0.86    0.01
#>  3 critic_sco… No       5671  5498   68.4   14.0     0.19  19      -0.55    0.08
#>  4 critic_sco… Yes      2466  3084   70.3   13.7     0.28  18      -0.77    0.38
#>  5 eu_sales    No      11169     0    0.14   0.53    0      0.1    22.4   930.  
#>  6 eu_sales    Yes      5550     0    0.16   0.46    0.01   0.12    7.59   81.8 
#>  7 global_sal… No      11169     0    0.56   1.69    0.02   0.43   18.6   626.  
#>  8 global_sal… Yes      5550     0    0.48   1.21    0.02   0.35    7.53   80.2 
#>  9 jp_sales    No      11169     0    0.09   0.34    0      0.03   10.4   170.  
#> 10 jp_sales    Yes      5550     0    0.06   0.22    0      0.04   12.8   221.  
#> 11 na_sales    No      11169     0    0.29   0.9     0.01   0.25   19.5   638.  
#> 12 na_sales    Yes      5550     0    0.21   0.61    0.01   0.18    8.92  127.  
#> 13 other_sales No      11169     0    0.05   0.2     0      0.03   26.7  1104.  
#> 14 other_sales Yes      5550     0    0.05   0.15    0      0.04    9.56  151.  
#> 15 user_count  No       4964  6205   86.6  356.      5.05  37      13.1   249.  
#> 16 user_count  Yes      2626  2924  305.   800.     15.6  198       6.41   55.4 
#> 17 user_score  No       4964  6205    7.35   1.44    0.02   1.7    -1.47    2.68
#> 18 user_score  Yes      2626  2924    6.7    1.52    0.03   1.9    -1.02    0.87
#> 19 year_of_re… No      11169     0 2004.     4.99    0.05   7      -1.56    3.44
#> 20 year_of_re… Yes      5281   269 2012.     2.04    0.03   3       0.43   -1.15
#> # … with 17 more variables: p00 <dbl>, p01 <dbl>, p05 <dbl>, p10 <dbl>,
#> #   p20 <dbl>, p25 <dbl>, p30 <dbl>, p40 <dbl>, p50 <dbl>, p60 <dbl>,
#> #   p70 <dbl>, p75 <dbl>, p80 <dbl>, p90 <dbl>, p95 <dbl>, p99 <dbl>,
#> #   p100 <dbl>, and abbreviated variable names ¹​described_variables,
#> #   ²​post_2010, ³​skewness, ⁴​kurtosis

Funkce normality() provádí Shapiro-Wilkův test normality dat. U tak velkého vzorku, jako máme by, si můžeme být téměř jisti, že bude test signifikantní, i kdyby odchylka od normality byla minimální. Obecně nás spíš může zajímá samotná testová statistika, která nabývá hodnot od 0 do 1 a udává, jak těsně se naše data shodují s normálně rozdělenou proměnnou.

normality(df, contains("sales")) %>% 
  arrange(statistic)
#> # A tibble: 5 × 4
#>   vars         statistic  p_value sample
#>   <chr>            <dbl>    <dbl>  <dbl>
#> 1 other_sales      0.219 4.34e-90   5000
#> 2 jp_sales         0.237 1.48e-89   5000
#> 3 na_sales         0.277 2.48e-88   5000
#> 4 eu_sales         0.279 2.98e-88   5000
#> 5 global_sales     0.283 3.82e-88   5000

Pomocí funkce plot_normality() si pak můžeme nechat zobrazit histogram a Q-Q graf původních hodnot a dva histogramy (left a right) hodnot po transformaci. V nápovědě k funkci ?plot_normality je přehled všech tranformací, které můžeme specikovat argumenty left a right.

plot_normality(df, global_sales,
               left = "1/x", right = "log")

Funkce normality() i plot_normality() navíc podporují rozdělení datasetu pomocí group_by().

df %>% 
  group_by(decade) %>% 
  normality(global_sales) %>% 
  arrange(statistic)
#> # A tibble: 5 × 5
#>   variable     decade statistic  p_value sample
#>   <chr>        <fct>      <dbl>    <dbl>  <dbl>
#> 1 global_sales 2000s      0.277 2.52e-88   5000
#> 2 global_sales 1980s      0.327 7.25e-27    205
#> 3 global_sales 2010s      0.366 2.37e-85   5000
#> 4 global_sales 1990s      0.388 9.21e-61   1771
#> 5 global_sales <NA>       0.567 2.32e-25    269

df %>% 
  group_by(post_2010) %>% 
  plot_normality(global_sales,
                 left = "sqrt", right = "log")

5 Explorační grafy

Pro tvorbu exploračních grafů jsem vytvořil několik funkcí, které umožňují snadno vytvořit i několik grafů najednou. Všechny funkce mají jako první argument použitý dataset. Jejich názvy načínají slovem plot_, pak následuje typ grafu, např. bar. V případě, že se jedná o funkci, která dovede vytvořit více grafů zároveň, končí příponou _mult.

5.1 Sloupcové grafy

Funkce plot_bar() vytváří jeden sloupcový graf a má tyto argumenty:

  • data: použitý dataset.
  • var: četnosti které proměnné chceme zjistit (character).
  • fill: nastavení výplňě podle další proměnné (character).
  • facet: fazetovat graf podle další proměnné¨(character).
  • add_n: přidat labely s absolutními četnostmi (logical).
  • sort: seřadit sloupce podle četností (logical)?

Funkce plot_bar_mult() vytváří více sloupcových grafů a má stejné argumenty jako funkce plot_bar(), ale umožňuje do argumentu var vložit více názvů proměnných (jako textový vektor).

plot_bar(df, var = "genre",  fill = "decade", facet = "handheld")


plot_bar_mult(data = df, var = c("decade", "genre", "rating"),
              sort = FALSE)
#> [[1]]

#> 
#> [[2]]

#> 
#> [[3]]

5.2 Skládané sloupcové grafy

Pro tvorbu skládaných sloupcových grafů můžete využít funkce plot_stackbar(), která vytváří jeden skládanýc sloupcový graf a `plot_stackbar_mult()`` pro tvorbu více sloupcových grafů zároveň. Mají tyto argumenty:

  • data: použitý dataset.
  • var: četnosti které proměnné chceme zjistit (character).
  • fill: nastavení výplňě podle další proměnné (character).
  • facet: fazetovat graf podle další proměnné¨(character).
plot_stackbar(df, var = "handheld", fill = "genre")


plot_stackbar_mult(df, var = c("handheld", "decade"), fill = c("rating", "genre"))
#> [[1]]

#> 
#> [[2]]

5.3 Histogramy

Pro tvorbu histogramu můžeme použít funkce plot_histogram() a plot_histogram_mult(), které mají tyto argumenty:

  • data: použitý dataset.
  • var: zobrazená proměnná (character).
  • facet: fazetovat graf podle další proměnné¨(character).
  • geom: "hist" pro histogram, "density" pro graf hustoty pravděpodobnosti, nebo "freqpoly"pro frekvenční polygon.
  • transform_fun: funkce použitý k případné transformaci hodnot
plot_histogram(df, "critic_score")

plot_histogram(df, "critic_score", geom = "density")

plot_histogram(df, "critic_score", geom = "freqpoly")


plot_histogram(df, var = "global_sales",
               facet = "platform_type", 
               geom = "density",
               transform_fun = log10)


plot_histogram_mult(df, var = c("critic_score", "year_of_release"))
#> [[1]]

#> 
#> [[2]]

5.4 Boxploty

K tvorbě boxplotů můžeme použít funkce plot_box() a plot_box_mult(), které mají tyto argumenty:

  • data: použitý dataset.
  • var: zobrazená proměnná (character).
  • groups: rozdělení do skupin podle jiné proměnné (character).
  • fill: nastavení výplňě podle další proměnné (character).
  • facet: fazetovat graf podle další proměnné¨(character).
  • geom: "boxplot" pro klasický krabicový graf, "violin" pro houslouvý graf.
  • order: Seřadit “krabice” podle madiánu.
plot_box(df, "user_score", groups = "genre")

plot_box_mult(df, 
              var = c("user_score", "critic_score"), 
              groups = "genre")
#> [[1]]

#> 
#> [[2]]

5.5 Bodové grafy (scatterplots)

K tvorbě bodových grafů (scatterplots) můžeme použít funkce plot_box() a plot_box_mult(), které mají tyto argumenty:

  • data: použitý dataset.
  • xvar: proměnná zobrazená na ose X (character).
  • yvar: proměnná zobrazená na ose Y (character).
  • color: nastavení barvy bodů podle další proměnné (character)
  • geom: "point" pro klasický bodový graf, "jitter" pro bodový graf s malým hánodným vychýlením bodů a "count" pro zobrazení nakupení více případů velikostí bodu.
  • alpha: míra neprůhlednosti bodů (od 0 do 1).
  • sample: nezobrazovat všechny případy, pouze náhodný vzorek n-případů (integer).
  • fit_line: zobrazit trend? (logical).
  • method: funkce pro vytvoření spojnice trendu (např. “loess” nebo “lm”)
plot_scatter(df, xvar = "user_score", yvar = "critic_score", 
             color = "handheld",
             geom = "jitter",
             sample = 1000)


plot_scatter_mult(df,
                  xvar = c("user_score", "user_score"),
                  yvar = c("critic_score", "log10(global_sales)"),
                  geom = "jitter", method = "lm",
                  sample = 1000)
#> [[1]]

#> 
#> [[2]]

5.6 Mozaikové grafy

Mozaikové grafy jsou velmi podobné skládaným sloupcovým grafům, ale kromě toho informují o zastopení jednotlivých kategorií na ose X prostřednictvím šířky sloupců. K tvorbě mozaikových grafů můžeme použít funkce plot_mosaic() a plot_mosaic_mult(), které mají tyto argumenty:

  • data: použitý dataset.
  • xvar: proměnná zobrazená na ose X, o které uvažujeme jako o “nezávislé” (character).
  • yvar: proměnná zobrazená na ose Y a pomocí výplně (character), o které uvažujeme jako o “závislé”.
plot_mosaic(df, "genre", "rating")


plot_mosaic(df %>% drop_na(rating),
            xvar = "genre", yvar = "rating")


plot_mosaic_mult(df %>% drop_na(rating), 
                 xvar = "platform_type", 
                 yvar = c("genre", "handheld", "rating"))
#> [[1]]

#> 
#> [[2]]

#> 
#> [[3]]

6 Tabulky s četnostmi

Tabulky s četnostmi je možné vytvořit s využítím balíčku dplyr, a to s využitím funkce count. Můžeme například chtít zjistit četnosti žánrů v závislosti na dekádě.

# Nejdříve si vypočteme absolutní četnosti
freq <-  df %>% 
  mutate(genre = fct_explicit_na(genre, na_level = "Undetermined"),
         decade = fct_explicit_na(decade, na_level = "Not released yet")) %>% 
  count(decade, genre,
        .drop = FALSE)

# Pak dopočteme relativní četnost žánrů v rámci dekády
freq <- freq %>% 
  group_by(decade) %>% 
  mutate(p = n/sum(n))
freq
#> # A tibble: 65 × 4
#> # Groups:   decade [5]
#>    decade genre            n       p
#>    <fct>  <fct>        <int>   <dbl>
#>  1 1980s  Action          66 0.322  
#>  2 1980s  Adventure        2 0.00976
#>  3 1980s  Fighting         4 0.0195 
#>  4 1980s  Misc             8 0.0390 
#>  5 1980s  Platform        33 0.161  
#>  6 1980s  Puzzle          19 0.0927 
#>  7 1980s  Racing           8 0.0390 
#>  8 1980s  Role-Playing     9 0.0439 
#>  9 1980s  Shooter         30 0.146  
#> 10 1980s  Simulation       3 0.0146 
#> # … with 55 more rows

# A nakonec můžeme údaj o dekádě, ze které data pocházejí, "rozprostřít"
# do sloupců pomocí pivot_wider()
freq %>% 
  pivot_wider(names_from = decade, 
              values_from = c(n, p),
              names_vary = "slowest")
#> # A tibble: 13 × 11
#>    genre n_1980s p_1980s n_1990s p_1990s n_2000s p_2000s n_2010s p_2010s n_Not…¹
#>    <fct>   <int>   <dbl>   <int>   <dbl>   <int>   <dbl>   <int>   <dbl>   <int>
#>  1 Acti…      66 0.322       162 0.0915     1584  0.172     1496  0.283       62
#>  2 Adve…       2 0.00976      97 0.0548      631  0.0686     563  0.107       10
#>  3 Figh…       4 0.0195      193 0.109       441  0.0480     199  0.0377      12
#>  4 Misc        8 0.0390      116 0.0655     1021  0.111      576  0.109       29
#>  5 Plat…      33 0.161       125 0.0706      567  0.0617     153  0.0290      10
#>  6 Puzz…      19 0.0927       71 0.0401      365  0.0397     114  0.0216      11
#>  7 Raci…       8 0.0390      183 0.103       798  0.0868     237  0.0449      23
#>  8 Role…       9 0.0439      172 0.0971      732  0.0796     570  0.108       17
#>  9 Shoo…      30 0.146       137 0.0774      719  0.0782     410  0.0776      27
#> 10 Simu…       3 0.0146       86 0.0486      551  0.0599     218  0.0413      16
#> 11 Spor…      23 0.112       304 0.172      1404  0.153      575  0.109       42
#> 12 Stra…       0 0           123 0.0695      380  0.0413     170  0.0322      10
#> 13 Unde…       0 0             2 0.00113       0  0            0  0            0
#> # … with 1 more variable: `p_Not released yet` <dbl>, and abbreviated variable
#> #   name ¹​`n_Not released yet`

7 Psychologická data

Na závěr si ukážeme operace, které se budou hodit při práci s psychologickými daty. Ke cvičením použijeme dataset, který obsahuje pouze několik proměnných: id respondenta, jeho pohlaví, věk a odpovědi na otázky Rosenberg self-esteem scale. Níže je import dat a jejich základní přehled.

rses <- haven::read_sav("https://is.muni.cz/el/fss/podzim2022/PSYn5320/um/datasets/rses_data.sav")
glimpse(rses)
#> Rows: 11,089
#> Columns: 13
#> $ id     <dbl> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, …
#> $ sex    <dbl+lbl> NA,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,…
#> $ age    <dbl> 99, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17,…
#> $ rses01 <dbl+lbl> 4, 4, 3, 5, 5, 3, 5, 2, 5, 5, 5, 4, 4, 2, 5, 4, 1, 4, 5, 6,…
#> $ rses02 <dbl+lbl> 5, 6, 5, 4, 3, 4, 5, 5, 4, 2, 6, 4, 5, 4, 3, 4, 6, 4, 2, 5,…
#> $ rses03 <dbl+lbl> 2, 5, 3, 5, 4, 4, 5, 4, 5, 6, 5, 4, 4, 4, 5, 5, 5, 4, 4, 5,…
#> $ rses04 <dbl+lbl> 4, 4, 4, 5, 5, 5, 5, 4, 5, 6, 4, 4, 4, 4, 5, 6, 4, 5, 4, 4,…
#> $ rses05 <dbl+lbl> 4, 5, 4, 3, 3, 3, 2, 4, 4, 1, 3, 3, 3, 4, 5, 3, 4, 2, 3, 2,…
#> $ rses06 <dbl+lbl> 4, 6, 4, 3, 2, 3, 2, 2, 3, 1, 5, 4, 5, 4, 3, 4, 4, 4, 3, 3,…
#> $ rses07 <dbl+lbl> 3, 6, 5, 5, 5, 3, 4, 4, 5, 6, 4, 4, 4, 4, 5, 5, 3, 6, 5, 5,…
#> $ rses08 <dbl+lbl> 5, 3, 5, 3, 5, 4, 4, 5, 5, 2, 3, 5, 5, 5, 5, 5, 3, 4, 4, 4,…
#> $ rses09 <dbl+lbl> 2, 5, 5, 3, 4, 3, 2, 2, 3, 2, 5, 5, 5, 4, 4, 4, 3, 2, 3, 3,…
#> $ rses10 <dbl+lbl> 2, 3, 3, 5, 4, 4, 5, 4, 5, 5, 4, 4, 3, 2, 5, 6, 2, 5, 4, 5,…
summary(rses)
#>        id                 sex             age            rses01     
#>  Min.   :      0.0   Min.   :0.000   Min.   : 0.00   Min.   :1.000  
#>  1st Qu.:     22.0   1st Qu.:1.000   1st Qu.:18.00   1st Qu.:2.000  
#>  Median :    523.5   Median :2.000   Median :20.00   Median :3.000  
#>  Mean   : 145046.4   Mean   :1.503   Mean   :19.98   Mean   :2.774  
#>  3rd Qu.:   1790.2   3rd Qu.:2.000   3rd Qu.:22.00   3rd Qu.:3.000  
#>  Max.   :1427587.0   Max.   :2.000   Max.   :99.00   Max.   :6.000  
#>  NA's   :8285        NA's   :402     NA's   :629     NA's   :1703   
#>      rses02          rses03          rses04          rses05     
#>  Min.   :1.000   Min.   :1.000   Min.   :1.000   Min.   :1.000  
#>  1st Qu.:2.000   1st Qu.:2.000   1st Qu.:2.000   1st Qu.:2.000  
#>  Median :2.000   Median :3.000   Median :3.000   Median :2.000  
#>  Mean   :2.485   Mean   :2.864   Mean   :2.904   Mean   :2.466  
#>  3rd Qu.:3.000   3rd Qu.:4.000   3rd Qu.:4.000   3rd Qu.:3.000  
#>  Max.   :6.000   Max.   :6.000   Max.   :6.000   Max.   :6.000  
#>  NA's   :1721    NA's   :2480    NA's   :1714    NA's   :1737   
#>      rses06          rses07          rses08          rses09     
#>  Min.   :1.000   Min.   :1.000   Min.   :1.000   Min.   :1.000  
#>  1st Qu.:2.000   1st Qu.:2.000   1st Qu.:2.000   1st Qu.:1.000  
#>  Median :2.000   Median :3.000   Median :3.000   Median :3.000  
#>  Mean   :2.408   Mean   :2.904   Mean   :2.682   Mean   :2.493  
#>  3rd Qu.:3.000   3rd Qu.:4.000   3rd Qu.:4.000   3rd Qu.:3.000  
#>  Max.   :6.000   Max.   :6.000   Max.   :6.000   Max.   :6.000  
#>  NA's   :2160    NA's   :1722    NA's   :2487    NA's   :2474   
#>      rses10     
#>  Min.   :1.000  
#>  1st Qu.:2.000  
#>  Median :3.000  
#>  Mean   :2.762  
#>  3rd Qu.:4.000  
#>  Max.   :6.000  
#>  NA's   :2475

Pokud pomocí balíčku haven importujeme dataset z SPSS, můžeme si pomocí funkce metadata() nechat zobrazit metadata z SPSS: kromě názvů proměnných tedy i jejich popisky a popisky hodnot

rses_md <- metadata(rses)
rses_md
#> # A tibble: 13 × 3
#>    var_names var_labels                                                  value…¹
#>    <chr>     <chr>                                                       <chr>  
#>  1 id        "respondent's ID"                                           <NA>   
#>  2 sex       "respondent's sex"                                          1 = ma…
#>  3 age       "Respondent's age in years"                                 <NA>   
#>  4 rses01    "On the whole, I am satisfied with myself"                  1 = st…
#>  5 rses02    "At times I think I am no good at all."                     1 = st…
#>  6 rses03    "I feel that I have a number of good qualities."            1 = st…
#>  7 rses04    "I am able to do things as well as most other people. \r"   1 = st…
#>  8 rses05    "I feel I do not have much to be proud of."                 1 = st…
#>  9 rses06    "I certainly feel useless at times."                        1 = st…
#> 10 rses07    "I feel that I'm a person of worth, at least on an equal p… 1 = st…
#> 11 rses08    "I wish I could have more respect for myself. \r"           1 = st…
#> 12 rses09    "All in all, I am inclined to feel that I am a failure."    1 = st…
#> 13 rses10    "I take a positive attitude toward myself."                 1 = st…
#> # … with abbreviated variable name ¹​value_labels

První věc, kterou můžeme udělat, je podívat se na chybějící data. Kdybychom chtěli vypočíst počet nebo podíl chybějících nebo naopak validních hodnot pro každý řádek/případ ve specifikovaných sloupcích, můžeme použít tyto funkce v rámci mutate(). Stačí v nich pouze zadat vybrané sloupce (pokud všechny, můžeme použít èverything()):

  • row_missing_n(): počet chybějících hodnot.
  • row_missing_p(): podíl chybějících hodnot.
  • row_valid_n(): počet validních (nechybějících) hodnot.
  • row_valid_p(): podíl validních (nechybějících) hodnot.
rses <- rses %>% 
  mutate(
    n_miss_all = row_missing_n(everything()),
  )

rses %>% 
  filter(n_miss_all == 13)
#> # A tibble: 351 × 14
#>       id sex         age rses01 rses02 rses03 rses04 rses05 rses06 rses07 rses08
#>    <dbl> <dbl+lbl> <dbl> <dbl+> <dbl+> <dbl+> <dbl+> <dbl+> <dbl+> <dbl+> <dbl+>
#>  1    NA NA           NA NA     NA     NA     NA     NA     NA     NA     NA    
#>  2    NA NA           NA NA     NA     NA     NA     NA     NA     NA     NA    
#>  3    NA NA           NA NA     NA     NA     NA     NA     NA     NA     NA    
#>  4    NA NA           NA NA     NA     NA     NA     NA     NA     NA     NA    
#>  5    NA NA           NA NA     NA     NA     NA     NA     NA     NA     NA    
#>  6    NA NA           NA NA     NA     NA     NA     NA     NA     NA     NA    
#>  7    NA NA           NA NA     NA     NA     NA     NA     NA     NA     NA    
#>  8    NA NA           NA NA     NA     NA     NA     NA     NA     NA     NA    
#>  9    NA NA           NA NA     NA     NA     NA     NA     NA     NA     NA    
#> 10    NA NA           NA NA     NA     NA     NA     NA     NA     NA     NA    
#> # … with 341 more rows, and 3 more variables: rses09 <dbl+lbl>,
#> #   rses10 <dbl+lbl>, n_miss_all <dbl>

Vypadá to, že některé řádky/případy jsou prázdné nebo téměř úplně prázdné a ani nemají ID. Tyto řádky asi nemá smysl v datasetu ponechávat, takže je s klidným svědomím můžeme odstranit.

rses <- rses %>% 
  filter(n_miss_all < 13)

Další věc, kterou můžeme udělat, je převést pohlaví na faktor, protože defaultně balíček haven importoje kategorické proměnné jako vektory typu double, které ale mají uložné labely atributech. K tomu balíček haven nabízí funkci as_factor.

rses <- rses %>% 
  mutate(sex = as_factor(sex))

Lepší je ale ověřit, zda jsou všechny úrovně správně definovány, takže se podíváme na četnosti pomocí funkce count():

rses %>% 
  count(sex)
#> # A tibble: 4 × 2
#>   sex        n
#>   <fct>  <int>
#> 1 0          2
#> 2 male    5303
#> 3 female  5382
#> 4 <NA>      51

Jak vidíme, v datech se kromě hodnot male, female a NA vyskytly i hodnoty 0. Protože nevíme, co tyto hodnoty znamenají a jsou jen dvě, má asi největší smysl zacházet s nimi jako s missing values, tudíž je také převést na NA. K tomu má balíček forcats funkci fct_recode(). Ta má tuto obecnou podobu, kde x je modifikovaný faktor:

fct_recode(x, 
           "nový_label_1" = "starý_label_1",
           "nový_label_2" = "starý_label_2",
           NULL = "úroveň_která_bude_NA")
rses <- rses %>% 
  mutate(sex = fct_recode(sex,
                          "Muži" = "male", 
                          "Ženy" = "female",
                          NULL = "0"))
count(rses, sex)
#> # A tibble: 3 × 2
#>   sex       n
#>   <fct> <int>
#> 1 Muži   5303
#> 2 Ženy   5382
#> 3 <NA>     53
levels(rses$sex)
#> [1] "Muži" "Ženy"

Kdybychom chtěli s chybějícími hodnotami pracovat jako se svébytnou úrovní dané proměnné, je vhodné ji přidělit explicitní název pomocí funkce fct_explicit_na():

rses <- rses %>% 
  mutate(sex = fct_explicit_na(sex, na_level = "(missing)"))
count(rses, sex)
#> # A tibble: 3 × 2
#>   sex           n
#>   <fct>     <int>
#> 1 Muži       5303
#> 2 Ženy       5382
#> 3 (missing)    53

Dále se podíváem na proměnnou věk, jestli se v ní nevyskytují nějaké zjevně neplatné hodnoty. Vytvoříme tedy jednoduchý histogram věku.

# Histogram věku (age)
ggplot(rses, aes(age)) +
  geom_histogram() +
  scale_x_continuous(breaks = seq(0, 100, by = 5))

Vypadá to, že ano. Blíže se tedy zaměříme na věk mimo rozmezí 10–50 let.

# Pozor na to, že před between() je výkřičník kvůli negaci
rses %>% 
  filter(!between(age, 10, 50))
#> # A tibble: 11 × 14
#>           id sex       age rses01   rses02   rses03   rses04   rses05   rses06  
#>        <dbl> <fct>   <dbl> <dbl+lb> <dbl+lb> <dbl+lb> <dbl+lb> <dbl+lb> <dbl+lb>
#>  1  1   e+ 0 (missi…    99  4 [rat…  5 [agr…  2 [dis…  4 [rat…  4 [rat…  4 [rat…
#>  2  4.24e+ 2 Muži       99  5 [agr…  5 [agr…  4 [rat…  4 [rat… NA        4 [rat…
#>  3  5.1 e+ 2 Muži       99  4 [rat…  4 [rat…  3 [rat…  4 [rat…  3 [rat…  3 [rat…
#>  4  5.56e+ 2 (missi…    99  3 [rat…  5 [agr…  4 [rat…  5 [agr…  4 [rat…  4 [rat…
#>  5  1.2 e-12 Muži        0  4 [rat…  1 [str… NA        4 [rat…  1 [str…  4 [rat…
#>  6 NA        Ženy        0  4 [rat…  1 [str… NA        4 [rat…  4 [rat…  4 [rat…
#>  7 NA        Ženy        0  4 [rat…  2 [dis… NA        3 [rat…  2 [dis…  2 [dis…
#>  8 NA        Muži        2  2 [dis…  4 [rat…  3 [rat…  3 [rat…  2 [dis…  2 [dis…
#>  9 NA        Muži       59 NA       NA       NA       NA       NA       NA      
#> 10 NA        Ženy       62 NA       NA       NA       NA       NA       NA      
#> 11 NA        Ženy       54  2 [dis…  3 [rat…  2 [dis…  1 [str…  3 [rat…  2 [dis…
#> # … with 5 more variables: rses07 <dbl+lbl>, rses08 <dbl+lbl>,
#> #   rses09 <dbl+lbl>, rses10 <dbl+lbl>, n_miss_all <dbl>

Vypadá to, že hodnoty okolo 60 let jsou ještě uvěřitelné, ale hodnoty 0, 2 a 99 nikoli, konvertujeme je tedy na NA. Existuje několi možných způsobů, jak to udělat. Můžete si vybrat kterýkoli z nich:

# Pomocí mutate() a ifelse()
rses <- rses %>% 
  mutate(age = ifelse(age %in% c(0, 2, 99), # podmínka
                      NA, # if TRUE
                      age)) # if FALSE

# Subseting a assignment
rses$age[rses$age %in% c(0, 2, 99)] <- NA

Výsledek můžeme znovu zkontrolovat pomocí histogramu, ale můžeme si také zobrazit četnost (po zaokrouhlení věku na celá čísla):

rses %>% 
  count(age = round(age, digits = 0)) %>% 
  mutate(percent = n/sum(n)*100) %>% 
  print(n = Inf)
#> # A tibble: 42 × 3
#>      age     n  percent
#>    <dbl> <int>    <dbl>
#>  1    12   279  2.60   
#>  2    13   156  1.45   
#>  3    14     6  0.0559 
#>  4    15     3  0.0279 
#>  5    16   407  3.79   
#>  6    17  1013  9.43   
#>  7    18  1371 12.8    
#>  8    19  1794 16.7    
#>  9    20  1486 13.8    
#> 10    21  1176 11.0    
#> 11    22  1071  9.97   
#> 12    23   756  7.04   
#> 13    24   356  3.32   
#> 14    25   212  1.97   
#> 15    26    97  0.903  
#> 16    27    48  0.447  
#> 17    28    41  0.382  
#> 18    29    27  0.251  
#> 19    30    22  0.205  
#> 20    31    19  0.177  
#> 21    32    13  0.121  
#> 22    33    11  0.102  
#> 23    34    15  0.140  
#> 24    35     7  0.0652 
#> 25    36     8  0.0745 
#> 26    37     9  0.0838 
#> 27    38     5  0.0466 
#> 28    39     3  0.0279 
#> 29    40     5  0.0466 
#> 30    41     5  0.0466 
#> 31    42     9  0.0838 
#> 32    43     4  0.0373 
#> 33    44     1  0.00931
#> 34    45     5  0.0466 
#> 35    46     3  0.0279 
#> 36    47     3  0.0279 
#> 37    49     1  0.00931
#> 38    50     2  0.0186 
#> 39    54     1  0.00931
#> 40    59     1  0.00931
#> 41    62     1  0.00931
#> 42    NA   286  2.66

Někdy také může mít smysl nějakou proměnnou diskrtizovat. Ukážeme si to znovu na věku. Vytvoříme si novou proměnnou age_cat, která bude vyjadřovat různé “věkové kategorie”. K tomu použijeme funkci cut(), která má tyto hlavní argumenty:

  • x: vektor, který chceme diskretizovat.
  • breaks: předěly mezi jednotlivými intervaly včetně konců v podobě číselného věktoru.
  • labels: labely pro jednotlivé úrovně.
  • right: zda mají být intervaly zprava uzavřené a zleva otevřené (righ = TRUE), nebo naopak zprava otevřené a zleva uzavřené (righ = FALSE).
  • include.lowest: zda do intervalu zahrnout i úplně první hodnotu v breaks (je-li right = TRUE), resp. úplně poslední hodnotu breaks (je-li right = FALSE).

Zkusíme si vytvořit zprava otevřené, zleva uzavřené intervaly, a to od 10 let v šířce 5 let, ale poslední interval bude širší a bude zahrnovat osoby mající 30 či více let. Pro upřesnění, když je interval uzavřený, znamená to, že danou krajní hodnotu ještě zahrnuje, když je otevřený, znamená to, že danou krajní hodnotu už nezahrnuje.

rses <- rses%>% 
  mutate(
    age_cat= cut(age,
                 breaks = c(10, 15, 20, 25, 30, Inf),
                 labels = c("10–14.9", "15–19.9", "20–24.9", "25–29.9", "30+"),
                 right = FALSE,
                 include.lowest = TRUE)
  )

rses %>% 
  ggplot(aes(age, age_cat)) +
  geom_boxplot()

Aby se nám dále s položkami RSES lépe pracovalo, vytvoříme si dva vektory: items_all (s názvy všech položek) a items_reversed (pouze s názvy reverzních položek). K tomu použijeme funkci item_range(), která má tyto argumenty (pouze prefix a range jsou povinné):

  • prefix: předpona položek, např. “rses”.
  • range``: číselné rozmezí položek (např. číselný vektor1:10`).
  • suffix: volitelná přípona položek (není nutné specifikovat).
  • width: konstantní šířka číselného rozmezí položek. Na začátek čísel budou doplněny nuly, ať se dosáhne zadané šířky. Například pokud bychom nastavili width = 3 a použili range = 1:3, generovalo by se 001, 002, 003.
  • with.suffix: přidat příponu jen některým položkám (číslený vektor)

Oba vektory bychom vytvořili takto:

items_all <- item_range("rses", range = 1:10, width = 2)
items_all
#>  [1] "rses01" "rses02" "rses03" "rses04" "rses05" "rses06" "rses07" "rses08"
#>  [9] "rses09" "rses10"

# Položky 2, 5, 6, 8 a 9 jsou reverzní
items_reversed <- item_range("rses", range = c(2, 5, 6, 8, 9), width = 2)
items_reversed
#> [1] "rses02" "rses05" "rses06" "rses08" "rses09"

Kdybychom pak chtěli rekódovat opačně skórované položky a vytvořit nový dataframe s rekódovanými položkami, např. `rses_rec``, mohlo by nás napadnout ručně specifikovat každé jednotlivé rekódování:

rses_rec <- rses %>% 
  mutate(
    rses02 = 7 - rses02,
    rses05 = 7 - rses05,
    rses06 = 7 - rses06,
    rses08 = 7 - rses08,
    rses09 = 7 - rses09
  )

To je ale poměrně neefektivní, hlavně když máme mnoho škál a hodně položek a navíc přitom hrozí, že uděláme nějakou chybu. Místo toho můžeme využít toho, že máme připravený vektor s položkami a použít funkci reverse_score() v rámci across(). Pomocí across() můžeme totiž aplikovat tutéž funkci na několik sloupců najednou. Výběr můžeme provést stejně jako v rámci select(). Když máme textový vektor s položkami, použijeme all_of(items_reversed) pro výběr transformovaných sloupců. Pak uvedeme funkci, kterou chceme použít, na všechny vybrané sloupce, a to reverse_score(). Nakonec jen uvedeme dotatečné argumenty pro funkci reverse_score, a to min a max, kterými bychom měli specifikovat minimální a maximální hodnotu škály (zde bylo rozmezí 1–6 od silně nesouhlasím po silně souhlasím, proto min = 1 a max = 6).

rses_rec <- rses %>% 
  mutate(
    across(all_of(items_reversed), reverse_score, min = 1, max = 6)
  )

Když máme položky rekódovány, můžeme vypočíst celkový skór jako průměr položek pomocí funkce row_mean(). Vybrat sloupce v ní můžeme stejně jako ve funkci select() a pak argumentem min.valid můžeme specifikovat, kolik minimálně platných hodnot musí být na řádku ve vybranýých sloupcích (proměnných), aby se celkový skór vypočelt (pokud je počet platných hodnot menší než min.valid, vrátí nám to pro daný řádek/respondenta NA).

rses_rec <- rses_rec %>% 
  mutate(
    rses_total = row_mean(all_of(items_all), min.valid = 5)
  )

Nakonec se můžeme pomocí boxplotu podívat, jak vypadá rozdělení rses_total v závislosti na pohlaví a věkové kategorii.

rses_rec %>% 
  drop_na(age_cat, sex) %>% 
  ggplot(aes(age_cat, rses_total, color = sex)) +
  geom_boxplot()