Knihovna Polars: výkonnější alternativa ke knihovně Pandas

12. 1. 2023
Doba čtení: 34 minut

Sdílet

 Autor: Polars team, podle licence: CC BY-NC 2.5
Knihovna Pandas je v ekosystému Pythonu poměrně často používána například v oblasti zpracování dat a datových analýz. Mezi její nectnosti patří nižší výpočetní výkon i to, že zpracovávané datové rámce musí být uloženy v operační paměti.

Obsah

1. Knihovna Polars: výkonnější alternativa ke knihovně Pandas

2. Je Polars skutečně plnohodnotnou náhradou ke knihovně Pandas?

3. Instalace knihovny Polars

4. Instalace dalších podpůrných balíčků

5. Základy práce s datovými řadami (serie)

6. Odlišné datové typy prvků datových řad

7. Speciální způsoby uložení pravdivostních hodnot, řetězců a chybějících hodnot

8. Explicitní specifikace typů prvků

9. Výběr prvků s využitím indexu, indexů a řezů

10. Výběr prvků s využitím filtru (podmínky)

11. Datové rámce

12. Načtení datových rámců ze souborů typu CSV

13. Práce s CSV soubory bez hlavičky

14. Načtení datových rámců ze souborů typu TSV

15. Získání metainformací o datovém rámci

16. Načtení dat obsahujících časová razítka

17. Načtení datových rámců z relační databáze

18. Složitější dotaz s klauzulí WHERE, popř. ORDER BY

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

20. Odkazy na Internetu

1. Knihovna Polars: výkonnější alternativa ke knihovně Pandas

Knihovna Pandas, s níž jsme se na stránkách Roota seznámili v tomto seriálu, se v ekosystému programovacího jazyka Python poměrně často používá, a to například v oblasti zpracování dat a datových analýz. Mezi nectnosti této knihovny patří nižší výpočetní výkon i fakt, že zpracovávané datové rámce (data frames) musí být uloženy v operační paměti, což omezuje celkový objem zpracovávaných dat. Poměrně nedávno však vznikla alternativa ke knihovně Pandas, která se jmenuje Polars, jež tyto problémy do jisté míry řeší. Název knihovny Polars naznačuje, že její výpočetní jádro je naprogramováno v jazyce Rust, ovšem celé rozhraní je určeno přímo pro Python (jen na několika místech se setkáme s označením datového typu pocházejícího z Rustu). V právě začínajícím miniseriálu se seznámíme se základními vlastnostmi ale i některými omezeními Polarsu.

2. Je Polars skutečně plnohodnotnou náhradou ke knihovně Pandas?

V ideálním světě by mělo být možné přejít z knihovny Pandas na knihovnu Polars jedinou změnou ve zdrojových kódech:

import pandas

by se nahradilo za:

import polars

Ve skutečnosti se však obě knihovny od sebe v několika ohledech liší. Především v knihovně Polars je možné jako indexy prvků v datových řadách, popř. jako indexy záznamů v datových rámcích použít pouze celá kladná čísla, zatímco v knihovně Pandas mohou být jak indexy použity například řetězce či dokonce složitější struktury. To vede k nutnosti změny programové logiky, což se mnohdy nevyplatí.

Dále některé funkce a metody v Polars (prozatím) nenajdeme, popř. je omezena jejich funkcionalita. To se týká i tak základních operací, jako je načítání dat ze souborů či z databází apod. Polars tedy může být dobrou alternativou, ale nikoli plnohodnotnou náhradou za knihovnu Pandas (minimálně v současnosti)

Poznámka: o některých rozdílech se zmíníme později.

3. Instalace knihovny Polars

Knihovna Polars se skládá z několika zdrojových kódů napsaných v Pythonu (rozhraní) a z jedné nativní knihovny, jejíž původní zdrojové kódy jsou napsány v Rustu. Pro instalaci Polars však není zapotřebí mít překladač Rustu nainstalován. Instalaci provedeme standardně – nástrojem pip, resp. pip3:

$ pip3 install --user polars
 
Collecting polars
  Downloading polars-0.15.11-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (14.6 MB)
     |████████████████████████████████| 14.6 MB 508 kB/s
Collecting typing_extensions>=4.0.0; python_version < "3.10"
  Using cached typing_extensions-4.4.0-py3-none-any.whl (26 kB)
Installing collected packages: typing-extensions, polars
Successfully installed polars-0.15.11 typing-extensions-4.4.0

Po instalaci si zkontrolujeme, zda je balíček dostupný:

$ pip3 freeze | grep polars
polars==0.15.11

Popř:

$ python3
 
Python 3.8.10 (default, Nov 14 2022, 12:59:47)
[GCC 9.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import polars

Pro zajímavost: interní struktura nainstalovaného balíčku vypadá následovně:

.
├── internals
│   ├── dataframe
│   │   └── __pycache__
│   ├── expr
│   │   └── __pycache__
│   ├── lazyframe
│   │   └── __pycache__
│   ├── __pycache__
│   ├── series
│   │   └── __pycache__
│   └── sql
│       └── __pycache__
├── __pycache__
└── testing
    └── __pycache__

Nejdůležitější je přitom nativní knihovna polars.abi3.so o velikosti přibližně 40MB, v níž je realizována naprostá většina funkcionality Polars:

$ ls -l polars.abi3.so
-rwxrwxr-x 1 ptisnovs ptisnovs 44313016 Jan  4 11:29 polars.abi3.so

4. Instalace dalších podpůrných balíčků

Pro některé operace, například pro načítání datových rámců přímo z databází, je nutné mít nainstalovány i další podpůrné balíčky. Jedním z těchto balíčků je balíček nazvaný connectorx, jenž se nainstaluje snadno:

$ pip3 install --user connectorx>=0.3.1
 
Collecting connectorx
  Downloading connectorx-0.3.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (46.6 MB)
Installing collected packages: connectorx
Successfully installed connectorx-0.3.1

Dále je mnohdy zapotřebí nainstalovat i balíček pyarrow (o jehož významu se zmíníme příště):

$ pip3 install --user pyarrow
 
Collecting pyarrow
  Downloading pyarrow-10.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (36.0 MB)
     |████████████████████████████████| 36.0 MB 3.4 MB/s
Collecting numpy>=1.16.6
  Downloading numpy-1.24.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (17.3 MB)
     |████████████████████████████████| 17.3 MB 11.2 MB/s
Installing collected packages: numpy, pyarrow
Successfully installed numpy-1.24.1 pyarrow-10.0.1
Poznámka: povšimněte si, že se jako závislost nainstaloval i slavný balíček NumPy:

V případě, že nebudou tyto tři balíčky nainstalovány, nebude možné spustit dnešní dva poslední příklady, které načítají data z relační databáze. Uvidíte přitom zhruba následující chybové hlášení:

  File "data_frame_09_load_sql.py", line 11, in <module>
    df = polars.read_sql(query, connection_string)
  File "/home/ptisnovs/.local/lib/python3.8/site-packages/polars/io.py", line 1099, in read_sql
    raise ImportError(
ImportError: connectorx is not installed. Please run `pip install connectorx>=0.3.1`.

5. Základy práce s datovými řadami (serie)

Základním stavebním kamenem knihovny Polars je datový typ Series (datová řada), který zapouzdřuje jednodimenzionální pole (nikoli seznam) s přidanými metainformacemi. Datová řada představuje uspořádaný sloupec údajů, které mají shodný typ (například polars.Int8 nebo polars.Float64 atd.), přičemž každému prvku je přiřazen jednoznačný index. V konkurenční knihovně Pandas se v případě indexu nemusí jednat o celé číslo, protože indexem mohou být i řetězce atd. – což je velmi užitečné, ovšem ne příliš rychlé a paměťově efektivní řešení. Z tohoto důvodu se v současné verzi knihovny Polars používají jako indexy pouze celá čísla začínající nulou a tvořící ucelenou aritmetickou posloupnost.

Instance třídy Series mají několik užitečných atributů:

# Atribut Stručný popis
1 name jméno řady (pokud je specifikováno)
2 dtype typ prvků uložených v datové řadě
3 flags informace, zda jsou prvky seřazeny či nikoli
4 shape n-tice obsahující informaci o tvaru datové řady (obsahuje jedinou hodnotu s počtem prvků)

Dále instance této třídy podporují několik desítek metod; s některými z nich se podrobněji seznámíme v dalším textu.

Nejprve si ukážeme, jakým způsobem se datové řady vytváří. Nejjednodušší je situace ve chvíli, kdy jsou hodnoty, které se mají převést na datovou řadu, připraveny ve formě seznamů nebo n-tic (ve smyslu základních datových typů programovacího jazyka Python). Z takto připravených hodnot se datová řada připraví přímočaře, a to konstruktorem polars.Series. Začneme využitím seznamu, který v našem konkrétním případě obsahuje sekvenci celých čísel, tedy prvků shodného datového typu:

#!/usr/bin/env python3
# vim: set fileencoding=utf-8
 
import polars
 
# vytvoření datové řady
s = polars.Series("sloupec", [1, 2, 3, 4])
 
# zobrazíme datovou řadu
print(s)
print()
 
# podrobnější informace o datové řadě
print("Column type")
print(s.dtype)
print()
Poznámka: úplný zdrojový kód tohoto demonstračního příkladu získáte na adrese https://github.com/tisnik/most-popular-python-libs/blob/master/polars/se­ries01_from_list.py.

Výsledná datová řada i základní informace o ní se zobrazí v tomto formátu:

shape: (4,)
Series: 'sloupec' [i64]
[
        1
        2
        3
        4
]
 
Column type
Int64
Poznámka: povšimněte si, že Polars použil pro uložení prvků datový typ polars.Int64. Tento typ (a tím pádem i objem obsazené paměti atd. lze měnit jak při konstrukci datové řady, tak i později její transformací.

Ve druhém demonstračním příkladu předáváme konstruktoru n-tici, proto se používá dvojice kulatých závorek:

#!/usr/bin/env python3
# vim: set fileencoding=utf-8
 
import polars
 
# vytvoření datové řady
s = polars.Series("sloupec", (1, 2, 3, 4))
 
# zobrazíme datovou řadu
print(s)
print()
 
# podrobnější informace o datové řadě
print("Column type")
print(s.dtype)
print()
Poznámka: úplný zdrojový kód tohoto demonstračního příkladu získáte na adrese https://github.com/tisnik/most-popular-python-libs/blob/master/polars/se­ries02_from_tuple.py.

Výsledek by měl být totožný s prvním demonstračním příkladem:

shape: (4,)
Series: 'sloupec' [i64]
[
        1
        2
        3
        4
]
 
Column type
Int64

Pro konstrukci datové řady je však v případě potřeby možné použít i standardní generátor range, popř. podobné funkce z knihovny Numpy:

#!/usr/bin/env python3
# vim: set fileencoding=utf-8
 
import polars
 
# vytvoření datové řady
s = polars.Series("sloupec", range(10))
 
# zobrazíme datovou řadu
print(s)
print()
 
# podrobnější informace o datové řadě
print("Column type")
print(s.dtype)
print()
Poznámka: úplný zdrojový kód tohoto demonstračního příkladu získáte na adrese https://github.com/tisnik/most-popular-python-libs/blob/master/polars/se­ries03_from_range.py.

Opět se podívejme na výslednou datovou řadu, která by nyní měla obsahovat deset prvků:

Series: 'sloupec' [i64]
[
        0
        1
        2
        3
        4
        5
        6
        7
        8
        9
]
 
Column type
Int64

Nic nám ovšem nebrání ani ve využití „generátorů řad“ poskytovaných knihovnou Numpy:

#!/usr/bin/env python3
# vim: set fileencoding=utf-8
 
import polars
import numpy as np
 
# vytvoření datové řady
s = polars.Series("sloupec", np.arange(10, 11, 0.1))
 
# zobrazíme datovou řadu
print(s)
print()
 
# podrobnější informace o datové řadě
print("Column type")
print(s.dtype)
print()

Výsledek:

shape: (10,)
Series: 'sloupec' [f64]
[
        10.0
        10.1
        10.2
        10.3
        10.4
        10.5
        10.6
        10.7
        10.8
        10.9
]
 
Column type
Float64

Využití generátoru linspace je stejně snadné:

#!/usr/bin/env python3
# vim: set fileencoding=utf-8
 
import polars
import numpy as np
 
# vytvoření datové řady
s = polars.Series("sloupec", np.linspace(10, 11, 10))
 
# zobrazíme datovou řadu
print(s)
print()
 
# podrobnější informace o datové řadě
print("Column type")
print(s.dtype)
print()

Výsledek:

shape: (10,)
Series: 'sloupec' [f64]
[
        10.0
        10.111111
        10.222222
        10.333333
        10.444444
        10.555556
        10.666667
        10.777778
        10.888889
        11.0
]
 
Column type
Float64

6. Odlišné datové typy prvků datových řad

V dalším demonstračním příkladu se pokoušíme vytvořit datovou řadu na základě obsahu seznamu, který obsahuje jak celá čísla, tak i hodnoty s plovoucí řádovou čárkou. Knihovna Polars by měla správně odvodit, že prvky datové řady budou typu polars.Float64:

#!/usr/bin/env python3
# vim: set fileencoding=utf-8
 
import polars
 
# vytvoření datové řady
s = polars.Series("sloupec", [1.2, 2, 3, 4.5])
 
# zobrazíme datovou řadu
print(s)
print()
 
# podrobnější informace o datové řadě
print("Column type")
print(s.dtype)
print()
Poznámka: úplný zdrojový kód tohoto demonstračního příkladu získáte na adrese https://github.com/tisnik/most-popular-python-libs/blob/master/polars/se­ries04_from_other_type.py.

Z vypsaných výsledků je patrné, že se skutečně odvodil datový typ polars.Float64 (v hranaté závorce se přitom používá jméno typu f64 z Rustu, což na jednu stranu není příliš konzistentní, ale ke zmatkům by nemělo v praxi dojít):

shape: (4,)
Series: 'sloupec' [f64]
[
        1.2
        2.0
        3.0
        4.5
]
 
Column type
Float64

V následující tabulce jsou vypsány všechny datové typy, s nimiž je možné v datových řadách i v datových rámcích pracovat. S těmito typy se postupně seznámíme v dalším textu:

Datový typ Stručný popis
Int8 osmibitové celé číslo se znaménkem
Int16 šestnáctibitové celé číslo se znaménkem
Int32 32bitové celé číslo se znaménkem
Int64 64bitové celé číslo se znaménkem
UInt8 osmibitové celé číslo bez znaménka
UInt16 šestnáctibitové celé číslo bez znaménka
UInt32 32bitové celé číslo bez znaménka
UInt64 64bitové celé číslo bez znaménka
Float32 32bitové číslo s plovoucí řádovou čárkou (single, float)
Float64 64bitové číslo s plovoucí řádovou čárkou (double)
Boolean pravdivostní hodnoty uložené v úsporném formátu
Utf8 úsporně uložené řetězce
Binary sekvence bajtů
List seznamy, opět uložené úsporným způsobem
Struct datové struktury
Object atributy objektů
Date datum
Time čas v rámci jednoho dne reprezentovaný v nanosekundách
Datetime časové razítko
Duration časový úsek reprezentovaný v mikrosekundách

7. Speciální způsoby uložení pravdivostních hodnot, řetězců a chybějících hodnot

Některé typy hodnot jsou v datové řadě (a taktéž v datovém rámci) uloženy specifickým způsobem tak, aby se buď snížila doba přístupu nebo (častěji) došlo k ušetření obsazené kapacity operační paměti.

Týká se to řetězců v datové řadě. Ve skutečnosti mohou být všechny řetězce uloženy do jediného bloku (jsou totiž neměnné) a datová řada bude pouze obsahovat offsety (celá čísla) a délky jednotlivých segmentů. To například znamená, že tři řetězce „foo“, „bar“ a „baz“ mohou být uloženy jako jediný řetězec „foobarbaz“ a v datové řadě se budou namísto objektů typu str ukládat pouze hodnoty 0, 3, 6 (offsety) a 3, 3, 3 (délky – toto je ovšem zjednodušený pohled, ve skutečnosti je to poněkud složitější). Nicméně z pohledu uživatele se s datovou řadou obsahující řetězce pracuje zcela běžným způsobem:

#!/usr/bin/env python3
# vim: set fileencoding=utf-8
 
import polars
 
# vytvoření datové řady
s = polars.Series("sloupec", ("foo", "bar", "baz"))
 
# zobrazíme datovou řadu
print(s)
print()
 
# podrobnější informace o datové řadě
print("Column type")
print(s.dtype)
print()
Poznámka: úplný zdrojový kód tohoto demonstračního příkladu získáte na adrese https://github.com/tisnik/most-popular-python-libs/blob/master/polars/se­ries05_strings.py.

A takto bude vypadat výsledná datová řada z pohledu uživatele:

shape: (3,)
Series: 'sloupec' [str]
[
        "foo"
        "bar"
        "baz"
]
 
Column type
Utf8

Specificky uložené jsou taktéž datové řady obsahující pravdivostní hodnoty True a False. Z těchto hodnot je interně vytvořen bitový vektor, tj. vždy osm za sebou jdoucích hodnot je uloženo v jednom bajtu. Oproti uložení reference na objekty (přístup Pythonu) se tedy jedná o dosti výrazné ušetření kapacity paměti a i přístup k takovým hodnotám je rychlejší:

#!/usr/bin/env python3
# vim: set fileencoding=utf-8
 
import polars
 
# vytvoření datové řady
s = polars.Series("sloupec", (True, False, True))
 
# zobrazíme datovou řadu
print(s)
print()
 
# podrobnější informace o datové řadě
print("Column type")
print(s.dtype)
print()
Poznámka: úplný zdrojový kód tohoto demonstračního příkladu získáte na adrese https://github.com/tisnik/most-popular-python-libs/blob/master/polars/se­ries06_booleans.py.

Výsledek získaný po spuštění skriptu:

shape: (3,)
Series: 'sloupec' [bool]
[
        true
        false
        true
]
 
Column type
Boolean

Některé hodnoty v datové řadě mohou chybět. To představuje poměrně zajímavý problém, protože například celá čísla, pravdivostní hodnoty či řetězce nemají mechanismus pro reprezentaci chybějící hodnoty. V knihovně Polars je tento problém vyřešen tak, že se u datových řad, v nichž se mohou vyskytovat chybějící hodnoty, používá bitový vektor, který tyto hodnoty maskuje. Datová řada z následujícího příkladu by tedy byla uložena jako sekvence hodnot 1, 2, 100 (například), 3, 4 a bitový vektor 00100 (v knihovně Pandas je to řešeno speciálními datovými typy):

#!/usr/bin/env python3
# vim: set fileencoding=utf-8
 
import polars
 
# vytvoření datové řady
s = polars.Series("sloupec", [1, 2, None, 3, 4])
 
# zobrazíme datovou řadu
print(s)
print()
 
# podrobnější informace o datové řadě
print("Column type")
print(s.dtype)
print()
Poznámka: úplný zdrojový kód tohoto demonstračního příkladu získáte na adrese https://github.com/tisnik/most-popular-python-libs/blob/master/polars/se­ries07_null_value.py.

Výsledek:

shape: (5,)
Series: 'sloupec' [i64]
[
        1
        2
        null
        3
        4
]
 
Column type
Int64

8. Explicitní specifikace typů prvků

Při načítání, resp. konstrukci datové řady je možné explicitně specifikovat, jakého typu mají být prvky v řadě uložené. Knihovna Polars se v takovém případě pokusí o provedení datových konverzí tak, aby požadavek splnila. Příkladem může být konverze všech celočíselných hodnot na typ Int8, tedy na celá čísla uložená v jediném bajtu (se znaménkem):

#!/usr/bin/env python3
# vim: set fileencoding=utf-8
 
import polars
 
# vytvoření datové řady
s = polars.Series("sloupec", [1, 2, 3, 4], polars.Int8)
 
# zobrazíme datovou řadu
print(s)
print()
 
# podrobnější informace o datové řadě
print("Column type")
print(s.dtype)
print()
Poznámka: úplný zdrojový kód tohoto demonstračního příkladu získáte na adrese https://github.com/tisnik/most-popular-python-libs/blob/master/polars/se­ries08_type_specification­.py.

Výsledný datový rámec obsahuje i informace o typu prvků, což je ostatně patrné i z následujícího výpisu:

Series: 'sloupec' [i8]
[
        1
        2
        3
        4
]
 
Column type
Int8

9. Výběr prvků s využitím indexu, indexů a řezů

Velmi často je zapotřebí z datové řady získat prvky s určitým indexem, indexy (více prvků), nebo získat řez (slice) prvků. I tyto operace jsou knihovnou Polars poskytovány. Pro datovou řadu s s deseti prvky s indexy od 0 do 10 jsou k dispozici tyto „indexovací“ operátory:

Zápis Význam
s[1] výběr jediného prvku z datové řady
s[2, 4, 6] výběr více prvků, vrátí se nová datová řada
s[2:6] řez datovou řadu s indexy od-do (kromě), vrátí se nová datová řada
s[1:8:2] řez s uvedením kroku mezi prvky
s[::2] opět řez, bez uvedení mezí, ale s krokem
s[11:0:-1] řez s otočením pořadí prvků (bez prvního prvku původní řady)
s[11::-1] řez s otočením pořadí prvků (obsahuje všechny prvky původní řady)

Podívejme se nyní na praktický příklad:

#!/usr/bin/env python3
# vim: set fileencoding=utf-8
 
import polars
 
# vytvoření datové řady
s = polars.Series("sloupec", range(1, 11))
 
# zobrazíme datovou řadu
print(s)
print()
 
# podrobnější informace o datové řadě
print("Column type")
print(s.dtype)
print()
 
# výběr prvků
print(s[1])
print(s[2, 4, 6])
print(s[2:6])
print(s[1:8:2])
print(s[::2])
print(s[11:0:-1])
print(s[11::-1])
Poznámka: úplný zdrojový kód tohoto demonstračního příkladu získáte na adrese https://github.com/tisnik/most-popular-python-libs/blob/master/polars/se­ries09_select.py.

Podívejme se nyní na výsledky činnosti tohoto skriptu. Je patrné, že se z původní datové řady vytváří nové datové řady:

shape: (10,)
Series: 'sloupec' [i64]
[
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
]
 
Column type
Int64
 
2
 
shape: (3,)
Series: 'sloupec' [i64]
[
    3
    5
    7
]
shape: (4,)
Series: 'sloupec' [i64]
[
    3
    4
    5
    6
]
shape: (4,)
Series: 'sloupec' [i64]
[
    2
    4
    6
    8
]
shape: (5,)
Series: 'sloupec' [i64]
[
    1
    3
    5
    7
    9
]
shape: (9,)
Series: 'sloupec' [i64]
[
    10
    9
    8
    7
    6
    5
    4
    3
    2
]
shape: (10,)
Series: 'sloupec' [i64]
[
    10
    9
    8
    7
    6
    5
    4
    3
    2
    1
]

10. Výběr prvků s využitím filtru (podmínky)

Další variantou, jak vybírat prvky z datových řad, je (nepřímé) použití datových řad obsahujících pouze pravdivostní hodnoty. Pokud n-tý prvek v takto zkonstruované datové řadě obsahuje hodnotu True, je prvek ze zdrojové datové řady přidán do řady cílové. A tuto pomocnou datovou řadu (říkejme jí výběrový vektor) lze vytvořit například výrazem s > 5, který zkonstruuje výběrový vektor obsahující hodnoty True pouze pro ty prvky, které jsou větší než 5 a False pro všechny ostatní prvky. Následně tento výběrový vektor použijeme pro výběr prvků ze zdrojové datové řady (tj. s je použito jak v podmínce, tak i jako zdrojová datová řada):

Zápis Význam
s[s > 5] výsledkem je datová řada obsahující pouze prvky větší než 5
s[s < 5] výsledkem je datová řada obsahující pouze prvky menší než 5
s[s != 5] výsledkem je datová řada obsahující prvky odlišné od 5
s[s %2 == 1] výběr lichých prvků (nedělitelných dvěma)
s[s %2 != 1] výběr sudých prvků (dělitelných dvěma)

Opět se podívejme na demonstrační příklad, v němž jsou tyto zápisy použity:

#!/usr/bin/env python3
# vim: set fileencoding=utf-8
 
import polars
 
# vytvoření datové řady
s = polars.Series("sloupec", range(1, 11))
 
# zobrazíme datovou řadu
print(s)
print()
 
# podrobnější informace o datové řadě
print("Column type")
print(s.dtype)
print()
 
# výběr prvků na základě filtru
print(s[s > 5])
print(s[s < 5])
print(s[s != 5])
print(s[s %2 == 1])
print(s[s %2 != 1])
Poznámka: úplný zdrojový kód tohoto demonstračního příkladu získáte na adrese https://github.com/tisnik/most-popular-python-libs/blob/master/polars/se­ries10_filter.py.

Výsledky získané po spuštění tohoto příkladu ukazují, že se (podle očekávání) vrátí nové datové řady:

shape: (10,)
Series: 'sloupec' [i64]
[
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
]
 
Column type
Int64
 
shape: (5,)
Series: 'sloupec' [i64]
[
    6
    7
    8
    9
    10
]
shape: (4,)
Series: 'sloupec' [i64]
[
    1
    2
    3
    4
]
shape: (9,)
Series: 'sloupec' [i64]
[
    1
    2
    3
    4
    6
    7
    8
    9
    10
]
shape: (5,)
Series: 'sloupec' [i64]
[
    1
    3
    5
    7
    9
]
shape: (5,)
Series: 'sloupec' [i64]
[
    2
    4
    6
    8
    10
]

11. Datové rámce

Složením několika datových řad o stejném počtu prvků vznikne velmi důležitá datová struktura nazvaná datový rámec (data frame). Datové rámce se v určitém ohledu podobají tabulkám používaným v relačních databázích: jednotlivé sloupce jsou pojmenované a současně může být každý sloupec jiného datového typu (všechny prvky ve sloupci toto kritérium musí splňovat, což ovšem vede k určitým problémům, o nichž si řekneme v navazujících kapitolách a zejména pak v navazujícím článku). Tato vlastnost odlišuje datové rámce od dvourozměrných polí masivně využívaných například v knihovně Numpy; na druhou stranu je ovšem možné s jednotlivými sloupci datového rámce pracovat jako s jednorozměrným polem kompatibilním právě s knihovnou Numpy a jejími datovými typy.

Poznámka: v knihovně Polars je každý záznam (řádek) v datovém rámci jednoznačně identifikován svým indexem, což je celé číslo (řada začíná od nuly). Tím se Polars odlišuje od knihovny Pandas, ve které mohou být indexy tvořeny například řetězci apod.

12. Načtení datových rámců ze souborů typu CSV

CSV neboli Comma-Separated Values [1] je jedním z nejčastěji používaných souborových formátů v této oblasti, a to přesto, že je export a import CSV v některých případech problematický (například některé české mutace Excelu namísto čárek používají středníky, problémy nastávají s buňkami obsahujícími znaky pro konec řádku atd.). Tyto soubory jsou mnohdy obrovské a i z tohoto důvodu se začínají v některých oblastech nahrazovat například za Parquet soubory atd. I přesto se ale s CSV setkáme, a to poměrně často. Příkladem může být export dat z Promethea, z některých systémů pro strukturované logy atd.

Jeden z nejjednodušších příkladů používajících knihovnu Polars bude načítat soubor CSV (Comma-Separated Values), jehož obsah lze najít na adrese https://github.com/tisnik/most-popular-python-libs/blob/master/polars/ha­ll_of_fame.csv. V tomto souboru je uložena tabulka se dvěma sloupci získaná z https://www.tiobe.com/tiobe-index/, přičemž soubor má i řádek s hlavičkou (ta ovšem někdy může chybět, jak ostatně uvidíme dále):

Year,Winner
2022,C++
2021,Python
2020,Python
2019,C
2018,Python
2017,C
2016,Go
2015,Java
2014,JavaScript
2013,Transact-SQL
2012,Objective-C
2011,Objective-C
2010,Python
2009,Go
2008,C
2007,Python
2006,Ruby
2005,Java
2004,PHP
2003,C++

V demonstračním příkladu provedeme načtení souboru s využitím funkce nazvané polars.read_csv, které prozatím předáme jediný parametr obsahující název souboru. Vytvořený datový rámec zobrazíme funkcí print a navíc si necháme vypsat i datové typy přiřazené jednotlivým sloupcům:

#!/usr/bin/env python3
# vim: set fileencoding=utf-8
 
import polars
 
# přečtení zdrojových dat
df = polars.read_csv("hall_of_fame.csv")
 
# zobrazíme datový rámec
print(df)
print()
 
# podrobnější informace o datovém rámci
print("Column types")
print(df.dtypes)
print()
Poznámka: úplný zdrojový kód tohoto demonstračního příkladu získáte na adrese https://github.com/tisnik/most-popular-python-libs/blob/master/polars/da­ta_frame01_load_csv.py.

Výsledek získaný po spuštění tohoto demonstračního příkladu by měl vypadat následovně:

shape: (20, 2)
┌──────┬────────┐
│ Year ┆ Winner │
│ ---  ┆ ---    │
│ i64  ┆ str    │
╞══════╪════════╡
│ 2022 ┆ C++    │
│ 2021 ┆ Python │
│ 2020 ┆ Python │
│ 2019 ┆ C      │
│ ...  ┆ ...    │
│ 2006 ┆ Ruby   │
│ 2005 ┆ Java   │
│ 2004 ┆ PHP    │
│ 2003 ┆ C++    │
└──────┴────────┘
 
Column types
[Int64, Utf8]
Poznámka: povšimněte si, že Polars správně odvodil typy dat uložených v jednotlivých sloupcích. Jak uvidíme dále, je možné i tyto typy specifikovat v průběhu inicializace datového rámce.

13. Práce s CSV soubory bez hlavičky

Pokusme se nyní o načtení CSV souboru, který na prvním řádku neobsahuje hlavičku, resp. přesněji řečeno názvy sloupců. I s takovými soubory se můžeme v praxi setkat:

2022,C++
2021,Python
2020,Python
2019,C
2018,Python
2017,C
2016,Go
2015,Java
2014,JavaScript
2013,Transact-SQL
2012,Objective-C
2011,Objective-C
2010,Python
2009,Go
2008,C
2007,Python
2006,Ruby
2005,Java
2004,PHP
2003,C++

Načtení obsahu tohoto datového souboru provedeme skriptem, který se vlastně nijak zásadně (pochopitelně až na odlišné jméno vstupního souboru) neliší od skriptu popsaného v předchozí kapitole:

#!/usr/bin/env python3
# vim: set fileencoding=utf-8
 
import polars
 
# přečtení zdrojových dat
df = polars.read_csv("hall_of_fame_no_header.csv")
 
# zobrazíme datový rámec
print(df)
print()
 
# podrobnější informace o datovém rámci
print("Column types")
print(df.dtypes)
print()
Poznámka: úplný zdrojový kód tohoto demonstračního příkladu získáte na adrese https://github.com/tisnik/most-popular-python-libs/blob/master/polars/da­ta_frame02_load_csv_no_he­ader_A.py.

Výsledek ovšem v tomto případě nebude korektní, protože se data uložená v prvním řádku použila pro získání jmen sloupců:

shape: (19, 2)
┌──────┬────────┐
│ 2022 ┆ C++    │
│ ---  ┆ ---    │
│ i64  ┆ str    │
╞══════╪════════╡
│ 2021 ┆ Python │
│ 2020 ┆ Python │
│ 2019 ┆ C      │
│ 2018 ┆ Python │
│ ...  ┆ ...    │
│ 2006 ┆ Ruby   │
│ 2005 ┆ Java   │
│ 2004 ┆ PHP    │
│ 2003 ┆ C++    │
└──────┴────────┘
 
Column types
[Int64, Utf8]

Úprava nebo přesněji řečeno oprava tohoto skriptu je ve skutečnosti snadná – postačuje funkci read_csv předat nepovinný parametr has_header nastavený na hodnotu False:

#!/usr/bin/env python3
# vim: set fileencoding=utf-8
 
import polars
 
# přečtení zdrojových dat
df = polars.read_csv("hall_of_fame_no_header.csv", has_header=False)
 
# zobrazíme datový rámec
print(df)
print()
 
# podrobnější informace o datovém rámci
print("Column types")
print(df.dtypes)
print()
Poznámka: úplný zdrojový kód tohoto demonstračního příkladu získáte na adrese https://github.com/tisnik/most-popular-python-libs/blob/master/polars/da­ta_frame03_load_csv_no_he­ader_B.py.

Výsledek je nyní korektní, i když jména sloupců musela být doplněna automaticky:

shape: (20, 2)
┌──────────┬──────────┐
│ column_1 ┆ column_2 │
│ ---      ┆ ---      │
│ i64      ┆ str      │
╞══════════╪══════════╡
│ 2022     ┆ C++      │
│ 2021     ┆ Python   │
│ 2020     ┆ Python   │
│ 2019     ┆ C        │
│ ...      ┆ ...      │
│ 2006     ┆ Ruby     │
│ 2005     ┆ Java     │
│ 2004     ┆ PHP      │
│ 2003     ┆ C++      │
└──────────┴──────────┘
 
Column types
[Int64, Utf8]

Chybějící jména sloupců můžeme doplnit nepovinným parametrem new_columns (viz zvýrazněný kód):

#!/usr/bin/env python3
# vim: set fileencoding=utf-8
 
import polars
 
# přečtení zdrojových dat
df = polars.read_csv("hall_of_fame_no_header.csv", has_header=False, new_columns=["Rok", "Vítěz"])
 
# zobrazíme datový rámec
print(df)
print()
 
# podrobnější informace o datovém rámci
print("Column types")
print(df.dtypes)
print()
Poznámka: úplný zdrojový kód tohoto demonstračního příkladu získáte na adrese https://github.com/tisnik/most-popular-python-libs/blob/master/polars/da­ta_frame04_load_csv_no_he­ader_C.py.

Výsledek bude nyní vypadat odlišně – samotná data v datovém rámci jsou sice stejná, ovšem názvy sloupců se budou lišit:

shape: (20, 2)
┌──────┬────────┐
│ Rok  ┆ Vítěz  │
│ ---  ┆ ---    │
│ i64  ┆ str    │
╞══════╪════════╡
│ 2022 ┆ C++    │
│ 2021 ┆ Python │
│ 2020 ┆ Python │
│ 2019 ┆ C      │
│ ...  ┆ ...    │
│ 2006 ┆ Ruby   │
│ 2005 ┆ Java   │
│ 2004 ┆ PHP    │
│ 2003 ┆ C++    │
└──────┴────────┘
 
Column types
[Int64, Utf8]

14. Načtení datových rámců ze souborů typu TSV

V dalším kroku se pokusíme načíst soubor https://github.com/tisnik/most-popular-python-libs/blob/master/polars/ha­ll_of_fame.tsv. TSV neboli Tab-Separated Values [2] [3] je velmi podobným formátem jako CSV, ovšem s tím rozdílem, že oddělovačem jednotlivých buněk je znak tabulátoru (tím současně odpadají mnohé problémy CSV zmíněné výše). Podobně jako v případě CSV i zde možnost ukládat na první řádek souboru hlavičku:

Year    Winner
2022    C++
2021    Python
2020    Python
2019    C
2018    Python
2017    C
2016    Go
2015    Java
2014    JavaScript
2013    Transact-SQL
2012    Objective-C
2011    Objective-C
2010    Python
2009    Go
2008    C
2007    Python
2006    Ruby
2005    Java
2004    PHP
2003    C++

Podobně jako v knihovně Pandas není ani v knihovně Polars při běžném použití importní funkce polars.read_csv tento formát správně rozpoznán, o čemž se můžeme velmi snadno přesvědčit spuštěním následujícího skriptu:

#!/usr/bin/env python3
# vim: set fileencoding=utf-8
 
import polars
 
# přečtení zdrojových dat
df = polars.read_csv("hall_of_fame.tsv")
 
# zobrazíme datový rámec
print(df)
print()
 
# podrobnější informace o datovém rámci
print("Column types")
print(df.dtypes)
print()
Poznámka: úplný zdrojový kód tohoto demonstračního příkladu získáte na adrese https://github.com/tisnik/most-popular-python-libs/blob/master/polars/da­ta_frame05_load_tsv_A.py.

Výsledkem činnosti tohoto skriptu bude datový rámec obsahující pouze jediný sloupec. Navíc znaky \t, které nejsou dále zpracovány, „rozhodí“ interní formátování datových rámců na terminálu:

shape: (20, 1)
┌────────────┐
│ Year  Winner │
│ ---        │
│ str        │
╞════════════╡
│ 2022  C++    │
│ 2021  Python │
│ 2020  Python │
│ 2019  C      │
│ ...        │
│ 2006  Ruby   │
│ 2005  Java   │
│ 2004  PHP    │
│ 2003  C++    │
└────────────┘
 
Column types
[Utf8]

Soubory založené na formátu TSV lze načíst tak, že nepovinným (pojmenovaným) parametrem sep specifikujeme oddělovač mezi záznamy (což je mimochodem zcela kompatibilní s knihovnou Pandas). V tomto případě se jedná o znak „\t“ (Python používá céčkovský způsob zápisu řídicích znaků):

#!/usr/bin/env python3
# vim: set fileencoding=utf-8
 
import polars
 
# přečtení zdrojových dat
df = polars.read_csv("hall_of_fame.tsv", sep="\t")
 
# zobrazíme datový rámec
print(df)
print()
 
# podrobnější informace o datovém rámci
print("Column types")
print(df.dtypes)
print()
Poznámka: úplný zdrojový kód tohoto demonstračního příkladu získáte na adrese https://github.com/tisnik/most-popular-python-libs/blob/master/polars/da­ta_frame06_load_tsv_B.py.

Výsledek nyní bude plně odpovídat očekávání:

shape: (20, 2)
┌──────┬────────┐
│ Year ┆ Winner │
│ ---  ┆ ---    │
│ i64  ┆ str    │
╞══════╪════════╡
│ 2022 ┆ C++    │
│ 2021 ┆ Python │
│ 2020 ┆ Python │
│ 2019 ┆ C      │
│ ...  ┆ ...    │
│ 2006 ┆ Ruby   │
│ 2005 ┆ Java   │
│ 2004 ┆ PHP    │
│ 2003 ┆ C++    │
└──────┴────────┘
 
Column types
[Int64, Utf8]

15. Získání metainformací o datovém rámci

Podobně jako v případě systému Pandas je možné i v Polars velmi snadno získat základní metainformace a taktéž statistické informace o vybraném datovém rámci. K tomuto účelu se používá metoda nazvaná describe, kterou zavoláme v následujícím demonstračním příkladu:

#!/usr/bin/env python3
# vim: set fileencoding=utf-8
 
import polars
 
# přečtení zdrojových dat
df = polars.read_csv("hall_of_fame.csv")
 
# zobrazíme datový rámec
print(df)
print()
 
# podrobnější informace o datovém rámci
print("Column types")
print(df.dtypes)
print()
 
# více podrobnějších informací o datovém rámci
print(df.describe())
print()
Poznámka: úplný zdrojový kód tohoto demonstračního příkladu získáte na adrese https://github.com/tisnik/most-popular-python-libs/blob/master/polars/da­ta_frame07_describe.py.

Po spuštění tohoto skriptu se nejprve zobrazí obsah datového rámce tak, jak to již dobře známe z předchozích kapitol:

shape: (20, 2)
┌──────┬────────┐
│ Year ┆ Winner │
│ ---  ┆ ---    │
│ i64  ┆ str    │
╞══════╪════════╡
│ 2022 ┆ C++    │
│ 2021 ┆ Python │
│ 2020 ┆ Python │
│ 2019 ┆ C      │
│ ...  ┆ ...    │
│ 2006 ┆ Ruby   │
│ 2005 ┆ Java   │
│ 2004 ┆ PHP    │
│ 2003 ┆ C++    │
└──────┴────────┘
 
Column types
[Int64, Utf8]

Následně se vypíšou statistické informace získané analýzou dat v datovém rámci. U sloupců s numerickými hodnotami se spočítají základní statistické veličiny, u sloupců s řetězci se alespoň nalezne první a poslední prvek z (potenciálně) seřazeného sloupce:

shape: (7, 3)
┌────────────┬─────────┬──────────────┐
│ describe   ┆ Year    ┆ Winner       │
│ ---        ┆ ---     ┆ ---          │
│ str        ┆ f64     ┆ str          │
╞════════════╪═════════╪══════════════╡
│ count      ┆ 20.0    ┆ 20           │
│ null_count ┆ 0.0     ┆ 0            │
│ mean       ┆ 2012.5  ┆ null         │
│ std        ┆ 5.91608 ┆ null         │
│ min        ┆ 2003.0  ┆ C            │
│ max        ┆ 2022.0  ┆ Transact-SQL │
│ median     ┆ 2012.5  ┆ null         │
└────────────┴─────────┴──────────────┘

16. Načtení dat obsahujících časová razítka

Pokusme se nyní načíst tabulku, která ve svém druhém sloupci obsahuje časová razítka, tedy jak plné datum, tak i čas (v tomto konkrétním případě s přesností na sekundy). Jedná se konkrétně o tento soubor:

n,Timestamp
1,2020-01-15 03:59:47
2,2020-01-15 08:19:25
3,2020-01-15 11:42:07
4,2020-01-15 14:58:48
5,2020-01-15 18:21:56
6,2020-01-15 21:10:01
7,2020-01-15 23:13:58
8,2020-01-16 01:51:52
9,2020-01-16 05:55:55
10,2020-01-16 10:11:54
11,2020-01-16 14:02:32
12,2020-01-16 17:35:25
13,2020-01-16 19:35:43
14,2020-01-16 22:29:24

Prozatím při načtení nebudeme žádným způsobem specifikovat typy sloupců, takže vlastně zopakujeme zdrojový kód prvního příkladu pro načtení datového rámce z CSV:

#!/usr/bin/env python3
# vim: set fileencoding=utf-8
 
import polars
 
# přečtení zdrojových dat
df = polars.read_csv("timestamps.csv")
 
# zobrazíme datový rámec
print(df)
print()
 
# podrobnější informace o datovém rámci
print("Column types")
print(df.dtypes)
print()
Poznámka: úplný zdrojový kód tohoto demonstračního příkladu získáte na adrese https://github.com/tisnik/most-popular-python-libs/blob/master/polars/da­ta_frame08_timestamps_A.py.

Z obsahu datového rámce vypsaného tímto skriptem je patrné, že se hodnoty ve druhém sloupci načetly jako řetězce:

shape: (14, 2)
┌─────┬─────────────────────┐
│ n   ┆ Timestamp           │
│ --- ┆ ---                 │
│ i64 ┆ str                 │
╞═════╪═════════════════════╡
│ 1   ┆ 2020-01-15 03:59:47 │
│ 2   ┆ 2020-01-15 08:19:25 │
│ 3   ┆ 2020-01-15 11:42:07 │
│ 4   ┆ 2020-01-15 14:58:48 │
│ ... ┆ ...                 │
│ 11  ┆ 2020-01-16 14:02:32 │
│ 12  ┆ 2020-01-16 17:35:25 │
│ 13  ┆ 2020-01-16 19:35:43 │
│ 14  ┆ 2020-01-16 22:29:24 │
└─────┴─────────────────────┘
 
Column types
[Int64, Utf8]

Výše uvedené chování nám samozřejmě nebude v mnoha případech vyhovovat, protože budeme chtít s časovými údaji provádět různé operace. Jedno z možných řešení tohoto problému spočívá v tom, že při načítání tabulky funkcí pandas.read_csv použijeme parametr parse_dates, kterému předáme buď hodnotu True nebo explicitně názvy sloupců, u nichž se má datum zpracovat (mimochodem – naprosto stejného chování dosáhneme se stejným parametrem u knihovny Pandas):

#!/usr/bin/env python3
# vim: set fileencoding=utf-8
 
import polars
 
# přečtení zdrojových dat
df = polars.read_csv("timestamps.csv", parse_dates=True)
 
# zobrazíme datový rámec
print(df)
print()
 
# podrobnější informace o datovém rámci
print("Column types")
print(df.dtypes)
print()
Poznámka: úplný zdrojový kód tohoto demonstračního příkladu získáte na adrese https://github.com/tisnik/most-popular-python-libs/blob/master/polars/da­ta_frame09_timestamps_B.py.

Obsah a především metainformace o datovém rámci prozrazují, že skutečně došlo ke konverzi časových údajů na hodnoty typu datetime uložené s přesností na mikrosekundy:

shape: (14, 2)
┌─────┬─────────────────────┐
│ n   ┆ Timestamp           │
│ --- ┆ ---                 │
│ i64 ┆ datetime[μs]        │
╞═════╪═════════════════════╡
│ 1   ┆ 2020-01-15 03:59:47 │
│ 2   ┆ 2020-01-15 08:19:25 │
│ 3   ┆ 2020-01-15 11:42:07 │
│ 4   ┆ 2020-01-15 14:58:48 │
│ ... ┆ ...                 │
│ 11  ┆ 2020-01-16 14:02:32 │
│ 12  ┆ 2020-01-16 17:35:25 │
│ 13  ┆ 2020-01-16 19:35:43 │
│ 14  ┆ 2020-01-16 22:29:24 │
└─────┴─────────────────────┘
 
Column types
[Int64, Datetime(tu='us', tz=None)]

17. Načtení datových rámců z relační databáze

V praxi se poměrně často setkáme s požadavkem na načtení dat uložených v nějaké formě databáze. Může se jednat například o relační databázi, z níž jsou data přečtena na základě uživatelem zapsaného dotazu (query). Dobrým příkladem může být načtení dat uložených v databázi PostgreSQL. Pro jednoduchost budeme načítat data z jediné tabulky, tj. dotaz nebude obsahovat klauzuli JOIN (i tu je však možné bez problémů v případě potřeby použít). Data budeme načítat z tabulky rule_hit, jejíž schéma je následující:

            Table "public.rule_hit"
    Column     |       Type        | Modifiers
---------------+-------------------+-----------
 org_id        | integer           | not null
 cluster_id    | character varying | not null
 rule_fqdn     | character varying | not null
 error_key     | character varying | not null
 template_data | character varying | not null

Povšimněte si, že pro načtení dat z databáze potřebujeme specifikovat takzvaný connection string, v němž je uvedena jak použitá databáze, tak i její umístění (jméno počítače+port) i přihlašovací údaje (uživatel postgres s heslem postgres). Každá databáze ovšem akceptuje odlišně strukturovaný connection string. A dále samozřejmě potřebujeme specifikovat vlastní dotaz. Oba údaje jsou předány do funkce read_sql:

#!/usr/bin/env python3
# vim: set fileencoding=utf-8
 
import polars
 
# connection string a vlastní dotaz
connection_string = "postgresql://postgres:postgres@localhost:5432/testdb"
 
query = "select * from rule_hit"
 
# přečtení zdrojových dat
df = polars.read_sql(query, connection_string)
 
# zobrazíme datový rámec
print(df)
print()
 
# podrobnější informace o datovém rámci
print("Column types")
print(df.dtypes)
print()
Poznámka: úplný zdrojový kód tohoto demonstračního příkladu získáte na adrese https://github.com/tisnik/most-popular-python-libs/blob/master/polars/da­ta_frame10_load_sql_A.py.

Výsledkem činnosti tohoto skriptu je plnohodnotný datový rámec, což je ostatně patrné i při pohledu na výsledek:

shape: (45, 5)
┌──────────┬───────────────────────┬───────────────────────┬───────────────────────┬───────────────┐
│ org_id   ┆ cluster_id            ┆ rule_fqdn             ┆ error_key             ┆ template_data │
│ ---      ┆ ---                   ┆ ---                   ┆ ---                   ┆ ---           │
│ i32      ┆ str                   ┆ str                   ┆ str                   ┆ str           │
╞══════════╪═══════════════════════╪═══════════════════════╪═══════════════════════╪═══════════════╡
│ 11789773 ┆ 6d5892d3-1f74-4ccf-91 ┆ ccx_rules_ocp.externa ┆ NODES_MINIMUM_REQUIRE ┆ {             │
│          ┆ af-548dfc97...        ┆ l.rules.nod...        ┆ MENTS_NOT_M...        ┆ "nodes": [... │
│ 11789773 ┆ 6d5892d3-1f74-4ccf-91 ┆ ccx_rules_ocp.externa ┆ BUGZILLA_BUG_1766907  ┆ {             │
│          ┆ af-548dfc97...        ┆ l.bug_rules...        ┆                       ┆ "type": "r... │
│ 11789773 ┆ 6d5892d3-1f74-4ccf-91 ┆ ccx_rules_ocp.externa ┆ NODE_KUBELET_VERSION  ┆ {             │
│          ┆ af-548dfc97...        ┆ l.rules.nod...        ┆                       ┆ "nodes_wit... │
│ 11789773 ┆ 6d5892d3-1f74-4ccf-91 ┆ ccx_rules_ocp.externa ┆ SAMPLES_FAILED_IMAGE_ ┆ {             │
│          ┆ af-548dfc97...        ┆ l.rules.sam...        ┆ IMPORT_ERR            ┆ "info": {     │
│          ┆                       ┆                       ┆                       ┆ ...           │
│ ...      ┆ ...                   ┆ ...                   ┆ ...                   ┆ ...           │
│ 1        ┆ 9d5892d3-1f74-4ccf-91 ┆ ccx_rules_ocp.externa ┆ BUGZILLA_BUG_1766907  ┆ {             │
│          ┆ af-548dfc97...        ┆ l.bug_rules...        ┆                       ┆ "type": "r... │
│ 1        ┆ 9d5892d3-1f74-4ccf-91 ┆ ccx_rules_ocp.externa ┆ NODE_KUBELET_VERSION  ┆ {             │
│          ┆ af-548dfc97...        ┆ l.rules.nod...        ┆                       ┆ "nodes_wit... │
│ 1        ┆ 9d5892d3-1f74-4ccf-91 ┆ ccx_rules_ocp.externa ┆ SAMPLES_FAILED_IMAGE_ ┆ {             │
│          ┆ af-548dfc97...        ┆ l.rules.sam...        ┆ IMPORT_ERR            ┆ "info": {     │
│          ┆                       ┆                       ┆                       ┆ ...           │
│ 1        ┆ 9d5892d3-1f74-4ccf-91 ┆ ccx_rules_ocp.externa ┆ AUTH_OPERATOR_PROXY_E ┆ {             │
│          ┆ af-548dfc97...        ┆ l.rules.clu...        ┆ RROR                  ┆ "op": {       │
│          ┆                       ┆                       ┆                       ┆   ...         │
└──────────┴───────────────────────┴───────────────────────┴───────────────────────┴───────────────┘
 
Column types
[Int32, Utf8, Utf8, Utf8, Utf8]
Poznámka: obsahy sloupců jsou zkráceny.

18. Složitější dotaz s klauzulí WHERE popř. ORDER BY

Dotazy, které se posílají do vybrané (relační) databáze, mohou být samozřejmě složitější, než tomu bylo v předchozím demonstračním příkladu. Můžeme například použít dotaz s podmínkou uvedenou za WHERE (tedy filtraci), popř. specifikovat řazení vrácených záznamů klauzulí ORDER BY. Opět se podívejme na jednoduchý příklad, který stále využívá stejnou tabulku:

bitcoin_skoleni

#!/usr/bin/env python3
# vim: set fileencoding=utf-8
 
import polars
 
# connection string a vlastní dotaz
connection_string = "postgresql://postgres:postgres@localhost:5432/testdb"
query = """
    SELECT org_id, cluster_id, rule_fqdn
      FROM rule_hit
     ORDER by org_id, cluster_id
"""
 
# přečtení zdrojových dat
df = polars.read_sql(query, connection_string)
 
# zobrazíme datový rámec
print(df)
print()
 
# podrobnější informace o datovém rámci
print("Column types")
print(df.dtypes)
print()
Poznámka: úplný zdrojový kód tohoto demonstračního příkladu získáte na adrese https://github.com/tisnik/most-popular-python-libs/blob/master/polars/da­ta_frame11_load_sql_B.py.

Nyní bude výsledný datový rámec vypadat odlišně, protože bude obsahovat pouze tři vybrané sloupce. Navíc si povšimněte, že záznamy uložené v datovém rámci jsou skutečně seřazeny, nejdříve podle čísla organizace a posléze podle jména ve druhém, resp. hodnoty ve třetím sloupci:

shape: (45, 3)
┌──────────┬─────────────────────────────────────┬─────────────────────────────────────┐
│ org_id   ┆ cluster_id                          ┆ rule_fqdn                           │
│ ---      ┆ ---                                 ┆ ---                                 │
│ i32      ┆ str                                 ┆ str                                 │
╞══════════╪═════════════════════════════════════╪═════════════════════════════════════╡
│ 1        ┆ 9d5892d3-1f74-4ccf-91af-548dfc97... ┆ ccx_rules_ocp.ocs.check_ocs_vers... │
│ 1        ┆ 9d5892d3-1f74-4ccf-91af-548dfc97... ┆ ccx_rules_ocp.ocs.check_pods_scc... │
│ 1        ┆ 9d5892d3-1f74-4ccf-91af-548dfc97... ┆ ccx_rules_ocp.ocs.ceph_check_mon... │
│ 1        ┆ 9d5892d3-1f74-4ccf-91af-548dfc97... ┆ ccx_rules_ocp.ocs.pvc_phase_chec... │
│ ...      ┆ ...                                 ┆ ...                                 │
│ 11789775 ┆ 8d5892d3-1f74-4ccf-91af-548dfc97... ┆ ccx_rules_ocp.external.rules.ima... │
│ 11789775 ┆ 8d5892d3-1f74-4ccf-91af-548dfc97... ┆ ccx_rules_ocp.external.rules.ima... │
│ 11789775 ┆ 8d5892d3-1f74-4ccf-91af-548dfc97... ┆ ccx_rules_ocp.external.bug_rules... │
│ 11789775 ┆ 8d5892d3-1f74-4ccf-91af-548dfc97... ┆ ccx_rules_ocp.external.bug_rules... │
└──────────┴─────────────────────────────────────┴─────────────────────────────────────┘
 
Column types
[Int32, Utf8, Utf8]

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

Zdrojové kódy všech prozatím popsaných demonstračních příkladů určených pro programovací jazyk Python 3 (nikoli ovšem pro starší verze Pythonu 2!) byly uloženy do Git repositáře dostupného na adrese https://github.com/tisnik/most-popular-python-libs. V případě, že nebudete chtít klonovat celý repositář (ten je ovšem stále velmi malý, dnes má velikost zhruba několik desítek kilobajtů), můžete namísto toho použít odkazy na jednotlivé příklady, které naleznete v následující tabulce:

# Demonstrační příklad Stručný popis příkladu Cesta
1 series01_from_list.py konstrukce datové řady ze seznamu https://github.com/tisnik/most-popular-python-libs/blob/master/polars/se­ries01_from_list.py
2 series02_from_tuple.py konstrukce datové řady z n-tice https://github.com/tisnik/most-popular-python-libs/blob/master/polars/se­ries02_from_tuple.py
3 series03_from_range.py konstrukce datové řady generátorem range https://github.com/tisnik/most-popular-python-libs/blob/master/polars/se­ries03_from_range.py
4 series04_from_other_type.py použití odlišných datových typů prvků v datové řadě https://github.com/tisnik/most-popular-python-libs/blob/master/polars/se­ries04_from_other_type.py
5 series05_strings.py datová řada obsahující řetězce https://github.com/tisnik/most-popular-python-libs/blob/master/polars/se­ries05_strings.py
6 series06_booleans.py datová řada obsahující pravdivostní hodnoty https://github.com/tisnik/most-popular-python-libs/blob/master/polars/se­ries06_booleans.py
7 series07_null_value.py datová řada obsahující prvky s hodnotou null https://github.com/tisnik/most-popular-python-libs/blob/master/polars/se­ries07_null_value.py
8 series08_type_specification.py explicitní specifikace datového typu https://github.com/tisnik/most-popular-python-libs/blob/master/polars/se­ries08_type_specification­.py
9 series09_select.py výběr prvků z datové řady pomocí indexu, indexů či řezu https://github.com/tisnik/most-popular-python-libs/blob/master/polars/se­ries09_select.py
10 series10_filter.py výběr prvků z datové řady pomocí filtru (podmínky) https://github.com/tisnik/most-popular-python-libs/blob/master/polars/se­ries10_filter.py
11 series11_from_arange.py konstrukce datové řady s využitím numpy.arange https://github.com/tisnik/most-popular-python-libs/blob/master/polars/se­ries11_from_arange.py
12 series12_from_linspace.py konstrukce datové řady s využitím numpy.linspace https://github.com/tisnik/most-popular-python-libs/blob/master/polars/se­ries12_from_linspace.py
       
13 data_frame01_load_csv.py načtení dat ze souboru typu CSV https://github.com/tisnik/most-popular-python-libs/blob/master/polars/da­ta_frame01_load_csv.py
14 data_frame02_load_csv_no_header_A.py práce se soubory typu CSV bez hlavičky (nekorektní varianta) https://github.com/tisnik/most-popular-python-libs/blob/master/polars/da­ta_frame02_load_csv_no_he­ader_A.py
15 data_frame03_load_csv_no_header_B.py práce se soubory typu CSV bez hlavičky (korektní varianta) https://github.com/tisnik/most-popular-python-libs/blob/master/polars/da­ta_frame03_load_csv_no_he­ader_B.py
16 data_frame04_load_csv_no_header_C.py práce se soubory typu CSV bez hlavičky (korektní varianta s explicitními jmény sloupců) https://github.com/tisnik/most-popular-python-libs/blob/master/polars/da­ta_frame04_load_csv_no_he­ader_B.py
17 data_frame05_load_tsv_A.py práce se soubory typu TSV (nekorektní varianta) https://github.com/tisnik/most-popular-python-libs/blob/master/polars/da­ta_frame05_load_tsv_A.py
18 data_frame06_load_tsv_B.py práce se soubory typu TSV (korektní varianta) https://github.com/tisnik/most-popular-python-libs/blob/master/polars/da­ta_frame06_load_tsv_B.py
19 data_frame07_describe.py získání metainformací o datovém rámci https://github.com/tisnik/most-popular-python-libs/blob/master/polars/da­ta_frame07_describe.py
20 data_frame08_timestamps_A.py načtení dat obsahujících časová razítka https://github.com/tisnik/most-popular-python-libs/blob/master/polars/da­ta_frame08_timestamps_A.py
21 data_frame09_timestamps_B.py načtení dat obsahujících časová razítka https://github.com/tisnik/most-popular-python-libs/blob/master/polars/da­ta_frame09_timestamps_B.py
22 data_frame10_load_sql_A.py načtení dat z relační databáze s uložením výsledku ve formě datového rámce https://github.com/tisnik/most-popular-python-libs/blob/master/polars/da­ta_frame10_load_sql_A.py
23 data_frame11_load_sql_B.py načtení dat z relační databáze s uložením výsledku ve formě datového rámce https://github.com/tisnik/most-popular-python-libs/blob/master/polars/da­ta_frame11_load_sql_B.py

20. Odkazy na Internetu

  1. Projekt Polars na GitHubu
    https://github.com/pola-rs/polars
  2. Dokumentace k projektu Polars (popis API)
    https://pola-rs.github.io/polars/py-polars/html/reference/index.html
  3. Polars: The Next Big Python Data Science Library… written in RUST?
    https://www.youtube.com/wat­ch?v=VHqn7ufiilE
  4. Polars API: funkce pro načtení datového rámce z CSV
    https://pola-rs.github.io/polars/py-polars/html/reference/api/po­lars.read_csv.html
  5. Polars API: funkce pro načtení datového rámce z relační databáze
    https://pola-rs.github.io/polars/py-polars/html/reference/api/po­lars.read_sql.html
  6. Python’s Pandas vs Polars: Who Wins this Fight in Library 
    https://analyticsindiamag.com/pythons-pandas-vs-polars-who-wins-this-fight-in-library/
  7. Polars vs Pandas: what is more convenient?
    https://medium.com/@ilia.oz­hmegov/polars-vs-pandas-what-is-more-convenient-331956742a69
  8. A Gentle Introduction to Pandas Data Analysis (on Kaggle)
    https://www.youtube.com/wat­ch?v=_Eb0utIRdkw&list=PL7RwtdVQXQ8o­YpuIIDWR0SaaSCe8ZeZ7t&index=4
  9. Speed Up Your Pandas Dataframes
    https://www.youtube.com/wat­ch?v=u4_c2LDi4b8&list=PL7RwtdVQXQ8o­YpuIIDWR0SaaSCe8ZeZ7t&index=5
  10. pandas.read_csv
    https://pandas.pydata.org/pandas-docs/stable/reference/api/pan­das.read_csv.html
  11. How to define format when using pandas to_datetime?
    https://stackoverflow.com/qu­estions/36848514/how-to-define-format-when-using-pandas-to-datetime
  12. Pandas : skip rows while reading csv file to a Dataframe using read_csv() in Python
    https://thispointer.com/pandas-skip-rows-while-reading-csv-file-to-a-dataframe-using-read_csv-in-python/
  13. Skip rows during csv import pandas
    https://stackoverflow.com/qu­estions/20637439/skip-rows-during-csv-import-pandas
  14. Denni kurz
    https://www.cnb.cz/cs/finan­cni_trhy/devizovy_trh/kur­zy_devizoveho_trhu/denni_kur­z.txt
  15. UUID objects according to RFC 4122 (knihovna pro Python)
    https://docs.python.org/3­.5/library/uuid.html#uuid­.uuid4
  16. Object identifier (Wikipedia)
    https://en.wikipedia.org/wi­ki/Object_identifier
  17. Digital object identifier (Wikipedia)
    https://en.wikipedia.org/wi­ki/Digital_object_identifi­er
  18. voluptuous na (na PyPi)
    https://pypi.python.org/py­pi/voluptuous
  19. Repositář knihovny voluptuous na GitHubu
    https://github.com/alectho­mas/voluptuous
  20. pytest-voluptuous 1.0.2 (na PyPi)
    https://pypi.org/project/pytest-voluptuous/
  21. pytest-voluptuous (na GitHubu)
    https://github.com/F-Secure/pytest-voluptuous
  22. schemagic 0.9.1 (na PyPi)
    https://pypi.python.org/py­pi/schemagic/0.9.1
  23. Schemagic / Schemagic.web (na GitHubu)
    https://github.com/Mechrop­hile/schemagic
  24. schema 0.6.7 (na PyPi)
    https://pypi.python.org/pypi/schema
  25. schema (na GitHubu)
    https://github.com/keleshev/schema

Autor článku

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