Xarray: sémantické rozšíření n-rozměrných polí z knihovny NumPy (dokončení)

2. 11. 2023
Doba čtení: 29 minut

Sdílet

Autor: PCWorld.com
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 souřadnicových osách a operací typu groupby.

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í

15. Zkrácený zápis výběru

16. Zkrácený zápis výběru a algoritmus „nearest“

17. Kam dál?

18. Repositář s demonstračními příklady

19. Odkazy na Internetu

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'.
Poznámka: velikosti jednotlivých polí mohou být (a zde jsou) rozdílné.

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))
Poznámka: povšimněte si souřadnic – probíhají od 20 do 2 s krokem 2 (resp. -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, ...
Poznámka: povšimněte si, že díky tomu, že jsou zachovány osy i souřadnice, víme o vybraném prvku nejenom jeho hodnotu, ale i jeho přesné souřadnice v původním i novém poli.

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")
Poznámka: v tomto případě na pořadí operací sel nemusí záležet.

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:

bitcoin_smenarna

<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):

# Příklad Stručný popis Adresa
1 01_help.py vestavěná nápověda k balíčku xarray https://github.com/tisnik/most-popular-python-libs/blob/master/xarray/01_help.py
2 02_raw_xarray.py konstrukce instance typu DataArray https://github.com/tisnik/most-popular-python-libs/blob/master/xarray/02_raw_xa­rray.py
3 03_xarray_metadata.py metadata přidaná k datové struktuře DataArray: specifikace dimenzí https://github.com/tisnik/most-popular-python-libs/blob/master/xarray/03_xa­rray_metadata.py
4 04_xarray_metadata.py metadata přidaná k datové struktuře DataArray: specifikace koordinát pro jednu dimenzi https://github.com/tisnik/most-popular-python-libs/blob/master/xarray/04_xa­rray_metadata.py
5 05_xarray_metadata.py metadata přidaná k datové struktuře DataArray: specifikace koordinát pro dvojici dimenzí https://github.com/tisnik/most-popular-python-libs/blob/master/xarray/05_xa­rray_metadata.py
6 06_xarray_metadata.py metadata přidaná k datové struktuře DataArray: specifikace jména n-dimenzionálního pole https://github.com/tisnik/most-popular-python-libs/blob/master/xarray/06_xa­rray_metadata.py
7 07_xarray_custom_attributes.py přidání vlastních atributů k datové struktuře DataArray https://github.com/tisnik/most-popular-python-libs/blob/master/xarray/07_xa­rray_custom_attributes.py
8 08_xarray_custom_attributes.py přidání vlastních atributů k datové struktuře DataArray https://github.com/tisnik/most-popular-python-libs/blob/master/xarray/08_xa­rray_custom_attributes.py
9 09_multiple_dimensions.py vícedimenzionální pole a jeho atributy (různé koordináty) https://github.com/tisnik/most-popular-python-libs/blob/master/xarray/09_mul­tiple_dimensions.py
10 10_multiple_dimensions.py vícedimenzionální pole a jeho atributy (tři rozměry) https://github.com/tisnik/most-popular-python-libs/blob/master/xarray/10_mul­tiple_dimensions.py
11 11_properties.py přečtení vlastností vícedimenzionálních polí https://github.com/tisnik/most-popular-python-libs/blob/master/xarray/11_pro­perties.py
12 12_netcdf_write.py uložení polí do formátu NetCDF https://github.com/tisnik/most-popular-python-libs/blob/master/xarray/12_net­cdf_write.py
13 13_get_coordinate.py získání zvolených koordinát https://github.com/tisnik/most-popular-python-libs/blob/master/xarray/13_get_co­ordinate.py
14 14_get_coordinate.py získání zvolených koordinát https://github.com/tisnik/most-popular-python-libs/blob/master/xarray/14_get_co­ordinate.py
15 15_get_by_coordinate.py přečtení části pole na základě zadaných koordinát https://github.com/tisnik/most-popular-python-libs/blob/master/xarray/15_get_by_co­ordinate.py
16 16_sel_isel.py výběr prvků pole metodami set a isel https://github.com/tisnik/most-popular-python-libs/blob/master/xarray/16_sel_isel.py
17 17_basic_math.py matematické operace nad celými poli (jako v Numpy) https://github.com/tisnik/most-popular-python-libs/blob/master/xarray/17_ba­sic_math.py
18 18_condition.py podmínka aplikovaná na všechny prvky pole https://github.com/tisnik/most-popular-python-libs/blob/master/xarray/18_con­dition.py
19 19_where.py transformace pole s využitím podmínky where https://github.com/tisnik/most-popular-python-libs/blob/master/xarray/19_where.py
20 21_dataset.py základní použití datového typu Dataset https://github.com/tisnik/most-popular-python-libs/blob/master/xarray/21_dataset.py
21 21_dataset.py kontrola, zda mají pole totožnou velikost https://github.com/tisnik/most-popular-python-libs/blob/master/xarray/21_dataset.py
22 22_dataset.py komplikovanější definice instance datového typu Dataset https://github.com/tisnik/most-popular-python-libs/blob/master/xarray/22_dataset.py
23 23_math_coords.py operace nad poli s odlišnou souřadnou mřížkou https://github.com/tisnik/most-popular-python-libs/blob/master/xarray/23_mat­h_coords.py
24 24_chess.py reprezentace šachovnice https://github.com/tisnik/most-popular-python-libs/blob/master/xarray/24_chess.py
       
25 25_1D_time.py jednorozměrné pole, jehož souřadnice jsou časovými razítky (explicitně zadanými) https://github.com/tisnik/most-popular-python-libs/blob/master/xarray/25_1D_time.py
26 26_1D_time.py jednorozměrné pole, jehož souřadnice jsou časovými razítky (vypočtenými přes arange) https://github.com/tisnik/most-popular-python-libs/blob/master/xarray/26_1D_time.py
27 27_1D_time_add.py operace nad jednorozměrnými poli se souřadnicemi, jenž jsou časovými razítky (průnik podle časové osy) https://github.com/tisnik/most-popular-python-libs/blob/master/xarray/27_1D_ti­me_add.py
       
28 28_time_groupby.py operace typu group_by nad 1D polem, jehož souřadnice jsou časovými razítky (po ročních obdobích) https://github.com/tisnik/most-popular-python-libs/blob/master/xarray/28_ti­me_groupby.py
29 29_time_groupby.py operace typu group_by nad 1D polem, jehož souřadnice jsou časovými razítky (po měsících) https://github.com/tisnik/most-popular-python-libs/blob/master/xarray/29_ti­me_groupby.py
30 30_time_groupby.py operace typu group_by nad 1D polem, jehož souřadnice jsou časovými razítky (jednotlivé dny v týdnu) https://github.com/tisnik/most-popular-python-libs/blob/master/xarray/30_ti­me_groupby.py
31 31_time_groupby.py operace typu group_by nad 1D polem, jehož souřadnice jsou časovými razítky (výpis polí) https://github.com/tisnik/most-popular-python-libs/blob/master/xarray/31_ti­me_groupby.py
32 32_time_groupby.py operace typu group_by nad 1D polem, jehož souřadnice jsou časovými razítky (výpis souřadnic) https://github.com/tisnik/most-popular-python-libs/blob/master/xarray/32_ti­me_groupby.py
33 33_time_group_by_mean.py výpočet průměrné hodnoty pro pole rozdělené do skupin https://github.com/tisnik/most-popular-python-libs/blob/master/xarray/33_ti­me_group_by_mean.py
       
34 34_select_isel.py výběr prvků polí operací isel https://github.com/tisnik/most-popular-python-libs/blob/master/xarray/34_se­lect_isel.py
35 35_select_sel.py výběr prvků polí operací sel https://github.com/tisnik/most-popular-python-libs/blob/master/xarray/35_se­lect_sel.py
36 36_select_middle.py pokus o výběr prvků zadáním neexistující souřadnice (pád skriptu) https://github.com/tisnik/most-popular-python-libs/blob/master/xarray/36_se­lect_middle.py
37 37_select_nearest.py algoritmus výběru „nearest“ (bez pádu skriptu) https://github.com/tisnik/most-popular-python-libs/blob/master/xarray/37_se­lect_nearest.py
38 38_select_limits.py algoritmus výběru „nearest“ a výběr prvků pole mimo limity (meze) https://github.com/tisnik/most-popular-python-libs/blob/master/xarray/38_se­lect_limits.py
       
39 39_select_3D.py výběr z 3D pole operací sel https://github.com/tisnik/most-popular-python-libs/blob/master/xarray/39_se­lect_3D.py
40 40_select_3D.py pokus o výběr prvků zadáním neexistující souřadnice https://github.com/tisnik/most-popular-python-libs/blob/master/xarray/40_se­lect_3D.py
41 41_select_3D.py algoritmus výběru „nearest“ a 3D pole https://github.com/tisnik/most-popular-python-libs/blob/master/xarray/41_se­lect_3D.py
42 42_multiple_select_3D.py zkrácený zápis výběru operací sel https://github.com/tisnik/most-popular-python-libs/blob/master/xarray/42_mul­tiple_select_3D.py
43 43_multiple_select_3D.py zkrácený zápis výběru operací sel https://github.com/tisnik/most-popular-python-libs/blob/master/xarray/43_mul­tiple_select_3D.py

19. Odkazy na Internetu

  1. Xarray documentation
    https://docs.xarray.dev/en/sta­ble/index.html
  2. Xarray na stránkách PyPi
    https://pypi.org/project/xarray/
  3. Xarray tutorial
    https://tutorial.xarray.dev/in­tro.html
  4. Repositář knihovny xarray
    https://github.com/pydata/xarray
  5. A Simple File Format for NumPy Arrays
    https://docs.scipy.org/doc/numpy-1.14.2/neps/npy-format.html
  6. Stránky projektu Numpy
    https://numpy.org/
  7. Stránky projektu Pandas
    https://pandas.pydata.org/
  8. numpy.lib.format
    https://numpy.org/devdocs/re­ference/generated/numpy.lib­.format.html
  9. The NumPy array: a structure for efficient numerical computation
    https://arxiv.org/pdf/1102.1523.pdf
  10. A Gentle Introduction to Pandas Data Analysis (on Kaggle)
    https://www.youtube.com/wat­ch?v=_Eb0utIRdkw&list=PL7RwtdVQXQ8o­YpuIIDWR0SaaSCe8ZeZ7t&index=4
  11. Speed Up Your Pandas Dataframes
    https://www.youtube.com/wat­ch?v=u4_c2LDi4b8&list=PL7RwtdVQXQ8o­YpuIIDWR0SaaSCe8ZeZ7t&index=5
  12. numpy.ndarray.tofile
    https://numpy.org/doc/sta­ble/reference/generated/num­py.ndarray.tofile.html#num­py.ndarray.tofile
  13. numpy.fromfile
    https://numpy.org/doc/sta­ble/reference/generated/num­py.fromfile.html
  14. How to read part of binary file with numpy?
    https://stackoverflow.com/qu­estions/14245094/how-to-read-part-of-binary-file-with-numpy
  15. How to read binary files in Python using NumPy?
    https://stackoverflow.com/qu­estions/39762019/how-to-read-binary-files-in-python-using-numpy
  16. numpy.save
    https://numpy.org/doc/sta­ble/reference/generated/num­py.save.html#numpy.save
  17. numpy.load
    https://numpy.org/doc/sta­ble/reference/generated/num­py.load.html#numpy.load
  18. SciPy
    https://scipy.org/
  19. 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/
  20. Network Common Data Form (NetCDF)
    https://www.unidata.ucar.e­du/software/netcdf/
  21. xray: N D Labeled Arrays and Datasets | SciPy 2015 | Stephan Hoyer
    https://www.youtube.com/wat­ch?v=X0pAhJgySxk
  22. Xarray Tutorial | xarray fundamentals (Youtube, přetočte si prvních osm nebo devět minut přípravy)
    https://www.youtube.com/wat­ch?v=a339Q5F48UQ

Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.