Obsah
1. Operace s daty uloženými v binárních souborech v knihovnách NumPy a Pandas (dokončení)
2. Krátké zopakování z minula – uložení matice do binárního souboru, opětovné načtení této matice
3. Načtení obsahu matice z binárního souboru se specifikací formátu a offsetu
4. Načtení obsahu matice z binárního souboru se specifikací formátu, offsetu a počtu prvků
5. Uložení obsahu rozsáhlejší matice do standardního binárního souboru
6. Přečtení obsahu matice ze standardního binárního souboru
7. Využití systémového volání mmap při práci s rozsáhlejšími maticemi
8. Porovnání operací použitých pro přečtení matice ze standardního binárního souboru
9. Standardní souborový formát pro uložení většího množství matic
10. Uložení většího množství matic do jediného souboru
11. Načtení většího množství matic z jediného souboru
12. Pojmenování matic v souboru NPZ
13. Komprimace obsahu matic v souboru NPZ
14. Porovnání souboru NPZ s komprimovanou a nekomprimovanou maticí
15. Repositář s demonstračními příklady
16. Odkazy na předchozí části seriálu o knihovně Pandas
1. Operace s daty uloženými v binárních souborech v knihovnách NumPy a Pandas (dokončení)
Na předchozí článek, ve kterém jsme si ukázali základy práce s daty (vektory, maticemi, řadami i datovými rámci) uloženými v binárních souborech v knihovnách NumPy a Pandas, dnes navážeme. Ukážeme si totiž některé další možnosti, které nám binární soubory (ať již „raw“, tedy pouze soubory s čistými daty, nebo standardní soubory NPY) nabízí. Jednou z největších předností binárních souborů oproti souborům textovým jsou rychlé přesuny (tedy operace typu seek) a taktéž možnost namapování obsahu souboru do paměti (mmap). To však není vše, protože si představíme i formát NPZ, který umožňuje uložit větší množství vektorů a matic knihovny NumPy, a to dokonce i s volitelnou komprimací (interně se ovšem nejedná o žádnou raketovou vědu, protože NPZ je vlastně běžný ZIP archiv obsahující soubory NPY).
2. Krátké zopakování z minula – uložení matice do binárního souboru, opětovné načtení této matice
V úvodním článku o problematice binárních souborů obsahujících data pro knihovnu NumPy popř. i datové řady nebo datové rámce pro knihovnu Pandas jsme si mj. ukázali i metodu nazvanou tofile třídy ndarray popř. funkci Numpy.tofile. S využitím těchto dvou volání lze zajistit uložení „čistých“ dat polí do souboru s možností jejich zpětného načtení. Základem je metoda ndarray.tofile:
tofile(...) a.tofile(fid, sep="", format="%s") Write array to a file as text or binary (default). Data is always written in 'C' order, independent of the order of `a`. The data produced by this method can be recovered using the function fromfile().
Tuto metodu použijeme pro uložení obsahu poměrně rozsáhlé matice s pěti sloupci a 500 řádky, přičemž jednotlivé prvky budou typu „i“ neboli celé číslo, jehož způsob uložení, rozsah atd. je závislý na použité platformě (viz též tento popis):
"""Uložení obsahu matice do binárního souboru.""" import numpy as np # rozměry matice rows = 500 columns = 5 # matice obsahující celočíselné hodnoty typu integer m = np.linspace(1, rows*columns, rows*columns, dtype="i").reshape(rows, columns) # tisk obsahu zkonstruované matice print(m) # uložení matice do souboru v čistém binárním formátu m.tofile("matrix3.bin")
Na platformě x86–64 bude výsledkem soubor o velikosti přesně 10 000 bajtů. Proč je tento soubor přesně takto velký, zjistíme snadno – uložilo se celkem 5×500=2500 prvků matice, přičemž každý prvek je uložen ve čtyřech bajtech. Obsah souboru si můžeme zobrazit s využitím příkazu od (octal dump) zmíněného minule:
$ od -t d4 matrix3.bin 0000000 1 2 3 4 0000020 5 6 7 8 0000040 9 10 11 12 0000060 13 14 15 16 0000100 17 18 19 20 0000120 21 22 23 24 0000140 25 26 27 28 0000160 29 30 31 32 0000200 33 34 35 36 0000220 37 38 39 40 0000240 41 42 43 44 0000260 45 46 47 48 0000700 113 114 115 116 0000720 117 118 119 120 0000740 121 122 123 124 0000760 125 126 127 128 0001000 129 130 131 132 0001020 133 134 135 136 0001040 137 138 139 140 0001060 141 142 143 144 0001100 145 146 147 148 0001120 149 150 ... ... ... 0023120 2453 2454 2455 2456 0023140 2457 2458 2459 2460 0023160 2461 2462 2463 2464 0023200 2465 2466 2467 2468 0023220 2469 2470 2471 2472 0023240 2473 2474 2475 2476 0023260 2477 2478 2479 2480 0023300 2481 2482 2483 2484 0023320 2485 2486 2487 2488 0023340 2489 2490 2491 2492 0023360 2493 2494 2495 2496 0023400 2497 2498 2499 2500 0023420
Zpětné načtení obsahu matice se skládá ze dvou operací – načtení dat ve formě jednorozměrného vektoru (se specifikací formátu prvků) a vytvoření matice z tohoto vektoru metodou reshape. I přes tento název se interně žádné přesuny prvků neprovádí, takže reshape pracuje velmi efektivně:
"""Načtení obsahu matice z binárního souboru se specifikací formátu.""" import numpy as np # rozměry výsledné matice rows = 500 columns = 5 # načtení prvků matice z binárního souboru a konstrukce matice m = np.fromfile("matrix3.bin", dtype="i").reshape(rows, columns) # výpis obsahu právě zkonstruované matice print(m)
Výsledek po spuštění:
[[ 1 2 3 4 5] [ 6 7 8 9 10] [ 11 12 13 14 15] ... [2486 2487 2488 2489 2490] [2491 2492 2493 2494 2495] [2496 2497 2498 2499 2500]]
3. Načtení obsahu matice z binárního souboru se specifikací formátu a offsetu
Velmi často se v praxi můžeme setkat s požadavkem na zpracování dat (typicky rozměrných matic) uložených v binárních souborech o značné velikosti. V případě, že se má zpracovat pouze podmnožina těchto dat, a navíc dokážeme získat počáteční index „podmatice“, lze tuto operaci provést relativně snadno – budeme pouze muset specifikovat offset v rámci binárního souboru. Malý problém spočívá v tom, že offset není specifikován jako index prvku, ale index prvního bajtu, který se má načíst. Na základě velikosti matice (počtu sloupců), požadovaném prvním řádku, který se má načíst a známém typu prvků musíme provést tento výpočet:
# první řádek matice, který chceme načíst z binárního souboru first_row = 250 # platformově nezávislé získání počtu bajtů pro každý prvek typu integer item_size = np.dtype("i").itemsize # výpočet offsetu při čtení řádků od first_row z binárního souboru offset = first_row * columns * item_size
Offset se zadává formou nepovinného parametru funkce numpy.fromfile:
# načtení prvků matice z binárního souboru a konstrukce matice m = np.fromfile("matrix3.bin", dtype="i", offset=offset).reshape(rows, columns)
Ukažme si celý postup na dalším demonstračním příkladu:
"""Načtení obsahu matice z binárního souboru se specifikací formátu a offsetu.""" import numpy as np # rozměry výsledné matice rows = 250 columns = 5 # první řádek matice načtený z binárního souboru first_row = 250 # platformově nezávislé získání počtu bajtů pro každý prvek typu integer item_size = np.dtype("i").itemsize # výpočet offsetu při čtení řádků od first_row z binárního souboru offset = first_row * columns * item_size print("offset=", offset, "bytes") print() # načtení prvků matice z binárního souboru a konstrukce matice m = np.fromfile("matrix3.bin", dtype="i", offset=offset).reshape(rows, columns) # výpis obsahu právě zkonstruované matice print(m)
Výsledek získaný po spuštění tohoto příkladu:
offset= 5000 bytes [[1251 1252 1253 1254 1255] [1256 1257 1258 1259 1260] [1261 1262 1263 1264 1265] ... [2486 2487 2488 2489 2490] [2491 2492 2493 2494 2495] [2496 2497 2498 2499 2500]]
4. Načtení obsahu matice z binárního souboru se specifikací formátu, offsetu a počtu prvků
V předchozí kapitole jsme specifikovali, že načítání prvků má začít od offsetu 5000. Od tohoto offsetu byl načten zbytek souboru, tedy 10000–5000=5000 bajtů, což odpovídá 1250 prvkům. Pro omezení velikosti načítaných dat však můžeme určit jak offset, tak i počet prvků, přičemž počet prvků je specifikován nepovinným parametrem count:
# načtení prvků matice z binárního souboru a konstrukce matice m = np.fromfile("matrix3.bin", dtype="i", offset=offset, count=count).reshape(last_row-first_row, columns)
Výpočet počtu prvků lze provést na základě prvního a posledního řádku, který se má načíst (a pochopitelně i počtu sloupců v matici):
# první řádek matice načtený z binárního souboru first_row = 200 # poslední řádek matice načtený z binárního souboru last_row = 350 # počet sloupců matice columns = 5 # počet prvků, které se mají načíst count = (last_row - first_row) * columns
Vše si opět ukážeme na jednoduchém demonstračním příkladu:
"""Načtení obsahu matice z binárního souboru se specifikací formátu, offsetu a počtu prvků.""" import numpy as np # první řádek matice načtený z binárního souboru first_row = 200 # poslední řádek matice načtený z binárního souboru last_row = 350 # počet sloupců matice columns = 5 # platformově nezávislé získání počtu bajtů pro každý prvek typu integer item_size = np.dtype("i").itemsize # výpočet offsetu při čtení řádků od first_row z binárního souboru offset = first_row * columns * item_size count = (last_row - first_row) * columns print("offset=", offset, "bytes") print("count=", count, "items") print() # načtení prvků matice z binárního souboru a konstrukce matice m = np.fromfile("matrix3.bin", dtype="i", offset=offset, count=count).reshape(last_row-first_row, columns) # výpis obsahu právě zkonstruované matice print(m)
Výsledkem bude mnohem menší matice:
offset= 4000 bytes count= 750 items [[1001 1002 1003 1004 1005] [1006 1007 1008 1009 1010] [1011 1012 1013 1014 1015] [1016 1017 1018 1019 1020] [1021 1022 1023 1024 1025] [1026 1027 1028 1029 1030] [1031 1032 1033 1034 1035] [1036 1037 1038 1039 1040] [1041 1042 1043 1044 1045] [1046 1047 1048 1049 1050] [1051 1052 1053 1054 1055] ... ... ... [1696 1697 1698 1699 1700] [1701 1702 1703 1704 1705] [1706 1707 1708 1709 1710] [1711 1712 1713 1714 1715] [1716 1717 1718 1719 1720] [1721 1722 1723 1724 1725] [1726 1727 1728 1729 1730] [1731 1732 1733 1734 1735] [1736 1737 1738 1739 1740] [1741 1742 1743 1744 1745] [1746 1747 1748 1749 1750]]
5. Uložení obsahu rozsáhlejší matice do standardního binárního souboru
Minule jsme se zmínili i o možnosti uložení obsahu vektorů a matic do standardního binárního souboru. Předchozí pokusy o serializaci dat měly totiž jednu zásadní nevýhodu – bude se jednat skutečně pouze o čisté hodnoty prvků. Žádné další informace se neuloží – tedy ani typ prvků ani tvar pole. To není ani zdaleka ideální situace a proto byl vyvinut dnes již standardní binární formát určený pro ukládání n-rozměrných polí. Tento formát se nazývá NPY a jeho popis lze nalézt na stránce https://numpy.org/devdocs/reference/generated/numpy.lib.format.html. Jedná se o přímou serializaci pole do souboru (což již velmi dobře známe), ovšem před vlastní hodnoty prvků je uložena jednoduchá hlavička se všemi důležitými informacemi – včetně endianity, kterou jsme prozatím vůbec neřešili.
Pro uložení se používá funkce numpy.save:
save(file, arr, allow_pickle=True, fix_imports=True) Save an array to a binary file in NumPy ``.npy`` format. Parameters ---------- file : file, str, or pathlib.Path File or filename to which the data is saved. If file is a file-object, then the filename is unchanged. If file is a string or Path, a ``.npy`` extension will be appended to the filename if it does not already have one. ... ... ...
Následující skript po svém spuštění uloží rozsáhlou matici s 1000 řádky pěti sloupci do standardního souboru NPY:
"""Uložení obsahu matice do standardního binárního souboru.""" import numpy as np # rozměry matice rows = 1000 columns = 5 # matice obsahující celočíselné hodnoty typu integer m = np.linspace(1, rows*columns, rows*columns, dtype="i").reshape(rows, columns) # tisk obsahu zkonstruované matice print(m) # uložení matice do souboru ve standardním formátu np.save("matrix3.npy", m, allow_pickle=False)
Obsah vytvořeného binárního souboru „matrix3.npy“ si opět můžeme zobrazit nástrojem od. Prvních 128 bajtů je určeno pro uložení hlavičky, která obsahuje i metadata v textové podobě. Ve skutečnosti je hlavičkou obsazeno jen 74 bajtů, ovšem v případě rozsáhlejších matic s větším počtem dimenzí je možné, že bude obsazena celá hlavička, protože se zvětší ta část hlavičky, která obsahuje textovou podobu atributu shape:
0000000 1297436307 88400 662372470 1668506980 >.NUMPY..v.{'desc< 0000020 540682098 879311911 656419879 1953656678 >r': '<i4', 'fort< 0000040 1601069426 1701081711 540682098 1936482630 >ran_order': Fals< 0000060 656419941 1885431923 540682085 741356328 >e, 'shape': (30,< 0000100 740898080 539000096 538976288 538976288 > 5), } < 0000120 538976288 538976288 538976288 538976288 > < * 0000160 538976288 538976288 538976288 169877536 > .<
Od offsetu 0200 (dekadicky tedy od offsetu 128) začínají hodnoty jednotlivých prvků. Každý prvek je uložen ve čtyřech bajtech a vzhledem k tomu, že soubor vznikl na platformě x86–64, používá se pořadí bajtů little endian, což znamená, že nejdříve jsou uloženy bajty s nižší vahou. Nástroj od dokáže s tímto formátem nativně pracovat, takže lze zbytek souboru zobrazit snadno tak, aby byl dobře čitelný:
0000200 1 2 3 4 >................< 0000220 5 6 7 8 >................< 0000240 9 10 11 12 >................< 0000260 13 14 15 16 >................< 0000300 17 18 19 20 >................< 0000320 21 22 23 24 >................< 0000340 25 26 27 28 >................< 0000360 29 30 31 32 >............ ...< 0000400 33 34 35 36 >!..."...#...$...< 0000420 37 38 39 40 >%...&...'...(...< 0000440 41 42 43 44 >)...*...+...,...< 0000460 45 46 47 48 >-......./...0...< 0000500 49 50 51 52 >1...2...3...4...< 0000520 53 54 55 56 >5...6...7...8...< 0000540 57 58 59 60 >9...:...;...<...< 0000560 61 62 63 64 >=...>...?...@...< 0000600 65 66 67 68 >A...B...C...D...< 0000620 69 70 71 72 >E...F...G...H...< 0000640 73 74 75 76 >I...J...K...L...< 0000660 77 78 79 80 >M...N...O...P...< 0000700 81 82 83 84 >Q...R...S...T...< 0000720 85 86 87 88 >U...V...W...X...< 0000740 89 90 91 92 >Y...Z...[...\...< 0000760 93 94 95 96 >]...^..._...`...< 0001000 97 98 99 100 >a...b...c...d...< 0001020 101 102 103 104 >e...f...g...h...< 0001040 105 106 107 108 >i...j...k...l...< 0001060 109 110 111 112 >m...n...o...p...< 0001100 113 114 115 116 >q...r...s...t...< 0001120 117 118 119 120 >u...v...w...x...< 0001140 121 122 123 124 >y...z...{...|...< 0001160 125 126 127 128 >}...~...........< 0001200 129 130 131 132 >................< 0001220 133 134 135 136 >................< 0001240 137 138 139 140 >................< 0001260 141 142 143 144 >................< 0001300 145 146 147 148 >................< 0001320 149 150 >........< 0001330
6. Přečtení obsahu matice ze standardního binárního souboru
V případě, že budeme chtít načíst obsah celé matice ze standardního binárního souboru, je situace poměrně jednoduchá, protože pro tento účel můžeme použít standardní funkci nazvanou numpy.load, které předáme pouze jméno souboru, ve kterém je matice uložena (jedná se tedy o opak funkce numpy.save):
load(file, mmap_mode=None, allow_pickle=False, fix_imports=True, encoding='ASCII') Load arrays or pickled objects from ``.npy``, ``.npz`` or pickled files. .. warning:: Loading files that contain object arrays uses the ``pickle`` module, which is not secure against erroneous or maliciously constructed data. Consider passing ``allow_pickle=False`` to load data that is known not to contain object arrays for the safer handling of untrusted sources.
V případě, že budeme chtít načíst celou matici (nejenom její část), předá se funkci numpy.load pouze jméno souboru s uloženou maticí:
"""Přečtení obsahu matice ze standardního binárního souboru.""" import numpy as np # načtení matice ze standardního binárního souboru m = np.load("matrix3.npy") # zobrazení obsahu matice print(m) print() # zobrazení dalších informací o matici print("Dimensions:", m.ndim) print("Data type:", m.dtype) print("Item size:", m.itemsize, "bytes") print("Array size:", m.size, "items")
S touto funkcí jsme se seznámili již minule, takže pro nás nebude příliš překvapující fakt, že počet dimenzí, tvar, datový typ prvků i celková velikost matice byly plně obnoveny:
[[ 1 2 3 4 5] [ 6 7 8 9 10] [ 11 12 13 14 15] ... [4986 4987 4988 4989 4990] [4991 4992 4993 4994 4995] [4996 4997 4998 4999 5000]] Dimensions: 2 Data type: int32 Item size: 4 bytes Array size: 5000 items
7. Využití systémového volání mmap při práci s rozsáhlejšími maticemi
Při práci se standardními soubory s maticemi lze namísto dvojice open+read využít i systémové volání mmap, které umožňuje k souboru přistupovat jako k regionu ve virtuální paměti (ne ovšem nutně v paměti fyzické). Tento režim se povoluje volbou mmap_mode, přičemž pro režim čtení (bez modifikace) je nutné této nepovinné volbě (parametru) předat řetězec „r“. Přístup k souboru pak bude interně proveden zcela odlišným způsobem:
"""Přečtení obsahu matice ze standardního binárního souboru.""" import numpy as np # použití režimu mmap m = np.load("matrix3.npy", mmap_mode="r") # zobrazení dalších informací o matici print("Dimensions:", m.ndim) print("Data type:", m.dtype) print("Item size:", m.itemsize, "bytes") print("Array size:", m.size, "items")
Výsledek:
Dimensions: 2 Data type: int32 Item size: 4 bytes Array size: 5000 items
Pokud budeme přistupovat pouze k části matice, měl by se počet I/O operací snížit:
"""Přečtení obsahu matice ze standardního binárního souboru.""" import numpy as np # použití režimu mmap m = np.load("matrix3.npy", mmap_mode="r") # zobrazení dalších informací o matici print("Dimensions:", m.ndim) print("Data type:", m.dtype) print("Item size:", m.itemsize, "bytes") print("Array size:", m.size, "items") print(m[500:510])
S výsledkem:
Dimensions: 2 Data type: int32 Item size: 4 bytes Array size: 5000 items [[2501 2502 2503 2504 2505] [2506 2507 2508 2509 2510] [2511 2512 2513 2514 2515] [2516 2517 2518 2519 2520] [2521 2522 2523 2524 2525] [2526 2527 2528 2529 2530] [2531 2532 2533 2534 2535] [2536 2537 2538 2539 2540] [2541 2542 2543 2544 2545] [2546 2547 2548 2549 2550]]
8. Porovnání operací použitých pro přečtení matice ze standardního binárního souboru
Podívejme se nyní na to, jak se interně změní systémová volání ve chvíli, kdy načteme soubor s maticí běžným způsobem v porovnání s otevřením v režimu mmap. Nejdříve spustíme skript, který matici otevře a přečte funkcí numpy.load ve standardním režimu. Budeme přitom sledovat pouze operace prováděné nad vstupním souborem s maticí:
$ strace -P matrix3.npy python3 matrix_load_3.py strace: Requested path 'matrix3.npy' resolved into '/home/ptisnovs/src/python/most-popular-python-libs/numpy/matrix3.npy' openat(AT_FDCWD, "matrix3.npy", O_RDONLY|O_CLOEXEC) = 3 fstat(3, {st_mode=S_IFREG|0664, st_size=20128, ...}) = 0 ioctl(3, TCGETS, 0x7ffe69bc6210) = -1 ENOTTY (Inappropriate ioctl for device) lseek(3, 0, SEEK_CUR) = 0 read(3, "\223NUMPY\1\0v\0{'descr': '<i4', 'fort"..., 4096) = 4096 lseek(3, 0, SEEK_CUR) = 4096 fcntl(3, F_DUPFD_CLOEXEC, 0) = 4 fcntl(4, F_GETFL) = 0x8000 (flags O_RDONLY|O_LARGEFILE) lseek(4, 0, SEEK_CUR) = 4096 lseek(3, 0, SEEK_CUR) = 4096 fstat(4, {st_mode=S_IFREG|0664, st_size=20128, ...}) = 0 lseek(4, 0, SEEK_SET) = 0 read(4, "\223NUMPY\1\0v\0{'descr': '<i4', 'fort"..., 128) = 128 lseek(4, 0, SEEK_SET) = 0 read(4, "\223NUMPY\1\0v\0{'descr': '<i4', 'fort"..., 4096) = 4096 read(4, "\341\3\0\0\342\3\0\0\343\3\0\0\344\3\0\0\345\3\0\0\346\3\0\0\347\3\0\0\350\3\0\0"..., 12288) = 12288 read(4, "\341\17\0\0\342\17\0\0\343\17\0\0\344\17\0\0\345\17\0\0\346\17\0\0\347\17\0\0\350\17\0\0"..., 4096) = 3744 close(4) = 0 lseek(3, 4096, SEEK_SET) = 4096 lseek(3, 20128, SEEK_SET) = 20128 close(3) = 0 +++ exited with 0 +++
Z výsledků je patrné, že se soubor otevře dvakrát, ovšem vlastní čtení probíhá pouze se druhým otevřeným souborem. Celá matice je načtena sekvencí systémových volání read (první z těchto volání přečte hlavičku o délce 128 bajtů).
Dále se podíváme na výsledek v případě, že je soubor s maticí otevřen v režimu mmap:
$ strace -P matrix3.npy python3 matrix_load_5.py strace: Requested path 'matrix3.npy' resolved into '/home/ptisnovs/src/python/most-popular-python-libs/numpy/matrix3.npy' openat(AT_FDCWD, "matrix3.npy", O_RDONLY|O_CLOEXEC) = 3 fstat(3, {st_mode=S_IFREG|0664, st_size=20128, ...}) = 0 ioctl(3, TCGETS, 0x7fffd302e2d0) = -1 ENOTTY (Inappropriate ioctl for device) lseek(3, 0, SEEK_CUR) = 0 read(3, "\223NUMPY\1\0v\0{'descr': '<i4', 'fort"..., 4096) = 4096 lseek(3, 0, SEEK_CUR) = 4096 openat(AT_FDCWD, "matrix3.npy", O_RDONLY|O_CLOEXEC) = 4 fstat(4, {st_mode=S_IFREG|0664, st_size=20128, ...}) = 0 ioctl(4, TCGETS, 0x7fffd302dff0) = -1 ENOTTY (Inappropriate ioctl for device) lseek(4, 0, SEEK_CUR) = 0 read(4, "\223NUMPY\1\0v\0{'descr': '<i4', 'fort"..., 4096) = 4096 lseek(4, 0, SEEK_CUR) = 4096 close(4) = 0 openat(AT_FDCWD, "matrix3.npy", O_RDONLY|O_CLOEXEC) = 4 fstat(4, {st_mode=S_IFREG|0664, st_size=20128, ...}) = 0 ioctl(4, TCGETS, 0x7fffd302dae0) = -1 ENOTTY (Inappropriate ioctl for device) lseek(4, 0, SEEK_CUR) = 0 lseek(4, 0, SEEK_CUR) = 0 lseek(4, 0, SEEK_END) = 20128 lseek(4, 0, SEEK_CUR) = 20128 fstat(4, {st_mode=S_IFREG|0664, st_size=20128, ...}) = 0 fcntl(4, F_DUPFD_CLOEXEC, 0) = 5 mmap(NULL, 20128, PROT_READ, MAP_SHARED, 4, 0) = 0x7f941fa52000 close(4) = 0 close(3) = 0 close(5) = 0 +++ exited with 0 +++
V tomto případě je opět přečtena hlavička, ovšem poté se zavolá systémové volání mmap a další operace typu read zde již nenalezneme.
9. Standardní souborový formát pro uložení většího množství matic
Nyní již víme, jak vypadá standardní souborový formát NPY, který je určený pro uložení obsahu jediné matice, a to včetně všech důležitých metainformací o této matici (tvar, typ prvků, endianita). Ovšem mnohdy jsme postaveni před úkol přenášet větší množství matic. To lze pochopitelně zajistit použitím většího množství souborů NPY, ovšem nemusí se vždy jednat o to nejvýhodnější řešení. Z tohoto důvodu začal být knihovnou NumPy oficiálně podporován ještě jeden souborový formát nazvaný NPZ, v němž je možné ukládat větší množství matic. Interně se jedná o formát založený na klasickém ZIPu, tedy na formátu, v němž je možné uložit větší množství souborů NPY. V navazujících kapitolách si ukážeme, jakým způsobem je možné tyto soubory vytvářet a pochopitelně i to, jak se načítají jednotlivé matice do tohoto souboru uložené.
10. Uložení většího množství matic do jediného souboru
Pro uložení většího množství matic do jediného souboru typu NPZ se používá funkce pojmenovaná numpy.savez:
savez(file, *args, **kwds) Save several arrays into a single file in uncompressed ``.npz`` format. If arguments are passed in with no keywords, the corresponding variable names, in the ``.npz`` file, are 'arr_0', 'arr_1', etc. If keyword arguments are given, the corresponding variable names, in the ``.npz`` file will match the keyword names.
Podívejme se nyní na základní způsob použití této funkce při uložení tří matic do jediného souboru:
"""Uložení více matic do standardního binárního souboru.""" import numpy as np # rozměry první matice rows1 = 10 columns1 = 10 # rozměry druhé matice rows2 = 100 columns2 = 1 # rozměry třetí matice rows3 = 1 columns3 = 100 # tři matice obsahující celočíselné hodnoty různých typů m1 = np.linspace(1, rows1*columns1, rows1*columns1, dtype="b").reshape(rows1, columns1) m2 = np.linspace(1, rows2*columns2, rows2*columns2, dtype="i").reshape(rows2, columns2) m3 = np.linspace(1, rows3*columns3, rows3*columns3, dtype="f").reshape(rows3, columns3) # uložení matic do souboru ve standardním formátu np.savez("matrix.npz", m1, m2, m3)
Výsledkem je soubor typu NPZ pojmenovaný „matrix.npz“. Vzhledem k tomu, že víme, že se jedná o standardní formát ZIP, můžeme si obsah tohoto souboru vypsat nástrojem unzip:
$ unzip -l matrix.npz
Archive: matrix.npz Length Date Time Name --------- ---------- ----- ---- 228 01-01-1980 00:00 arr_0.npy 528 01-01-1980 00:00 arr_1.npy 528 01-01-1980 00:00 arr_2.npy --------- ------- 1284 3 files
Pochopitelně si můžeme tento soubor rozbalit, a to opět standardním nástrojem unzip:
$ unzip matrix.npz
Prozkoumat můžeme například první rozbalený soubor arr0.npy:
$ od -t d1z arr_0.npy
Prvních 128 bajtů představuje nám již známou hlavičku:
0000000 -109 78 85 77 80 89 1 0 118 0 123 39 100 101 115 99 >.NUMPY..v.{'desc< 0000020 114 39 58 32 39 124 105 49 39 44 32 39 102 111 114 116 >r': '|i1', 'fort< 0000040 114 97 110 95 111 114 100 101 114 39 58 32 70 97 108 115 >ran_order': Fals< 0000060 101 44 32 39 115 104 97 112 101 39 58 32 40 49 48 44 >e, 'shape': (10,< 0000100 32 49 48 41 44 32 125 32 32 32 32 32 32 32 32 32 > 10), } < 0000120 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 > < 0000160 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 10 > .<
Za hlavičkou jsou již uloženy hodnoty jednotlivých prvků matice:
0000200 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 >................< 0000220 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 >............... < 0000240 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 >!"#$%&'()*+,-./0< 0000260 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 >123456789:;<=>?@< 0000300 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 >ABCDEFGHIJKLMNOP< 0000320 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 >QRSTUVWXYZ[\]^_`< 0000340 97 98 99 100 >abcd< 0000344
11. Načtení většího množství matic z jediného souboru
Matice uložené do souboru typu NPZ je nutné načítat na základě jména souboru v archivu. Tato jména jsou dostupná přes atribut files. Všechny (tři) matice tedy dokážeme načíst a vypsat jejich obsah následujícím skriptem:
"""Načtení většího množství matic ze standardního binárního souboru.""" import numpy as np # načtení souboru s větším množstvím matic npzfile = np.load("matrix.npz") # tisk názvů souborů print(npzfile.files) # přístup k jednotlivým maticím for f in npzfile.files: m = npzfile[f] print(m)
12. Pojmenování matic v souboru NPZ
Matice a tím pádem i korespondující soubory v archivu je vhodné pojmenovat. Pro tento účel je podporován tento způsob volání funkce numpy.savez postavený na tzv. keyword argumentech:
np.savez("matrix.npz", first=matice1, second=matice2, third=matice3, ...)
V případě, že tyto názvy, tedy konkrétně „first“, „second“ a „third“ použijeme v upraveném skriptu, vznikne archiv s odlišně pojmenovanými soubory uvnitř:
"""Uložení více matic do standardního binárního souboru.""" import numpy as np # rozměry první matice rows1 = 10 columns1 = 10 # rozměry druhé matice rows2 = 100 columns2 = 1 # rozměry třetí matice rows3 = 1 columns3 = 100 # tři matice obsahující celočíselné hodnoty různých typů m1 = np.linspace(1, rows1*columns1, rows1*columns1, dtype="b").reshape(rows1, columns1) m2 = np.linspace(1, rows2*columns2, rows2*columns2, dtype="i").reshape(rows2, columns2) m3 = np.linspace(1, rows3*columns3, rows3*columns3, dtype="f").reshape(rows3, columns3) # uložení matic do souboru ve standardním formátu np.savez("matrix.npz", first=m1, second=m2, third=m3)
Pochopitelně si opět můžeme zobrazit obsah vytvořeného archivu:
$ unzip -l matrix.npz Archive: matrix.npz Length Date Time Name --------- ---------- ----- ---- 228 01-01-1980 00:00 first.npy 528 01-01-1980 00:00 second.npy 528 01-01-1980 00:00 third.npy --------- ------- 1284 3 files
13. Komprimace obsahu matic v souboru NPZ
Díky tomu, že standardní ZIP archivy mohou být komprimovány, je možné se pokusit rozsáhlé matice zkomprimovat. V tomto případě se však namísto nám již známé funkce numpy.savez musí použít funkce pojmenovaná numpy.savez_compressed:
savez_compressed(file, *args, **kwds) Save several arrays into a single file in compressed ``.npz`` format. If keyword arguments are given, then filenames are taken from the keywords. If arguments are passed in with no keywords, then stored filenames are arr_0, arr_1, etc. ... ... ...
Parametry i způsob použití této funkce se nijak neliší od numpy.savez, takže si můžeme otestovat způsob jejího použití. Vytvoříme nejdříve relativně velkou matici 1000×1000 prvků typu „celé čtyřbajtové číslo“, přičemž všechny prvky budou nulové. Následně tuto matici uložíme do dvou souborů NZP – jednou v nezkomprimované a podruhé ve zkomprimované podobě:
"""Uložení více matic do standardního binárního souboru.""" import numpy as np # rozměry matice rows = 1000 columns = 1000 # vytvoření větší matice o zadaném tvaru m0 = np.zeros((rows, columns), dtype="i") # uložení matice do souboru ve standardním formátu np.savez("matrix_uncompressed.npz", m0) # uložení matice do souboru ve standardním zkomprimovaném formátu np.savez_compressed("matrix_compressed.npz", m0)
14. Porovnání souboru NPZ s komprimovanou a nekomprimovanou maticí
Oba vytvořené soubory sice obsahují stejná data, ovšem jednou v nezkomprimovaném a podruhé ve zkomprimovaném tvaru, takže se velikosti obou archivů budou odlišovat:
$ ls -l matrix_*.npz -rw-rw-r--. 1 ptisnovs ptisnovs 4114 Nov 3 17:08 matrix_compressed.npz -rw-rw-r--. 1 ptisnovs ptisnovs 4000264 Nov 3 17:08 matrix_uncompressed.npz
Přesvědčit se můžeme i o tom, že délka rozbalených souborů by byla naprosto totožná (první sloupec):
$ unzip -lv matrix_uncompressed.npz Archive: matrix_uncompressed.npz Length Method Size Cmpr Date Time CRC-32 Name -------- ------ ------- ---- ---------- ----- -------- ---- 4000128 Stored 4000128 0% 01-01-1980 00:00 569b177d arr_0.npy -------- ------- --- ------- 4000128 4000128 0% 1 file $ unzip -lv matrix_compressed.npz Archive: matrix_compressed.npz Length Method Size Cmpr Date Time CRC-32 Name -------- ------ ------- ---- ---------- ----- -------- ---- 4000128 Defl:N 3978 100% 01-01-1980 00:00 569b177d arr_0.npy -------- ------- --- ------- 4000128 3978 100% 1 file
15. Repositář s demonstračními příklady
Zdrojové kódy všech minule i dnes popsaných demonstračních příkladů určených pro programovací jazyk Python 3 a nejnovější stabilní verzi knihoven Numpy a 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ích tabulkách.
Demonstrační příklady určené pouze pro knihovnu Numpy:
# | Demonstrační příklad | Stručný popis příkladu | Cesta |
---|---|---|---|
1 | vector_to_file1.py | uložení obsahu vektoru do textového souboru se specifikací oddělovače | https://github.com/tisnik/most-popular-python-libs/blob/master/numpy/vector_to_file1.py |
2 | vector_to_file2.py | uložení obsahu vektoru do textového souboru se specifikací oddělovače a formátu jednotlivých prvků | https://github.com/tisnik/most-popular-python-libs/blob/master/numpy/vector_to_file2.py |
3 | vector_to_file3.py | uložení obsahu vektoru s prvky typu „byte“ do binárního souboru | https://github.com/tisnik/most-popular-python-libs/blob/master/numpy/vector_to_file3.py |
4 | vector_to_file4.py | uložení obsahu vektoru s prvky typu „half integer“ do binárního souboru | https://github.com/tisnik/most-popular-python-libs/blob/master/numpy/vector_to_file4.py |
5 | vector_to_file5.py | uložení obsahu vektoru s prvky typu „integer“ do binárního souboru | https://github.com/tisnik/most-popular-python-libs/blob/master/numpy/vector_to_file5.py |
6 | vector_to_file6.py | uložení obsahu vektoru s prvky typu „long integer“ do binárního souboru | https://github.com/tisnik/most-popular-python-libs/blob/master/numpy/vector_to_file6.py |
7 | vector_to_file7.py | uložení obsahu vektoru s prvky typu „single“ do binárního souboru | https://github.com/tisnik/most-popular-python-libs/blob/master/numpy/vector_to_file7.py |
8 | vector_to_file8.py | uložení obsahu vektoru s prvky typu „double“ do binárního souboru | https://github.com/tisnik/most-popular-python-libs/blob/master/numpy/vector_to_file8.py |
9 | vector_to_file9.py | uložení obsahu vektoru s prvky typu „half“ do binárního souboru | https://github.com/tisnik/most-popular-python-libs/blob/master/numpy/vector_to_file9.py |
10 | vector_from_file1.py | načtení obsahu vektoru z textového souboru se specifikací oddělovače | https://github.com/tisnik/most-popular-python-libs/blob/master/numpy/vector_from_file1.py |
11 | vector_from_file2.py | načtení obsahu vektoru z textového souboru se specifikací oddělovače a s konverzí | https://github.com/tisnik/most-popular-python-libs/blob/master/numpy/vector_from_file2.py |
12 | vector_from_file3.py | načtení obsahu vektoru z binárního souboru (nekorektní použití) | https://github.com/tisnik/most-popular-python-libs/blob/master/numpy/vector_from_file3.py |
13 | vector_from_file4.py | načtení obsahu vektoru z binárního souboru s konverzí | https://github.com/tisnik/most-popular-python-libs/blob/master/numpy/vector_from_file4.py |
14 | vector_save.py | uložení obsahu vektoru do standardního binárního souboru | https://github.com/tisnik/most-popular-python-libs/blob/master/numpy/vector_save.py |
15 | vector_load.py | načtení obsahu vektoru ze standardního binárního souboru | https://github.com/tisnik/most-popular-python-libs/blob/master/numpy/vector_load.py |
16 | matrix_save1.py | uložení matice s prvky typu „byte“ do standardního binárního souboru | https://github.com/tisnik/most-popular-python-libs/blob/master/numpy/matrix_save1.py |
17 | matrix_save2.py | uložení matice s prvky typu „float“ do standardního binárního souboru | https://github.com/tisnik/most-popular-python-libs/blob/master/numpy/matrix_save2.py |
18 | matrix_load1.py | načtení matice s prvky typu „byte“ ze standardního binárního souboru | https://github.com/tisnik/most-popular-python-libs/blob/master/numpy/matrix_load1.py |
19 | matrix_load2.py | načtení matice s prvky typu „float“ ze standardního binárního souboru | https://github.com/tisnik/most-popular-python-libs/blob/master/numpy/matrix_load2.py |
20 | matrix_to_file1.py | export obsahu matice do textového souboru | https://github.com/tisnik/most-popular-python-libs/blob/master/numpy/matrix_to_file1.py |
21 | matrix_to_file2.py | export obsahu matice do binárního souboru | https://github.com/tisnik/most-popular-python-libs/blob/master/numpy/matrix_to_file2.py |
22 | matrix_from_file1.py | načtení matice z textového souboru | https://github.com/tisnik/most-popular-python-libs/blob/master/numpy/matrix_from_file1.py |
23 | matrix_from_file2.py | načtení matice z textového souboru s konverzí na jiný typ | https://github.com/tisnik/most-popular-python-libs/blob/master/numpy/matrix_from_file2.py |
24 | matrix_from_file3.py | načtení matice z binárního souboru bez specifikace formátu | https://github.com/tisnik/most-popular-python-libs/blob/master/numpy/matrix_from_file3.py |
25 | matrix_from_file4.py | načtení matice z binárního souboru se specifikací formátu | https://github.com/tisnik/most-popular-python-libs/blob/master/numpy/matrix_from_file4.py |
26 | matrix_to_file3.py | uložení obsahu rozsáhlejší matice do binárního souboru | https://github.com/tisnik/most-popular-python-libs/blob/master/numpy/matrix_to_file3.py |
27 | matrix_from_file5.py | načtení obsahu matice z binárního souboru se specifikací formátu | https://github.com/tisnik/most-popular-python-libs/blob/master/numpy/matrix_from_file5.py |
28 | matrix_from_file6.py | načtení obsahu matice z binárního souboru se specifikací formátu a offsetu | https://github.com/tisnik/most-popular-python-libs/blob/master/numpy/matrix_from_file6.py |
29 | matrix_from_file7.py | načtení obsahu matice z binárního souboru se specifikací formátu, offsetu a počtu prvků | https://github.com/tisnik/most-popular-python-libs/blob/master/numpy/matrix_from_file7.py |
30 | matrix_save3.py | uložení obsahu rozsáhlejší matice do standardního binárního souboru | https://github.com/tisnik/most-popular-python-libs/blob/master/numpy/matrix_save3.py |
31 | matrix_load3.py | přečtení obsahu matice ze standardního binárního souboru | https://github.com/tisnik/most-popular-python-libs/blob/master/numpy/matrix_load3.py |
32 | matrix_load4.py | otevření souboru s maticí a přístup s využitím mmap | https://github.com/tisnik/most-popular-python-libs/blob/master/numpy/matrix_load4.py |
33 | matrix_load5.py | přečtení části rozsáhlé matice s využitím mmap | https://github.com/tisnik/most-popular-python-libs/blob/master/numpy/matrix_load5.py |
34 | matrix_save_multiple.py | uložení více matic do standardního binárního souboru typu npz | https://github.com/tisnik/most-popular-python-libs/blob/master/numpy/matrix_save_multiple.py |
35 | matrix_load_multiple.py | načtení více matic ze standardního binárního souboru typu npz | https://github.com/tisnik/most-popular-python-libs/blob/master/numpy/matrix_load_multiple.py |
36 | matrix_save_multiple2.py | uložení více matic do standardního binárního souboru typu npz se specifikací názvů matic | https://github.com/tisnik/most-popular-python-libs/blob/master/numpy/matrix_save_multiple2.py |
37 | matrix_save_multiple3.py | uložení více matic do standardního komprimovaného binárního souboru typu npz se specifikací názvů matic | https://github.com/tisnik/most-popular-python-libs/blob/master/numpy/matrix_save_multiple3.py |
Demonstrační příklady určené pro knihovnu Pandas:
# | Demonstrační příklad | Stručný popis příkladu | Cesta |
---|---|---|---|
1 | vector_to_file4.py | uložení obsahu vektoru s prvky typu „half integer“ do binárního souboru | https://github.com/tisnik/most-popular-python-libs/blob/master/pandas/vector_to_file4.py |
2 | matrix_to_file2.py | export obsahu matice do binárního souboru | https://github.com/tisnik/most-popular-python-libs/blob/master/pandas/matrix_to_file2.py |
3 | binary_df1.c | vygenerování binárního souboru s prvky různých typů | https://github.com/tisnik/most-popular-python-libs/blob/master/pandas/binary_df1.c |
4 | binary_df2.c | vygenerování binárního souboru s prvky různých typů, včetně řetězců | https://github.com/tisnik/most-popular-python-libs/blob/master/pandas/binary_df2.c |
5 | serie_from_file.py | načtení obsahu datové řady z binárního souboru s konverzí | https://github.com/tisnik/most-popular-python-libs/blob/master/pandas/serie_from_file.py |
6 | dataframe_from_file1.py | načtení obsahu datového rámce z binárního souboru se specifikací formátu společného pro všechny sloupce | https://github.com/tisnik/most-popular-python-libs/blob/master/pandas/dataframe_from_file1.py |
7 | dataframe_from_file2.py | načtení obsahu datového rámce z binárního souboru se specifikací formátu | https://github.com/tisnik/most-popular-python-libs/blob/master/pandas/dataframe_from_file2.py |
8 | dataframe_from_file3.py | načtení obsahu datového rámce z binárního souboru se specifikací formátu i endianity | https://github.com/tisnik/most-popular-python-libs/blob/master/pandas/dataframe_from_file3.py |
9 | dataframe_from_file4.py | načtení obsahu datového rámce z binárního souboru se specifikací formátu i endianity, bytové pole | https://github.com/tisnik/most-popular-python-libs/blob/master/pandas/dataframe_from_file4.py |
10 | dataframe_from_file5.py | načtení obsahu datového rámce z binárního souboru se specifikací formátu i endianity, řetězce | https://github.com/tisnik/most-popular-python-libs/blob/master/pandas/dataframe_from_file5.py |
16. Odkazy na předchozí části seriálu o knihovně Pandas
Popisem knihovny Pandas (a do jisté míry i Numpy) jsme se již na stránkách Roota zabývali. Pod tímto odstavcem naleznete odkazy na jednotlivé články, které již o knihovně Pandas vyšly:
- 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/ - Knihovna Pandas: práce se seskupenými záznamy, vytvoření multiindexů
https://www.root.cz/clanky/knihovna-pandas-prace-se-seskupenymi-zaznamy-vytvoreni-multiindexu/ - Operace s daty uloženými v binárních souborech v knihovnách NumPy a Pandas
https://www.root.cz/clanky/operace-s-daty-ulozenymi-v-binarnich-souborech-v-knihovnach-numpy-a-pandas/
17. Odkazy na Internetu
- mmap(2) — Linux manual page
https://www.man7.org/linux/man-pages/man2/mmap.2.html - mmap (Wikipedia)
https://en.wikipedia.org/wiki/Mmap - ISO/IEC 21320–1:2015 Information technology — Document Container File — Part 1: Core
https://www.iso.org/standard/60101.html - ZIP (file format)
https://en.wikipedia.org/wiki/ZIP_(file_format) - A Simple File Format for NumPy Arrays
https://docs.scipy.org/doc/numpy-1.14.2/neps/npy-format.html - numpy.lib.format
https://numpy.org/devdocs/reference/generated/numpy.lib.format.html - The NumPy array: a structure for efficient numerical computation
https://arxiv.org/pdf/1102.1523.pdf - numpy.ndarray.tofile
https://numpy.org/doc/stable/reference/generated/numpy.ndarray.tofile.html#numpy.ndarray.tofile - numpy.fromfile
https://numpy.org/doc/stable/reference/generated/numpy.fromfile.html - How to read part of binary file with numpy?
https://stackoverflow.com/questions/14245094/how-to-read-part-of-binary-file-with-numpy - How to read binary files in Python using NumPy?
https://stackoverflow.com/questions/39762019/how-to-read-binary-files-in-python-using-numpy - numpy.save
https://numpy.org/doc/stable/reference/generated/numpy.save.html#numpy.save - numpy.load
https://numpy.org/doc/stable/reference/generated/numpy.load.html#numpy.load - Operace typu mmap
https://en.wikipedia.org/wiki/Mmap - Funkce fseek
https://en.cppreference.com/w/c/io/fseek - Funkce ftell
https://en.cppreference.com/w/c/io/ftell - Loading binary data to NumPy/Pandas
https://towardsdatascience.com/loading-binary-data-to-numpy-pandas-9caa03eb0672 - 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