4  Čistenie dát

Čistenie dát (data cleaning, data cleansing) je proces, pri ktorom odstraňujeme alebo orpavujeme chyby a nezrovnalosti v dátach, aby boli použiteľné na analýzu. Cieľom je, aby údaje boli presné, úplné, konzistentné a mali zmysel pre ďalšie spracovanie.

Typické problémy, ktoré sa pri čistení dát riešia:

Dobre vyčistené dáta sú základom každej analýzy. Ak dáta nie sú kvalitné, výsledky môžu byť zavádzajúce - od nespresných štatistík až po nespoľahlivé predikcie.

4.1 Knižnica Janitor

Táto knižnica poskytuje jednoduché funkcie, ktoré nám pomôžu rýchlo pripraviť dáta na analýzu - napríklad vyčistiť názvy stĺpcov, nájsť duplicity alebo doplniť chýbajúce hodnoty.

#install.packages("janitor")
library(janitor)

Attaching package: 'janitor'
The following objects are masked from 'package:stats':

    chisq.test, fisher.test

4.1.1 Čistenie názvov stĺpcov

Častým problémom sú názvy stĺpcov s nesprávnym syntaxom napr. s medzerami, veľkými písmenami alebo špeciálnymi znakmi. Funkcia clean_names() všetky názvy prevedie do jednotného, konzistentného tvaru.

Funkcia:

  • Upraví písmená a oddeľovače na konzistentný formát (predvolený je snake_case, ale dostupné sú aj iné napr. camelCase)

  • Rieši špeciálne znaky a medzery, vrátane transiliterácie znakov

  • Pridáva čísla k duplicitným názvnom stĺpcov

  • Konvertuje znaky % na percent a # na number, aby sa zachoval význam názvu.

  • Zachováva medzery (alebo ich absenciu) okolo čísel

Príklad

data <- as.data.frame(matrix(ncol = 6))
names(data) <- c("First Name", "Age (Years)", "score %", "repeatVálue", "repeatValue", "")
data %>%
  clean_names()
  first_name age_years score_percent repeat_value repeat_value_2  x
1         NA        NA            NA           NA             NA NA
#>   first_name age_years score_percent repeat_value repeat_value_2  x
#> 1         NA        NA            NA           NA             NA NA

Základné R by vytvorilo toto:

make.names(names(data))
[1] "First.Name"  "Age..Years." "score.."     "repeatVálue" "repeatValue"
[6] "X"          
#> [1] "First.Name"  "Age..Years." "score.."     "repeatVálue" "repeatValue" "X"

4.1.2 Kontrola kompatibility stĺpcov

Pri práci s viacerými súbormi dát, ktoré by mali byť indetické sa často stane, že ich chceme načítať a zlúčiť na analýzu. Niekedy však funkcie ako napr. dplyr::bind_rows() alebo rbind() zlyhajúce, pretože stĺpce sa líšia názvom alebo typom.

Funkcia compare_df_cols() umožňuje rýchlo porovnať viacero data.frameov. Prijíma názvy bez úvodzoviek alebo zoznam data.frameov a vracia zhrnutie ich porovnania. Môžeme zistiť:

  • Aký je typ jednotlivých stĺpcov

  • Ktoré stĺpce chýbajú alebo sú prítomné v rôznych objetoch.

  • Kde sa typy stĺpcov líšia.

Príklad

df1 <- data.frame(a = 1:2, b = c("big", "small"))
df2 <- data.frame(a = 10:12, b = c("medium", "small", "big"), c = 0, stringsAsFactors = TRUE) # here, column b is a factor
df3 <- df1 %>%
  dplyr::mutate(b = as.character(b))

# Porovnanie všetkých stĺpcov
compare_df_cols(df1, df2, df3)
  column_name       df1     df2       df3
1           a   integer integer   integer
2           b character  factor character
3           c      <NA> numeric      <NA>
#>   column_name       df1     df2       df3
#> 1           a   integer integer   integer
#> 2           b character  factor character
#> 3           c      <NA> numeric      <NA>

# Len rozdiely medzi stĺpcami
compare_df_cols(df1, df2, df3, return = "mismatch")
  column_name       df1    df2       df3
1           b character factor character
#>   column_name       df1    df2       df3
#> 1           b character factor character

# Špecifikácia metódy spájania stĺpcov
compare_df_cols(df1, df2, df3, return = "mismatch", bind_method = "rbind") # default is dplyr::bind_rows
  column_name       df1     df2       df3
1           b character  factor character
2           c      <NA> numeric      <NA>
#>   column_name       df1     df2       df3
#> 1           b character  factor character
#> 2           c      <NA> numeric      <NA>

Ak chceme jednoducho zistiť či je možné data.frames úspešne zlúčiť podľa stĺpcov, použijeme compare_df_cols_same(). Funkcia vráti TRUE alebo FALSE.

compare_df_cols_same(df1, df3)
[1] TRUE
#> [1] TRUE
compare_df_cols_same(df2, df3)
  column_name    ..1       ..2
1           b factor character
[1] FALSE
#>   column_name    ..1       ..2
#> 1           b factor character
#> [1] FALSE

4.1.3 Kontrola duplikátov

Pri čistení dát je často potrebné overiť, či sa v dátach nenachádzajú neželané duplikáty. Funkcie v balíku janitor umožňujú rýchlo identifikovať a preskúmať duplicitné záznamy, čo je dôležité najmä v prípadoch, keď by sa mal každý unikátny záznam objaviť v dátach len raz.

Jednou z takýchto funkcií je get_dupes(). T8 vracia všetky záznamy, ktoré sú duplicitné a pridáva stĺpec s počtom opakovaní, aby bolo jednoduché odhaliť problematické záznamy.

get_dupes(mtcars, wt, cyl) # or mtcars %>% get_dupes(wt, cyl) if you prefer to pipe
    wt cyl dupe_count  mpg  disp  hp drat  qsec vs am gear carb
1 3.44   6          2 19.2 167.6 123 3.92 18.30  1  0    4    4
2 3.44   6          2 17.8 167.6 123 3.92 18.90  1  0    4    4
3 3.57   8          2 14.3 360.0 245 3.21 15.84  0  0    3    4
4 3.57   8          2 15.0 301.0 335 3.54 14.60  0  1    5    8
#>     wt cyl dupe_count  mpg  disp  hp drat  qsec vs am gear carb
#> 1 3.44   6          2 19.2 167.6 123 3.92 18.30  1  0    4    4
#> 2 3.44   6          2 17.8 167.6 123 3.92 18.90  1  0    4    4
#> 3 3.57   8          2 14.3 360.0 245 3.21 15.84  0  0    3    4
#> 4 3.57   8          2 15.0 301.0 335 3.54 14.60  0  1    5    8

4.1.4 Preskúmanie vzťahov medzi stĺpcami

Pri práci s dátami je užitočné vedieť, či niektoré stĺpce majú jedinečné vzťahy medzi sebou, napr. keď každý záznam v jednom stĺpci zodpovedá presne jednému záznamu v inom stĺpci. Funkcia get_one_to_one() zobazuje, ktoré stĺpce v dátovom rámci majú medzi sebou one-to-one vzťahy.

library(dplyr)

Attaching package: 'dplyr'
The following objects are masked from 'package:stats':

    filter, lag
The following objects are masked from 'package:base':

    intersect, setdiff, setequal, union
starwars[1:4,] %>%
  get_one_to_one()
[[1]]
[1] "name"       "height"     "mass"       "skin_color" "birth_year"
[6] "films"     

[[2]]
[1] "hair_color" "starships" 

[[3]]
[1] "sex"     "species"
#> [[1]]
#> [1] "name"       "height"     "mass"       "skin_color" "birth_year"
#> [6] "films"     
#> 
#> [[2]]
#> [1] "hair_color" "starships" 
#> 
#> [[3]]
#> [1] "sex"     "species"

4.1.5 Odstraňovanie prázdnych riadkov a stĺpcov

Pri čistení dát z Excelu alebo iných zdrojov sa často stretneme s prázdnymi riadkami a stĺpcami, ktoré po načítaní do R nemajú žiadne hodnoty. Takéto riadky a stĺpce môžu zbytočne komplikovať analýzu a vizualizácie.

Na odstránenie prázdnych riadkov a stĺpcov môžeme použiť funkciu remove_empty().

q <- data.frame(v1 = c(1, NA, 3),
                v2 = c(NA, NA, NA),
                v3 = c("a", NA, "b"))
q %>%
  remove_empty(c("rows", "cols"))
  v1 v3
1  1  a
3  3  b
#>   v1 v3
#> 1  1  a
#> 3  3  b

4.1.6 Odstraňovanie stĺpcov s konštantnými hodnotami

V niektorých dátových súboroch sa môžeme stretnúť so stĺpcami, ktoré odstraňujú len jednu hodnotu vo všetkých riadkoch. Takéto stĺpce zvyčajne neposkytujú žiadnu informatívnu hodnotu a môžu zbytočne zaťažovať analýzu.

Na ich odstránenie môžeme využiť funkciu remove_constant(). Táto funkcia tiež umožňuje nastaviť, či sa majú považovať hodnoty NA za odlišné od konštanty prostredníctvom parametra na.rm. Funkcia pracuje nielen s data.frames, ale aj s maticami.

a <- data.frame(good = 1:3, boring = "the same")
a %>% remove_constant()
  good
1    1
2    2
3    3
#>   good
#> 1    1
#> 2    2
#> 3    3

4.1.7 Zaokrúhľovanie čísel

V R sa štandartne používa tzv. “bankers rounding”, čo znamená, že polovičné hodnoty (napr. 2.5, 3.5) sa zaokrúhľujú na najbližšie párne číslo. V praxi to môže byť mätúce, keď očakávame, že 2.5 sa zaokrúhli nahor na 3.

Funkcia round_half_up() rieši tento problém tým, že všetky polovičné hodnoty zaokrúhľuje nahor - presne podľa bežného očakávania.

nums <- c(2.5, 3.5)
round(nums)
[1] 2 4
#> [1] 2 4
round_half_up(nums)
[1] 3 4
#> [1] 3 4

4.1.8 Konverzia čísel z Excelu na dátumy

Pri práci s dátami z Excelu sa často stretávame s číslami, ktoré predstavujú dátumy - napríklad hodnota 42223 by mala byť dátumom. Funkcia excel_numeric_to_date() umožňuje tieto Excel serial numbers previesť na objekt triedy Date alebo POSIXlt, pričom podporuje rôzne systémy kódovania dátumov v Exceli a zachováva aj časové zložky ak sú obsiahnuté v zlomkoch dňa.

excel_numeric_to_date(41103)
[1] "2012-07-13"
#> [1] "2012-07-13"
excel_numeric_to_date(41103.01) # ignores decimal places, returns Date object
[1] "2012-07-13"
#> [1] "2012-07-13"
excel_numeric_to_date(41103.01, include_time = TRUE) # returns POSIXlt object
[1] "2012-07-13 00:14:24 CEST"
#> [1] "2012-07-13 00:14:24 EDT"
excel_numeric_to_date(41103.01, date_system = "mac pre-2011")
[1] "2016-07-14"
#> [1] "2016-07-14"

Takáto konverzia je užitočná, keď načítavame Excel súbory do R a chceme pracovať s dátumami priamo ako s dátovými typmi, namiesto manuálneho prevodu alebo zložitej úpravy čísel.

4.1.9 Konverzia zmiešaných dátumov a časov na dátum

Funkcie convert_to_date() a covert_to_datetime() nadväzujú na excel_numeric_to_date() a umožňujú robustnejšie spracovanie zmiešaných vstupov - napr. kombinácie textových dátumov a Excel serial numbers. Sú veľmi užitočné, keď načítavame viacero tabuliek, ktoré by mali mať rovnaké formáty stĺpcov, ale v praxi sa líšia.

convert_to_date(c("2020-02-29", "40000.1"))
[1] "2020-02-29" "2009-07-06"
#> [1] "2020-02-29" "2009-07-06"

convert_to_datetime(44874.5)
[1] "2022-11-09 12:00:00 UTC"
#> [1] "2022-11-09 12:00:00 UTC"

4.2 Cvičenia

Očistime dáta v Titanic datasete, ktorý obsahuje informácie o pasažiaroch Titanicu. Tento dataset je verejne dostupný a využívaný napr. na riešenie klasifikačných problémov. Tento dataset obsahuje nejednotné názvy stĺpcov, napr. “PassengerId a”Name”, ktoré je potrebné upraviť.

library(janitor)

url <- "https://raw.githubusercontent.com/datasciencedojo/datasets/master/titanic.csv"
df <- read.csv(url)

head(df)
  PassengerId Survived Pclass
1           1        0      3
2           2        1      1
3           3        1      3
4           4        1      1
5           5        0      3
6           6        0      3
                                                 Name    Sex Age SibSp Parch
1                             Braund, Mr. Owen Harris   male  22     1     0
2 Cumings, Mrs. John Bradley (Florence Briggs Thayer) female  38     1     0
3                              Heikkinen, Miss. Laina female  26     0     0
4        Futrelle, Mrs. Jacques Heath (Lily May Peel) female  35     1     0
5                            Allen, Mr. William Henry   male  35     0     0
6                                    Moran, Mr. James   male  NA     0     0
            Ticket    Fare Cabin Embarked
1        A/5 21171  7.2500              S
2         PC 17599 71.2833   C85        C
3 STON/O2. 3101282  7.9250              S
4           113803 53.1000  C123        S
5           373450  8.0500              S
6           330877  8.4583              Q