Obsah
1. Xarray: sémantické rozšíření n-rozměrných polí z knihovny NumPy (dokončení)
2. Koordináty obsahující časová razítka
3. Operace nad dvojicí polí s odlišným rozsahem (časových) souřadnic
4. Operace groupby a osa s časovými razítky
5. Rozdělení pole s údaji z celého roku podle dnů v týdnu
6. Obsah jednotlivých polí získaných operací groupby
7. Výpočty nad celými poli získanými operací groupby
8. Algoritmy pro výběr prvků polí
9. Zopakování role operací isel a sel
10. Výběr prvku s využitím neexistující souřadnice
11. Výběr nejbližšího prvku – algoritmus „nearest“
12. Chování operace sel v případě, že souřadnice překročí meze
13. Operace sel a vícerozměrná pole
14. Algoritmus „nearest“ při výběru z vícerozměrných polí
16. Zkrácený zápis výběru a algoritmus „nearest“
18. Repositář s demonstračními příklady
1. Xarray: sémantické rozšíření n-rozměrných polí z knihovny NumPy (dokončení)
Dnes dokončíme popis zajímavé a v některých oblastech velmi užitečné knihovny xarray, jejíž základní popis jsme si uvedli minule. Zabývat se budeme především použitím časových razítek na osách polí, protože se jedná o velmi často řešenou problematiku. S tímto tématem do značné míry souvisí i operace groupby určená pro rozdělení původního pole do více polí na základě zadaných kritérií (což typicky souvisí právě s časovými razítky a časovými intervaly). A nezapomeneme ani na výběr prvků s využitím logických souřadnic. V této oblasti se mnohdy uplatní algoritmus „nearest“, který umožňuje, aby souřadnice nebyly zadány tak, aby ležely přesně ve vrcholech souřadné mřížky, ale aby bylo možné přistoupit k hodnotě na jakémkoli místě souřadného (n-rozměrného) prostoru (to by samozřejmě bylo možné realizovat programově, ale xarray má všechny potřebné informace již připraveny).
2. Koordináty obsahující časová razítka
V předchozím článku jsme si řekli, že jedním z nejpodstatnějších rozdílů mezi běžným n-rozměrným polem knihovny NumPy (tedy datovou strukturou NDArray) a datovou strukturou DataArray z knihovny Xarray je fakt, že v Xarray je možné specifikovat nejenom jména jednotlivých os, ale taktéž souřadnice na těchto osách. Ukázali jsme si i některé příklady, ovšem zajímavější a užitečnější bude příklad pole (pro jednoduchost zůstaneme u jednorozměrného pole – vektoru), které má na ose časová razítka.
Pole s takovými koordináty lze ve skutečnosti vytvořit velmi snadno. Povšimněte si především konverze vektoru se souřadnicemi (koordináty) na datový typ datetime64[ns]. To je provedeno z toho důvodu, aby se zabránilo varování zobrazeného při konstrukci pole:
import numpy as np import xarray as xr measured_at = np.array([ np.datetime64("2023-01-01"), np.datetime64("2023-01-02"), np.datetime64("2023-01-03"), np.datetime64("2023-01-04"), np.datetime64("2023-01-05")]).astype('datetime64[ns]') temperatures = 10 + 20 * np.random.rand(5) array = xr.DataArray(temperatures, name="Temperature measurement", dims=("time",), coords={"time":measured_at}, attrs=dict( units = "centigrees", description ="Local temperature values measured in time serie #1", measured_by = {"name": "ThermometerBot", "vendor": "BIY", "version": (1, 0, 0)} )) print(array) print() print()
Právě vytvořené pole vypadá po své konstrukci následovně. Opět – povšimněte si především nastavených souřadnic:
<xarray.DataArray 'Temperature measurement' (time: 5)> array([24.42617304, 12.76946827, 27.01005185, 27.12089079, 13.26704237]) Coordinates: * time (time) datetime64[ns] 2023-01-01 2023-01-02 ... 2023-01-05 Attributes: units: centigrees description: Local temperature values measured in time serie #1 measured_by: {'name': 'ThermometerBot', 'vendor': 'BIY', 'version': (1, ...
Ruční zadávání časových razítek je pochopitelně mnohdy zbytečné, protože namísto toho můžeme použít funkce (generátory) arange či linspace poskytované knihovnou NumPy. V dalším demonstračním příkladu je vytvořeno 365 časových razítek reprezentujících všechny dny v roce 2023 (povšimněte si způsobu chápání horního limitu u generátoru arange):
import numpy as np import xarray as xr measured_at = np.arange( np.datetime64("2023-01-01"), np.datetime64("2024-01-01"), np.timedelta64(1, "D")).astype('datetime64[ns]') temperatures = 10 + 20 * np.random.rand(365) array = xr.DataArray(temperatures, name="Temperature measurement", dims=("time",), coords={"time":measured_at}, attrs=dict( units = "centigrees", description ="Local temperature values measured in time serie #1", measured_by = {"name": "ThermometerBot", "vendor": "BIY", "version": (1, 0, 0)} )) print(array) print() print()
Z vypsaných výsledků je patrné, že se skutečně vytvořilo pole s jedinou souřadnou osou, na které je vyneseno 365 hodnot (dnů):
<xarray.DataArray 'Temperature measurement' (time: 365)> array([16.69809061, 17.9471186 , 10.69089126, 14.07249443, 20.75962881, 15.16586884, 21.27013911, 20.0414658 , 21.46678879, 24.35425061, 25.89708917, 23.12375698, 21.1931765 , 11.29005738, 24.40243319, 13.55830944, 25.87711127, 24.02223318, 11.30939269, 15.59333975, 24.40714312, 27.54910437, 29.72431042, 26.32393269, 18.47943443, 25.27926622, 15.99778701, 21.97646558, 29.69356852, 18.03158688, 11.89993374, 29.55571228, 18.98800409, 28.02424155, 22.41224725, 10.65068148, 20.22457221, 23.87890981, 18.92442357, 12.42192433, 14.31374106, 17.27557421, 24.67542279, 11.70242191, 11.90913089, 13.29168489, 26.40857275, 27.31246024, 27.74210402, 20.0963876 , 27.66758364, 20.95749067, 13.57811154, 19.15037105, 24.85993313, 26.46056389, 16.47250752, 27.82869884, 16.42734926, 24.8441215 , 29.06835855, 15.79269966, 20.32796479, 28.35527805, 11.25244013, 22.45913564, 24.75168745, 26.51228795, 19.09767414, 15.02188485, 12.25942992, 14.40639456, 14.34841429, 20.69815824, 15.44072352, 10.73828694, 26.82379847, 14.47662358, 23.09668975, 28.88486404, 24.02448014, 19.48073573, 19.40396085, 25.17661352, 27.97567438, 26.97418278, 14.6491084 , 29.61079971, 22.94745502, 24.45475775, 16.17038519, 11.22939573, 26.96102549, 12.07546254, 19.92214118, 25.46811138, 21.63630953, 26.27214023, 18.39894448, 18.45638725, ... 23.97717569, 10.92348384, 22.92346376, 29.68955083, 20.17896718, 26.03322964, 18.26671029, 21.57628268, 27.64036865, 23.6106621 , 10.43337402, 14.23590088, 10.0674822 , 27.138415 , 19.57572504, 23.15388949, 10.96899275, 29.10749052, 17.6301598 , 14.98110912, 17.65642913, 21.20132777, 29.68874887, 16.37432153, 17.83109369, 18.82661965, 26.49180497, 28.23873758, 13.01932509, 18.12355911, 13.51990729, 28.91758677, 18.43136498, 12.60841701, 24.06871448, 15.52078281, 21.03621895, 25.61510147, 13.36782963, 17.60767229, 19.02772397, 13.6248905 , 25.41454055, 25.33607015, 23.55239486, 22.30727979, 24.47298807, 29.0512182 , 19.10730599, 25.41305518, 21.09735286, 13.1892987 , 26.80862567, 20.27117865, 10.68246808, 23.50905274, 22.11559066, 20.72593798, 28.94334155, 25.92037088, 24.30192641, 28.90908114, 28.78343595, 17.69460991, 16.33773279, 29.52980743, 25.13065911, 12.277315 , 28.76075631, 22.31716126, 19.65036415, 20.47742402, 28.27023332, 14.78380202, 21.86892701, 13.49866575, 14.4617529 , 21.53282766, 18.7879031 , 14.82060967, 25.78862457, 17.73334715, 29.04422133, 25.27967342, 22.27591471, 22.8619508 , 13.76574921, 15.91377999, 11.39879063, 25.94333495, 20.73400279, 29.4301411 , 23.36826227, 23.91582252, 17.84809599, 20.12099055, 26.34597634, 25.74065665, 11.17329109, 18.45936967]) Coordinates: * time (time) datetime64[ns] 2023-01-01 2023-01-02 ... 2023-12-31 Attributes: units: centigrees description: Local temperature values measured in time serie #1 measured_by: {'name': 'ThermometerBot', 'vendor': 'BIY', 'version': (1, ...
3. Operace nad dvojicí polí s odlišným rozsahem (časových) souřadnic
Vyzkoušejme si nyní, co se stane ve chvíli, kdy budeme mít dvojici polí, ovšem s poněkud odlišnými souřadnicemi, s nimiž budeme provádět nějaké operace (například součet polí). První pole (vektor) bude obsahovat hodnoty pro první část roku 2023:
import numpy as np import xarray as xr measured_at1 = np.arange( np.datetime64("2023-01-01"), np.datetime64("2023-06-01"), np.timedelta64(1, "D")).astype('datetime64[ns]') temperatures1 = 10 + 20 * np.random.rand(len(measured_at1)) array1 = xr.DataArray(temperatures1, name="Temperature measurement", dims=("time",), coords={"time":measured_at1}, attrs={ "units": "centigrees", "description": "Local temperature values measured in time serie #1", "measured_by": {"name": "ThermometerBot", "vendor": "BIY", "version": (1, 0, 0)} })
Druhý vektor bude obsahovat hodnoty od března do srpna:
measured_at2 = np.arange( np.datetime64("2023-03-01"), np.datetime64("2023-09-01"), np.timedelta64(1, "D")).astype('datetime64[ns]') temperatures2 = 10 + 20 * np.random.rand(len(measured_at2)) array2 = xr.DataArray(temperatures2, name="Temperature measurement", dims=("time",), coords={"time":measured_at2}, attrs={ "units": "centigrees", "description": "Local temperature values measured in time serie #2", "measured_by": {"name": "ThermometerBot", "vendor": "BIY", "version": (1, 0, 0)} })
V dalším kroku se pokusíme obě pole sečíst:
print(array1 + array2) print() print()
Výsledkem bude podle očekávání nové jednorozměrné pole, které ovšem bude obsahovat pouze hodnoty se souřadnicemi (= časovými razítky) mezi březnem a květnem, což je společný průnik obou časových řad:
<xarray.DataArray 'Temperature measurement' (time: 92)> array([39.08330001, 22.39697443, 39.3158045 , 37.62029874, 37.8517195 , 37.40600102, 38.03291695, 27.38973346, 27.96966817, 40.92085726, 49.0424246 , 31.49930257, 39.18036632, 35.42385655, 50.51938957, 22.53491503, 43.45544427, 51.97480936, 34.30217284, 49.05549477, 39.16610119, 37.24460668, 29.13750116, 41.79158629, 44.121824 , 50.98595409, 36.23266048, 28.73140993, 34.38266955, 58.20906019, 32.83250154, 38.91478992, 32.15136311, 31.99814299, 49.8861842 , 43.11384933, 40.6614451 , 55.64066836, 36.05435105, 44.24529579, 45.57250206, 41.2724343 , 39.79041848, 39.86912709, 38.43563982, 43.84765904, 56.14190396, 43.66638008, 31.79688017, 25.6734173 , 25.86267094, 49.89023734, 39.84240294, 48.43538181, 42.7785676 , 33.92913039, 29.74145876, 37.3188353 , 38.88399571, 39.81674006, 39.47094644, 32.99727018, 43.43064681, 45.25053488, 43.89335455, 48.1264902 , 55.76387314, 31.81350268, 32.4660512 , 41.99701592, 42.05934515, 40.83239548, 43.69627854, 39.6858416 , 46.36764653, 38.74761654, 50.15516074, 36.60246749, 43.15104267, 38.50493449, 47.06145523, 41.16953553, 38.81741141, 40.65699449, 35.18871086, 40.88507074, 38.4106508 , 31.93311267, 37.99057185, 50.00273585, 51.86847388, 43.92847762]) Coordinates: * time (time) datetime64[ns] 2023-03-01 2023-03-02 ... 2023-05-31
4. Operace groupby a osa s časovými razítky
Jednou z nejužitečnějších operací s poli nabízenými knihovnou xarray je operace nazvaná groupby. Jak již název této operace naznačuje, je pole rozděleno na větší množství polí na základě zadaného kritéria. Výsledkem je datová struktura Dataset zmíněná minule. Pravděpodobně nejčastěji se pole rozdělují podle údajů (koordinát) zapsaných na nějaké časové ose, přičemž knihovna xarray nabízí i poměrně sofistikovaná kritéria.
Příkladem použití operace groupby může být rozdělení jednorozměrného pole (tedy vektoru), na jehož ose jsou vyneseny časové značky představující všechny dny roku 2023. Tento vektor rozdělíme na čtyři vektory podle ročních období. V prvním výsledném poli budou dny spadající do prosince, ledna, února a března atd. Jednotlivá výsledná pole mají názvy „DJF“, „JJA“, „MAM“ a „SON“, což jsou zkratky odvozené z prvních písmen názvů měsíců v daném období:
import numpy as np import xarray as xr measured_at = np.arange( np.datetime64("2023-01-01"), np.datetime64("2024-01-01"), np.timedelta64(1, "D")).astype('datetime64[ns]') temperatures = 10 + 20 * np.random.rand(365) array = xr.DataArray(temperatures, name="Temperature measurement", dims=("time",), coords={"time":measured_at}, attrs=dict( units = "centigrees", description ="Local temperature values measured in time serie #1", measured_by = {"name": "ThermometerBot", "vendor": "BIY", "version": (1, 0, 0)} )) print(array.groupby("time.season")) print() print()
A takto bude vypadat výsledek:
DataArrayGroupBy, grouped over 'season' 4 groups with labels 'DJF', 'JJA', 'MAM', 'SON'.
Samozřejmě můžeme provádět i další podobné operace, například rozdělení původního pole na dvanáct polí podle měsíců, což je často používané v praxi:
import numpy as np import xarray as xr measured_at = np.arange( np.datetime64("2023-01-01"), np.datetime64("2024-01-01"), np.timedelta64(1, "D")).astype('datetime64[ns]') temperatures = 10 + 20 * np.random.rand(365) array = xr.DataArray(temperatures, name="Temperature measurement", dims=("time",), coords={"time":measured_at}, attrs=dict( units = "centigrees", description ="Local temperature values measured in time serie #1", measured_by = {"name": "ThermometerBot", "vendor": "BIY", "version": (1, 0, 0)} )) print(array.groupby("time.month")) print() print()
Výsledkem jsou pole s názvy „1“, „2“ až „12“ (jedná se o řetězce):
DataArrayGroupBy, grouped over 'month' 12 groups with labels 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12.
5. Rozdělení pole s údaji z celého roku podle dnů v týdnu
Předchozí operace groupby rozdělily původní vektor na několik částí: buď na čtyři pole nebo na 12 polí, přičemž prvky v každém novém poli byly stále uspořádány stejně, jako v poli původním (což ve skutečnosti u příkladu s ročními obdobími není zcela pravda, protože zimní období začíná prosincem). Ovšem rozdělení je možné v případě potřeby provést i jinak, například podle dnů v týdnu. To má také své praktické důvody, například při sledování hustoty dopravy, počtu vydaných obědů v restauraci a dalších vlastností, které závisí na tom, o jaký den v týdnu se jedná (a řekněme méně na konkrétním měsíci). I takový způsob rozdělení pole operace typu groupby nabízí – viz následující příklad:
import numpy as np import xarray as xr measured_at = np.arange( np.datetime64("2023-01-01"), np.datetime64("2024-01-01"), np.timedelta64(1, "D")).astype('datetime64[ns]') temperatures = 10 + 20 * np.random.rand(365) array = xr.DataArray(temperatures, name="Temperature measurement", dims=("time",), coords={"time":measured_at}, attrs=dict( units = "centigrees", description ="Local temperature values measured in time serie #1", measured_by = {"name": "ThermometerBot", "vendor": "BIY", "version": (1, 0, 0)} )) print(array.groupby("time.dayofweek")) print() print()
Výsledkem takto zapsané operace groupby bude datová struktura Dataset obsahující pouze sedm polí, jejichž jména (návěští) budou „0“, „1“ až „6“ (opět se pochopitelně jedná o řetězce):
DataArrayGroupBy, grouped over 'dayofweek' 7 groups with labels 0, 1, 2, 3, 4, 5, 6.
6. Obsah jednotlivých polí získaných operací groupby
V dalším demonstračním příkladu je nejprve původní vektor s hodnotami naměřenými pro všechny dny v roce rozdělen podle dnů v týdnu na sedm nových vektorů uložených do datové struktury Dataset. Posléze je obsah těchto vektorů postupně vypsán na terminál:
import numpy as np import xarray as xr measured_at = np.arange( np.datetime64("2023-01-01"), np.datetime64("2024-01-01"), np.timedelta64(1, "D")).astype('datetime64[ns]') temperatures = 10 + 20 * np.random.rand(365) array = xr.DataArray(temperatures, name="Temperature measurement", dims=("time",), coords={"time":measured_at}, attrs={ "units": "centigrees", "description": "Local temperature values measured in time serie #1", "measured_by": {"name": "ThermometerBot", "vendor": "BIY", "version": (1, 0, 0)} }) grouped = array.groupby("time.dayofweek") for name, group in grouped: print(f"Array/group: {name}") print(group)
Výsledek (zkrácený) bude vypadat následovně:
Array/group: 0 <xarray.DataArray 'Temperature measurement' (time: 52)> array([24.19112597, 27.46228501, 11.06272603, 19.47535722, 21.90839105, 29.28507459, 25.39463838, 27.50084503, 23.90625516, 11.92492741, 16.59059645, 17.76477675, 25.69802797, 21.31843992, 24.56749047, 29.97893866, 11.62499355, 17.39460298, 11.28123725, 29.42650171, 27.77986402, 28.14953127, 15.90162502, 20.36037532, 13.88020205, 27.56137221, 19.11380084, 17.43702898, 19.23403563, 28.40341516, 16.75251694, 14.6735367 , 23.72056639, 24.10970917, 12.46957388, 26.20774144, 13.79778087, 26.53920512, 10.45459565, 25.53695589, 17.49350843, 26.23626461, 14.50675843, 17.08154507, 20.32117202, 11.81172377, 26.16647364, 13.46482212, 15.35602107, 22.40810397, 10.03840427, 29.2934803 ]) Coordinates: * time (time) datetime64[ns] 2023-01-02 2023-01-09 ... 2023-12-25 Attributes: units: centigrees description: Local temperature values measured in time serie #1 measured_by: {'name': 'ThermometerBot', 'vendor': 'BIY', 'version': (1, ... Array/group: 1 <xarray.DataArray 'Temperature measurement' (time: 52)> array([18.40500425, 29.86794871, 20.77310735, 28.36174987, 10.21042199, 25.37662103, 19.6147659 , 22.11128139, 29.33946608, 13.32291607, 21.56506527, 27.4218817 , 26.0743042 , 21.20190991, 23.26819343, 28.49461763, 28.78121913, 23.90906384, 11.55611772, 18.01717672, 24.15991125, 10.297097 , 14.32445564, 27.7100129 , 20.07109125, 17.99284896, 23.6796529 , 28.73283782, 25.28226018, 25.54709046, 25.24141336, 18.12522317, 14.06141045, 24.83846351, 28.8072209 , 29.84369044, 13.9816837 , 12.09319739, 19.84670402, 27.91357661, 28.2336777 , 24.7504579 , 26.79116983, 21.60342932, 27.21936666, 20.31419914, 11.40003037, 20.33443451, 24.38489008, 25.91152872, 10.43260153, 19.56574869]) Coordinates: * time (time) datetime64[ns] 2023-01-03 2023-01-10 ... 2023-12-26 Attributes: units: centigrees description: Local temperature values measured in time serie #1 measured_by: {'name': 'ThermometerBot', 'vendor': 'BIY', 'version': (1, ... Array/group: 2 <xarray.DataArray 'Temperature measurement' (time: 52)> array([20.10526985, 12.07074806, 18.97594393, 26.79980018, 26.00018717, 22.74454939, 14.54705706, 21.38840199, 12.86079937, 21.22812031, 18.38129976, 29.78534125, 14.0229466 , 14.93392276, 13.39795203, 28.38275479, 15.29807613, 14.82036469, 10.61553467, 15.32264321, 25.28673836, 16.49941767, 11.79536739, 28.79704657, 24.23690671, 17.15265509, 27.30252537, 11.26109704, 16.49061453, 25.52158316, 26.90438255, 10.41766229, 19.70646716, 13.79409111, 25.54524613, 11.63935946, 25.91060615, 19.59793907, 15.85999707, 17.0018503 , 19.85062506, 16.35058243, 28.77920221, 14.72744925, 28.33520522, 28.96330656, 21.3036428 , 23.22084282, 21.8220597 , 28.44843363, 29.57274751, 26.25114517]) Coordinates: * time (time) datetime64[ns] 2023-01-04 2023-01-11 ... 2023-12-27 Attributes: units: centigrees description: Local temperature values measured in time serie #1 measured_by: {'name': 'ThermometerBot', 'vendor': 'BIY', 'version': (1, ... Array/group: 3 <xarray.DataArray 'Temperature measurement' (time: 52)> array([27.69838869, 14.74989799, 25.08470384, 12.96825908, 26.73656238, 22.58095914, 28.09668617, 12.64946506, 29.34244579, 29.78444025, 21.07058696, 28.49554376, 22.57645259, 21.59299967, 19.37547962, 22.30235982, 25.56839185, 27.28263874, 16.89016522, 12.85603831, 13.00948724, 20.42918232, 25.74124236, 20.32321635, 14.52442431, 23.65589301, 14.02695301, 19.01846545, 29.3019622 , 25.6949505 , 21.56929064, 15.50573772, 26.76518737, 18.46408979, 12.61863129, 25.88378322, 12.20482438, 22.22400112, 19.01224523, 10.59362492, 14.85628557, 26.17728199, 12.89725971, 22.05238158, 27.46705407, 25.88179483, 29.31646138, 28.96020957, 25.39324172, 11.98994027, 22.68413333, 17.44727855]) Coordinates: * time (time) datetime64[ns] 2023-01-05 2023-01-12 ... 2023-12-28 Attributes: units: centigrees description: Local temperature values measured in time serie #1 measured_by: {'name': 'ThermometerBot', 'vendor': 'BIY', 'version': (1, ... Array/group: 4 <xarray.DataArray 'Temperature measurement' (time: 52)> array([11.35274504, 23.87846193, 18.47447323, 12.11164437, 12.6387716 , 16.88724285, 26.95087732, 21.00773756, 14.2245302 , 28.43677614, 19.61301878, 24.12376894, 22.14000049, 25.9329429 , 20.77930421, 10.67714575, 23.3699305 , 15.8032759 , 11.73956395, 10.50640687, 24.56217383, 10.51224647, 15.6043811 , 23.72800148, 24.99331213, 10.20220201, 27.86577519, 18.12034981, 18.98159548, 17.01498389, 29.17402953, 13.1660347 , 28.49894178, 14.92960635, 10.70636003, 11.14461135, 28.8902448 , 17.42036838, 19.79498743, 10.00638099, 11.29022475, 11.34256278, 16.18591646, 15.1844423 , 20.77302185, 17.38743408, 12.90261999, 19.2384736 , 27.36250961, 10.27131049, 20.34979236, 26.16360191]) Coordinates: * time (time) datetime64[ns] 2023-01-06 2023-01-13 ... 2023-12-29 Attributes: units: centigrees description: Local temperature values measured in time serie #1 measured_by: {'name': 'ThermometerBot', 'vendor': 'BIY', 'version': (1, ... Array/group: 5 <xarray.DataArray 'Temperature measurement' (time: 52)> array([27.81447333, 10.2423783 , 14.36432648, 21.21993795, 20.35035241, 25.10057806, 27.00077673, 29.28706533, 29.25338592, 19.75875879, 15.07224154, 19.0788796 , 28.88857831, 23.01146255, 13.48386631, 18.06881353, 29.46858252, 19.83525248, 16.57271777, 28.03094095, 18.93757992, 25.77476091, 17.62901127, 27.37547793, 21.80345395, 14.80277404, 10.67343844, 28.93986654, 21.39444236, 12.88633284, 12.76525569, 22.43674424, 14.3018384 , 10.0939414 , 17.89860252, 14.43926999, 17.16885531, 10.59145214, 27.4419339 , 12.74038373, 15.81035599, 24.3515538 , 29.31085346, 18.99828825, 23.14403976, 12.95311234, 23.15310332, 16.86813715, 27.09138827, 22.09035768, 27.48213125, 18.45399222]) Coordinates: * time (time) datetime64[ns] 2023-01-07 2023-01-14 ... 2023-12-30 Attributes: units: centigrees description: Local temperature values measured in time serie #1 measured_by: {'name': 'ThermometerBot', 'vendor': 'BIY', 'version': (1, ... Array/group: 6 <xarray.DataArray 'Temperature measurement' (time: 53)> array([22.70227407, 21.75458274, 29.38047238, 12.05946007, 26.80655685, 29.49253674, 27.96736864, 18.36335449, 10.23408297, 10.77397455, 13.31157725, 26.0757748 , 24.19122802, 11.05758169, 26.2807821 , 10.60338207, 21.32784301, 21.42672727, 15.31952073, 21.46662872, 13.48452954, 17.37770037, 18.8722718 , 10.85142333, 25.33919303, 24.46285297, 28.49603264, 14.67223937, 22.51043314, 11.99262845, 26.30048189, 24.59064434, 26.23905318, 21.20254882, 29.40789675, 12.45982529, 12.20464364, 26.89524802, 11.50180701, 20.93822937, 12.53206518, 13.44614048, 24.47031947, 22.03105375, 12.57536833, 20.53527654, 23.37364934, 29.40537761, 17.8899516 , 22.30589286, 21.07269505, 12.94133772, 21.63328235]) Coordinates: * time (time) datetime64[ns] 2023-01-01 2023-01-08 ... 2023-12-31 Attributes: units: centigrees description: Local temperature values measured in time serie #1 measured_by: {'name': 'ThermometerBot', 'vendor': 'BIY', 'version': (1, ...
Pokud budeme chtít zjistit, jaké záznamy jsou v každém z vektorů, můžeme si nechat vypsat pouze koordináty jednotlivých vektorů. Ty se od sebe budou (pochopitelně) lišit, protože každý z vektorů obsahuje pouze podmnožinu z původního vektoru (a tím pádem i podmnožinu koordinát):
import numpy as np import xarray as xr measured_at = np.arange( np.datetime64("2023-01-01"), np.datetime64("2024-01-01"), np.timedelta64(1, "D")).astype('datetime64[ns]') temperatures = 10 + 20 * np.random.rand(365) array = xr.DataArray(temperatures, name="Temperature measurement", dims=("time",), coords={"time":measured_at}, attrs={ "units": "centigrees", "description": "Local temperature values measured in time serie #1", "measured_by": {"name": "ThermometerBot", "vendor": "BIY", "version": (1, 0, 0)} }) grouped = array.groupby("time.dayofweek") for name, group in grouped: print(f"Array/group: {name}") print(group.coords)
Výsledky:
Array/group: 0 Coordinates: * time (time) datetime64[ns] 2023-01-02 2023-01-09 ... 2023-12-25 Array/group: 1 Coordinates: * time (time) datetime64[ns] 2023-01-03 2023-01-10 ... 2023-12-26 Array/group: 2 Coordinates: * time (time) datetime64[ns] 2023-01-04 2023-01-11 ... 2023-12-27 Array/group: 3 Coordinates: * time (time) datetime64[ns] 2023-01-05 2023-01-12 ... 2023-12-28 Array/group: 4 Coordinates: * time (time) datetime64[ns] 2023-01-06 2023-01-13 ... 2023-12-29 Array/group: 5 Coordinates: * time (time) datetime64[ns] 2023-01-07 2023-01-14 ... 2023-12-30 Array/group: 6 Coordinates: * time (time) datetime64[ns] 2023-01-01 2023-01-08 ... 2023-12-31
V první skupině (se jménem „0“) bude den 2.1.2023, 9.1.2023 atd. (začínáme tedy druhý den v roce). Proč tomu tak je prozradí pohled na kalendář:
$ cal 1 2023 January 2023 Su Mo Tu We Th Fr Sa 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
7. Výpočty nad celými poli získanými operací groupby
Pole, která jsou získána operací groupby (a uložena do struktury Dataset) jsou stále plnohodnotnými poli typu DataArray. To mj. znamená, že je nad nimi možné provádět různé výpočty. Pokusme se například spočítat průměrnou teplotu v každém měsíci roku 2023. Pro tento účel použijeme metodu DataArray.mean, jejíž výsledky pro větší čitelnost převedeme na celá čísla. Samotný výpočet je v takovém případě triviální:
grouped = array.groupby("time.month") for name, group in grouped: print(f"Month: {name}") print(f"Average temperature: {int(group.mean())}")
A takto vypadá úplný skript, který vytvoří původní pole, potom ho rozdělí na měsíce a následně vypočte průměrnou teplotu pro každý měsíc:
import numpy as np import xarray as xr measured_at = np.arange( np.datetime64("2023-01-01"), np.datetime64("2024-01-01"), np.timedelta64(1, "D")).astype('datetime64[ns]') temperatures = 10 + 20 * np.random.rand(365) array = xr.DataArray(temperatures, name="Temperature measurement", dims=("time",), coords={"time":measured_at}, attrs={ "units": "centigrees", "description": "Local temperature values measured in time serie #1", "measured_by": {"name": "ThermometerBot", "vendor": "BIY", "version": (1, 0, 0)} }) grouped = array.groupby("time.month") for name, group in grouped: print(f"Month: {name}") print(f"Average temperature: {int(group.mean())}")
Výsledky (kvůli náhodnému generování teplot pochopitelně neodpovídají reálným datům):
Month: 1 Average temperature: 19 Month: 2 Average temperature: 21 Month: 3 Average temperature: 19 Month: 4 Average temperature: 18 Month: 5 Average temperature: 20 Month: 6 Average temperature: 19 Month: 7 Average temperature: 20 Month: 8 Average temperature: 18 Month: 9 Average temperature: 20 Month: 10 Average temperature: 19 Month: 11 Average temperature: 21 Month: 12 Average temperature: 20
8. Algoritmy pro výběr prvků polí
Vzhledem k tomu, že knihovna xarray nabízí programátorům pole s fyzickými a současně i logickými souřadnicemi (tj. s celočíselnými indexy prvků i s osami, na nichž mohou být prakticky libovolné hodnoty), rozšiřuje se i počet možností, jak k prvkům polí přistupovat. Základem je stále přístup přes indexy, s čímž souvisí i podpora pro tvorbu řezů (slice) či „vektorizovaných indexů“ (pole se indexuje jiným polem), což jsou koncepty plně převzaté z knihovny NumPy. Navíc je však umožněn i přístup k prvkům přes logické souřadnice – a zde se nabízí otázka, co se stane ve chvíli, kdy se budeme snažit vyhledat prvek na souřadnici, která na ose není explicitně vynesena.
Praktickým příkladem může být 2D pole obsahující teploty naměřené v pravidelné mřížce. Každý vrchol mřížky odpovídá souřadnicím na Zemi (šířka, délka). Ovšem existuje celkem logický požadavek na možnost zjištění teploty v libovolném bodě plochy, nejenom na vrcholech mřížky. To knihovna xarray umožňuje, protože je možné použít algoritmus, který nalezne nejbližší bod (tedy hodnotu) a tu vrátí (a navíc je zde i možnost realizace složitějších algoritmů).
9. Zopakování role operací isel a sel
Připomeňme si, že k prvkům n-dimenzionálních polí (což jsou buď přímo skalární prvky, vektory, matice nebo další pole) lze přistupovat s využitím operace nazvané isel, která umožňuje specifikovat osu (její název) a současně i index (nikoli souřadnici!) na této ose. To je mnohdy mnohem čitelnější, než použití pozičních argumentů. Ostatně si to můžeme velmi snadno otestovat na příkladu pole s jednou dimenzí (a souřadnicemi, které zde ovšem přímo nevyužijeme):
import numpy as np import xarray as xr values = np.arange(0, 10) xcoords = np.linspace(20, 2, 10) array = xr.DataArray(values, name="Temperature measurement", dims=("x",), coords={"x":xcoords}) print(array) print() print(array.isel(x=0)) print() print(array.isel(x=4)) print() print(array.isel(x=9))
Výsledky by v tomto případě měly být snadno pochopitelné (viz zvýrazněné části textu):
<xarray.DataArray 'Temperature measurement' (x: 10)> array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) Coordinates: * x (x) float64 20.0 18.0 16.0 14.0 12.0 10.0 8.0 6.0 4.0 2.0 <xarray.DataArray 'Temperature measurement' ()> array(0) Coordinates: x float64 20.0 <xarray.DataArray 'Temperature measurement' ()> array(4) Coordinates: x float64 12.0 <xarray.DataArray 'Temperature measurement' ()> array(9) Coordinates: x float64 2.0
Ještě užitečnější je v praxi možnost specifikace osy a souřadnice na této ose (tedy nikoli indexu). Pro tento účel je určena operace nazvaná sel, kterou si opět otestujeme:
import numpy as np import xarray as xr values = np.arange(0, 10) xcoords = np.linspace(20, 2, 10) array = xr.DataArray(values, name="Temperature measurement", dims=("x",), coords={"x":xcoords}) print(array) print() print(array.sel(x=20)) print() print(array.sel(x=10)) print() print(array.sel(x=2))
Výsledky jsou nyní zajímavé – byly vybrány prvky s hodnotami 0, 5 a 9 (stále pole) ze souřadnic 20, 10 a 2:
<xarray.DataArray 'Temperature measurement' (x: 10)> array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) Coordinates: * x (x) float64 20.0 18.0 16.0 14.0 12.0 10.0 8.0 6.0 4.0 2.0 <xarray.DataArray 'Temperature measurement' ()> array(0) Coordinates: x float64 20.0 <xarray.DataArray 'Temperature measurement' ()> array(5) Coordinates: x float64 10.0 <xarray.DataArray 'Temperature measurement' ()> array(9) Coordinates: x float64 2.0
10. Výběr prvku s využitím neexistující souřadnice
Pokusme se nyní ze stejného pole, jaké jsme vytvořili v předchozím demonstračním příkladu, získat prvky se souřadnicemi 19, 9 a 1. Tyto prvky neexistují, protože validní souřadnice prvků jsou 20, 18, 16 … 2:
import numpy as np import xarray as xr values = np.arange(0, 10) xcoords = np.linspace(20, 2, 10) array = xr.DataArray(values, name="Temperature measurement", dims=("x",), coords={"x":xcoords}) print(array) print() print(array.sel(x=19)) print() print(array.sel(x=9)) print() print(array.sel(x=1))
Při pokusu o spuštění tohoto skriptu dojde k vyhození výjimky:
Traceback (most recent call last): File "/home/ptisnovs/.local/lib/python3.8/site-packages/pandas/core/indexes/base.py", line 3653, in get_loc return self._engine.get_loc(casted_key) File "pandas/_libs/index.pyx", line 147, in pandas._libs.index.IndexEngine.get_loc File "pandas/_libs/index.pyx", line 176, in pandas._libs.index.IndexEngine.get_loc File "pandas/_libs/hashtable_class_helper.pxi", line 1698, in pandas._libs.hashtable.Float64HashTable.get_item File "pandas/_libs/hashtable_class_helper.pxi", line 1722, in pandas._libs.hashtable.Float64HashTable.get_item KeyError: 19.0
11. Výběr nejbližšího prvku – algoritmus „nearest“
V praxi se velmi často dostaneme do situace, v níž se vybírá prvek na souřadnici, která přesně neodpovídá žádnému prvku. Knihovna xarray nám v tomto případě nabízí poměrně elegantní řešení – můžeme použít algoritmus „nearest“, který se pro zadanou souřadnici pokusí nalézt nejbližší prvek a vrátit jeho hodnotu. Použití tohoto algoritmu je snadné, jak je to ostatně patrné i z následujícího demonstračního příkladu:
import numpy as np import xarray as xr values = np.arange(0, 10) xcoords = np.linspace(20, 2, 10) array = xr.DataArray(values, name="Temperature measurement", dims=("x",), coords={"x":xcoords}) print(array) print() print(array.sel(x=19, method="nearest")) print() print(array.sel(x=9, method="nearest")) print() print(array.sel(x=1, method="nearest"))
I když hledáme prvky na souřadnicích 19, 9 a 1, nedojde nyní k běhové chybě, ale vrátí se nejbližší prvky (což je vidět i na hodnotách Coordinates):
<xarray.DataArray 'Temperature measurement' (x: 10)> array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) Coordinates: * x (x) float64 20.0 18.0 16.0 14.0 12.0 10.0 8.0 6.0 4.0 2.0 <xarray.DataArray 'Temperature measurement' ()> array(0) Coordinates: x float64 20.0 <xarray.DataArray 'Temperature measurement' ()> array(5) Coordinates: x float64 10.0 <xarray.DataArray 'Temperature measurement' ()> array(9) Coordinates: x float64 2.0
12. Chování operace sel v případě, že souřadnice překročí meze
Algoritmus „nearest“ určený pro výběr prvků z pole umožňuje, aby zadaná souřadnice přesáhla meze na zvolené souřadné ose. Hodnota souřadnice může být příliš velká nebo naopak příliš malá. V takovém případě neskončí skript s chybou, ale vybere se buď první nebo naopak poslední prvek z pole (protože jeden z těchto prvků bude mít souřadnici skutečně nejblíže ke specifikované souřadnici).
Vyzkoušejme si výběr prvku se souřadnicí 1000 a –1000, a to přesto, že hodnoty v poli odpovídají souřadnicím v rozsahu 2 až 20:
import numpy as np import xarray as xr values = np.arange(0, 10) xcoords = np.linspace(20, 2, 10) array = xr.DataArray(values, name="Temperature measurement", dims=("x",), coords={"x":xcoords}) print(array) print() print(array.sel(x=1000, method="nearest")) print() print(array.sel(x=-1000, method="nearest"))
Výsledkem běhu tohoto skriptu budou prvky s hodnotami 0 a 9, jejichž souřadnice jsou rovny 20 a 2:
<xarray.DataArray 'Temperature measurement' (x: 10)> array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) Coordinates: * x (x) float64 20.0 18.0 16.0 14.0 12.0 10.0 8.0 6.0 4.0 2.0 <xarray.DataArray 'Temperature measurement' ()> array(0) Coordinates: x float64 20.0 <xarray.DataArray 'Temperature measurement' ()> array(9) Coordinates: x float64 2.0
13. Operace sel a vícerozměrná pole
Operaci sel, tedy výběr prvků na základě specifikovaných souřadnic, je možné použít i pro vícerozměrná pole. První možnost spočívá ve zřetězení těchto operací, což je možné, protože výsledkem každé operace sel je nová struktura typu DataArray. Takto můžeme pokračovat ve „snižování dimenzí“ až do chvíle, kdy získáme jednoprvkové pole typu DataArray:
import numpy as np import xarray as xr temperatures = np.arange(0, 300).reshape((10, 10, 3)) xcoords = np.linspace(10, 100, 10) ycoords = np.linspace(-100, -10, 10) times = ["2023-10-01", "2023-10-02", "2023-10-03"] array = xr.DataArray(temperatures, name="Temperature measurement", dims=("x", "y", "time"), coords={"x":xcoords, "y":ycoords, "time":times}, attrs={ "units": "centigrees", "description": "Local temperature values measured in grid", "measured_by": {"name": "ThermometerBot", "vendor": "BIY", "version": (1, 0, 0)} }) print(array) print() print(array.sel(time="2023-10-02").sel(x=50).sel(y=-50)) print()
Výsledek – původní pole a nové jednoprvkové pole:
<xarray.DataArray 'Temperature measurement' (x: 10, y: 10, time: 3)> array([[[ 0, 1, 2], [ 3, 4, 5], [ 6, 7, 8], [ 9, 10, 11], [ 12, 13, 14], [ 15, 16, 17], [ 18, 19, 20], [ 21, 22, 23], [ 24, 25, 26], [ 27, 28, 29]], ... ... ... [[ 30, 31, 32], [[270, 271, 272], [273, 274, 275], [276, 277, 278], [279, 280, 281], [282, 283, 284], [285, 286, 287], [288, 289, 290], [291, 292, 293], [294, 295, 296], [297, 298, 299]]]) Coordinates: * x (x) float64 10.0 20.0 30.0 40.0 50.0 60.0 70.0 80.0 90.0 100.0 * y (y) float64 -100.0 -90.0 -80.0 -70.0 ... -40.0 -30.0 -20.0 -10.0 * time (time) <U10 '2023-10-01' '2023-10-02' '2023-10-03' Attributes: units: centigrees description: Local temperature values measured in grid measured_by: {'name': 'ThermometerBot', 'vendor': 'BIY', 'version': (1, ... <xarray.DataArray 'Temperature measurement' ()> array(136) Coordinates: x float64 50.0 y float64 -50.0 time <U10 '2023-10-02' Attributes: units: centigrees description: Local temperature values measured in grid measured_by: {'name': 'ThermometerBot', 'vendor': 'BIY', 'version': (1, ...
14. Algoritmus „nearest“ při výběru z vícerozměrných polí
V případě, že se pokusíme z pole vybrat prvek (tedy pole s nižší dimenzí) pomocí operace sel a nezadáme přesnou souřadnici prvku, dojde opět k vyhození výjimky:
import numpy as np import xarray as xr temperatures = np.arange(0, 300).reshape((10, 10, 3)) xcoords = np.linspace(10, 100, 10) ycoords = np.linspace(-100, -10, 10) times = ["2023-10-01", "2023-10-02", "2023-10-03"] array = xr.DataArray(temperatures, name="Temperature measurement", dims=("x", "y", "time"), coords={"x":xcoords, "y":ycoords, "time":times}, attrs={ "units": "centigrees", "description": "Local temperature values measured in grid", "measured_by": {"name": "ThermometerBot", "vendor": "BIY", "version": (1, 0, 0)} }) print(array) print() print(array.sel(time="2023-10-02").sel(x=51).sel(y=-51)) print()
Výjimka:
Traceback (most recent call last): File "/home/ptisnovs/.local/lib/python3.8/site-packages/pandas/core/indexes/base.py", line 3653, in get_loc return self._engine.get_loc(casted_key) File "pandas/_libs/index.pyx", line 147, in pandas._libs.index.IndexEngine.get_loc File "pandas/_libs/index.pyx", line 176, in pandas._libs.index.IndexEngine.get_loc File "pandas/_libs/hashtable_class_helper.pxi", line 1698, in pandas._libs.hashtable.Float64HashTable.get_item File "pandas/_libs/hashtable_class_helper.pxi", line 1722, in pandas._libs.hashtable.Float64HashTable.get_item KeyError: 51.0
Ovšem nic nám pochopitelně nebrání ve využití již výše popsaného algoritmu „nearest“ určeného pro výběr nejbližšího prvku (tedy nejbližšího pole s nižší dimenzí):
import numpy as np import xarray as xr temperatures = np.arange(0, 300).reshape((10, 10, 3)) xcoords = np.linspace(10, 100, 10) ycoords = np.linspace(-100, -10, 10) times = ["2023-10-01", "2023-10-02", "2023-10-03"] array = xr.DataArray(temperatures, name="Temperature measurement", dims=("x", "y", "time"), coords={"x":xcoords, "y":ycoords, "time":times}, attrs={ "units": "centigrees", "description": "Local temperature values measured in grid", "measured_by": {"name": "ThermometerBot", "vendor": "BIY", "version": (1, 0, 0)} }) print(array) print() print(array.sel(time="2023-10-02").sel(x=51, method="nearest").sel(y=-51, method="nearest")) print()
Nyní již bude vše funkční, ovšem souřadnice vybraného prvku budou odlišné od zadaných souřadnic (což je ovšem očekávatelné):
<xarray.DataArray 'Temperature measurement' ()> array(136) Coordinates: x float64 50.0 y float64 -50.0 time <U10 '2023-10-02' Attributes: units: centigrees description: Local temperature values measured in grid measured_by: {'name': 'ThermometerBot', 'vendor': 'BIY', 'version': (1, ...
15. Zkrácený zápis výběru
V předchozích kapitolách jsme si ukázali, jakým způsobem je možné operace výběru zřetězit (protože výsledkem je vždy datová struktura typu DataArray). Takový zápis ovšem není příliš stručný, což je jasně patrné z následujícího úryvku kódu:
array.sel(time="2023-10-02").sel(x=50).sel(y=-50)
Knihovna xarray nám v takových případech umožňuje zápis zkrátit tak, že se provede pouze jediná operace sel, které se předají všechny souřadnice:
array.sel(time="2023-10-02", x=50, y=-50)
Vyzkoušejme si tedy tuto operaci:
import numpy as np import xarray as xr temperatures = np.arange(0, 300).reshape((10, 10, 3)) xcoords = np.linspace(10, 100, 10) ycoords = np.linspace(-100, -10, 10) times = ["2023-10-01", "2023-10-02", "2023-10-03"] array = xr.DataArray(temperatures, name="Temperature measurement", dims=("x", "y", "time"), coords={"x":xcoords, "y":ycoords, "time":times}, attrs={ "units": "centigrees", "description": "Local temperature values measured in grid", "measured_by": {"name": "ThermometerBot", "vendor": "BIY", "version": (1, 0, 0)} }) print(array) print() print(array.sel(time="2023-10-02", x=50, y=-50)) print()
Výsledkem bude (opět) struktura typu DataArray s jediným prvkem (s přesně stanovenými souřadnicemi):
<xarray.DataArray 'Temperature measurement' ()> array(136) Coordinates: x float64 50.0 y float64 -50.0 time <U10 '2023-10-02' Attributes: units: centigrees description: Local temperature values measured in grid measured_by: {'name': 'ThermometerBot', 'vendor': 'BIY', 'version': (1, ...
16. Zkrácený zápis výběru a algoritmus „nearest“
Současná verze knihovny xarray neumožňuje zkombinovat specifikaci algoritmu „nearest“ společně se specifikací textových hodnot na nějaké ose (což vlastně je logické a očekávatelné). Ovšem to v důsledku znamená, že nelze zapsat a vykonat tuto operaci výběru:
array.sel(time="2023-10-02", x=51, y=-51, method="nearest")
Namísto toho je nutné tuto operaci sel rozdělit na dvě operace, a to například následujícím způsobem:
array.sel(time="2023-10-02").sel(x=51, y=-51, method="nearest")
Skript, který tuto operaci provede, může vypadat následovně:
import numpy as np import xarray as xr temperatures = np.arange(0, 300).reshape((10, 10, 3)) xcoords = np.linspace(10, 100, 10) ycoords = np.linspace(-100, -10, 10) times = ["2023-10-01", "2023-10-02", "2023-10-03"] array = xr.DataArray(temperatures, name="Temperature measurement", dims=("x", "y", "time"), coords={"x":xcoords, "y":ycoords, "time":times}, attrs={ "units": "centigrees", "description": "Local temperature values measured in grid", "measured_by": {"name": "ThermometerBot", "vendor": "BIY", "version": (1, 0, 0)} }) print(array) print() print(array.sel(time="2023-10-02").sel(x=51, y=-51, method="nearest")) print()
Výsledkem bude pole (typu DataArray) s jediným prvkem, jehož hodnota a souřadnice jsou zobrazeny níže:
<xarray.DataArray 'Temperature measurement' ()> array(136) Coordinates: x float64 50.0 y float64 -50.0 time <U10 '2023-10-02' Attributes: units: centigrees description: Local temperature values measured in grid measured_by: {'name': 'ThermometerBot', 'vendor': 'BIY', 'version': (1, ...
17. Kam dál?
Knihovna xarray nabízí uživatelům (což většinou nejsou profesionální programátoři) i další zajímavé vlastnosti a operace. Jedná se například o operace resample, rolling, coarsen atd. A nesmíme zapomenout ani na poměrně dobře realizovanou integraci s Jupyter Notebookem a s knihovnou Matplotlib. Těmto tématům se ovšem budeme věnovat později v samostatném článku.
18. Repositář s demonstračními příklady
Všechny Pythonovské skripty, které jsme si ukázali v dnešním článku, naleznete na adrese https://github.com/tisnik/most-popular-python-libs. Následují odkazy na jednotlivé příklady (pro jejich spuštění je nutné mít nainstalovánu knihovnu xarray a její závislosti, zejména Numpy):
19. Odkazy na Internetu
- Xarray documentation
https://docs.xarray.dev/en/stable/index.html - Xarray na stránkách PyPi
https://pypi.org/project/xarray/ - Xarray tutorial
https://tutorial.xarray.dev/intro.html - Repositář knihovny xarray
https://github.com/pydata/xarray - A Simple File Format for NumPy Arrays
https://docs.scipy.org/doc/numpy-1.14.2/neps/npy-format.html - Stránky projektu Numpy
https://numpy.org/ - Stránky projektu Pandas
https://pandas.pydata.org/ - 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 - A Gentle Introduction to Pandas Data Analysis (on Kaggle)
https://www.youtube.com/watch?v=_Eb0utIRdkw&list=PL7RwtdVQXQ8oYpuIIDWR0SaaSCe8ZeZ7t&index=4 - Speed Up Your Pandas Dataframes
https://www.youtube.com/watch?v=u4_c2LDi4b8&list=PL7RwtdVQXQ8oYpuIIDWR0SaaSCe8ZeZ7t&index=5 - 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 - SciPy
https://scipy.org/ - Načítání a ukládání dat uložených v N-rozměrných polích v jazyku Go
https://www.root.cz/clanky/nacitani-a-ukladani-dat-ulozenych-v-n-rozmernych-polich-v-jazyku-go/ - Network Common Data Form (NetCDF)
https://www.unidata.ucar.edu/software/netcdf/ - xray: N D Labeled Arrays and Datasets | SciPy 2015 | Stephan Hoyer
https://www.youtube.com/watch?v=X0pAhJgySxk - Xarray Tutorial | xarray fundamentals (Youtube, přetočte si prvních osm nebo devět minut přípravy)
https://www.youtube.com/watch?v=a339Q5F48UQ