Nauč se Python > Kurzy > Datový kurz PyLadies > EDA — Nulové hodnoty a odlehlá pozorování > EDA - Nulové hodnoty a odlehlá pozorování

EDA - Nulové hodnoty a odlehlá pozorování

Dnes se společně podíváme, jak to vypadá, když data nejsou tak úplně v pořádku. K analýze použijeme data o filmech z let 2016 až 2018 a jejich hodnocení z Internet movie database.

V datech máme opět náhodně upravené některé hodnoty, aby příklady hezky vycházely.

Data jsou k dispozici v následujících souborech:

In [1]:
import pandas as pd

%matplotlib inline

Načtení, kontrola a příprava dat

Data jsou distribuována ve vice souborech a tak je budeme muset načíst samostatně a pak z nich vytvořit jednu datovou sadu k dalšímu zpracování. To se stává nejčastěji u historických dat, kterých může být velké množství a často by bylo zbytečné stahovat celou historii. V této podobě si může analytik vybrat časové období, které ho zajímá nebo jen část dat, kterou potřebuje.

Začneme daty o filmech z roku 2016.

In [2]:
movies_2016 = pd.read_csv("static/movies_2016.tsv")
movies_2016
---------------------------------------------------------------------------
ParserError                               Traceback (most recent call last)
<ipython-input-2-de331fdcf179> in <module>
----> 1 movies_2016 = pd.read_csv("static/movies_2016.tsv")
      2 movies_2016

~/.virtualenvs/naucse/lib/python3.7/site-packages/pandas/io/parsers.py in parser_f(filepath_or_buffer, sep, delimiter, header, names, index_col, usecols, squeeze, prefix, mangle_dupe_cols, dtype, engine, converters, true_values, false_values, skipinitialspace, skiprows, skipfooter, nrows, na_values, keep_default_na, na_filter, verbose, skip_blank_lines, parse_dates, infer_datetime_format, keep_date_col, date_parser, dayfirst, cache_dates, iterator, chunksize, compression, thousands, decimal, lineterminator, quotechar, quoting, doublequote, escapechar, comment, encoding, dialect, error_bad_lines, warn_bad_lines, delim_whitespace, low_memory, memory_map, float_precision)
    674         )
    675 
--> 676         return _read(filepath_or_buffer, kwds)
    677 
    678     parser_f.__name__ = name

~/.virtualenvs/naucse/lib/python3.7/site-packages/pandas/io/parsers.py in _read(filepath_or_buffer, kwds)
    452 
    453     try:
--> 454         data = parser.read(nrows)
    455     finally:
    456         parser.close()

~/.virtualenvs/naucse/lib/python3.7/site-packages/pandas/io/parsers.py in read(self, nrows)
   1131     def read(self, nrows=None):
   1132         nrows = _validate_integer("nrows", nrows)
-> 1133         ret = self._engine.read(nrows)
   1134 
   1135         # May alter columns / col_dict

~/.virtualenvs/naucse/lib/python3.7/site-packages/pandas/io/parsers.py in read(self, nrows)
   2035     def read(self, nrows=None):
   2036         try:
-> 2037             data = self._reader.read(nrows)
   2038         except StopIteration:
   2039             if self._first_chunk:

pandas/_libs/parsers.pyx in pandas._libs.parsers.TextReader.read()

pandas/_libs/parsers.pyx in pandas._libs.parsers.TextReader._read_low_memory()

pandas/_libs/parsers.pyx in pandas._libs.parsers.TextReader._read_rows()

pandas/_libs/parsers.pyx in pandas._libs.parsers.TextReader._tokenize_rows()

pandas/_libs/parsers.pyx in pandas._libs.parsers.raise_parser_error()

ParserError: Error tokenizing data. C error: Expected 3 fields in line 7, saw 5

Koncovka tsv naznačuje, že budeme potřebovat jiný způsob načtení. Zatímco u CSV je oddělovačem hodnot čárka, TSV používá tabulátor. Je to naštěstí také známý formát a tak jej umí pandas snadno zpracovat.

In [3]:
movies_2016 = pd.read_table("static/movies_2016.tsv")
movies_2016
Out[3]:
tconst titleType primaryTitle originalTitle isAdult startYear endYear runtimeMinutes genres
0 tt0315642 movie Wazir Wazir 0 2016 NaN 103.0 Action,Crime,Drama
1 tt0364201 movie Aman Ke Farishtey Aman Ke Farishtey 0 2016 NaN 137.0 Action
2 tt0376479 movie American Pastoral American Pastoral 0 2016 NaN 108.0 Crime,Drama
3 tt0443533 movie The History of Love The History of Love 0 2016 NaN 134.0 Drama,Romance,War
4 tt0470936 movie Hot Country, Cold Winter Tak erkir, tsurt dzmer 0 2016 NaN 104.0 Drama
... ... ... ... ... ... ... ... ... ...
17655 tt9906270 movie Footloose in Italy III: 3 Naples, Sorrento, Am... Footloose in Italy III: 3 Naples, Sorrento, Am... 0 2016 NaN 150.0 NaN
17656 tt9906278 movie A Classic Tour of Scotland: Footloose Special A Classic Tour of Scotland: Footloose Special 0 2016 NaN 151.0 NaN
17657 tt9906696 movie Footloose in Italy IV: 4 Rimini Tuscany Rome Footloose in Italy IV: 4 Rimini Tuscany Rome 0 2016 NaN 150.0 NaN
17658 tt9907396 movie Footloose in the Cotswolds - Part 1 Footloose in the Cotswolds - Part 1 0 2016 NaN 118.0 NaN
17659 tt9907608 movie Footloose in the Cotswolds - Part 2 Footloose in the Cotswolds - Part 2 0 2016 NaN 102.0 NaN

17660 rows × 9 columns

Stejným způsobem si načteme i další soubory.

In [4]:
movies_2017 = pd.read_table("static/movies_2017.tsv")
movies_2017
Out[4]:
tconst titleType primaryTitle originalTitle isAdult startYear endYear runtimeMinutes genres
0 tt0100275 movie The Wandering Soap Opera La Telenovela Errante 0 2017 NaN 80.0 Comedy,Drama,Fantasy
1 tt0112502 movie Bigfoot Bigfoot 0 2017 NaN NaN Horror,Thriller
2 tt0137204 movie Joe Finds Grace Joe Finds Grace 0 2017 NaN 83.0 Adventure,Animation,Comedy
3 tt0326716 movie 5-25-77 '77 0 2017 NaN 113.0 Comedy,Drama
4 tt0331314 movie Bunyan and Babe Bunyan and Babe 0 2017 NaN 84.0 Adventure,Animation,Comedy
... ... ... ... ... ... ... ... ... ...
17997 tt9910238 movie Be 5min. vede Be 5min. vede 0 2017 NaN 80.0 Comedy,Romance
17998 tt9913594 movie Bacchanalia Bacchanalia 0 2017 NaN 72.0 Drama,Mystery,Thriller
17999 tt9914642 movie Albatross Albatross 0 2017 NaN NaN Documentary
18000 tt9916186 movie Illenau - die Geschichte einer ehemaligen Heil... Illenau - die Geschichte einer ehemaligen Heil... 0 2017 NaN 84.0 Documentary
18001 tt9916730 movie 6 Gunn 6 Gunn 0 2017 NaN 116.0 NaN

18002 rows × 9 columns

In [5]:
movies_2018 = pd.read_table("static/movies_2018.tsv")
movies_2018
Out[5]:
tconst titleType primaryTitle originalTitle isAdult startYear endYear runtimeMinutes genres
0 tt0069049 movie The Other Side of the Wind The Other Side of the Wind 0 2018 NaN 122.0 Drama
1 tt0069204 movie Sabse Bada Sukh Sabse Bada Sukh 0 2018 NaN NaN Comedy,Drama
2 tt0111414 movie A Thin Life A Thin Life 0 2018 NaN 75.0 Comedy
3 tt0170651 movie T.G.M. - osvoboditel T.G.M. - osvoboditel 0 2018 NaN 60.0 Documentary
4 tt0192528 movie Heaven & Hell Reverse Heaven 0 2018 NaN 104.0 Drama
... ... ... ... ... ... ... ... ... ...
17429 tt9909940 movie Hot Scent Hot Scent 0 2018 NaN NaN Family
17430 tt9910688 movie Luz, Câmera e Barreto Luz, Câmera e Barreto 0 2018 NaN NaN Documentary
17431 tt9914644 movie 9/11: Escape from the Towers 9/11: Escape from the Towers 0 2018 NaN 120.0 Documentary
17432 tt9914662 movie Wien is 't Hof van Commerce Wien is 't Hof van Commerce 0 2018 NaN NaN Comedy
17433 tt9916132 movie The Mystery of a Buryat Lama The Mystery of a Buryat Lama 0 2018 NaN 94.0 Biography,Documentary,History

17434 rows × 9 columns

V poslední tabulce se nachází hodnocení filmů.

In [6]:
ratings = pd.read_table("static/ratings.tsv")
ratings
Out[6]:
tconst averageRating numVotes
0 tt0069049 6.9 5016
1 tt0069204 6.1 13
2 tt0100275 6.6 121
3 tt0112502 4.2 33
4 tt0137204 8.6 264
... ... ... ...
26093 xx9914003 1.0 9
26094 xx9914004 2.0 8
26095 xx9914005 3.0 4
26096 xx9914006 4.0 8
26097 xx9914007 5.0 4

26098 rows × 3 columns

Spojení v jednu datovou sadu (do délky)

Nejprve budeme potřebovat spojit primární data o filmech do jedné datové sady. Naštěstí pro nás mají všechny zdroje stejné sloupce, což také nebývá pravidlem, především když se v čase mění způsob zpracování a publikace dat.

Náš příklad je však jednoduchý a tak i spojení více zdrojů do jednoho nevyžaduje žádné mezikroky.

In [7]:
movies = pd.concat((movies_2016, movies_2017, movies_2018))
movies
Out[7]:
tconst titleType primaryTitle originalTitle isAdult startYear endYear runtimeMinutes genres
0 tt0315642 movie Wazir Wazir 0 2016 NaN 103.0 Action,Crime,Drama
1 tt0364201 movie Aman Ke Farishtey Aman Ke Farishtey 0 2016 NaN 137.0 Action
2 tt0376479 movie American Pastoral American Pastoral 0 2016 NaN 108.0 Crime,Drama
3 tt0443533 movie The History of Love The History of Love 0 2016 NaN 134.0 Drama,Romance,War
4 tt0470936 movie Hot Country, Cold Winter Tak erkir, tsurt dzmer 0 2016 NaN 104.0 Drama
... ... ... ... ... ... ... ... ... ...
17429 tt9909940 movie Hot Scent Hot Scent 0 2018 NaN NaN Family
17430 tt9910688 movie Luz, Câmera e Barreto Luz, Câmera e Barreto 0 2018 NaN NaN Documentary
17431 tt9914644 movie 9/11: Escape from the Towers 9/11: Escape from the Towers 0 2018 NaN 120.0 Documentary
17432 tt9914662 movie Wien is 't Hof van Commerce Wien is 't Hof van Commerce 0 2018 NaN NaN Comedy
17433 tt9916132 movie The Mystery of a Buryat Lama The Mystery of a Buryat Lama 0 2018 NaN 94.0 Biography,Documentary,History

53096 rows × 9 columns

concat umožňuje provádět i složitější magii a spojovat různé zdroje do jedné datové sady na základě různých kritérií. Složitost je nejčastěji závislá na společných prvcích jednotlivých zdrojů, jako jsou indexy či názvy sloupců. V základním nastavení spojuje zdroje do délky a protože náš index je automatický a nemá pro nás význam, je ignorován a ve výsledku přepočítán.

Spojení v jednu datovou sadu (do šířky)

Nadešel čas k existující datové sadě připojit informace o hodnocení jednotlivých filmů. Každý film má jednoznačný identifikátor ve sloupci tconst, který je obsažen i v datech o hodnocení, takže bude snadné identifikovat, které hodnocení patří ke kterému filmu. Je tu ovšem jedna potíž, na které závisí naše další počínání.

In [8]:
movies.shape
Out[8]:
(53096, 9)
In [9]:
ratings.shape
Out[9]:
(26098, 3)

Zatímco hlavní sada obsahuje základní informace o více než 50 000 filmech, informaci o hodnocení máme jen pro cca 26 000 z nich. Teď se musíme rozhodnout pro jednu ze čtyř možných strategií spojování, která ovlivní výsledek. Kdo někdy spojoval tabulky v SQL dotazech, bude mít hned jasno.

Grafická reprezentace zmíněných čtyř možností pomocí Vénnových diagramů vypadá následovně:

joins

Obrázek adaptován z Wikimedia (autor: Anthony Beck)

Inner

Inner vloží do výsledku jen ty řádky, které mají zastoupení v obou zdrojových tabulkách. V našem případě to znamená, že by ve výsledku byly jen ty filmy, pro které existuje hodnocení. Jinak řečeno se nad sloupcem pro identifikaci řádků, které k sobě patří, provede průnik a teprve pak dojde ke spojení.

In [10]:
pd.merge(movies, ratings, how="inner", left_on="tconst", right_on="tconst")
Out[10]:
tconst titleType primaryTitle originalTitle isAdult startYear endYear runtimeMinutes genres averageRating numVotes
0 tt0315642 movie Wazir Wazir 0 2016 NaN 103.0 Action,Crime,Drama 7.1 15911
1 tt0364201 movie Aman Ke Farishtey Aman Ke Farishtey 0 2016 NaN 137.0 Action 6.4 16
2 tt0376479 movie American Pastoral American Pastoral 0 2016 NaN 108.0 Crime,Drama 6.1 13546
3 tt0443533 movie The History of Love The History of Love 0 2016 NaN 134.0 Drama,Romance,War 6.3 1086
4 tt0470936 movie Hot Country, Cold Winter Tak erkir, tsurt dzmer 0 2016 NaN 104.0 Drama 4.0 62
... ... ... ... ... ... ... ... ... ... ... ...
26085 tt9903952 movie BADMEN with a good behavior BADMEN with a good behavior 0 2018 NaN 87.0 Comedy,Horror 7.8 6
26086 tt9904014 movie Lost in Klessin Lost in Klessin 0 2018 NaN 90.0 War 7.5 14
26087 tt9904530 movie Scream Returns Scream Returns 0 2018 NaN 48.0 Horror,Thriller 3.0 6
26088 tt9908960 movie Pliusas Pliusas 0 2018 NaN 90.0 Comedy 4.1 18
26089 tt9914644 movie 9/11: Escape from the Towers 9/11: Escape from the Towers 0 2018 NaN 120.0 Documentary 8.4 27

26090 rows × 11 columns

Parametry left_on a right_on říkáme, které sloupce slouží pro identifikaci k sobě patřících záznamů v levé a pravé tabulce. Použít pro to lze samozřejmě i index v kterékoli z nich.

Na množství záznamů ve výsledku je vidět, že touto strategií spojení jsme přišli o několik hodnocení, ke kterým se nepodařilo najít film, a spoustu filmů, pro které se nepodařilo najít hodnocení.

Outer

Outer způsobí, že se zdroje spojí dohromady v plné velikosti, protože se nad sloupcem pro identifikaci provede nejdříve sjednocení. Pokud jedna z tabulek nebude mít ve druhé řádek, se kterým se bude moci spojit, budou hodnoty nahrazeny nulovými hodnotami.

In [11]:
pd.merge(movies, ratings, how="outer", left_on="tconst", right_on="tconst")
Out[11]:
tconst titleType primaryTitle originalTitle isAdult startYear endYear runtimeMinutes genres averageRating numVotes
0 tt0315642 movie Wazir Wazir 0.0 2016.0 NaN 103.0 Action,Crime,Drama 7.1 15911.0
1 tt0364201 movie Aman Ke Farishtey Aman Ke Farishtey 0.0 2016.0 NaN 137.0 Action 6.4 16.0
2 tt0376479 movie American Pastoral American Pastoral 0.0 2016.0 NaN 108.0 Crime,Drama 6.1 13546.0
3 tt0443533 movie The History of Love The History of Love 0.0 2016.0 NaN 134.0 Drama,Romance,War 6.3 1086.0
4 tt0470936 movie Hot Country, Cold Winter Tak erkir, tsurt dzmer 0.0 2016.0 NaN 104.0 Drama 4.0 62.0
... ... ... ... ... ... ... ... ... ... ... ...
53099 xx9914003 NaN NaN NaN NaN NaN NaN NaN NaN 1.0 9.0
53100 xx9914004 NaN NaN NaN NaN NaN NaN NaN NaN 2.0 8.0
53101 xx9914005 NaN NaN NaN NaN NaN NaN NaN NaN 3.0 4.0
53102 xx9914006 NaN NaN NaN NaN NaN NaN NaN NaN 4.0 8.0
53103 xx9914007 NaN NaN NaN NaN NaN NaN NaN NaN 5.0 4.0

53104 rows × 11 columns

V tomto případě nepřijdeme o žádné informace o filmech, ale u spousty z nich nebudeme mít informaci o hodnocení a u několika hodnocení zase budou chybět základní informace o filmech.

Left a Right

Strategie left a right berou jako základ pro spojení levou/pravou tabulku, která tedy zůstane v původní podobě. Druhá tabulka do dvojice se použije jen pro doplnění informací tam, kde je to možné.

In [12]:
pd.merge(movies, ratings, how="left", left_on="tconst", right_on="tconst")
Out[12]:
tconst titleType primaryTitle originalTitle isAdult startYear endYear runtimeMinutes genres averageRating numVotes
0 tt0315642 movie Wazir Wazir 0 2016 NaN 103.0 Action,Crime,Drama 7.1 15911.0
1 tt0364201 movie Aman Ke Farishtey Aman Ke Farishtey 0 2016 NaN 137.0 Action 6.4 16.0
2 tt0376479 movie American Pastoral American Pastoral 0 2016 NaN 108.0 Crime,Drama 6.1 13546.0
3 tt0443533 movie The History of Love The History of Love 0 2016 NaN 134.0 Drama,Romance,War 6.3 1086.0
4 tt0470936 movie Hot Country, Cold Winter Tak erkir, tsurt dzmer 0 2016 NaN 104.0 Drama 4.0 62.0
... ... ... ... ... ... ... ... ... ... ... ...
53091 tt9909940 movie Hot Scent Hot Scent 0 2018 NaN NaN Family NaN NaN
53092 tt9910688 movie Luz, Câmera e Barreto Luz, Câmera e Barreto 0 2018 NaN NaN Documentary NaN NaN
53093 tt9914644 movie 9/11: Escape from the Towers 9/11: Escape from the Towers 0 2018 NaN 120.0 Documentary 8.4 27.0
53094 tt9914662 movie Wien is 't Hof van Commerce Wien is 't Hof van Commerce 0 2018 NaN NaN Comedy NaN NaN
53095 tt9916132 movie The Mystery of a Buryat Lama The Mystery of a Buryat Lama 0 2018 NaN 94.0 Biography,Documentary,History NaN NaN

53096 rows × 11 columns

Tady máme k dispozici všechny filmy a doplněné hodnocení tam, kde to bylo možné.

In [13]:
pd.merge(movies, ratings, how="right", left_on="tconst", right_on="tconst")
Out[13]:
tconst titleType primaryTitle originalTitle isAdult startYear endYear runtimeMinutes genres averageRating numVotes
0 tt0315642 movie Wazir Wazir 0.0 2016.0 NaN 103.0 Action,Crime,Drama 7.1 15911
1 tt0364201 movie Aman Ke Farishtey Aman Ke Farishtey 0.0 2016.0 NaN 137.0 Action 6.4 16
2 tt0376479 movie American Pastoral American Pastoral 0.0 2016.0 NaN 108.0 Crime,Drama 6.1 13546
3 tt0443533 movie The History of Love The History of Love 0.0 2016.0 NaN 134.0 Drama,Romance,War 6.3 1086
4 tt0470936 movie Hot Country, Cold Winter Tak erkir, tsurt dzmer 0.0 2016.0 NaN 104.0 Drama 4.0 62
... ... ... ... ... ... ... ... ... ... ... ...
26093 xx9914003 NaN NaN NaN NaN NaN NaN NaN NaN 1.0 9
26094 xx9914004 NaN NaN NaN NaN NaN NaN NaN NaN 2.0 8
26095 xx9914005 NaN NaN NaN NaN NaN NaN NaN NaN 3.0 4
26096 xx9914006 NaN NaN NaN NaN NaN NaN NaN NaN 4.0 8
26097 xx9914007 NaN NaN NaN NaN NaN NaN NaN NaN 5.0 4

26098 rows × 11 columns

Tady máme naopak všechna hodnocení a informace o filmu jen tam, kde je bylo možné dohledat.

Finální strategie vždy záleží na tom, která data potřebujeme a která si můžeme dovolit vypustit. Více si toto rozebereme v dalších kapitolách. Pro tuto chvíli pro nás budou důležitější informace o filmech a tak zůstaneme u strategie, která použije filmy jako základ pro spojení.

In [14]:
data = pd.merge(movies, ratings, how="left", left_on="tconst", right_on="tconst")

Nulové hodnoty

Nulové (neboli chybějící) hodnoty jsou snadno identifikovatelné a znamenají v podstatě chybějící data v datasetu. Nejčastěji jsou označeny jako NaN, což je ekvivalent pythoního None a může být použit i přímo z knihovny numpy.

In [15]:
import numpy as np

np.nan
Out[15]:
nan

Pandas identifikuje chybějící hodnoty automaticky, pokud je buňka ve zdrojovém souboru zcela prázdná. Pokud zdrojová data obsahují nějakou jinou reprezentaci (např.: N/A, \\N a podobně). je potřeba je na np.nan nejdříve převézt. Po převodu se nám bude s daty mnohem lépe pracovat.

Informace o chybějících hodnotách lze vyčíst z informací o datech, deskriptivních statistikách a pak také přímo jejich součtem napříč daty.

In [16]:
data.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 53096 entries, 0 to 53095
Data columns (total 11 columns):
 #   Column          Non-Null Count  Dtype  
---  ------          --------------  -----  
 0   tconst          53096 non-null  object 
 1   titleType       53096 non-null  object 
 2   primaryTitle    53096 non-null  object 
 3   originalTitle   53080 non-null  object 
 4   isAdult         53096 non-null  int64  
 5   startYear       53096 non-null  int64  
 6   endYear         0 non-null      float64
 7   runtimeMinutes  40554 non-null  float64
 8   genres          50434 non-null  object 
 9   averageRating   26090 non-null  float64
 10  numVotes        26090 non-null  float64
dtypes: float64(4), int64(2), object(5)
memory usage: 4.9+ MB
In [17]:
data.describe()
Out[17]:
isAdult startYear endYear runtimeMinutes averageRating numVotes
count 53096.000000 53096.000000 0.0 40554.000000 26090.000000 26090.000000
mean 0.004482 2016.995744 NaN 86.083222 6.302986 2701.542162
std 0.066801 0.812987 NaN 37.709587 1.498641 22314.076056
min 0.000000 2016.000000 NaN 1.000000 1.000000 5.000000
25% 0.000000 2016.000000 NaN 71.000000 5.400000 15.000000
50% 0.000000 2017.000000 NaN 88.000000 6.500000 55.000000
75% 0.000000 2018.000000 NaN 100.000000 7.400000 294.000000
max 1.000000 2018.000000 NaN 2160.000000 10.000000 845855.000000
In [18]:
data.isnull().sum()
Out[18]:
tconst                0
titleType             0
primaryTitle          0
originalTitle        16
isAdult               0
startYear             0
endYear           53096
runtimeMinutes    12542
genres             2662
averageRating     27006
numVotes          27006
dtype: int64

Poslední možnost je sice nejdelší na psaní, ale výsledek je nejlépe vidět a nevyžaduje odečítání od celkového počtu záznamů.

Pro práci s nulovými hodnotami neexistuje jedna správná a univerzální cesta. V podstatě si musíme velmi uváženě vybrat jednu ze dvou možností:

  1. Mazání
  2. Doplnění

Mazání

Mazání je velmi jednoduché. Smazat můžeme celé sloupce nebo řádky, ale přijdeme tím i o potencionálně užitečná data a snížíme tím jejich reprezentativnost.

V našem případě můžeme smazat sloupec endYear, který neobsahuje ani jednu hodnotu a tak jeho smazáním o nic nepřijdeme.

In [19]:
data = data.drop(columns=["endYear"])

Pro mazání související přímo s chybějícími hodnotami máme k dispozici metodu dropna, která umí mazat řádky či sloupce obsahující alespoň jednu chybějící hodnotu nebo zcela naplněné chybějícími hodnotami. Nic dalšího mazat nebudeme, ale výsledek jednotlivých strategií si přes to ukážeme.

In [20]:
data.shape
Out[20]:
(53096, 10)

Aktuálně máme v datech 53096 záznamů. Takto by to vypadalo, pokud bychom nechali smazat všechny řádky, které obsahují alespoň jednu chybějící hodnotu.

In [21]:
data.dropna(how="any")
Out[21]:
tconst titleType primaryTitle originalTitle isAdult startYear runtimeMinutes genres averageRating numVotes
0 tt0315642 movie Wazir Wazir 0 2016 103.0 Action,Crime,Drama 7.1 15911.0
1 tt0364201 movie Aman Ke Farishtey Aman Ke Farishtey 0 2016 137.0 Action 6.4 16.0
2 tt0376479 movie American Pastoral American Pastoral 0 2016 108.0 Crime,Drama 6.1 13546.0
3 tt0443533 movie The History of Love The History of Love 0 2016 134.0 Drama,Romance,War 6.3 1086.0
4 tt0470936 movie Hot Country, Cold Winter Tak erkir, tsurt dzmer 0 2016 104.0 Drama 4.0 62.0
... ... ... ... ... ... ... ... ... ... ...
53081 tt9903952 movie BADMEN with a good behavior BADMEN with a good behavior 0 2018 87.0 Comedy,Horror 7.8 6.0
53082 tt9904014 movie Lost in Klessin Lost in Klessin 0 2018 90.0 War 7.5 14.0
53083 tt9904530 movie Scream Returns Scream Returns 0 2018 48.0 Horror,Thriller 3.0 6.0
53088 tt9908960 movie Pliusas Pliusas 0 2018 90.0 Comedy 4.1 18.0
53093 tt9914644 movie 9/11: Escape from the Towers 9/11: Escape from the Towers 0 2018 120.0 Documentary 8.4 27.0

23304 rows × 10 columns

A takto, pokud bychom smazali řádky, které mají pouze chybějící hodnoty:

In [22]:
data.dropna(how="all")
Out[22]:
tconst titleType primaryTitle originalTitle isAdult startYear runtimeMinutes genres averageRating numVotes
0 tt0315642 movie Wazir Wazir 0 2016 103.0 Action,Crime,Drama 7.1 15911.0
1 tt0364201 movie Aman Ke Farishtey Aman Ke Farishtey 0 2016 137.0 Action 6.4 16.0
2 tt0376479 movie American Pastoral American Pastoral 0 2016 108.0 Crime,Drama 6.1 13546.0
3 tt0443533 movie The History of Love The History of Love 0 2016 134.0 Drama,Romance,War 6.3 1086.0
4 tt0470936 movie Hot Country, Cold Winter Tak erkir, tsurt dzmer 0 2016 104.0 Drama 4.0 62.0
... ... ... ... ... ... ... ... ... ... ...
53091 tt9909940 movie Hot Scent Hot Scent 0 2018 NaN Family NaN NaN
53092 tt9910688 movie Luz, Câmera e Barreto Luz, Câmera e Barreto 0 2018 NaN Documentary NaN NaN
53093 tt9914644 movie 9/11: Escape from the Towers 9/11: Escape from the Towers 0 2018 120.0 Documentary 8.4 27.0
53094 tt9914662 movie Wien is 't Hof van Commerce Wien is 't Hof van Commerce 0 2018 NaN Comedy NaN NaN
53095 tt9916132 movie The Mystery of a Buryat Lama The Mystery of a Buryat Lama 0 2018 94.0 Biography,Documentary,History NaN NaN

53096 rows × 10 columns

Z počtu zbylých řádků je jasné, že se žádný nesmazal a každý řádek tedy obsahuje alespoň nějakou hodnotu.

Mazání by mohlo způsobit ztrátu potencionálně užitečných dat. Smazáním filmů bez hodnocení bychom přišli o polovinu filmů, což by pro analýzu některých jejich vlastností mohlo být nežádoucí.

Doplnění

Bez mazání musíme v dalších krocích buď počítat s tím, že máme v datech chybějící hodnoty, nebo je do dat nějak doplnit.

Doplnit data lze mnoha způsoby a záleží hlavně na proměnné, kterou budeme doplňovat. Je třeba mít také na paměti, že doplněná data nejsou reálná a měla by plnit jen pomocnou funkci. Možnosti jsou následující:

  • Nahradit chybějící hodnoty průměrem, mediánem či modem (nejčastěji se vyskytující hodnotou). Dopočtená hodnota přitom může vycházet z celého zbytku datasetu nebo jen několika řádků okolo chybějící hodnoty (KNN - K Nearest Neighbors).
  • Pokud se jedná o data sbíraná v čase, můžeme chybějící hodnotu nahradit poslední hodnotou naměřenou před tou chybějící nebo tou první následující po chybějící hodnotě.
  • Pokud je v datech viditelný trend, můžeme zkusit chybějící data dopočítat např. pomocí lineární regrese, o které si budeme povídat v lekcích o machine learningu.

U více než 12 000 filmů nám chybí informace o jejich stopáži. Pojďme zkusit najít nejvhodnější cestu k doplnění.

In [23]:
data.runtimeMinutes.hist(bins=30);

Histogram trpí tím, že v datech máme i film dlouhý 36 hodin. Jak na to zareagují popisné statistiky?

In [24]:
data.runtimeMinutes.describe()
Out[24]:
count    40554.000000
mean        86.083222
std         37.709587
min          1.000000
25%         71.000000
50%         88.000000
75%        100.000000
max       2160.000000
Name: runtimeMinutes, dtype: float64

75 % filmů je kratších než 100 minut a medián s průměrem příliš nereflektují extrémní hodnoty, takže bychom jednu z těchto hodnot mohli použít. Pro úplnost se ještě podíváme, která hodnota se vyskytuje v datech úplně nejčastěji.

In [25]:
data.runtimeMinutes.mode()
Out[25]:
0    90.0
dtype: float64
In [26]:
data.runtimeMinutes.value_counts()
Out[26]:
90.0     2691
80.0     1174
85.0      966
95.0      883
100.0     874
         ... 
400.0       1
238.0       1
447.0       1
495.0       1
330.0       1
Name: runtimeMinutes, Length: 285, dtype: int64

Vzhledem k malým rozdílům moc nesejde na tom, zda použijeme mean, medián nebo mode. Zkusme tedy medián.

In [27]:
data.runtimeMinutes = data.runtimeMinutes.fillna(data.runtimeMinutes.median())

Nový sloupec s doplněnými chybějícími hodnotami uložíme zpět do původních dat a máme hotovo.

In [28]:
data.isnull().sum()
Out[28]:
tconst                0
titleType             0
primaryTitle          0
originalTitle        16
isAdult               0
startYear             0
runtimeMinutes        0
genres             2662
averageRating     27006
numVotes          27006
dtype: int64

U kategoriální proměnné genres nemáme tolik možností jako u numerických proměnných a navíc tento sloupec obsahuje různé kombinace žánrů. V neposlední řadě se s chybějícími hodnotami nepracuje u textových sloupců tak snadno.

In [29]:
data.genres.value_counts()[:]
Out[29]:
Documentary                       13461
Drama                              8047
Comedy                             3278
Horror                             1689
Comedy,Drama                       1209
                                  ...  
Family,Mystery,Sport                  1
History,Horror,Thriller               1
Adventure,Animation,Crime             1
Documentary,History,Reality-TV        1
Family,Western                        1
Name: genres, Length: 814, dtype: int64

Data obsahují celkem 814 unikátních kombinací žánrů a více než třetina filmů jsou dokumenty. Doplnit takovou proměnnou o nejčastější hodnotu by asi nebyl nejlepší nápad a tak budeme předpokládat, že chybějící hodnota znamená, že se film zkrátka nepodařilo zařadit do žádné z připravených škatulek.

In [30]:
data[data.genres.isnull()].shape
Out[30]:
(2662, 10)
In [31]:
data[(data.genres.isnull()) & (data.averageRating.isnull())].shape
Out[31]:
(2424, 10)

Navíc je vidět, že jen okolo 200 filmů z těch s chybějícím žánrem má nějaké hodnocení, což podporuje pocit, že se nejedná o běžné snímky. Nicméně je to stále jen pocit.

Chybějící hodnoty ve sloupci s originálním názvem nepůjde doplnit tak snadno a doplňovat hodnocení filmů jakýmkoli způsobem také není dobrý nápad, protože by to mohlo ovlivnit výsledky případné navazující analýzy.

Jak vidno, mazání či doplnění hodnot je vždy rizikové a takovému kroku by mělo předcházet dobré zvážení a zdůvodnění. Je také více než rozumné si na konci analýz sesumírovat své výsledky a zkusit odhadnout, jaký vliv na ně mohla mít strategie nakládání s chybějícími hodnotami.

Odlehlá měření

Chybějící hodnoty na nás v ideálním případě vyskočí hned po načtení dat, případně je objevíme záhy při pohledu na jednotlivé proměnné. S odlehlými měřeními je to složitější, protože na ně v tom nejhorším případě nemusíme narazit vůbec a jejich výskyt ovlivní výsledky naší analýzy.

Že nám nějaká hodnota nesedí do zbytku dat, ještě nemusí nic zásadního znamenat a hlavně nemusí jít vždy o chybu. Stejně jako v případě chybějících hodnot je i zde před opravou či jiným zásahem třeba dobře zvážit jeho následky. Nejčastější příčiny výskytu odlehlých měření jsou:

  • Lidská chyba (např. při zápisu dat v dotazníku)
  • Chyba měření (např. závada na měřícím přístroji)
  • Chyba při manipulace s daty, agregaci, zpracování
  • Validní leč extrémní hodnota

Při hledání odlehlých měření se nejdříve zaměříme na jednotlivé proměnné a pak na jejich kombinace. Základní přehled a představu o tom, co můžeme dále očekávat nám poskytne známy krabicový graf.

IQR

První metodou pro detekci odlehlých měření je inter-quartile range, který jsme si popsali při analýze jedné proměnné u popisu krabicového grafu. Pro zopakování: vezme se rozsah mezi prvním a třetím quartilem a vynásobí se 1,5×. Tím se vytvoří horní a spodní hranice (vodorovné čárky v grafu) a co se mezi ně nevejde, je označeno jako odlehlé měření.

In [32]:
data.plot.box(figsize=(15,15), subplots=True, layout=(3,3));

S daty už umíme lépe pracovat, takže si můžeme zkusit vypočítat tuto metodu i ručně.

In [33]:
q1 = data.runtimeMinutes.quantile(0.25)
q3 = data.runtimeMinutes.quantile(0.75)
iqr = q3 - q1
dolni_hranice = q1-1.5*iqr
horni_hranice = q3+1.5*iqr
print(f"Q1: {q1}, Q3: {q3}, IQR: {iqr}, dolní hranice: {dolni_hranice}, horní hranice: {horni_hranice}")
Q1: 77.0, Q3: 95.0, IQR: 18.0, dolní hranice: 50.0, horní hranice: 122.0

Vypočtené hranice nám poslouží pro manuální filtraci dat, takže uvidíme i v tabulce to, co je vidět v grafu.

In [34]:
data[data.runtimeMinutes < dolni_hranice]
Out[34]:
tconst titleType primaryTitle originalTitle isAdult startYear runtimeMinutes genres averageRating numVotes
35 tt10023318 movie Waiting for the Boatman Waiting for the Boatman 0 2016 45.0 Drama NaN NaN
36 tt10023330 movie Deep Swimmer Deep Swimmer 0 2016 45.0 Drama NaN NaN
68 tt10065960 movie Snatched Snatched 0 2016 11.0 Drama NaN NaN
69 tt10066186 movie 88A-12 88A-12 0 2016 8.0 Fantasy NaN NaN
133 tt10160180 movie Caboverdianamente Caboverdianamente 0 2016 49.0 Documentary,Music NaN NaN
... ... ... ... ... ... ... ... ... ... ...
52994 tt9848240 movie Pattern Pattern 0 2018 31.0 Documentary NaN NaN
53044 tt9879042 movie Saudades do futuro Saudades do futuro 0 2018 45.0 Crime NaN NaN
53069 tt9892842 movie Tutto davanti a questi occhi Tutto davanti a questi occhi 0 2018 45.0 Documentary NaN NaN
53083 tt9904530 movie Scream Returns Scream Returns 0 2018 48.0 Horror,Thriller 3.0 6.0
53084 tt9904670 movie Mountain Quest Mountain Quest 0 2018 45.0 Documentary NaN NaN

3628 rows × 10 columns

In [35]:
data[data.runtimeMinutes > horni_hranice]
Out[35]:
tconst titleType primaryTitle originalTitle isAdult startYear runtimeMinutes genres averageRating numVotes
1 tt0364201 movie Aman Ke Farishtey Aman Ke Farishtey 0 2016 137.0 Action 6.4 16.0
3 tt0443533 movie The History of Love The History of Love 0 2016 134.0 Drama,Romance,War 6.3 1086.0
6 tt0490215 movie Silence Silence 0 2016 161.0 Drama,History 7.1 88843.0
10 tt0803096 movie Warcraft Warcraft 0 2016 123.0 Action,Adventure,Fantasy 6.8 230205.0
29 tt10011852 movie Saalaiyoram Saalaiyoram 0 2016 125.0 Drama NaN NaN
... ... ... ... ... ... ... ... ... ... ...
53057 tt9889918 movie 2 Friends 2 Friends 0 2018 134.0 Comedy,Drama,Romance NaN NaN
53058 tt9889926 movie Ratham Ratham 0 2018 132.0 Comedy,Drama,Romance 5.5 16.0
53063 tt9890312 movie Changa Changa 0 2018 127.0 Comedy NaN NaN
53086 tt9907032 movie Footloose in London II: 2 Undiscovered and Unu... Footloose in London II: 2 Undiscovered and Unu... 0 2018 124.0 NaN NaN NaN
53089 tt9909086 movie Pheriaa Come Back Pheriaa Come Back 0 2018 137.0 Drama NaN NaN

3261 rows × 10 columns

Skoro 7 tisíc filmů je z pohledu své stopáže označeno jako odlehlá měření. Výpočet pomocí IQR nabízí jednoduchou cestu k výsledkům, ale není jednoduše možné si rozsah hodnot upravit podle svých představ. To je možné v druhé populární metodě detekce odlehlých měření - Z score.

Z score

z score je metoda detekce odlehlých měření definovaná jednoduchým vzorcem a závislá na směrodatné odchylce. Právě díky jednoduchému vzorečku a nastavitelné hranici je možné si určit, co pro nás znamená odlehlé měření. Vzorec vypadá následovně:

$$ z = \frac{x - \mu}{\sigma} $$

x označuje konkrétní hodnotu proměnné, 𝜇 označuje průměr pro danou proměnnou a 𝜎 směrodatnou odchylku. Z score uvádí, kolik násobků směrodatné odchylky je daná hodnota vzdálená od průměru. Čím blíže bude zkoumaná hodnota průměru, tím blíže bude Z score nule. Výhodou je, že si pro Z score můžeme sami určit hranice pro detekci odlehlých měření a také to, že díky násobkům směrodatné odchylky, ve kterých se tato hranice stanovuje, víme, kolik procent záznamů máme pokryto. O směrodatné odchylce a normálním rozložení si budeme ještě povídat v lekci o reprezentativnosti dat.

U proměnných s normálním rozložením je nejvíce výskytů kolem průměru a s rostoucí vzdáleností od průměru klesá počet výskytů. Do vzdálenosti jedné směrodatné odchylky se vejde 68 % všech hodnot, dvojnásobek směrodatné odchylky pak pokrývá 95 % všech hodnot a trojnásobek 99 %.

Je čas to vyzkoušet:

In [36]:
abs(20 - data.runtimeMinutes.mean()) / data.runtimeMinutes.std()
Out[36]:
2.018309124232446
In [37]:
abs(2 - data.runtimeMinutes.mean()) / data.runtimeMinutes.std()
Out[37]:
2.564322845007194

Dvacetiminutový film je od průměrné hodnoty vzdálen přibližně dvojnásobek směrodatné odchylky. Striktně vzato by se nezařadil mezi 95 % nejčastějších hodnot.

In [38]:
abs(180 - data.runtimeMinutes.mean()) / data.runtimeMinutes.std()
Out[38]:
2.835146171543095

Tři hodiny trvající film je od průměru vzdálen ještě o něco více to skoro o trojnásobek směrodatné odchylky.

Z score vypočtené pro všechny záznamy nám může také posloužit pro filtraci dat.

In [39]:
zs = abs((data.runtimeMinutes - data.runtimeMinutes.mean()) / data.runtimeMinutes.std())
In [40]:
data[zs > 3].sort_values(by="runtimeMinutes")
Out[40]:
tconst titleType primaryTitle originalTitle isAdult startYear runtimeMinutes genres averageRating numVotes
35211 tt9049488 movie Juyin Sarauta Juyin Sarauta 0 2017 186.0 Drama NaN NaN
31044 tt7385130 movie McLuhan Unclaimed: Western Cynical McLuhan Unclaimed: Western Cynical 0 2017 186.0 Documentary NaN NaN
40403 tt6628102 movie The Wild Pear Tree Ahlat Agaci 0 2018 188.0 Drama 8.1 14116.0
16762 tt7830972 movie Song of the Starslayer Song of the Starslayer 0 2016 188.0 Adventure,Fantasy 5.5 24.0
47708 tt8514324 movie The Fate of Cysalion Das Schicksal von Cysalion 0 2018 188.0 Adventure,Fantasy,Musical 7.9 15.0
... ... ... ... ... ... ... ... ... ... ...
37189 tt1277455 movie A Time to Stir A Time to Stir 0 2018 1320.0 Documentary NaN NaN
12824 tt6127010 movie Funusion Funusion 0 2016 1440.0 Fantasy,Sci-Fi NaN NaN
11330 tt5942280 movie The Nothing Movie The Nothing Movie 0 2016 1559.0 Drama NaN NaN
16483 tt7467634 movie Aberration Aberration 0 2016 1834.0 Drama NaN NaN
30685 tt7321476 movie h36: h36: 0 2017 2160.0 Action NaN NaN

181 rows × 10 columns

Z score nám na spodní hranici nedetekovalo žádná odlehlá měření a na té horní pak 180 filmů delších než tři hodiny.

Více proměnných najednou

Pro kompletní detekci odlehlých měření je ještě potřeba se na data podívat z pohledu více dimenzí. Kombinace více vlastností totiž může být zcela mimo očekávanou hodnotu, i když každá z vlastností samostatně vůbec nemusí vybočovat z řady.

Pro detekci takových případů použijeme stejně jako pro hledání trendů a korelací scatter plot.

In [41]:
from pandas.plotting import scatter_matrix
scatter_matrix(data, figsize=(20, 20));

Ve scatter plotech není na první pohled nic vidět, resp. jsou zde patrná odlehlá měření, která jsme již detekovali dříve.

Jeden zajímavý bod zde ale přeci jen máme. Je vidět v grafu závislosti celkové délky stopáže a počtu hodnocení.

In [42]:
data.plot.scatter(x="runtimeMinutes", y="numVotes", grid=True);

Že se v počtu hlasování objeví extrémně často hodnocené filmy, to je očekávané a víme to z předchozích kroků analýzy. Stejná je situace u extrémních hodnot délky filmů, kde se nějaké extrémy na obě strany dají očekávat. Kombinace obou těchto vlastností má také více méně očekávaný charakter a lidé nejčastěji hodnotí filmy s průměrnou délkou. Až na jednu výjimku, jak je patrné z grafu.

I když film mající 300 000 hodnocení není nijak výjimečný a délka 400 minut také není z nejextrémnějších, společná kombinace těchto hodnot je přinejmenším podezřelá a zasloužila by si v běžné analýze další zkoumání.

Stejně jako v předchozích krocích u IQR a Z score, můžeme i zde použít algoritmy pro detekci konkrétních odlehlých pozorování ve dvou a více dimenzích. Jejich rozbor a porovnání je mimo záběr této lekce, ale jeden jednoduchý a celkem populární si přeci jen ukážeme.

DBSCAN

DBSCAN (Density-Based Spatial Clustering of Applications with Noise) je algoritmus, který dokáže shlukovat body v prostoru (o libovolném počtu dimenzí) k sobě do skupin podle toho, jak moc jsou body blízko u sebe. Jednoduše řečeno prohledá okolí bodů v prostoru a pokud najde dostatek sousedů, přidá je do společné skupinky. Pokud ovšem body nemají dostatek sousedů, jsou označeny jako šum (noise) a v našem případě je lze považovat za odlehlá měření, protože se zkrátka vyskytují příliš daleko od ostatních a není jich dost, aby si vytvořili vlastní skupinku.

Velikost prohledávaného okolí a nezbytný počet sousedů jsou nastavitelné parametry, takže i výsledky tohoto algoritmu budou stejně jako u Z score záviset na správném nastavení a interpretaci. Pro jednoduchost se budeme pohybovat jen v prostoru o dvou dimenzích, což nám umožní výsledky pěkně vizualizovat.

Celý proces bude trošku komplikovaný, ale podstatné pro nás je porozumnět všem krokům než si hned zapamatovat celý postup a umět jej naprogramovat.

Nejprve musíme z dat odebrat nulové hodnoty, protože s těmi si algoritmy často neumí poradit. Nechceme při tom zasahovat do originálních dat, takže si výsledek uložíme do nové proměnné. Při odstraňování chybějících dat jsme zvolili strategii any takže stačí jedna nulová hodnota pro odstranění řádku a subset nastavil sloupce, kam se bude pandas dívat, protože např. chybějící hodnota ve sloupci s originálním názvem nás aktuálně nezajímá a není třeba, aby způsobila odstranění celého řádku.

In [43]:
no_null_values = data.dropna(how="any", subset=["runtimeMinutes", "numVotes"])
no_null_values.isnull().sum()
Out[43]:
tconst              0
titleType           0
primaryTitle        0
originalTitle       0
isAdult             0
startYear           0
runtimeMinutes      0
genres            238
averageRating       0
numVotes            0
dtype: int64

Nyní potřebujeme upravit škálu našich dat. Problém je v tom, že délka filmu v minutách se pohybuje v dost odlišných cifrách než počet hlasujících, což by nám komplikovalo správné nastavení algoritmu a výpočet vzdálenosti mezi jednotlivými body. Více o škálování bude řeč v dalších lekcích, protože je to celkem běžná součást přípravy dat pro strojové učení. Škálování na menší hodnoty se provede pro oba sloupce najednou, takže vztah mezi nimi zůstane zachován.

In [44]:
from sklearn.preprocessing import StandardScaler

sc = StandardScaler()
transformed = sc.fit_transform(no_null_values[["runtimeMinutes", "numVotes"]])
transformed
Out[44]:
array([[ 0.34837288,  0.59199005],
       [ 1.67166739, -0.12035424],
       [ 0.54297501,  0.48600111],
       ...,
       [-1.7922506 , -0.12080239],
       [-0.15759267, -0.12026461],
       [ 1.01002013, -0.11986127]])

Výsledkem je dvourozměrná matice, kterou si pro ověření můžeme také vizualizovat. Převodem z pandas DataFrame na numpy matici jsme přišli o názvy sloupců, ale ke sloupcům samotným se dokážeme dostat díky rozšířené indexaci.

In [45]:
from matplotlib import pyplot as plt

plt.scatter(x=transformed[:, 0], y=transformed[:, 1]);

V následujícím kroku si vytvoříme model, který bude prohledávat okolí o velikosti 4 a za skupinku bude považovat shluk minimálně dvou bodů.

In [46]:
from sklearn.cluster import DBSCAN

model = DBSCAN(eps=4, min_samples=2)

Necháme model naučit se naše transformovaná data.

In [47]:
result = model.fit(transformed)

Výsledek obsahuje mimo jiné labels_, což je seznam tzv. značek, kterými označil jednotlivé řádky z našich dat a tím je zařadil do skupin.

In [48]:
result.labels_
Out[48]:
array([0, 0, 0, ..., 0, 0, 0])

Převodem na množinu (set) dokážeme zjistit, kolik takových skupinek vytvořil.

In [49]:
len(set(result.labels_))
Out[49]:
3

A teď už k vizualizaci. K tomu budeme potřebovat vykreslit do scatter plotu různé barvy, abychom mezi skupinkami dokázali rozlišit. U takto malého počtu bychom si dokázali barvy určit i ručně, ale pomoci nám může barevná mapa, která hodnoty pro jednotlivé skupiny rozloží na vybrané barevné škále a my je tím pádem dokážeme rozeznat v grafu.

In [50]:
from matplotlib import cm

cmap = cm.get_cmap("cividis")

no_null_values.plot.scatter(x="runtimeMinutes", y="numVotes", c=result.labels_, cmap=cmap);

Šedá barva označuje skupinku číslo 0, která obsahuje nejvíce filmů. -1 označuje odlehlá měření, která jsou daleko a navíc jich je příliš málo na vytvoření skupiny. Žlutá je skupinka číslo 1, která není dost blízko ostatním, ale je dost početná na to, aby si vytvořila skupinku vlastní.

Takto může ve výsledku vypadat automatická detekce odlehlých měření i v mnoha dimenzích. Co se ale s odlehlými měřeními nakonec stane a jak ovlivní výsledek celé analýzy, to už záleží jen na analytikovi a zvolených postupech.

Závěr

Dnešní lekce ukázala několik způsobů, jak detekovat a jak se vypořádat s chybějícími daty a odlehlými měřeními. Finální volba je vždy závislá na našich cílech. Jinak se budeme chovat k datům připravovaným pro explorativní analýzu a jinak k datům pro budoucí model strojového učení. Je také třeba být velmi opatrný s mazáním potencionálně užitečných dat či kalkulací s vymyšlenými daty.

Čas na hraní

Pokud máš po minulé lekci hotovou svou vlastní analýzu, můžeš do ní zkusit přidat dnes získané znalosti a ověřit, jak se to promítne do výsledků a závěrů.


Toto je stránka lekce z kurzu, který probíhá nebo proběhl naživo s instruktorem.