Obsah
1. Načtení datového rámce z externího datového souboru (tabulky)
2. Zpracování jednotlivých sloupců v datových rámcích – map a transform
3. Agregace dat z datových rámců, resp. z vybraných sloupců
5. Spojení datových rámců metodou append
6. Příklad použití metody append
7. Spojení rámců po sloupcích nebo po řádcích funkcí concat
8. Příklady použití funkce concat
10. Inner join (vnitřní spojení) založený na funkci merge
11. Left join (vnější spojení „zleva“) založený na funkci merge
12. Right join (vnější spojení „zprava“) založený na funkci merge
13. Outer join (vnější spojení) založený na funkci merge
14. Použití metody join pro spojení dvou datových rámců
15. Inner join (vnitřní spojení) založený na metodě join
16. Left join (vnější spojení „zleva“) založený na metodě join
17. Right join (vnější spojení „zprava“) založený na metodě join
18. Outer join (vnější spojení) založený na metodě join
19. Repositář s demonstračními příklady
1. Načtení datového rámce z externího datového souboru (tabulky)
Nejdříve si zopakujme, jakým způsobem je možné načíst tabulku do datového rámce. Knihovna Pandas umožňuje načtení tabulky z různých datových zdrojů, například ze:
- Souborů CSV (Comma-Separated Values)
- Souborů TSV (Tab-Separated Values)
- Textových souborů s volitelným oddělovačem a formátem sloupců
- Tabulek z tabulkových procesorů (xls, xlsx, xlsm, xlsb, odf, ods, odt)
- Souborů JSON se strukturovanými daty
- Načítání z relačních databází s využitím SQL driverů
- Načítání z Parquet souborů
- atd.
V demonstračních příkladech popsaných v navazujících kapitolách budeme používat tabulku uloženou ve formátu TSV (Tab-Separated Values), kterou je možné načíst funkcí nazvanou read_csv, ovšem jen v případě, že budeme explicitně specifikovat formát oddělovače:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import pandas # přečtení zdrojových dat df = pandas.read_csv("tiobe.tsv", sep="\t") # specifikace indexu - má se získat ze sloupce Language df.set_index("Language", inplace=True) # datový rámec zobrazíme print(df) print() # podrobnější informace o datovém rámci print(df.dtypes) print() # více podrobnějších informací o datovém rámci print(df.info()) print()
Po spuštění tohoto demonstračního příkladu by se měl nejprve zobrazit obsah celého datového rámce:
Sep 2020 Sep 2019 Change Ratings Changep Language C 1 2 change 15.95 0.74 Java 2 1 change 13.48 -3.18 Python 3 3 NaN 10.47 0.59 C++ 4 4 NaN 7.11 1.48 C# 5 5 NaN 4.58 1.18 Visual Basic 6 6 NaN 4.12 0.83 JavaScript 7 7 NaN 2.54 0.41 PHP 8 9 change 2.49 0.62 R 9 19 change 2.37 1.33 SQL 10 8 change 1.76 -0.19 Go 11 14 change 1.46 0.24 Swift 12 16 change 1.38 0.28 Perl 13 20 change 1.30 0.26 Assembly language 14 12 change 1.30 -0.08 Ruby 15 15 NaN 1.24 0.03 MATLAB 16 18 change 1.10 0.04 Groovy 17 11 change 0.99 -0.52 Rust 18 33 change 0.92 0.55 Objective-C 19 10 change 0.85 -0.99 Dart 20 24 change 0.77 0.13
Následovat by měl výpis struktury jednotlivých sloupců:
Sep 2020 int64 Sep 2019 int64 Change object Ratings float64 Changep float64 dtype: object
Nakonec se zobrazí informace o indexech, popis všech pěti datových sloupců a obsazení paměti datovým rámcem:
<class 'pandas.core.frame.DataFrame'> Index: 20 entries, C to Dart Data columns (total 5 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 Sep 2020 20 non-null int64 1 Sep 2019 20 non-null int64 2 Change 14 non-null object 3 Ratings 20 non-null float64 4 Changep 20 non-null float64 dtypes: float64(2), int64(2), object(1) memory usage: 960.0+ bytes None
2. Zpracování jednotlivých sloupců v datových rámcích – map a transform
V předchozím článku o knihovně Pandas jsme si popsali mnoho funkcí určených pro zpracování datových řad (series). Všechny tyto funkce jsou přitom plně aplikovatelné i na sloupce datových rámců, protože každý sloupec je možné považovat za plnohodnotnou datovou řadu. Vzhledem k tomu, že jednotlivé funkce již byly poměrně podrobně popsány minule, ukážeme si dnes jen několik příkladů pouze se základním popisem.
Vytvoření nového sloupce založeného na hodnotách z existujícího sloupce „Ratings“:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import pandas # přečtení zdrojových dat df = pandas.read_csv("tiobe.tsv", sep="\t") # specifikace indexu - má se získat ze sloupce Language df.set_index("Language", inplace=True) # převod na skutečný poměr <0, 1> df["Ratings as ratio"] = df["Ratings"].map(lambda x: x/100.0) # datový rámec zobrazíme print(df) print() # podrobnější informace o datovém rámci print(df.dtypes) print() # více podrobnějších informací o datovém rámci print(df.info()) print()
Výsledkem bude následující datový rámec:
Sep 2020 Sep 2019 ... Changep Ratings as ratio Language C 1 2 ... 0.74 0.1595 Java 2 1 ... -3.18 0.1348 Python 3 3 ... 0.59 0.1047 C++ 4 4 ... 1.48 0.0711 C# 5 5 ... 1.18 0.0458 Visual Basic 6 6 ... 0.83 0.0412 JavaScript 7 7 ... 0.41 0.0254 PHP 8 9 ... 0.62 0.0249 R 9 19 ... 1.33 0.0237 SQL 10 8 ... -0.19 0.0176 Go 11 14 ... 0.24 0.0146 Swift 12 16 ... 0.28 0.0138 Perl 13 20 ... 0.26 0.0130 Assembly language 14 12 ... -0.08 0.0130 Ruby 15 15 ... 0.03 0.0124 MATLAB 16 18 ... 0.04 0.0110 Groovy 17 11 ... -0.52 0.0099 Rust 18 33 ... 0.55 0.0092 Objective-C 19 10 ... -0.99 0.0085 Dart 20 24 ... 0.13 0.0077 [20 rows x 6 columns] Sep 2020 int64 Sep 2019 int64 Change object Ratings float64 Changep float64 Ratings as ratio float64 dtype: object <class 'pandas.core.frame.DataFrame'> Index: 20 entries, C to Dart Data columns (total 6 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 Sep 2020 20 non-null int64 1 Sep 2019 20 non-null int64 2 Change 14 non-null object 3 Ratings 20 non-null float64 4 Changep 20 non-null float64 5 Ratings as ratio 20 non-null float64 dtypes: float64(3), int64(2), object(1) memory usage: 1.1+ KB None
Přepis původního sloupce „Ratings“ zkonvertovanými hodnotami:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import pandas # přečtení zdrojových dat df = pandas.read_csv("tiobe.tsv", sep="\t") # specifikace indexu - má se získat ze sloupce Language df.set_index("Language", inplace=True) # převod na skutečný poměr <0, 1> df["Ratings"] = df["Ratings"].map(lambda x: x/100.0) # datový rámec zobrazíme print(df) print() # podrobnější informace o datovém rámci print(df.dtypes) print() # více podrobnějších informací o datovém rámci print(df.info()) print()
Výsledkem tohoto příkladu bude následující datový rámec:
Sep 2020 Sep 2019 Change Ratings Changep Language C 1 2 change 0.1595 0.74 Java 2 1 change 0.1348 -3.18 Python 3 3 NaN 0.1047 0.59 C++ 4 4 NaN 0.0711 1.48 C# 5 5 NaN 0.0458 1.18 Visual Basic 6 6 NaN 0.0412 0.83 JavaScript 7 7 NaN 0.0254 0.41 PHP 8 9 change 0.0249 0.62 R 9 19 change 0.0237 1.33 SQL 10 8 change 0.0176 -0.19 Go 11 14 change 0.0146 0.24 Swift 12 16 change 0.0138 0.28 Perl 13 20 change 0.0130 0.26 Assembly language 14 12 change 0.0130 -0.08 Ruby 15 15 NaN 0.0124 0.03 MATLAB 16 18 change 0.0110 0.04 Groovy 17 11 change 0.0099 -0.52 Rust 18 33 change 0.0092 0.55 Objective-C 19 10 change 0.0085 -0.99 Dart 20 24 change 0.0077 0.13 Sep 2020 int64 Sep 2019 int64 Change object Ratings float64 Changep float64 dtype: object <class 'pandas.core.frame.DataFrame'> Index: 20 entries, C to Dart Data columns (total 5 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 Sep 2020 20 non-null int64 1 Sep 2019 20 non-null int64 2 Change 14 non-null object 3 Ratings 20 non-null float64 4 Changep 20 non-null float64 dtypes: float64(2), int64(2), object(1) memory usage: 960.0+ bytes None
Přímá aplikace funkce format bez nutnosti použití lambda výrazu:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import pandas # přečtení zdrojových dat df = pandas.read_csv("tiobe.tsv", sep="\t") # specifikace indexu - má se získat ze sloupce Language df.set_index("Language", inplace=True) # formát hodnot ve sloupci df["Ratings"] = df["Ratings"].map("Rating is {:4.1f}%".format) # datový rámec zobrazíme print(df) print() # podrobnější informace o datovém rámci print(df.dtypes) print() # více podrobnějších informací o datovém rámci print(df.info()) print()
Výsledek:
Sep 2020 Sep 2019 Change Ratings Changep Language C 1 2 change Rating is 15.9% 0.74 Java 2 1 change Rating is 13.5% -3.18 Python 3 3 NaN Rating is 10.5% 0.59 C++ 4 4 NaN Rating is 7.1% 1.48 C# 5 5 NaN Rating is 4.6% 1.18 Visual Basic 6 6 NaN Rating is 4.1% 0.83 JavaScript 7 7 NaN Rating is 2.5% 0.41 PHP 8 9 change Rating is 2.5% 0.62 R 9 19 change Rating is 2.4% 1.33 SQL 10 8 change Rating is 1.8% -0.19 Go 11 14 change Rating is 1.5% 0.24 Swift 12 16 change Rating is 1.4% 0.28 Perl 13 20 change Rating is 1.3% 0.26 Assembly language 14 12 change Rating is 1.3% -0.08 Ruby 15 15 NaN Rating is 1.2% 0.03 MATLAB 16 18 change Rating is 1.1% 0.04 Groovy 17 11 change Rating is 1.0% -0.52 Rust 18 33 change Rating is 0.9% 0.55 Objective-C 19 10 change Rating is 0.8% -0.99 Dart 20 24 change Rating is 0.8% 0.13 Sep 2020 int64 Sep 2019 int64 Change object Ratings object Changep float64 dtype: object <class 'pandas.core.frame.DataFrame'> Index: 20 entries, C to Dart Data columns (total 5 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 Sep 2020 20 non-null int64 1 Sep 2019 20 non-null int64 2 Change 14 non-null object 3 Ratings 20 non-null object 4 Changep 20 non-null float64 dtypes: float64(1), int64(2), object(2) memory usage: 960.0+ bytes None
Metoda transform odpovídá výše zmíněné metodě map:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import pandas # přečtení zdrojových dat df = pandas.read_csv("tiobe.tsv", sep="\t") # specifikace indexu - má se získat ze sloupce Language df.set_index("Language", inplace=True) # převod na skutečný poměr <0, 1> df["Ratings as ratio"] = df["Ratings"].transform(lambda x: x/100.0) # datový rámec zobrazíme print(df) print() # podrobnější informace o datovém rámci print(df.dtypes) print() # více podrobnějších informací o datovém rámci print(df.info()) print()
Výsledek:
Sep 2020 Sep 2019 ... Changep Ratings as ratio Language C 1 2 ... 0.74 0.1595 Java 2 1 ... -3.18 0.1348 Python 3 3 ... 0.59 0.1047 C++ 4 4 ... 1.48 0.0711 C# 5 5 ... 1.18 0.0458 Visual Basic 6 6 ... 0.83 0.0412 JavaScript 7 7 ... 0.41 0.0254 PHP 8 9 ... 0.62 0.0249 R 9 19 ... 1.33 0.0237 SQL 10 8 ... -0.19 0.0176 Go 11 14 ... 0.24 0.0146 Swift 12 16 ... 0.28 0.0138 Perl 13 20 ... 0.26 0.0130 Assembly language 14 12 ... -0.08 0.0130 Ruby 15 15 ... 0.03 0.0124 MATLAB 16 18 ... 0.04 0.0110 Groovy 17 11 ... -0.52 0.0099 Rust 18 33 ... 0.55 0.0092 Objective-C 19 10 ... -0.99 0.0085 Dart 20 24 ... 0.13 0.0077 [20 rows x 6 columns] Sep 2020 int64 Sep 2019 int64 Change object Ratings float64 Changep float64 Ratings as ratio float64 dtype: object <class 'pandas.core.frame.DataFrame'> Index: 20 entries, C to Dart Data columns (total 6 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 Sep 2020 20 non-null int64 1 Sep 2019 20 non-null int64 2 Change 14 non-null object 3 Ratings 20 non-null float64 4 Changep 20 non-null float64 5 Ratings as ratio 20 non-null float64 dtypes: float64(3), int64(2), object(1) memory usage: 1.7+ KB None
Dtto pro změnu obsahu vybraného sloupce „Ratings“:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import pandas # přečtení zdrojových dat df = pandas.read_csv("tiobe.tsv", sep="\t") # specifikace indexu - má se získat ze sloupce Language df.set_index("Language", inplace=True) # převod na skutečný poměr <0, 1> df["Ratings"] = df["Ratings"].transform(lambda x: x/100.0) # datový rámec zobrazíme print(df) print() # podrobnější informace o datovém rámci print(df.dtypes) print() # více podrobnějších informací o datovém rámci print(df.info()) print()
Výsledek:
Sep 2020 Sep 2019 Change Ratings Changep Language C 1 2 change 0.1595 0.74 Java 2 1 change 0.1348 -3.18 Python 3 3 NaN 0.1047 0.59 C++ 4 4 NaN 0.0711 1.48 C# 5 5 NaN 0.0458 1.18 Visual Basic 6 6 NaN 0.0412 0.83 JavaScript 7 7 NaN 0.0254 0.41 PHP 8 9 change 0.0249 0.62 R 9 19 change 0.0237 1.33 SQL 10 8 change 0.0176 -0.19 Go 11 14 change 0.0146 0.24 Swift 12 16 change 0.0138 0.28 Perl 13 20 change 0.0130 0.26 Assembly language 14 12 change 0.0130 -0.08 Ruby 15 15 NaN 0.0124 0.03 MATLAB 16 18 change 0.0110 0.04 Groovy 17 11 change 0.0099 -0.52 Rust 18 33 change 0.0092 0.55 Objective-C 19 10 change 0.0085 -0.99 Dart 20 24 change 0.0077 0.13 Sep 2020 int64 Sep 2019 int64 Change object Ratings float64 Changep float64 dtype: object <class 'pandas.core.frame.DataFrame'> Index: 20 entries, C to Dart Data columns (total 5 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 Sep 2020 20 non-null int64 1 Sep 2019 20 non-null int64 2 Change 14 non-null object 3 Ratings 20 non-null float64 4 Changep 20 non-null float64 dtypes: float64(2), int64(2), object(1) memory usage: 1.6+ KB None
Přímá specifikace metody format namísto lambda výrazu:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import pandas # přečtení zdrojových dat df = pandas.read_csv("tiobe.tsv", sep="\t") # specifikace indexu - má se získat ze sloupce Language df.set_index("Language", inplace=True) # formát hodnot ve sloupci df["Ratings"] = df["Ratings"].transform("Rating is {:4.1f}%".format) # datový rámec zobrazíme print(df) print() # podrobnější informace o datovém rámci print(df.dtypes) print() # více podrobnějších informací o datovém rámci print(df.info()) print()
S výsledkem:
Sep 2020 Sep 2019 Change Ratings Changep Language C 1 2 change Rating is 15.9% 0.74 Java 2 1 change Rating is 13.5% -3.18 Python 3 3 NaN Rating is 10.5% 0.59 C++ 4 4 NaN Rating is 7.1% 1.48 C# 5 5 NaN Rating is 4.6% 1.18 Visual Basic 6 6 NaN Rating is 4.1% 0.83 JavaScript 7 7 NaN Rating is 2.5% 0.41 PHP 8 9 change Rating is 2.5% 0.62 R 9 19 change Rating is 2.4% 1.33 SQL 10 8 change Rating is 1.8% -0.19 Go 11 14 change Rating is 1.5% 0.24 Swift 12 16 change Rating is 1.4% 0.28 Perl 13 20 change Rating is 1.3% 0.26 Assembly language 14 12 change Rating is 1.3% -0.08 Ruby 15 15 NaN Rating is 1.2% 0.03 MATLAB 16 18 change Rating is 1.1% 0.04 Groovy 17 11 change Rating is 1.0% -0.52 Rust 18 33 change Rating is 0.9% 0.55 Objective-C 19 10 change Rating is 0.8% -0.99 Dart 20 24 change Rating is 0.8% 0.13 Sep 2020 int64 Sep 2019 int64 Change object Ratings object Changep float64 dtype: object <class 'pandas.core.frame.DataFrame'> Index: 20 entries, C to Dart Data columns (total 5 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 Sep 2020 20 non-null int64 1 Sep 2019 20 non-null int64 2 Change 14 non-null object 3 Ratings 20 non-null object 4 Changep 20 non-null float64 dtypes: float64(1), int64(2), object(2) memory usage: 1.6+ KB None
3. Agregace dat z datových rámců, resp. z vybraných sloupců
S využitím agregačních funkcí je možné například získat maximální, minimální a průměrnou hodnotu ve vybraném sloupci, vypočítat součet nebo produkt hodnot ve sloupci atd. Zavolat lze funkce pro zpracování prvků polí poskytované knihovnou Numpy tak, jak je to ukázáno v dalším příkladu:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import pandas import numpy as np # přečtení zdrojových dat df = pandas.read_csv("tiobe.tsv", sep="\t") # specifikace indexu - má se získat ze sloupce Language df.set_index("Language", inplace=True) # agregace výsledků results = df["Ratings"].agg([np.min, np.max, np.sum, np.mean]) # tisk vypočtených výsledků print("Results") print(results)
Získané výsledky mají tvar datové řady:
Results amin 0.770 amax 15.950 sum 76.180 mean 3.809 Name: Ratings, dtype: float64
Podobně koncipovaný příklad, ovšem nyní se použijí jména funkcí (uložené v řetězci) a nikoli reference na funkce:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import pandas # přečtení zdrojových dat df = pandas.read_csv("tiobe.tsv", sep="\t") # specifikace indexu - má se získat ze sloupce Language df.set_index("Language", inplace=True) # agregace výsledků results = df["Ratings"].agg(["min", "max", "sum", "mean"]) # tisk vypočtených výsledků print("Results") print(results)
Vypočtené výsledky mají opět tvar datové řady:
Results min 0.770 max 15.950 sum 76.180 mean 3.809 Name: Ratings, dtype: float64
4. Použití metody combine
Další užitečná metoda (popsaná minule), která je určená pro zpracování hodnot v datových řadách a tím pádem i ve sloupcích datových rámců, se jmenuje Series.combine. Tato metoda umožňuje zkombinovat prvky dvou řad (pokud jsou jejich indexy kompatibilní) popř. prvky jedné řady se skalární hodnotou. Ukažme si nyní druhý zmíněný případ, kdy zkombinujeme (postupně) hodnotu prvků z datové řady s hodnotou 2, resp. 10, přičemž kombinace bude provedena funkcemi min a max. V prvním případě tedy nahradíme ty prvky z řady, které jsou větší než 10 hodnotou 10 a následně ty prvky z řady, které jsou menší než 2 právě hodnotou 2:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import pandas # přečtení zdrojových dat df = pandas.read_csv("tiobe.tsv", sep="\t") # specifikace indexu - má se získat ze sloupce Language df.set_index("Language", inplace=True) # omezení hodnot df["Ratings"] = df["Ratings"].combine(10, min) # omezení hodnot df["Ratings"] = df["Ratings"].combine(2, max) # datový rámec zobrazíme print(df) print() # podrobnější informace o datovém rámci print(df.dtypes) print() # více podrobnějších informací o datovém rámci print(df.info()) print()
Výsledek je patrný při pohledu na nový obsah sloupce „Ratings“:
Sep 2020 Sep 2019 Change Ratings Changep Language C 1 2 change 10.00 0.74 Java 2 1 change 10.00 -3.18 Python 3 3 NaN 10.00 0.59 C++ 4 4 NaN 7.11 1.48 C# 5 5 NaN 4.58 1.18 Visual Basic 6 6 NaN 4.12 0.83 JavaScript 7 7 NaN 2.54 0.41 PHP 8 9 change 2.49 0.62 R 9 19 change 2.37 1.33 SQL 10 8 change 2.00 -0.19 Go 11 14 change 2.00 0.24 Swift 12 16 change 2.00 0.28 Perl 13 20 change 2.00 0.26 Assembly language 14 12 change 2.00 -0.08 Ruby 15 15 NaN 2.00 0.03 MATLAB 16 18 change 2.00 0.04 Groovy 17 11 change 2.00 -0.52 Rust 18 33 change 2.00 0.55 Objective-C 19 10 change 2.00 -0.99 Dart 20 24 change 2.00 0.13 Sep 2020 int64 Sep 2019 int64 Change object Ratings float64 Changep float64 dtype: object <class 'pandas.core.frame.DataFrame'> Index: 20 entries, C to Dart Data columns (total 5 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 Sep 2020 20 non-null int64 1 Sep 2019 20 non-null int64 2 Change 14 non-null object 3 Ratings 20 non-null float64 4 Changep 20 non-null float64 dtypes: float64(2), int64(2), object(1) memory usage: 960.0+ bytes None
5. Spojení datových rámců metodou append
Ve druhé části dnešního článku se budeme zabývat velmi častou operací – spojením dvou (nebo i většího množství) datových rámců. V knihovně Pandas je možné rámce spojit jak „po řádcích“, tak i „po sloupcích“ a popř. i vyřešit splynutí hodnot z těch sloupců, které si logicky odpovídají. Nejdříve si ukážeme, jakým způsobem je možné spojit dva datové rámce „po řádcích“. K tomu využijeme datové soubory tiobeC.tsv a tiobeD.tsv.
První z těchto souborů tiobeC.tsv obsahuje horní polovinu tabulky:
Sep 2020 Sep 2019 Change Language Ratings Changep 1 2 change C 15.95 +0.74 2 1 change Java 13.48 -3.18 3 3 Python 10.47 +0.59 4 4 C++ 7.11 +1.48 5 5 C# 4.58 +1.18 6 6 Visual Basic 4.12 +0.83 7 7 JavaScript 2.54 +0.41 8 9 change PHP 2.49 +0.62 9 19 change R 2.37 +1.33 10 8 change SQL 1.76 -0.19
Druhá polovina tabulky je uložena v souboru tiobeD.tsv:
Sep 2020 Sep 2019 Change Language Ratings Changep 11 14 change Go 1.46 +0.24 12 16 change Swift 1.38 +0.28 13 20 change Perl 1.30 +0.26 14 12 change Assembly language 1.30 -0.08 15 15 Ruby 1.24 +0.03 16 18 change MATLAB 1.10 +0.04 17 11 change Groovy 0.99 -0.52 18 33 change Rust 0.92 +0.55 19 10 change Objective-C 0.85 -0.99 20 24 change Dart 0.77 +0.13
6. Příklad použití metody append
Metoda append třídy DataFrame připojí k datovému rámci obsah druhého datového rámce. Použití této metody v praxi je tedy velmi jednoduché, ovšem lze ji použít pouze pro základní operace s datovými rámci (na rozdíl od dále popsaných operací typu „merge“ a „join“):
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import pandas # přečtení zdrojových dat df1 = pandas.read_csv("tiobeC.tsv", sep="\t") df2 = pandas.read_csv("tiobeD.tsv", sep="\t") # specifikace indexu - má se získat ze sloupce Language df1.set_index("Language", inplace=True) df2.set_index("Language", inplace=True) # datové rámce zobrazíme print(df1) print() print(df2) print() # spojení obou datových rámců concatenated = df1.append(df2) # výpis výsledku print(concatenated)
Po spuštění tohoto příkladu se nejdříve zobrazí původní obsah obou datových rámců:
Sep 2020 Sep 2019 Change Ratings Changep Language C 1 2 change 15.95 0.74 Java 2 1 change 13.48 -3.18 Python 3 3 NaN 10.47 0.59 C++ 4 4 NaN 7.11 1.48 C# 5 5 NaN 4.58 1.18 Visual Basic 6 6 NaN 4.12 0.83 JavaScript 7 7 NaN 2.54 0.41 PHP 8 9 change 2.49 0.62 R 9 19 change 2.37 1.33 SQL 10 8 change 1.76 -0.19 Sep 2020 Sep 2019 Change Ratings Changep Language Go 11 14 change 1.46 0.24 Swift 12 16 change 1.38 0.28 Perl 13 20 change 1.30 0.26 Assembly language 14 12 change 1.30 -0.08 Ruby 15 15 NaN 1.24 0.03 MATLAB 16 18 change 1.10 0.04 Groovy 17 11 change 0.99 -0.52 Rust 18 33 change 0.92 0.55 Objective-C 19 10 change 0.85 -0.99 Dart 20 24 change 0.77 0.13
Poté dojde k připojení druhého rámce k rámci prvnímu s tímto výsledkem:
Sep 2020 Sep 2019 Change Ratings Changep Language C 1 2 change 15.95 0.74 Java 2 1 change 13.48 -3.18 Python 3 3 NaN 10.47 0.59 C++ 4 4 NaN 7.11 1.48 C# 5 5 NaN 4.58 1.18 Visual Basic 6 6 NaN 4.12 0.83 JavaScript 7 7 NaN 2.54 0.41 PHP 8 9 change 2.49 0.62 R 9 19 change 2.37 1.33 SQL 10 8 change 1.76 -0.19 Go 11 14 change 1.46 0.24 Swift 12 16 change 1.38 0.28 Perl 13 20 change 1.30 0.26 Assembly language 14 12 change 1.30 -0.08 Ruby 15 15 NaN 1.24 0.03 MATLAB 16 18 change 1.10 0.04 Groovy 17 11 change 0.99 -0.52 Rust 18 33 change 0.92 0.55 Objective-C 19 10 change 0.85 -0.99 Dart 20 24 change 0.77 0.13
7. Spojení rámců po sloupcích nebo po řádcích funkcí concat
Alternativní možnost spojení dvou datových rámců nabízí funkce nazvaná concat, která je aplikovatelná pro libovolný počet instancí třídy DataFrame. Tato funkce dokáže datové rámce spojit buď po sloupcích nebo po řádcích, a to v závislosti na hodnotě parametru axis, který by měl obsahovat hodnotu 0 nebo 1 (popř. nebýt vůbec uveden):
concat(objs:Union[Iterable[~FrameOrSeries], Mapping[collections.abc.Hashable, ~FrameOrSeries]], axis=0, join='outer', ignore_index:bool=False, keys=None, levels=None, names=None, verify_integrity:bool=False, sort:bool=False, copy:bool=True) -> Union[_ForwardRef('DataFrame'), _ForwardRef('Series')] Concatenate pandas objects along a particular axis with optional set logic along the other axes. Can also add a layer of hierarchical indexing on the concatenation axis, which may be useful if the labels are the same (or overlapping) on the passed axis number.
Pro otestování spojení datových rámců „po řádcích“ opět použijeme datové soubory tiobeC.tsv a tiobeD.tsv popsané v rámci páté kapitoly. Ovšem pro spojení rámců „po sloupcích“ budou použity odlišné soubory – tiobeE.tsv a tiobeF.tsv.
Soubor tiobeE.tsv obsahuje pouze tři vybrané sloupce z původního rámce (tabulky):
Language Ratings Changep C 15.95 +0.74 Java 13.48 -3.18 Python 10.47 +0.59 C++ 7.11 +1.48 C# 4.58 +1.18 Visual Basic 4.12 +0.83 JavaScript 2.54 +0.41 PHP 2.49 +0.62 R 2.37 +1.33 SQL 1.76 -0.19 Go 1.46 +0.24 Swift 1.38 +0.28 Perl 1.30 +0.26 Assembly language 1.30 -0.08 Ruby 1.24 +0.03 MATLAB 1.10 +0.04 Groovy 0.99 -0.52 Rust 0.92 +0.55 Objective-C 0.85 -0.99 Dart 0.77 +0.13
Soubor tiobeF.tsv taktéž obsahuje pouze vybrané datové sloupce z původní tabulky:
Sep 2020 Sep 2019 Change Language 1 2 change C 2 1 change Java 3 3 Python 4 4 C++ 5 5 C# 6 6 Visual Basic 7 7 JavaScript 8 9 change PHP 9 19 change R 10 8 change SQL 11 14 change Go 12 16 change Swift 13 20 change Perl 14 12 change Assembly language 15 15 Ruby 16 18 change MATLAB 17 11 change Groovy 18 33 change Rust 19 10 change Objective-C 20 24 change Dart
8. Příklady použití funkce concat
Nejprve si ukažme, jak se provede spojení dvou datových rámců „po sloupcích“ s využitím funkce concat. V tomto případě je nutné funkci předat seznam nebo n-tici, jejímiž prvky jsou reference na spojované datové rámce:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import pandas # přečtení zdrojových dat df1 = pandas.read_csv("tiobeE.tsv", sep="\t") df2 = pandas.read_csv("tiobeF.tsv", sep="\t") # specifikace indexu - má se získat ze sloupce Language df1.set_index("Language", inplace=True) df2.set_index("Language", inplace=True) # datové rámce zobrazíme print(df1) print() print(df2) print() # spojení obou datových rámců concatenated = pandas.concat([df1, df2], axis=1) # výpis výsledku print(concatenated)
Příklad nejdříve vypíše obsah původních datových rámců:
Ratings Changep Language C 15.95 0.74 Java 13.48 -3.18 Python 10.47 0.59 C++ 7.11 1.48 C# 4.58 1.18 Visual Basic 4.12 0.83 JavaScript 2.54 0.41 PHP 2.49 0.62 R 2.37 1.33 SQL 1.76 -0.19 Go 1.46 0.24 Swift 1.38 0.28 Perl 1.30 0.26 Assembly language 1.30 -0.08 Ruby 1.24 0.03 MATLAB 1.10 0.04 Groovy 0.99 -0.52 Rust 0.92 0.55 Objective-C 0.85 -0.99 Dart 0.77 0.13 Sep 2020 Sep 2019 Change Language C 1 2 change Java 2 1 change Python 3 3 NaN C++ 4 4 NaN C# 5 5 NaN Visual Basic 6 6 NaN JavaScript 7 7 NaN PHP 8 9 change R 9 19 change SQL 10 8 change Go 11 14 change Swift 12 16 change Perl 13 20 change Assembly language 14 12 change Ruby 15 15 NaN MATLAB 16 18 change Groovy 17 11 change Rust 18 33 change Objective-C 19 10 change Dart 20 24 change
Poté se vypíše obsah rámce získaného funkcí concat:
Ratings Changep Sep 2020 Sep 2019 Change Language C 15.95 0.74 1 2 change Java 13.48 -3.18 2 1 change Python 10.47 0.59 3 3 NaN C++ 7.11 1.48 4 4 NaN C# 4.58 1.18 5 5 NaN Visual Basic 4.12 0.83 6 6 NaN JavaScript 2.54 0.41 7 7 NaN PHP 2.49 0.62 8 9 change R 2.37 1.33 9 19 change SQL 1.76 -0.19 10 8 change Go 1.46 0.24 11 14 change Swift 1.38 0.28 12 16 change Perl 1.30 0.26 13 20 change Assembly language 1.30 -0.08 14 12 change Ruby 1.24 0.03 15 15 NaN MATLAB 1.10 0.04 16 18 change Groovy 0.99 -0.52 17 11 change Rust 0.92 0.55 18 33 change Objective-C 0.85 -0.99 19 10 change Dart 0.77 0.13 20 24 change
Spojení po řádcích je nepatrně jednodušší, neboť není zapotřebí specifikovat hodnotu nepovinného parametru axis:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import pandas # přečtení zdrojových dat df1 = pandas.read_csv("tiobeC.tsv", sep="\t") df2 = pandas.read_csv("tiobeD.tsv", sep="\t") # specifikace indexu - má se získat ze sloupce Language df1.set_index("Language", inplace=True) df2.set_index("Language", inplace=True) # datové rámce zobrazíme print(df1) print() print(df2) print() # spojení obou datových rámců concatenated = pandas.concat([df1, df2]) # výpis výsledku print(concatenated)
Příklad opět vypíše obsah původních rámců:
Sep 2020 Sep 2019 Change Ratings Changep Language C 1 2 change 15.95 0.74 Java 2 1 change 13.48 -3.18 Python 3 3 NaN 10.47 0.59 C++ 4 4 NaN 7.11 1.48 C# 5 5 NaN 4.58 1.18 Visual Basic 6 6 NaN 4.12 0.83 JavaScript 7 7 NaN 2.54 0.41 PHP 8 9 change 2.49 0.62 R 9 19 change 2.37 1.33 SQL 10 8 change 1.76 -0.19 Sep 2020 Sep 2019 Change Ratings Changep Language Go 11 14 change 1.46 0.24 Swift 12 16 change 1.38 0.28 Perl 13 20 change 1.30 0.26 Assembly language 14 12 change 1.30 -0.08 Ruby 15 15 NaN 1.24 0.03 MATLAB 16 18 change 1.10 0.04 Groovy 17 11 change 0.99 -0.52 Rust 18 33 change 0.92 0.55 Objective-C 19 10 change 0.85 -0.99 Dart 20 24 change 0.77 0.13
A následně rámec získaný spojením přes index:
Sep 2020 Sep 2019 Change Ratings Changep Language C 1 2 change 15.95 0.74 Java 2 1 change 13.48 -3.18 Python 3 3 NaN 10.47 0.59 C++ 4 4 NaN 7.11 1.48 C# 5 5 NaN 4.58 1.18 Visual Basic 6 6 NaN 4.12 0.83 JavaScript 7 7 NaN 2.54 0.41 PHP 8 9 change 2.49 0.62 R 9 19 change 2.37 1.33 SQL 10 8 change 1.76 -0.19 Go 11 14 change 1.46 0.24 Swift 12 16 change 1.38 0.28 Perl 13 20 change 1.30 0.26 Assembly language 14 12 change 1.30 -0.08 Ruby 15 15 NaN 1.24 0.03 MATLAB 16 18 change 1.10 0.04 Groovy 17 11 change 0.99 -0.52 Rust 18 33 change 0.92 0.55 Objective-C 19 10 change 0.85 -0.99 Dart 20 24 change 0.77 0.13
9. Funkce merge
Velmi často se setkáme s nutností spojit dvě tabulky, které sice obsahují shodné sloupce, ovšem ne všechny řádky (resp. záznamy) nalezneme v obou spojovaných tabulkách. Taková operace je zcela běžná v oblasti relačních databází (přesněji řečeno v SQL databázích), kde pro ni existuje i klauzule JOIN. Podle toho, jakým způsobem jsou do výsledku zařazeny ty záznamy, které nejsou nalezeny v obou spojovaných tabulkách, rozlišujeme:
- vnitřní spojení (inner join)
- vnější spojení (outer join)
Vnější spojení je dále děleno na:
- úplně vnější spojení (outer join)
- vnější spojení zleva (left join)
- vnější spojení zprava (right join)
Tato operace je v knihovně Pandas realizována funkcí nazvanou merge; současně se jedná o jednu z funkcí, která dokáže do značné míry konfigurovat přesný postup operace spojení dvou datových rámců:
merge(left, right, how:str='inner', on=None, left_on=None, right_on=None, left_index:bool=False, right_index:bool=False, sort:bool=False, suffixes=('_x', '_y'), copy:bool=True, indicator:bool=False, validate=None) -> 'DataFrame' Merge DataFrame or named Series objects with a database-style join. The join is done on columns or indexes. If joining columns on columns, the DataFrame indexes *will be ignored*. Otherwise if joining indexes on indexes or indexes on a column or columns, the index will be passed on. Parameters ---------- left : DataFrame right : DataFrame or named Series Object to merge with. how : {'left', 'right', 'outer', 'inner'}, default 'inner' Type of merge to be performed. * left: use only keys from left frame, similar to a SQL left outer join; preserve key order. * right: use only keys from right frame, similar to a SQL right outer join; preserve key order. * outer: use union of keys from both frames, similar to a SQL full outer join; sort keys lexicographically. * inner: use intersection of keys from both frames, similar to a SQL inner join; preserve the order of the left keys. on : label or list Column or index level names to join on. These must be found in both DataFrames. If `on` is None and not merging on indexes then this defaults to the intersection of the columns in both DataFrames. left_on : label or list, or array-like Column or index level names to join on in the left DataFrame. Can also be an array or list of arrays of the length of the left DataFrame. These arrays are treated as if they are columns. right_on : label or list, or array-like Column or index level names to join on in the right DataFrame. Can also be an array or list of arrays of the length of the right DataFrame. These arrays are treated as if they are columns. left_index : bool, default False Use the index from the left DataFrame as the join key(s). If it is a MultiIndex, the number of keys in the other DataFrame (either the index right_index : bool, default False Use the index from the right DataFrame as the join key. Same caveats as left_index. sort : bool, default False Sort the join keys lexicographically in the result DataFrame. If False, the order of the join keys depends on the join type (how keyword). suffixes : list-like, default is ("_x", "_y") A length-2 sequence where each element is optionally a string indicating the suffix to add to overlapping column names in `left` and `right` respectively. Pass a value of `None` instead of a string to indicate that the column name from `left` or `right` should be left as-is, with no suffix. At least one of the values must not be None. copy : bool, default True If False, avoid copy if possible. indicator : bool or str, default False If True, adds a column to the output DataFrame called "_merge" with information on the source of each row. The column can be given a different name by providing a string argument. The column will have a Categorical type with the value of "left_only" for observations whose merge key only appears in the left DataFrame, "right_only" for observations whose merge key only appears in the right DataFrame, and "both" if the observation's merge key is found in both DataFrames.
Vzhledem k velké univerzálnosti této funkce si ukážeme její použití na sedmici demonstračních příkladů. Použijeme přitom datové rámce, které vzniknou načtením dvojice tabulek, které mají některé řádky shodné a jiné naopak chybí.
V první tabulce je patnáct řádků původně získaných z Tiobe indexu:
Sep 2019 Change Language Ratings Changep 2 change C 15.95 +0.74 1 change Java 13.48 -3.18 3 Python 10.47 +0.59 4 C++ 7.11 +1.48 5 C# 4.58 +1.18 6 Visual Basic 4.12 +0.83 7 JavaScript 2.54 +0.41 9 change PHP 2.49 +0.62 19 change R 2.37 +1.33 8 change SQL 1.76 -0.19 14 change Go 1.46 +0.24 16 change Swift 1.38 +0.28 20 change Perl 1.30 +0.26 12 change Assembly language 1.30 -0.08 15 Ruby 1.24 +0.03
Ve druhé tabulce je taktéž patnáct řádků původně získaných z Tiobe indexu, ovšem posledních pět řádků je od předchozí tabulky odlišných:
Sep 2020 Change Language Ratings Changep 1 change C 15.95 +0.74 2 change Java 13.48 -3.18 3 Python 10.47 +0.59 4 C++ 7.11 +1.48 5 C# 4.58 +1.18 6 Visual Basic 4.12 +0.83 7 JavaScript 2.54 +0.41 8 change PHP 2.49 +0.62 9 change R 2.37 +1.33 10 change SQL 1.76 -0.19 16 change MATLAB 1.10 +0.04 17 change Groovy 0.99 -0.52 18 change Rust 0.92 +0.55 19 change Objective-C 0.85 -0.99 20 change Dart 0.77 +0.13
10. Inner join (vnitřní spojení) založený na funkci merge
Operace vnitřního spojení neboli inner join dokáže automaticky spojit ty řádky tabulek, které mají totožný obsah. Současně jsou i identifikovány sloupce se shodným názvem a typem (pokud neurčíme jinak). Podívejme se nyní na způsob provedení této operace u datových rámců, které mají implicitní (celočíselné) indexy:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import pandas # přečtení zdrojových dat df1 = pandas.read_csv("tiobeA.tsv", sep="\t") df2 = pandas.read_csv("tiobeB.tsv", sep="\t") # datové rámce zobrazíme print(df1) print() print(df2) print() # spojení obou datových rámců merged = pandas.merge(df1, df2) # výpis výsledku print(merged)
Výsledný datový rámec obsahuje pouze deset řádků obsažených v levém i pravém rámci vstupujícím do operace JOIN:
Sep 2019 Change Language Ratings Changep Sep 2020 0 2 change C 15.95 0.74 1 1 1 change Java 13.48 -3.18 2 2 3 NaN Python 10.47 0.59 3 3 4 NaN C++ 7.11 1.48 4 4 5 NaN C# 4.58 1.18 5 5 6 NaN Visual Basic 4.12 0.83 6 6 7 NaN JavaScript 2.54 0.41 7 7 9 change PHP 2.49 0.62 8 8 19 change R 2.37 1.33 9 9 8 change SQL 1.76 -0.19 10
Spojení dvou datových rámců s explicitně nastavenými indexy získanými ze sloupce „Language“:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import pandas # přečtení zdrojových dat df1 = pandas.read_csv("tiobeA.tsv", sep="\t") df2 = pandas.read_csv("tiobeB.tsv", sep="\t") # specifikace indexu - má se získat ze sloupce Language df1.set_index("Language", inplace=True) df2.set_index("Language", inplace=True) # datové rámce zobrazíme print(df1) print() print(df2) print() # spojení obou datových rámců merged = pandas.merge(df1, df2) # výpis výsledku print(merged)
Povšimněte si, že v tomto případě (ovšem i v příkladu předchozím!) došlo k odstranění původních indexů – ty byly nahrazeny indexem celočíselným založeným na generátoru range:
Sep 2019 Change Ratings Changep Sep 2020 0 2 change 15.95 0.74 1 1 1 change 13.48 -3.18 2 2 3 NaN 10.47 0.59 3 3 4 NaN 7.11 1.48 4 4 5 NaN 4.58 1.18 5 5 6 NaN 4.12 0.83 6 6 7 NaN 2.54 0.41 7 7 9 change 2.49 0.62 8 8 19 change 2.37 1.33 9 9 8 change 1.76 -0.19 10
Použití indexů si ovšem můžeme vynutit nepovinnými parametry left_index a right_index nastavenými na hodnotu True:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import pandas # přečtení zdrojových dat df1 = pandas.read_csv("tiobeA.tsv", sep="\t") df2 = pandas.read_csv("tiobeB.tsv", sep="\t") # specifikace indexu - má se získat ze sloupce Language df1.set_index("Language", inplace=True) df2.set_index("Language", inplace=True) # datové rámce zobrazíme print(df1) print() print(df2) print() # spojení obou datových rámců merged = pandas.merge(df1, df2, left_index=True, right_index=True) # výpis výsledku print(merged)
Nyní bude výsledek velmi odlišný, protože bude obsahovat dvojnásobné množství sloupců – ovšem datové rámce budou korektně spojeny na základě indexů:
Sep 2019 Change_x Ratings_x ... Change_y Ratings_y Changep_y Language C 2 change 15.95 ... change 15.95 0.74 Java 1 change 13.48 ... change 13.48 -3.18 Python 3 NaN 10.47 ... NaN 10.47 0.59 C++ 4 NaN 7.11 ... NaN 7.11 1.48 C# 5 NaN 4.58 ... NaN 4.58 1.18 Visual Basic 6 NaN 4.12 ... NaN 4.12 0.83 JavaScript 7 NaN 2.54 ... NaN 2.54 0.41 PHP 9 change 2.49 ... change 2.49 0.62 R 19 change 2.37 ... change 2.37 1.33 SQL 8 change 1.76 ... change 1.76 -0.19 [10 rows x 8 columns]
Předchozí demonstrační příklad lze rozšířit specifikací těch sloupců, které se skutečně mají spojit. To zařizuje nepovinný parametr on (což opět připomíná SQL konstrukci JOIN xxx ON):
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import pandas # přečtení zdrojových dat df1 = pandas.read_csv("tiobeA.tsv", sep="\t") df2 = pandas.read_csv("tiobeB.tsv", sep="\t") # specifikace indexu - má se získat ze sloupce Language df1.set_index("Language", inplace=True) df2.set_index("Language", inplace=True) # datové rámce zobrazíme print(df1) print() print(df2) print() # spojení obou datových rámců merged = pandas.merge(df1, df2, left_index=True, right_index=True, on=["Change", "Ratings", "Changep"]) # výpis výsledku print(merged)
Výsledek se asi nejvíce blíží ideálu – jsou zachovány indexy a současně nedošlo ke zdvojení všech sloupců v datovém rámci:
Sep 2019 Change Ratings Changep Sep 2020 Language C 2 change 15.95 0.74 1 Java 1 change 13.48 -3.18 2 Python 3 NaN 10.47 0.59 3 C++ 4 NaN 7.11 1.48 4 C# 5 NaN 4.58 1.18 5 Visual Basic 6 NaN 4.12 0.83 6 JavaScript 7 NaN 2.54 0.41 7 PHP 9 change 2.49 0.62 8 R 19 change 2.37 1.33 9 SQL 8 change 1.76 -0.19 10
11. Left join (vnější spojení „zleva“) založený na funkci merge
Ukažme si nyní způsob provedení vnějšího spojení dvou datových rámců zleva. Toto spojení je specifikováno parametrem how nastaveným na hodnotu „left“ (jedná se o řetězec). Ve výsledném datovém rámci budou za všech okolností všechny řádky z levého rámce, a to i ve chvíli, kdy k nim nebyly nalezeny odpovídající řádky v pravém rámci:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import pandas # přečtení zdrojových dat df1 = pandas.read_csv("tiobeA.tsv", sep="\t") df2 = pandas.read_csv("tiobeB.tsv", sep="\t") # specifikace indexu - má se získat ze sloupce Language df1.set_index("Language", inplace=True) df2.set_index("Language", inplace=True) # datové rámce zobrazíme print(df1) print() print(df2) print() # spojení obou datových rámců merged = pandas.merge(df1, df2, left_index=True, right_index=True, how="left", on=["Change", "Ratings", "Changep"]) # výpis výsledku print(merged)
Ve výsledném datovém rámci je po spojení uloženo celkem patnáct řádků, protože byly přidány i všechny řádky z prvního (levého) datového rámce, které ovšem nemají všechny potřebné údaje ve sloupci Sep 2020. Proto je namísto těchto hodnot použita NaN („Not a Number“):
Sep 2019 Change Ratings Changep Sep 2020 Language C 2 change 15.95 0.74 1.0 Java 1 change 13.48 -3.18 2.0 Python 3 NaN 10.47 0.59 3.0 C++ 4 NaN 7.11 1.48 4.0 C# 5 NaN 4.58 1.18 5.0 Visual Basic 6 NaN 4.12 0.83 6.0 JavaScript 7 NaN 2.54 0.41 7.0 PHP 9 change 2.49 0.62 8.0 R 19 change 2.37 1.33 9.0 SQL 8 change 1.76 -0.19 10.0 Go 14 change 1.46 0.24 NaN Swift 16 change 1.38 0.28 NaN Perl 20 change 1.30 0.26 NaN Assembly language 12 change 1.30 -0.08 NaN Ruby 15 NaN 1.24 0.03 NaN
12. Right join (vnější spojení „zprava“) založený na funkci merge
Následuje ukázka vnějšího spojení dvou datových rámců zprava. Toto spojení je specifikováno parametrem how nastaveným na hodnotu „right“ (opět se jedná o řetězec). Ve výsledném datovém rámci budou za všech okolností všechny řádky z pravého rámce, a to i ve chvíli, kdy k nim nebyly nalezeny odpovídající řádky v levém rámci:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import pandas # přečtení zdrojových dat df1 = pandas.read_csv("tiobeA.tsv", sep="\t") df2 = pandas.read_csv("tiobeB.tsv", sep="\t") # specifikace indexu - má se získat ze sloupce Language df1.set_index("Language", inplace=True) df2.set_index("Language", inplace=True) # datové rámce zobrazíme print(df1) print() print(df2) print() # spojení obou datových rámců merged = pandas.merge(df1, df2, left_index=True, right_index=True, how="right", on=["Change", "Ratings", "Changep"]) # výpis výsledku print(merged)
Výsledný datový rámec bude (opět) obsahovat patnáct řádků, ale v tomto případě se jedná o odlišné řádky, než tomu bylo v příkladu předchozím:
Sep 2019 Change Ratings Changep Sep 2020 Language C 2.0 change 15.95 0.74 1 Java 1.0 change 13.48 -3.18 2 Python 3.0 NaN 10.47 0.59 3 C++ 4.0 NaN 7.11 1.48 4 C# 5.0 NaN 4.58 1.18 5 Visual Basic 6.0 NaN 4.12 0.83 6 JavaScript 7.0 NaN 2.54 0.41 7 PHP 9.0 change 2.49 0.62 8 R 19.0 change 2.37 1.33 9 SQL 8.0 change 1.76 -0.19 10 MATLAB NaN NaN NaN NaN 16 Groovy NaN NaN NaN NaN 17 Rust NaN NaN NaN NaN 18 Objective-C NaN NaN NaN NaN 19 Dart NaN NaN NaN NaN 20
13. Outer join (vnější spojení) založený na funkci merge
Zbývá nám popis poslední varianty spojení datových rámců – plný outer join specifikovaný hodnotou „outer“ předanou do pojmenovaného parametru how:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import pandas # přečtení zdrojových dat df1 = pandas.read_csv("tiobeA.tsv", sep="\t") df2 = pandas.read_csv("tiobeB.tsv", sep="\t") # specifikace indexu - má se získat ze sloupce Language df1.set_index("Language", inplace=True) df2.set_index("Language", inplace=True) # datové rámce zobrazíme print(df1) print() print(df2) print() # spojení obou datových rámců merged = pandas.merge(df1, df2, left_index=True, right_index=True, how="outer", on=["Change", "Ratings", "Changep"]) # výpis výsledku print(merged)
Nyní bude výsledný datový rámec obsahovat všech dvacet řádků, ovšem některé sloupce musely být doplněny hodnotami NaN:
Sep 2019 Change Ratings Changep Sep 2020 Language Assembly language 12.0 change 1.30 -0.08 NaN C 2.0 change 15.95 0.74 1.0 C# 5.0 NaN 4.58 1.18 5.0 C++ 4.0 NaN 7.11 1.48 4.0 Dart NaN change 0.77 0.13 20.0 Go 14.0 change 1.46 0.24 NaN Groovy NaN change 0.99 -0.52 17.0 Java 1.0 change 13.48 -3.18 2.0 JavaScript 7.0 NaN 2.54 0.41 7.0 MATLAB NaN change 1.10 0.04 16.0 Objective-C NaN change 0.85 -0.99 19.0 PHP 9.0 change 2.49 0.62 8.0 Perl 20.0 change 1.30 0.26 NaN Python 3.0 NaN 10.47 0.59 3.0 R 19.0 change 2.37 1.33 9.0 Ruby 15.0 NaN 1.24 0.03 NaN Rust NaN change 0.92 0.55 18.0 SQL 8.0 change 1.76 -0.19 10.0 Swift 16.0 change 1.38 0.28 NaN Visual Basic 6.0 NaN 4.12 0.83 6.0
14. Použití metody join pro spojení dvou datových rámců
Namísto funkce merge, která dokáže spojit dva datové rámce, které jsou jí předány formou parametrů, existuje i metoda objektů typu DataFrame, která se jmenuje join:
join(other, on=None, how='left', lsuffix='', rsuffix='', sort=False) -> 'DataFrame' method of pandas.core.frame.DataFrame instance Join columns of another DataFrame. Join columns with `other` DataFrame either on index or on a key column. Efficiently join multiple DataFrame objects by index at once by passing a list.
Po funkční stránce se jedná o zjednodušenou variantu funkce merge, protože opět dojde ke spojení rámců s využitím zvolené varianty, ovšem některé parametry chybí, resp. nedávají v daném kontextu smysl. Implicitně je zjištění korespondujících řádků obou spojovaných datových rámců zajištěno na základě indexů.
15. Inner join (vnitřní spojení) založený na metodě join
Způsob použití metody join si nejdříve ukážeme na příkladu vnitřního spojení, tedy na operaci nazývané inner join. Použijeme, podobně jako v předchozích demonstračních příkladech, datové soubory „tiobeA.tsv“ a „tiobeB.tsv“. Povšimněte si specifikace suffixů, které budou přidány ke jménům sloupců:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import pandas # přečtení zdrojových dat df1 = pandas.read_csv("tiobeA.tsv", sep="\t") df2 = pandas.read_csv("tiobeB.tsv", sep="\t") # specifikace indexu - má se získat ze sloupce Language df1.set_index("Language", inplace=True) df2.set_index("Language", inplace=True) # datové rámce zobrazíme print(df1) print() print(df2) print() # spojení obou datových rámců merged = df1.join(df2, how="inner", lsuffix="_left", rsuffix="_right") # výpis výsledku print(merged)
Výsledkem bude nový datový rámec s deseti řádky a osmi sloupci – sloupce z prvního rámce tedy nejsou spojeny se sloupci z rámce druhého a odlišeny jsou názvem (resp. suffixem v názvu):
Sep 2019 Change_left ... Ratings_right Changep_right Language C 2 change ... 15.95 0.74 Java 1 change ... 13.48 -3.18 Python 3 NaN ... 10.47 0.59 C++ 4 NaN ... 7.11 1.48 C# 5 NaN ... 4.58 1.18 Visual Basic 6 NaN ... 4.12 0.83 JavaScript 7 NaN ... 2.54 0.41 PHP 9 change ... 2.49 0.62 R 19 change ... 2.37 1.33 SQL 8 change ... 1.76 -0.19 [10 rows x 8 columns]
16. Left join (vnější spojení „zleva“) založený na metodě join
Vnější spojení dvou datových rámců zleva neboli left join se opět provede metodou join, ovšem lišit se bude parametr how. Ostatní části demonstračního příkladu zůstanou nezměněné:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import pandas # přečtení zdrojových dat df1 = pandas.read_csv("tiobeA.tsv", sep="\t") df2 = pandas.read_csv("tiobeB.tsv", sep="\t") # specifikace indexu - má se získat ze sloupce Language df1.set_index("Language", inplace=True) df2.set_index("Language", inplace=True) # datové rámce zobrazíme print(df1) print() print(df2) print() # spojení obou datových rámců merged = df1.join(df2, how="left", lsuffix="_left", rsuffix="_right") # výpis výsledku print(merged)
Opět vznikne datový rámec s dvojnásobným množstvím sloupců, ovšem důležitější je, že nyní obsahuje 15 řádků – přidány byly ty řádky z levého datového rámce, které neexistují v rámci pravém:
Sep 2019 Change_left ... Ratings_right Changep_right Language C 2 change ... 15.95 0.74 Java 1 change ... 13.48 -3.18 Python 3 NaN ... 10.47 0.59 C++ 4 NaN ... 7.11 1.48 C# 5 NaN ... 4.58 1.18 Visual Basic 6 NaN ... 4.12 0.83 JavaScript 7 NaN ... 2.54 0.41 PHP 9 change ... 2.49 0.62 R 19 change ... 2.37 1.33 SQL 8 change ... 1.76 -0.19 Go 14 change ... NaN NaN Swift 16 change ... NaN NaN Perl 20 change ... NaN NaN Assembly language 12 change ... NaN NaN Ruby 15 NaN ... NaN NaN [15 rows x 8 columns]
17. Right join (vnější spojení „zprava“) založený na metodě join
Pochopitelně si ukážeme i způsob provedení vnějšího spojení dvou datových rámců zprava, tedy right join. Samotný zdrojový kód demonstračního příkladu se bude lišit pouze odlišnou hodnotou parametru how předaného metodě join:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import pandas # přečtení zdrojových dat df1 = pandas.read_csv("tiobeA.tsv", sep="\t") df2 = pandas.read_csv("tiobeB.tsv", sep="\t") # specifikace indexu - má se získat ze sloupce Language df1.set_index("Language", inplace=True) df2.set_index("Language", inplace=True) # datové rámce zobrazíme print(df1) print() print(df2) print() # spojení obou datových rámců merged = df1.join(df2, how="right", lsuffix="_left", rsuffix="_right") # výpis výsledku print(merged)
Výsledek nyní bude obsahovat patnáct řádků, protože do výsledného rámce byly přidány i ty řádky, které existují pouze v pravém (druhém) datovém rámci. Ty řádky, které existují pouze v levém rámci, nejsou do výsledku zařazeny:
Sep 2019 Change_left ... Ratings_right Changep_right Language C 2.0 change ... 15.95 0.74 Java 1.0 change ... 13.48 -3.18 Python 3.0 NaN ... 10.47 0.59 C++ 4.0 NaN ... 7.11 1.48 C# 5.0 NaN ... 4.58 1.18 Visual Basic 6.0 NaN ... 4.12 0.83 JavaScript 7.0 NaN ... 2.54 0.41 PHP 9.0 change ... 2.49 0.62 R 19.0 change ... 2.37 1.33 SQL 8.0 change ... 1.76 -0.19 MATLAB NaN NaN ... 1.10 0.04 Groovy NaN NaN ... 0.99 -0.52 Rust NaN NaN ... 0.92 0.55 Objective-C NaN NaN ... 0.85 -0.99 Dart NaN NaN ... 0.77 0.13 [15 rows x 8 columns]
18. Outer join (vnější spojení) založený na metodě join
Poslední formou spojení dvou datových rámců je úplné vnější spojení neboli outer join. Opět si ukážeme, jakým způsobem se tato operace provádí:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import pandas # přečtení zdrojových dat df1 = pandas.read_csv("tiobeA.tsv", sep="\t") df2 = pandas.read_csv("tiobeB.tsv", sep="\t") # specifikace indexu - má se získat ze sloupce Language df1.set_index("Language", inplace=True) df2.set_index("Language", inplace=True) # datové rámce zobrazíme print(df1) print() print(df2) print() # spojení obou datových rámců merged = df1.join(df2, how="outer", lsuffix="_left", rsuffix="_right") # výpis výsledku print(merged)
Nyní bude výsledný datový rámec obsahovat všech dvacet řádků, přičemž některé řádky jsou získány z obou datových rámců a další existují pouze v rámci levém nebo naopak v rámci pravém:
Sep 2019 Change_left ... Ratings_right Changep_right Language Assembly language 12.0 change ... NaN NaN C 2.0 change ... 15.95 0.74 C# 5.0 NaN ... 4.58 1.18 C++ 4.0 NaN ... 7.11 1.48 Dart NaN NaN ... 0.77 0.13 Go 14.0 change ... NaN NaN Groovy NaN NaN ... 0.99 -0.52 Java 1.0 change ... 13.48 -3.18 JavaScript 7.0 NaN ... 2.54 0.41 MATLAB NaN NaN ... 1.10 0.04 Objective-C NaN NaN ... 0.85 -0.99 PHP 9.0 change ... 2.49 0.62 Perl 20.0 change ... NaN NaN Python 3.0 NaN ... 10.47 0.59 R 19.0 change ... 2.37 1.33 Ruby 15.0 NaN ... NaN NaN Rust NaN NaN ... 0.92 0.55 SQL 8.0 change ... 1.76 -0.19 Swift 16.0 change ... NaN NaN Visual Basic 6.0 NaN ... 4.12 0.83 [20 rows x 8 columns]
19. Repositář s demonstračními příklady
Zdrojové kódy všech dnes popsaných demonstračních příkladů určených pro Python 3 a nejnovější stabilní verzi knihovny Pandas byly uloženy do Git repositáře dostupného na adrese https://github.com/tisnik/most-popular-python-libs. V případě, že nebudete chtít klonovat celý repositář (ten je ovšem stále velmi malý, dnes má velikost zhruba několik desítek kilobajtů), můžete namísto toho použít odkazy na jednotlivé příklady, které naleznete v následující tabulce:
Některé demonstrační příklady načítají následující soubory s daty:
20. Odkazy na Internetu
- Combining Data in Pandas With merge(), .join(), and concat()
https://realpython.com/pandas-merge-join-and-concat/ - JOIN
https://cs.wikipedia.org/wiki/JOIN - Plotting with matplotlib
https://pandas.pydata.org/pandas-docs/version/0.13/visualization.html - Plot With Pandas: Python Data Visualization for Beginners
https://realpython.com/pandas-plot-python/ - Pandas Dataframe: Plot Examples with Matplotlib and Pyplot
https://queirozf.com/entries/pandas-dataframe-plot-examples-with-matplotlib-pyplot - Opulent-Pandas na PyPi
https://pypi.org/project/opulent-pandas/ - pandas_validator na PyPi
https://pypi.org/project/pandas_validator/ - pandas-validator (dokumentace)
https://pandas-validator.readthedocs.io/en/latest/ - 7 Best Python Libraries for Validating Data
https://www.yeahhub.com/7-best-python-libraries-validating-data/ - Universally unique identifier (Wikipedia)
https://en.wikipedia.org/wiki/Universally_unique_identifier - Nullable integer data type
https://pandas.pydata.org/pandas-docs/stable/user_guide/integer_na.html - pandas.read_csv
https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.read_csv.html - How to define format when using pandas to_datetime?
https://stackoverflow.com/questions/36848514/how-to-define-format-when-using-pandas-to-datetime - Pandas : skip rows while reading csv file to a Dataframe using read_csv() in Python
https://thispointer.com/pandas-skip-rows-while-reading-csv-file-to-a-dataframe-using-read_csv-in-python/ - Skip rows during csv import pandas
https://stackoverflow.com/questions/20637439/skip-rows-during-csv-import-pandas - Denni kurz
https://www.cnb.cz/cs/financni_trhy/devizovy_trh/kurzy_devizoveho_trhu/denni_kurz.txt - UUID objects according to RFC 4122 (knihovna pro Python)
https://docs.python.org/3.5/library/uuid.html#uuid.uuid4 - Object identifier (Wikipedia)
https://en.wikipedia.org/wiki/Object_identifier - Digital object identifier (Wikipedia)
https://en.wikipedia.org/wiki/Digital_object_identifier - voluptuous na (na PyPi)
https://pypi.python.org/pypi/voluptuous - Repositář knihovny voluptuous na GitHubu
https://github.com/alecthomas/voluptuous - pytest-voluptuous 1.0.2 (na PyPi)
https://pypi.org/project/pytest-voluptuous/ - pytest-voluptuous (na GitHubu)
https://github.com/F-Secure/pytest-voluptuous - schemagic 0.9.1 (na PyPi)
https://pypi.python.org/pypi/schemagic/0.9.1 - Schemagic / Schemagic.web (na GitHubu)
https://github.com/Mechrophile/schemagic - schema 0.6.7 (na PyPi)
https://pypi.python.org/pypi/schema - schema (na GitHubu)
https://github.com/keleshev/schema - XML Schema validator and data conversion library for Python
https://github.com/brunato/xmlschema - xmlschema 0.9.7
https://pypi.python.org/pypi/xmlschema/0.9.7 - jsonschema 2.6.0
https://pypi.python.org/pypi/jsonschema - warlock 1.3.0
https://pypi.python.org/pypi/warlock - Python Virtual Environments – A Primer
https://realpython.com/python-virtual-environments-a-primer/ - pip 1.1 documentation: Requirements files
https://pip.readthedocs.io/en/1.1/requirements.html - unittest.mock — mock object library
https://docs.python.org/3.5/library/unittest.mock.html - mock 2.0.0
https://pypi.python.org/pypi/mock - An Introduction to Mocking in Python
https://www.toptal.com/python/an-introduction-to-mocking-in-python - Unit testing (Wikipedia)
https://en.wikipedia.org/wiki/Unit_testing - Unit testing
https://cs.wikipedia.org/wiki/Unit_testing - Test-driven development (Wikipedia)
https://en.wikipedia.org/wiki/Test-driven_development - Pip (dokumentace)
https://pip.pypa.io/en/stable/ - 5 Differences between clojure.spec and Schema
https://lispcast.com/clojure.spec-vs-schema/ - Schema: Clojure(Script) library for declarative data description and validation
https://github.com/plumatic/schema - clojure.spec – Rationale and Overview
https://clojure.org/about/spec