Obsah
1. Dokončení popisu operací se záznamy seskupenými metodou groupby
2. Seskupení záznamů podle hodnot v jednom sloupci
3. Seskupení záznamů podle hodnot ve dvou sloupcích
4. Jednoduchá agregace výsledků – výpočet počtu prvků v jednotlivých skupinách
5. Načtení tabulky s denními kurzy měn
6. Seskupení údajů podle kódu měny a základní agregace těchto údajů
7. Alternativní způsob specifikace agregačních operací
8. Transformace seskupených údajů
9. Tři datové struktury původní verze Pandasu
11. Multiindex vytvořený z n-tic
12. Vytvoření multiindexu z jiného datového typu
13. Pojmenování sloupců v multiindexu
14. Vytvoření multiindexu ze všech kombinací prvků ze dvou sekvencí
15. Pojmenování sloupců v multiindexu
16. Obsah následujícího článku
17. Repositář s demonstračními příklady
18. Datové soubory používané demonstračními příklady
19. Odkazy na předchozí části seriálu o knihovně Pandas
1. Dokončení popisu operací se záznamy seskupenými metodou groupby
V první polovině článku dokončíme popis operací se záznamy, které byly z datového rámce seskupeny metodou groupby, jejíž základní vlastnosti jsme si popsali minule. Budeme zpracovávat dva soubory s tabulkovými daty. První soubor obsahuje změny provedené ve vybraném Git repositáři. Aby byly změny dobře zpracovatelné knihovnou Pandas, byl zvolen vlastní formát s pevně nastavenými oddělovači i s pevně nastaveným (krátkým) formátem datových razítek:
git log --pretty=format:"%h|%ad|%an|%s" --date=short
Výsledek je dostupný na adrese https://github.com/tisnik/most-popular-python-libs/blob/master/pandas/git_log.txt; zde si uvedeme pouze několik řádků na ukázku:
Hash|Date|Author|Commit message 2bf9d6d|2020-08-26|Pavel Tisnovsky|Sheets with one boolean and one real value e9b3b65|2020-08-26|Pavel Tisnovsky|Sheet with just one integer value 9cabc4c|2020-08-25|Pavel Tisnovsky|Date, time, and datetime formats 046cb93|2019-05-09|Stanislav Kozina|Most of lesson1 (still missing conditions, switch and goto statements, _ idiom) 753512f|2019-11-05|Pavel Tisnovsky|Testing in Go workshop: 2nd version 38ff2f0|2019-10-17|Ivan Nečas|lesson6: update title 81642da|2019-10-17|Ivan Nečas|lesson6: http and friends 53fc80b|2019-10-09|Jakub Čajka|lesson5: Initilal version of lesson5 with examples 093c0cc|2019-10-04|Stanislav Kozina|Add sample TCP server code 3ea65d8|2019-09-23|Pavel Tisnovsky|Fixed typo 8bab20a|2019-05-02|Jakub Čajka|Initial commit
Druhý datový soubor byl získán agregací dat získaných z adresy https://www.cnb.cz/cs/financni_trhy/devizovy_trh/kurzy_devizoveho_trhu/denni_kurz.txt. Soubor obsahuje kurzy pro dva dny, ovšem je plně rozšiřitelný tak, že může obsahovat kurzy pro libovolný den:
země|měna|množství|kód|datum|kurz Austrálie|dolar|1|AUD|2021-01-05|16,463 Brazílie|real|1|BRL|2021-01-05|4,002 Bulharsko|lev|1|BGN|2021-01-05|13,408 Čína|žen-min-pi|1|CNY|2021-01-05|3,306 Dánsko|koruna|1|DKK|2021-01-05|3,525 EMU|euro|1|EUR|2021-01-05|26,225 Filipíny|peso|100|PHP|2021-01-05|44,429 Hongkong|dolar|1|HKD|2021-01-05|2,756 Chorvatsko|kuna|1|HRK|2021-01-05|3,469 Indie|rupie|100|INR|2021-01-05|29,181 Indonesie|rupie|1000|IDR|2021-01-05|1,536 Island|koruna|100|ISK|2021-01-05|16,800 Izrael|nový šekel|1|ILS|2021-01-05|6,677 Japonsko|jen|100|JPY|2021-01-05|20,770 Jižní Afrika|rand|1|ZAR|2021-01-05|1,424 Kanada|dolar|1|CAD|2021-01-05|16,759 Korejská republika|won|100|KRW|2021-01-05|1,963 Maďarsko|forint|100|HUF|2021-01-05|7,279 Malajsie|ringgit|1|MYR|2021-01-05|5,320 Mexiko|peso|1|MXN|2021-01-05|1,066 MMF|ZPČ|1|XDR|2021-01-05|30,959 Norsko|koruna|1|NOK|2021-01-05|2,505 Nový Zéland|dolar|1|NZD|2021-01-05|15,392 Polsko|zlotý|1|PLN|2021-01-05|5,767 Rumunsko|leu|1|RON|2021-01-05|5,382 Rusko|rubl|100|RUB|2021-01-05|28,611 Singapur|dolar|1|SGD|2021-01-05|16,206 Švédsko|koruna|1|SEK|2021-01-05|2,608 Švýcarsko|frank|1|CHF|2021-01-05|24,273 Thajsko|baht|100|THB|2021-01-05|71,292 Turecko|lira|1|TRY|2021-01-05|2,891 USA|dolar|1|USD|2021-01-05|21,369 Velká Británie|libra|1|GBP|2021-01-05|29,030 Austrálie|dolar|1|AUD|2020-11-20|16,231 Brazílie|real|1|BRL|2020-11-20|4,160 Bulharsko|lev|1|BGN|2020-11-20|13,467 Čína|žen-min-pi|1|CNY|2020-11-20|3,381 Dánsko|koruna|1|DKK|2020-11-20|3,536 EMU|euro|1|EUR|2020-11-20|26,340 Filipíny|peso|100|PHP|2020-11-20|46,038 Hongkong|dolar|1|HKD|2020-11-20|2,864 Chorvatsko|kuna|1|HRK|2020-11-20|3,481 Indie|rupie|100|INR|2020-11-20|29,950 Indonesie|rupie|1000|IDR|2020-11-20|1,567 Island|koruna|100|ISK|2020-11-20|16,330 Izrael|nový šekel|1|ILS|2020-11-20|6,649 Japonsko|jen|100|JPY|2020-11-20|21,383 Jižní Afrika|rand|1|ZAR|2020-11-20|1,445 Kanada|dolar|1|CAD|2020-11-20|17,011 Korejská republika|won|100|KRW|2020-11-20|1,990 Maďarsko|forint|100|HUF|2020-11-20|7,328 Malajsie|ringgit|1|MYR|2020-11-20|5,425 Mexiko|peso|1|MXN|2020-11-20|1,104 MMF|ZPČ|1|XDR|2020-11-20|31,598 Norsko|koruna|1|NOK|2020-11-20|2,471 Nový Zéland|dolar|1|NZD|2020-11-20|15,416 Polsko|zlotý|1|PLN|2020-11-20|5,900 Rumunsko|leu|1|RON|2020-11-20|5,405 Rusko|rubl|100|RUB|2020-11-20|29,180 Singapur|dolar|1|SGD|2020-11-20|16,530 Švédsko|koruna|1|SEK|2020-11-20|2,577 Švýcarsko|frank|1|CHF|2020-11-20|24,363 Thajsko|baht|100|THB|2020-11-20|73,313 Turecko|lira|1|TRY|2020-11-20|2,911 USA|dolar|1|USD|2020-11-20|22,201 Velká Británie|libra|1|GBP|2020-11-20|29,464
2. Seskupení záznamů podle hodnot v jednom sloupci
Nejprve si připomeneme, jakým způsobem se provádí seskupení záznamů podle hodnot v jednom sloupci. V dnešním prvním demonstračním příkladu seskupíme záznamy podle data, tj. v jedné skupině budou všechny změny provedené ve stejném dni:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import pandas import pprint # přečtení zdrojových dat s jejich konverzí do datového rámce df = pandas.read_csv("git_log.txt", sep="|") # datový rámec zobrazíme print(df) print() # rozdělení do skupin podle data (jednoho sloupce) gb = df.groupby(["Date"]) # zobrazení skupin vytvořených v rámci předchozího kroku print("Group by date") pprint.pprint(gb.groups) # zobrazení počtu skupin print() print("Number of groups:", len(gb))
Po spuštění příkladu se nejprve vypíše původní datový rámec:
Hash ... Commit message 0 2bf9d6d ... Sheets with one boolean and one real value 1 e9b3b65 ... Sheet with just one integer value 2 9cabc4c ... Date, time, and datetime formats 3 d829b47 ... Styles shared between cells 4 bf7a438 ... Style usages .. ... ... ... 242 a5a7be1 ... Example 2: defer with function 243 fcebed0 ... Example 1: defer statement 244 d33146d ... Outlines for lesson #2 and #3 245 0eb454b ... Fixed typo 246 8bab20a ... Initial commit [247 rows x 4 columns]
Následně jsou vypsány jednotlivé skupiny – jednoznačným klíčem je vždy datum (povšimněte si, že obsahem je datová série s indexy!):
Group by date {'2019-05-02': Int64Index([245, 246], dtype='int64'), '2019-05-06': Int64Index([244], dtype='int64'), '2019-05-09': Int64Index([231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243], dtype='int64'), '2019-05-10': Int64Index([226, 227, 228, 229, 230], dtype='int64'), '2019-05-14': Int64Index([218, 219, 220, 221, 222, 223, 224, 225], dtype='int64'), '2019-05-15': Int64Index([216, 217], dtype='int64'), '2019-05-16': Int64Index([212, 213, 214, 215], dtype='int64'), '2019-05-17': Int64Index([211], dtype='int64'), '2019-05-20': Int64Index([210], dtype='int64'), '2019-05-21': Int64Index([208, 209], dtype='int64'), '2019-06-05': Int64Index([207], dtype='int64'), '2019-08-12': Int64Index([206], dtype='int64'), '2019-09-05': Int64Index([204, 205], dtype='int64'), '2019-09-06': Int64Index([203], dtype='int64'), '2019-09-13': Int64Index([202], dtype='int64'), '2019-09-23': Int64Index([201], dtype='int64'), '2019-10-04': Int64Index([200], dtype='int64'), '2019-10-09': Int64Index([199], dtype='int64'), '2019-10-17': Int64Index([197, 198], dtype='int64'), '2019-11-05': Int64Index([194, 195, 196], dtype='int64'), '2019-11-06': Int64Index([189, 190, 191, 192, 193], dtype='int64'), '2019-11-07': Int64Index([188], dtype='int64'), '2019-11-08': Int64Index([186, 187], dtype='int64'), '2019-11-11': Int64Index([177, 178, 179, 180, 181, 182, 183, 184, 185], dtype='int64'), '2019-11-12': Int64Index([166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176], dtype='int64'), '2019-11-15': Int64Index([152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165], dtype='int64'), '2019-11-18': Int64Index([141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151], dtype='int64'), '2019-11-19': Int64Index([137, 138, 139, 140], dtype='int64'), '2019-11-20': Int64Index([131, 132, 133, 134, 135, 136], dtype='int64'), '2019-11-21': Int64Index([125, 126, 127, 128, 129, 130], dtype='int64'), '2019-11-22': Int64Index([124], dtype='int64'), '2019-11-25': Int64Index([120, 121, 122, 123], dtype='int64'), '2019-11-26': Int64Index([108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119], dtype='int64'), '2019-11-27': Int64Index([97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107], dtype='int64'), '2019-11-28': Int64Index([87, 88, 89, 90, 91, 92, 93, 94, 95, 96], dtype='int64'), '2019-12-02': Int64Index([81, 82, 83, 84, 85, 86], dtype='int64'), '2019-12-03': Int64Index([74, 75, 76, 77, 78, 79, 80], dtype='int64'), '2019-12-04': Int64Index([72, 73], dtype='int64'), '2019-12-05': Int64Index([61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71], dtype='int64'), '2019-12-06': Int64Index([54, 55, 56, 57, 58, 59, 60], dtype='int64'), '2019-12-09': Int64Index([48, 49, 50, 51, 52, 53], dtype='int64'), '2019-12-10': Int64Index([45, 46, 47], dtype='int64'), '2019-12-11': Int64Index([43, 44], dtype='int64'), '2020-05-13': Int64Index([31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42], dtype='int64'), '2020-05-14': Int64Index([29, 30], dtype='int64'), '2020-05-25': Int64Index([24, 25, 26, 27, 28], dtype='int64'), '2020-07-13': Int64Index([19, 20, 21, 22, 23], dtype='int64'), '2020-07-14': Int64Index([14, 15, 16, 17, 18], dtype='int64'), '2020-07-16': Int64Index([13], dtype='int64'), '2020-07-17': Int64Index([8, 9, 10, 11, 12], dtype='int64'), '2020-08-25': Int64Index([2, 3, 4, 5, 6, 7], dtype='int64'), '2020-08-26': Int64Index([0, 1], dtype='int64')}
A na závěr se vypíše celkový počet skupin:
Number of groups: 52
Záznamy je pochopitelně možné seskupit i podle autora:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import pandas import pprint # přečtení zdrojových dat s jejich konverzí do datového rámce df = pandas.read_csv("git_log.txt", sep="|") # datový rámec zobrazíme print(df) print() # rozdělení do skupin podle autora (jednoho sloupce) gb = df.groupby(["Author"]) # zobrazení skupin vytvořených v rámci předchozího kroku print("Group by author") pprint.pprint(gb.groups) # zobrazení počtu skupin print() print("Number of groups:", len(gb))
Nyní bude výsledek odlišný – skupin bude méně, ale budou obsahovat více položek (resp. přesněji řečeno indexů):
Hash ... Commit message 0 2bf9d6d ... Sheets with one boolean and one real value 1 e9b3b65 ... Sheet with just one integer value 2 9cabc4c ... Date, time, and datetime formats 3 d829b47 ... Styles shared between cells 4 bf7a438 ... Style usages .. ... ... ... 242 a5a7be1 ... Example 2: defer with function 243 fcebed0 ... Example 1: defer statement 244 d33146d ... Outlines for lesson #2 and #3 245 0eb454b ... Fixed typo 246 8bab20a ... Initial commit [247 rows x 4 columns] Group by author {'Dmitry Volodin': Int64Index([26, 27, 28, 30, 32, 33, 35, 38, 41], dtype='int64'), 'Ivan Nečas': Int64Index([197, 198], dtype='int64'), 'Jakub Čajka': Int64Index([199, 246], dtype='int64'), 'Jindrich Novy': Int64Index([205], dtype='int64'), 'José Luis Segura Lucas': Int64Index([44], dtype='int64'), 'Martin Styk': Int64Index([202], dtype='int64'), 'Pavel Tisnovsky': Int64Index([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ... 236, 237, 238, 239, 240, 241, 242, 243, 244, 245], dtype='int64', length=211), 'Pavel Tišnovský': Int64Index([24, 25, 29, 31, 34, 36, 37, 204], dtype='int64'), 'Stanislav Kozina': Int64Index([141, 189, 190, 191, 200, 203, 206, 207, 210, 211, 218, 231], dtype='int64')} Number of groups: 9
3. Seskupení záznamů podle hodnot ve dvou sloupcích
Ve skutečnosti jsou možnosti nabízené metodou groupby mnohem větší, protože seskupení lze provést na základě hodnot z většího množství sloupců, což je ukázáno v dnešním třetím demonstračním příkladu, který seskupí záznamy na základě autora a data, kdy byla změna provedena:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import pandas import pprint # přečtení zdrojových dat s jejich konverzí do datového rámce df = pandas.read_csv("git_log.txt", sep="|") # datový rámec zobrazíme print(df) print() # rozdělení do skupin podle data i autora (dvou sloupců) gb = df.groupby(["Date", "Author"]) # zobrazení skupin vytvořených v rámci předchozího kroku print("Group by date and author") pprint.pprint(gb.groups) # zobrazení počtu skupin print() print("Number of groups:", len(gb))
Výsledkem je struktura, kde klíčem je n-tice (dvojice) s datem a autorem (hodnotou pak datová série s indexy):
Hash ... Commit message 0 2bf9d6d ... Sheets with one boolean and one real value 1 e9b3b65 ... Sheet with just one integer value 2 9cabc4c ... Date, time, and datetime formats 3 d829b47 ... Styles shared between cells 4 bf7a438 ... Style usages .. ... ... ... 242 a5a7be1 ... Example 2: defer with function 243 fcebed0 ... Example 1: defer statement 244 d33146d ... Outlines for lesson #2 and #3 245 0eb454b ... Fixed typo 246 8bab20a ... Initial commit [247 rows x 4 columns] Group by date and author {('2019-05-02', 'Jakub Čajka'): Int64Index([246], dtype='int64'), ('2019-05-02', 'Pavel Tisnovsky'): Int64Index([245], dtype='int64'), ('2019-05-06', 'Pavel Tisnovsky'): Int64Index([244], dtype='int64'), ('2019-05-09', 'Pavel Tisnovsky'): Int64Index([232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243], dtype='int64'), ('2019-05-09', 'Stanislav Kozina'): Int64Index([231], dtype='int64'), ('2019-05-10', 'Pavel Tisnovsky'): Int64Index([226, 227, 228, 229, 230], dtype='int64'), ('2019-05-14', 'Pavel Tisnovsky'): Int64Index([219, 220, 221, 222, 223, 224, 225], dtype='int64'), ('2019-05-14', 'Stanislav Kozina'): Int64Index([218], dtype='int64'), ('2019-05-15', 'Pavel Tisnovsky'): Int64Index([216, 217], dtype='int64'), ('2019-05-16', 'Pavel Tisnovsky'): Int64Index([212, 213, 214, 215], dtype='int64'), ('2019-05-17', 'Stanislav Kozina'): Int64Index([211], dtype='int64'), ('2019-05-20', 'Stanislav Kozina'): Int64Index([210], dtype='int64'), ('2019-05-21', 'Pavel Tisnovsky'): Int64Index([208, 209], dtype='int64'), ('2019-06-05', 'Stanislav Kozina'): Int64Index([207], dtype='int64'), ('2019-08-12', 'Stanislav Kozina'): Int64Index([206], dtype='int64'), ('2019-09-05', 'Jindrich Novy'): Int64Index([205], dtype='int64'), ('2019-09-05', 'Pavel Tišnovský'): Int64Index([204], dtype='int64'), ('2019-09-06', 'Stanislav Kozina'): Int64Index([203], dtype='int64'), ('2019-09-13', 'Martin Styk'): Int64Index([202], dtype='int64'), ('2019-09-23', 'Pavel Tisnovsky'): Int64Index([201], dtype='int64'), ('2019-10-04', 'Stanislav Kozina'): Int64Index([200], dtype='int64'), ('2019-10-09', 'Jakub Čajka'): Int64Index([199], dtype='int64'), ('2019-10-17', 'Ivan Nečas'): Int64Index([197, 198], dtype='int64'), ('2019-11-05', 'Pavel Tisnovsky'): Int64Index([194, 195, 196], dtype='int64'), ('2019-11-06', 'Pavel Tisnovsky'): Int64Index([192, 193], dtype='int64'), ('2019-11-06', 'Stanislav Kozina'): Int64Index([189, 190, 191], dtype='int64'), ('2019-11-07', 'Pavel Tisnovsky'): Int64Index([188], dtype='int64'), ('2019-11-08', 'Pavel Tisnovsky'): Int64Index([186, 187], dtype='int64'), ('2019-11-11', 'Pavel Tisnovsky'): Int64Index([177, 178, 179, 180, 181, 182, 183, 184, 185], dtype='int64'), ('2019-11-12', 'Pavel Tisnovsky'): Int64Index([166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176], dtype='int64'), ('2019-11-15', 'Pavel Tisnovsky'): Int64Index([152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165], dtype='int64'), ('2019-11-18', 'Pavel Tisnovsky'): Int64Index([142, 143, 144, 145, 146, 147, 148, 149, 150, 151], dtype='int64'), ('2019-11-18', 'Stanislav Kozina'): Int64Index([141], dtype='int64'), ('2019-11-19', 'Pavel Tisnovsky'): Int64Index([137, 138, 139, 140], dtype='int64'), ('2019-11-20', 'Pavel Tisnovsky'): Int64Index([131, 132, 133, 134, 135, 136], dtype='int64'), ('2019-11-21', 'Pavel Tisnovsky'): Int64Index([125, 126, 127, 128, 129, 130], dtype='int64'), ('2019-11-22', 'Pavel Tisnovsky'): Int64Index([124], dtype='int64'), ('2019-11-25', 'Pavel Tisnovsky'): Int64Index([120, 121, 122, 123], dtype='int64'), ('2019-11-26', 'Pavel Tisnovsky'): Int64Index([108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119], dtype='int64'), ('2019-11-27', 'Pavel Tisnovsky'): Int64Index([97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107], dtype='int64'), ('2019-11-28', 'Pavel Tisnovsky'): Int64Index([87, 88, 89, 90, 91, 92, 93, 94, 95, 96], dtype='int64'), ('2019-12-02', 'Pavel Tisnovsky'): Int64Index([81, 82, 83, 84, 85, 86], dtype='int64'), ('2019-12-03', 'Pavel Tisnovsky'): Int64Index([74, 75, 76, 77, 78, 79, 80], dtype='int64'), ('2019-12-04', 'Pavel Tisnovsky'): Int64Index([72, 73], dtype='int64'), ('2019-12-05', 'Pavel Tisnovsky'): Int64Index([61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71], dtype='int64'), ('2019-12-06', 'Pavel Tisnovsky'): Int64Index([54, 55, 56, 57, 58, 59, 60], dtype='int64'), ('2019-12-09', 'Pavel Tisnovsky'): Int64Index([48, 49, 50, 51, 52, 53], dtype='int64'), ('2019-12-10', 'Pavel Tisnovsky'): Int64Index([45, 46, 47], dtype='int64'), ('2019-12-11', 'José Luis Segura Lucas'): Int64Index([44], dtype='int64'), ('2019-12-11', 'Pavel Tisnovsky'): Int64Index([43], dtype='int64'), ('2020-05-13', 'Dmitry Volodin'): Int64Index([32, 33, 35, 38, 41], dtype='int64'), ('2020-05-13', 'Pavel Tisnovsky'): Int64Index([39, 40, 42], dtype='int64'), ('2020-05-13', 'Pavel Tišnovský'): Int64Index([31, 34, 36, 37], dtype='int64'), ('2020-05-14', 'Dmitry Volodin'): Int64Index([30], dtype='int64'), ('2020-05-14', 'Pavel Tišnovský'): Int64Index([29], dtype='int64'), ('2020-05-25', 'Dmitry Volodin'): Int64Index([26, 27, 28], dtype='int64'), ('2020-05-25', 'Pavel Tišnovský'): Int64Index([24, 25], dtype='int64'), ('2020-07-13', 'Pavel Tisnovsky'): Int64Index([19, 20, 21, 22, 23], dtype='int64'), ('2020-07-14', 'Pavel Tisnovsky'): Int64Index([14, 15, 16, 17, 18], dtype='int64'), ('2020-07-16', 'Pavel Tisnovsky'): Int64Index([13], dtype='int64'), ('2020-07-17', 'Pavel Tisnovsky'): Int64Index([8, 9, 10, 11, 12], dtype='int64'), ('2020-08-25', 'Pavel Tisnovsky'): Int64Index([2, 3, 4, 5, 6, 7], dtype='int64'), ('2020-08-26', 'Pavel Tisnovsky'): Int64Index([0, 1], dtype='int64')} Number of groups: 63
4. Jednoduchá agregace výsledků – výpočet počtu prvků v jednotlivých skupinách
Nad seskupenými záznamy lze provádět různé agregační operace, a to metodou nazvanou agg. Nejjednodušší je zjištění počtu prvků ve skupinách, což je v některých případech použitelné jako dobrá statistická veličina:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import pandas import numpy as np # přečtení zdrojových dat s jejich konverzí do datového rámce df = pandas.read_csv("git_log.txt", sep="|") # datový rámec zobrazíme print(df) print() # rozdělení do skupin podle data, autora i podle obou sloupců gb1 = df.groupby(["Date"]) gb2 = df.groupby(["Author"]) gb3 = df.groupby(["Date", "Author"]) # zobrazení velikosti záznamů rozdělených do skupin print(gb1.agg(np.size)) print(gb2.agg(np.size)) print(gb3.agg(np.size))
Příklad opět nejdříve zobrazí část načteného datového rámce:
Hash ... Commit message 0 2bf9d6d ... Sheets with one boolean and one real value 1 e9b3b65 ... Sheet with just one integer value 2 9cabc4c ... Date, time, and datetime formats 3 d829b47 ... Styles shared between cells 4 bf7a438 ... Style usages .. ... ... ... 242 a5a7be1 ... Example 2: defer with function 243 fcebed0 ... Example 1: defer statement 244 d33146d ... Outlines for lesson #2 and #3 245 0eb454b ... Fixed typo 246 8bab20a ... Initial commit [247 rows x 4 columns]
Následně zobrazí počet prvků ve skupinách vytvořených na základě data:
Hash Author Commit message Date 2019-05-02 2 2 2 2019-05-06 1 1 1 2019-05-09 13 13 13 2019-05-10 5 5 5 2019-05-14 8 8 8 2019-05-15 2 2 2 2019-05-16 4 4 4 2019-05-17 1 1 1 2019-05-20 1 1 1 2019-05-21 2 2 2 2019-06-05 1 1 1 2019-08-12 1 1 1 2019-09-05 2 2 2 2019-09-06 1 1 1 2019-09-13 1 1 1 2019-09-23 1 1 1 2019-10-04 1 1 1 2019-10-09 1 1 1 2019-10-17 2 2 2 2019-11-05 3 3 3 2019-11-06 5 5 5 2019-11-07 1 1 1 2019-11-08 2 2 2 2019-11-11 9 9 9 2019-11-12 11 11 11 2019-11-15 14 14 14 2019-11-18 11 11 11 2019-11-19 4 4 4 2019-11-20 6 6 6 2019-11-21 6 6 6 2019-11-22 1 1 1 2019-11-25 4 4 4 2019-11-26 12 12 12 2019-11-27 11 11 11 2019-11-28 10 10 10 2019-12-02 6 6 6 2019-12-03 7 7 7 2019-12-04 2 2 2 2019-12-05 11 11 11 2019-12-06 7 7 7 2019-12-09 6 6 6 2019-12-10 3 3 3 2019-12-11 2 2 2 2020-05-13 12 12 12 2020-05-14 2 2 2 2020-05-25 5 5 5 2020-07-13 5 5 5 2020-07-14 5 5 5 2020-07-16 1 1 1 2020-07-17 5 5 5 2020-08-25 6 6 6 2020-08-26 2 2 2
Poté zobrazí počet záznamů ve skupinách vytvořených podle autora:
Hash Date Commit message Author Dmitry Volodin 9 9 9 Ivan Nečas 2 2 2 Jakub Čajka 2 2 2 Jindrich Novy 1 1 1 José Luis Segura Lucas 1 1 1 Martin Styk 1 1 1 Pavel Tisnovsky 211 211 211 Pavel Tišnovský 8 8 8 Stanislav Kozina 12 12 12
A konečně se zobrazí informace o skupinách rozdělených podle data a následně podle autora:
Hash Commit message Date Author 2019-05-02 Jakub Čajka 1 1 Pavel Tisnovsky 1 1 2019-05-06 Pavel Tisnovsky 1 1 2019-05-09 Pavel Tisnovsky 12 12 Stanislav Kozina 1 1 ... ... ... 2020-07-14 Pavel Tisnovsky 5 5 2020-07-16 Pavel Tisnovsky 1 1 2020-07-17 Pavel Tisnovsky 5 5 2020-08-25 Pavel Tisnovsky 6 6 2020-08-26 Pavel Tisnovsky 2 2 [63 rows x 2 columns]
5. Načtení tabulky s denními kurzy měn
Některé složitější operace prováděné nad seskupenými údaji budou ukázány nad tabulkou s denními kurzy měn – viz též úvodní kapitolu, v níž je obsah této tabulky zobrazen. Samotná tabulka je uložena takovým způsobem, že jednotlivé hodnoty v záznamech jsou odděleny znakem „|“ a tabulka obsahuje i nadpisy sloupců. Navíc je nutné upravit vlastní kurz – desetinná čárka se nahradí za desetinnou tečku a výsledek se převede na číselnou hodnotu, a to metodou to_numeric zkombinovanou s metodou str.replace:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import pandas # přečtení zdrojových dat s jejich konverzí do datového rámce df = pandas.read_csv("denni_kurz2.txt", sep="|", skiprows=0) # převod číselných hodnot df["kurz"] = pandas.to_numeric(df["kurz"].str.replace(',','.'), errors='coerce') # datový rámec zobrazíme print(df) print() print(df.describe())
Po spuštění tohoto příkladu se nejdříve zobrazí obsah datového rámce:
země měna množství kód datum kurz 0 Austrálie dolar 1 AUD 2021-01-05 16.463 1 Brazílie real 1 BRL 2021-01-05 4.002 2 Bulharsko lev 1 BGN 2021-01-05 13.408 3 Čína žen-min-pi 1 CNY 2021-01-05 3.306 4 Dánsko koruna 1 DKK 2021-01-05 3.525 .. ... ... ... ... ... ... 61 Švýcarsko frank 1 CHF 2020-11-20 24.363 62 Thajsko baht 100 THB 2020-11-20 73.313 63 Turecko lira 1 TRY 2020-11-20 2.911 64 USA dolar 1 USD 2020-11-20 22.201 65 Velká Británie libra 1 GBP 2020-11-20 29.464 [66 rows x 6 columns]
Následně se vytisknou základní statistické údaje o obsahu datového rámce:
množství kurz count 66.000000 66.000000 mean 55.272727 14.752303 std 173.578317 15.323648 min 1.000000 1.066000 25% 1.000000 3.324750 50% 1.000000 7.303500 75% 100.000000 21.996500 max 1000.000000 73.313000
6. Seskupení údajů podle kódu měny a základní agregace těchto údajů
Z načtené tabulky je již možné získat užitečné informace, například o kurzech – můžeme si zobrazit největší, nejmenší a průměrný kurz pro každou měnu (s přidáním směrodatné odchylky). Příklad výsledku pro jednu měnu:
amin amax mean std AUD 16.231 16.463 16.3470 0.164049
Nejprve záznamy rozdělíme do skupin na základě kódu měny:
# rozdělení do skupin podle data gb = df.groupby(["kód"])
Druhým krokem je agregace údajů v rámci každé skupiny, tedy vlastní výpočet:
# výpočet a zobrazení základní statistiky print(gb['kurz'].agg([np.min, np.max, np.mean, np.std]))
Úplný zdrojový kód tohoto demonstračního příkladu vypadá následovně:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import pandas import numpy as np # přečtení zdrojových dat s jejich konverzí do datového rámce df = pandas.read_csv("denni_kurz2.txt", sep="|", skiprows=0) # převod číselných hodnot df["kurz"] = pandas.to_numeric(df["kurz"].str.replace(',','.'), errors='coerce') # datový rámec zobrazíme print(df) print() # rozdělení do skupin podle data gb = df.groupby(["kód"]) # výpočet a zobrazení základní statistiky print(gb['kurz'].agg([np.min, np.max, np.mean, np.std]))
Po spuštění příkladu se opět nejdříve zobrazí datový rámec:
země měna množství kód datum kurz 0 Austrálie dolar 1 AUD 2021-01-05 16.463 1 Brazílie real 1 BRL 2021-01-05 4.002 2 Bulharsko lev 1 BGN 2021-01-05 13.408 3 Čína žen-min-pi 1 CNY 2021-01-05 3.306 4 Dánsko koruna 1 DKK 2021-01-05 3.525 .. ... ... ... ... ... ... 61 Švýcarsko frank 1 CHF 2020-11-20 24.363 62 Thajsko baht 100 THB 2020-11-20 73.313 63 Turecko lira 1 TRY 2020-11-20 2.911 64 USA dolar 1 USD 2020-11-20 22.201 65 Velká Británie libra 1 GBP 2020-11-20 29.464 [66 rows x 6 columns]
A posléze se již zobrazí všechny požadované informace pro každou měnu:
amin amax mean std kód AUD 16.231 16.463 16.3470 0.164049 BGN 13.408 13.467 13.4375 0.041719 BRL 4.002 4.160 4.0810 0.111723 CAD 16.759 17.011 16.8850 0.178191 CHF 24.273 24.363 24.3180 0.063640 CNY 3.306 3.381 3.3435 0.053033 DKK 3.525 3.536 3.5305 0.007778 EUR 26.225 26.340 26.2825 0.081317 GBP 29.030 29.464 29.2470 0.306884 HKD 2.756 2.864 2.8100 0.076368 HRK 3.469 3.481 3.4750 0.008485 HUF 7.279 7.328 7.3035 0.034648 IDR 1.536 1.567 1.5515 0.021920 ILS 6.649 6.677 6.6630 0.019799 INR 29.181 29.950 29.5655 0.543765 ISK 16.330 16.800 16.5650 0.332340 JPY 20.770 21.383 21.0765 0.433456 KRW 1.963 1.990 1.9765 0.019092 MXN 1.066 1.104 1.0850 0.026870 MYR 5.320 5.425 5.3725 0.074246 NOK 2.471 2.505 2.4880 0.024042 NZD 15.392 15.416 15.4040 0.016971 PHP 44.429 46.038 45.2335 1.137735 PLN 5.767 5.900 5.8335 0.094045 RON 5.382 5.405 5.3935 0.016263 RUB 28.611 29.180 28.8955 0.402344 SEK 2.577 2.608 2.5925 0.021920 SGD 16.206 16.530 16.3680 0.229103 THB 71.292 73.313 72.3025 1.429063 TRY 2.891 2.911 2.9010 0.014142 USD 21.369 22.201 21.7850 0.588313 XDR 30.959 31.598 31.2785 0.451841 ZAR 1.424 1.445 1.4345 0.014849
7. Alternativní způsob specifikace agregačních operací
V případě, že je nutné nad seskupenými daty provést větší množství agregačních operací, je možné použít alternativní způsob jejich zápisu, který spočívá v tom, že se použije slovník. Klíče slovníku obsahují jména sloupců (pro které se agregace provádí) a hodnotou pak může být sekvence operací, které se mají provést. Tuto sekvenci lze zapsat například formou seznamu:
# výpočet a zobrazení základní statistiky print(gb.agg({"kurz": ["min", "max"], "další sloupec": [operace, operace]}))
Předchozí demonstrační příklad je tedy možné přepsat následovně:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import pandas import numpy as np # přečtení zdrojových dat s jejich konverzí do datového rámce df = pandas.read_csv("denni_kurz2.txt", sep="|", skiprows=0) # převod číselných hodnot df["kurz"] = pandas.to_numeric(df["kurz"].str.replace(',','.'), errors='coerce') df["datum"] = pandas.to_datetime(df["datum"]) # datový rámec zobrazíme print(df) print() # rozdělení do skupin podle data gb = df.groupby(["kód"]) # výpočet a zobrazení základní statistiky print(gb.agg({"kurz": ["min", "max"]}))
S následujícím výsledkem:
země měna množství kód datum kurz 0 Austrálie dolar 1 AUD 2021-01-05 16.463 1 Brazílie real 1 BRL 2021-01-05 4.002 2 Bulharsko lev 1 BGN 2021-01-05 13.408 3 Čína žen-min-pi 1 CNY 2021-01-05 3.306 4 Dánsko koruna 1 DKK 2021-01-05 3.525 .. ... ... ... ... ... ... 61 Švýcarsko frank 1 CHF 2020-11-20 24.363 62 Thajsko baht 100 THB 2020-11-20 73.313 63 Turecko lira 1 TRY 2020-11-20 2.911 64 USA dolar 1 USD 2020-11-20 22.201 65 Velká Británie libra 1 GBP 2020-11-20 29.464 [66 rows x 6 columns] kurz min max kód AUD 16.231 16.463 BGN 13.408 13.467 BRL 4.002 4.160 CAD 16.759 17.011 CHF 24.273 24.363 CNY 3.306 3.381 DKK 3.525 3.536 EUR 26.225 26.340 GBP 29.030 29.464 HKD 2.756 2.864 HRK 3.469 3.481 HUF 7.279 7.328 IDR 1.536 1.567 ILS 6.649 6.677 INR 29.181 29.950 ISK 16.330 16.800 JPY 20.770 21.383 KRW 1.963 1.990 MXN 1.066 1.104 MYR 5.320 5.425 NOK 2.471 2.505 NZD 15.392 15.416 PHP 44.429 46.038 PLN 5.767 5.900 RON 5.382 5.405 RUB 28.611 29.180 SEK 2.577 2.608 SGD 16.206 16.530 THB 71.292 73.313 TRY 2.891 2.911 USD 21.369 22.201 XDR 30.959 31.598 ZAR 1.424 1.445
8. Transformace seskupených údajů
Seskupené údaje je možné transformovat (podobně jako celý datový rámec). V dalším demonstračním příkladu je ukázáno, jak se vytvoří nový sloupec obsahující výsledek výpočtu: rozdíl mezi nejvyšším a nejnižším kurzem pro danou měnu:
# nový sloupec df["rozdíl"]=gb["kurz"].transform(lambda x:x.max()-x.min())
Tento sloupec se stane plnohodnotnou součástí původního datového rámce:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import pandas import numpy as np # přečtení zdrojových dat s jejich konverzí do datového rámce df = pandas.read_csv("denni_kurz2.txt", sep="|", skiprows=0) # převod číselných hodnot df["kurz"] = pandas.to_numeric(df["kurz"].str.replace(',','.'), errors='coerce') df["datum"] = pandas.to_datetime(df["datum"]) # datový rámec zobrazíme print(df) print() # rozdělení do skupin podle data gb = df.groupby(["kód"]) # nový sloupec df["rozdíl"]=gb["kurz"].transform(lambda x:x.max()-x.min()) print(df[0:10]) print() print(df[33:43])
Podívejme se nyní na výsledky; nejdříve opět původní datový rámec:
země měna množství kód datum kurz 0 Austrálie dolar 1 AUD 2021-01-05 16.463 1 Brazílie real 1 BRL 2021-01-05 4.002 2 Bulharsko lev 1 BGN 2021-01-05 13.408 3 Čína žen-min-pi 1 CNY 2021-01-05 3.306 4 Dánsko koruna 1 DKK 2021-01-05 3.525 .. ... ... ... ... ... ... 61 Švýcarsko frank 1 CHF 2020-11-20 24.363 62 Thajsko baht 100 THB 2020-11-20 73.313 63 Turecko lira 1 TRY 2020-11-20 2.911 64 USA dolar 1 USD 2020-11-20 22.201 65 Velká Británie libra 1 GBP 2020-11-20 29.464 [66 rows x 6 columns]
A dále pro porovnání upravený datový rámec s kurzy pro datum 5.1.2021 a následně pro stejné měny, ovšem pro 20.11.2020:
země měna množství kód datum kurz rozdíl 0 Austrálie dolar 1 AUD 2021-01-05 16.463 0.232 1 Brazílie real 1 BRL 2021-01-05 4.002 0.158 2 Bulharsko lev 1 BGN 2021-01-05 13.408 0.059 3 Čína žen-min-pi 1 CNY 2021-01-05 3.306 0.075 4 Dánsko koruna 1 DKK 2021-01-05 3.525 0.011 5 EMU euro 1 EUR 2021-01-05 26.225 0.115 6 Filipíny peso 100 PHP 2021-01-05 44.429 1.609 7 Hongkong dolar 1 HKD 2021-01-05 2.756 0.108 8 Chorvatsko kuna 1 HRK 2021-01-05 3.469 0.012 9 Indie rupie 100 INR 2021-01-05 29.181 0.769 země měna množství kód datum kurz rozdíl 33 Austrálie dolar 1 AUD 2020-11-20 16.231 0.232 34 Brazílie real 1 BRL 2020-11-20 4.160 0.158 35 Bulharsko lev 1 BGN 2020-11-20 13.467 0.059 36 Čína žen-min-pi 1 CNY 2020-11-20 3.381 0.075 37 Dánsko koruna 1 DKK 2020-11-20 3.536 0.011 38 EMU euro 1 EUR 2020-11-20 26.340 0.115 39 Filipíny peso 100 PHP 2020-11-20 46.038 1.609 40 Hongkong dolar 1 HKD 2020-11-20 2.864 0.108 41 Chorvatsko kuna 1 HRK 2020-11-20 3.481 0.012 42 Indie rupie 100 INR 2020-11-20 29.950 0.769
9. Tři datové struktury původní verze Pandasu
Původní verze knihovny Pandas byla postavena na třech datových strukturách:
Datová struktura | Stručný popis |
---|---|
Serie | údaje reprezentované 1D sloupcem |
Data Frame | údaje reprezentované 2D tabulkou |
Panel | údaje reprezentované 3D „listy“ tabulek |
Právě od třetího datového typu je dokonce odvozen název celé knihovny: Pandas=Pan(el) da(ta)s. Později se ovšem od tohoto konceptu ustoupilo s tím, že jak k datovým řadám (Serie), tak i k datovým rámcům (Data Frame) je možné přidat namísto klasického indexu takzvané multiindexy.
10. Multiindexy
Multiindexy umožňují, aby byl řádek (resp. přesněji řečeno záznam) v datovém rámci určen nikoli pouze jedinou skalární hodnotou (což typicky bývá celé číslo nebo řetězec), ale několika hodnotami, které si můžeme představit jako běžnou n-tici. Díky tomu je možné i v 2D datové struktuře, tedy v datovém rámci, ukládat data de facto uložená ve vícedimenzionální struktuře – 3D poli, 4D poli atd.
Nejprve si ukážeme, jak se multiindexy konstruují a potom se seznámíme se způsobem jejich použití.
11. Multiindex vytvořený z n-tic
Multiindex lze zkonstruovat z n-tic funkcí Multiindex.from_tuples, například následovně:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import pandas sequence1 = range(10) sequence2 = ("foo", "bar", "baz", "foo", "bar", "baz", "foo", "bar", "baz", "*") zipped = zip(sequence1, sequence2) print("zipped sequences") print(zipped) print() tuples = tuple(zipped) print("converted to tuples") print(tuples) print() multiindex = pandas.MultiIndex.from_tuples(tuples) print("multiindex made from tuples") print(multiindex) print()
Výsledkem bude multiindex umožňující každý řádek určit dvojicí hodnot:
zipped sequences <zip object at 0x7f57385b0cc8> converted to tuples ((0, 'foo'), (1, 'bar'), (2, 'baz'), (3, 'foo'), (4, 'bar'), (5, 'baz'), (6, 'foo'), (7, 'bar'), (8, 'baz'), (9, '*')) multiindex made from tuples MultiIndex([(0, 'foo'), (1, 'bar'), (2, 'baz'), (3, 'foo'), (4, 'bar'), (5, 'baz'), (6, 'foo'), (7, 'bar'), (8, 'baz'), (9, '*')], )
12. Vytvoření multiindexu z jiného datového typu
Ve skutečnosti funkce Multiindex.from_tuples neakceptuje pouze n-tice, ale například i běžné seznamy, takže demonstrační příklad z předchozí kapitoly lze upravit takto:
sequence1 = range(10) sequence2 = ["foo", "bar", "baz", "foo", "bar", "baz", "foo", "bar", "baz", "*"] zipped = zip(sequence1, sequence2) print("zipped sequences") print(zipped) print() lst = list(list(x) for x in zipped) print("converted to list") print(lst) print() multiindex = pandas.MultiIndex.from_tuples(lst)
Výsledkem bude stejný multiindex, jako tomu bylo v předchozím demonstračním příkladu.
13. Pojmenování sloupců v multiindexu
Multiindex si můžeme taktéž představit jako speciální sloupce přiřazené k datovému rámci. A tyto sloupce je možné (a vhodné) pojmenovat, což se provede s využitím nepovinného parametru names předaného konstruktoru MultiIndex.from_tuples:
multiindex = pandas.MultiIndex.from_tuples(lst, names=["first", "second"])
Pokud tímto způsobem upravíme předchozí demonstrační příklad, získáme takto strukturovaný multiindex:
multiindex made from tuples MultiIndex([(0, 'foo'), (1, 'bar'), (2, 'baz'), (3, 'foo'), (4, 'bar'), (5, 'baz'), (6, 'foo'), (7, 'bar'), (8, 'baz'), (9, '*')], names=['first', 'second'])
14. Vytvoření multiindexu ze všech kombinací prvků ze dvou sekvencí
Relativně často se setkáme se situací, kdy se nějaké hodnoty v indexu opakují. Příkladem mohou být dny v týdnu, názvy měsíců atd. I takové multiindexy – kde se opakují hodnoty ve sloupcích – lze vytvořit, což je ukázáno v dalším demonstračním příkladu:
import pandas sequence1 = range(1, 6) sequence2 = ("foo", "bar", "baz") sequences = (sequence1, sequence2) print("sequences") print(sequences) print() multiindex = pandas.MultiIndex.from_product(sequences) print("multiindex made from tuples") print(multiindex) print()
Výsledkem nyní bude multiindex sestavený ze všech možných kombinací prvků z obou sekvencí:
multiindex made from tuples MultiIndex([(1, 'foo'), (1, 'bar'), (1, 'baz'), (2, 'foo'), (2, 'bar'), (2, 'baz'), (3, 'foo'), (3, 'bar'), (3, 'baz'), (4, 'foo'), (4, 'bar'), (4, 'baz'), (5, 'foo'), (5, 'bar'), (5, 'baz')], )
15. Pojmenování sloupců v multiindexu
Opět se vrátíme k již známým postupům a pojmenujeme sloupce v multiindexu tvořeného kombinací prvků ze dvou sekvencí:
sequence1 = range(1, 6) sequence2 = ("foo", "bar", "baz") sequences = (sequence1, sequence2) print("sequences") print(sequences) print() multiindex = pandas.MultiIndex.from_product(sequences, names=["first", "second"]) print("multiindex made from tuples") print(multiindex) print()
S výsledkem:
multiindex made from tuples MultiIndex([(1, 'foo'), (1, 'bar'), (1, 'baz'), (2, 'foo'), (2, 'bar'), (2, 'baz'), (3, 'foo'), (3, 'bar'), (3, 'baz'), (4, 'foo'), (4, 'bar'), (4, 'baz'), (5, 'foo'), (5, 'bar'), (5, 'baz')], names=['first', 'second'])
16. Obsah následujícího článku
V navazujícím článku o knihovně Pandas dokončíme popis multiindexů, samozřejmě i s jejich praktičtějším použitím.
17. 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:
18. Datové soubory používané demonstračními příklady
Některé demonstrační příklady načítají následující soubory s daty:
19. Odkazy na předchozí části seriálu o knihovně Pandas
- Knihovna Pandas: základy práce s datovými rámci
https://www.root.cz/clanky/knihovna-pandas-zaklady-prace-s-datovymi-ramci/ - Knihovna Pandas: zobrazení obsahu datových rámců, vykreslení grafů a validace dat
https://www.root.cz/clanky/knihovna-pandas-zobrazeni-obsahu-datovych-ramcu-vykresleni-grafu-a-validace-dat/ - Knihovna Pandas: práce s datovými řadami (series)
https://www.root.cz/clanky/knihovna-pandas-prace-s-datovymi-radami-series/ - Knihovna Pandas: pokročilejší práce s datovými řadami (series)
https://www.root.cz/clanky/knihovna-pandas-pokrocilejsi-prace-s-datovymi-radami-series/ - Knihovna Pandas: spojování datových rámců s využitím append, concat, merge a join
https://www.root.cz/clanky/knihovna-pandas-spojovani-datovych-ramcu-s-vyuzitim-append-concat-merge-a-join/ - Knihovna Pandas: použití metody groupby, naformátování a export tabulek pro tisk
https://www.root.cz/clanky/knihovna-pandas-pouziti-metody-groupby-naformatovani-a-export-tabulek-pro-tisk/
20. Odkazy na Internetu
- Combining Data in Pandas With merge(), .join(), and concat()
https://realpython.com/pandas-merge-join-and-concat/ - Repositář python-tabulate na GitHubu
https://github.com/astanin/python-tabulate - python-tabulate na PyPi
https://pypi.org/project/tabulate/ - Understanding Pandas groupby() function
https://www.askpython.com/python-modules/pandas/pandas-groupby-function - Python Pandas – GroupBy
https://www.tutorialspoint.com/python_pandas/python_pandas_groupby.htm - Pandas GroupBy: Group Data in Python
https://pythonspot.com/pandas-groupby/ - 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