Shluková analýza (clustering) a knihovna Scikit-learn

5. 12. 2023
Doba čtení: 25 minut

Sdílet

 Autor: Depositphotos
Knihovna Scikit-learn kromě modelů (a jejich tréninku) poskytuje i další nástroje. Jedním z užitečných nástrojů, které zde nalezneme, je podpora pro provádění takzvané shlukové analýzy (cluster analysis, clustering).

Obsah

1. Shluková analýza (clustering) a knihovna Scikit-learn

2. Příklad reálných dat, u nichž lze využít shlukovou analýzu

3. Příprava dat pro shlukovou analýzu

4. Vygenerování sady bodů v rovině funkcí sklearn.datasets.make_blobs

5. Malá odbočka: prohození x-ových a y-ových souřadnic

6. Korelační diagram (bodový graf)

7. Vykreslení bodů vygenerovaných funkcí sklearn.datasets.make_blobs

8. Úprava skriptu pro vykreslení většího množství bodů v rovině

9. Obarvení bodů na základě toho, do jakého clusteru patří

10. Algoritmus K-means pro shlukovou analýzu

11. Nalezení centroidů algoritmem K-means

12. Zobrazení centroidů společně se všemi body v rovině

13. Výpočet clusterů

14. Vizualizace clusterů

15. Clustering pro větší počet bodů v případě, kdy se oblasti centroidů překrývají

16. Pokus o nalezení clusterů v náhodných datech

17. Limity standardního algoritmu K-means

18. Obsah navazujícího článku

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

20. Odkazy na Internetu

1. Shluková analýza (clustering) a knihovna Scikit-learn

V článku Rozpoznávání obrázků knihovnou Scikit-learn: první kroky jsme se ve stručnosti seznámili s tím, jakým způsobem spolu dokážou kooperovat knihovny NumPy, Pandas (či Polars), Matplotlib a Scikit-Learn. Tuto kooperaci jsme si ukázali na příkladu rozpoznávání ručně napsaných číslic, přičemž knihovna Scikit-Learn byla použita jako zdroj dat pro trénink i validaci, ovšem navíc nám poskytla i model, který jsme mohli natrénovat. Knihovna NumPy v tomto případě zajišťovala podporu základních datových struktur (n-rozměrná pole, nd-array) i algoritmů a knihovna Matplotlib sloužila pro vizualizaci vstupních dat, mezivýsledků i výsledků poskytovaných natrénovaným modelem.

Obrázek 1: Prvních patnáct číslic, které jsme v předchozím článku použili pro trénink modelu.

Ovšem knihovna Scikit-learn ve skutečnosti nemusí být použita pouze pro trénink modelů; dokonce je možné říci, že je to pro mnoho uživatelů zcela okrajové téma. Scikit-learn totiž poskytuje i mnoho dalších nástrojů. Jedním z velmi užitečných nástrojů, které zde nalezneme, je podpora pro provádění takzvané shlukové analýzy (cluster analysis, clustering). Jedná se o proces, který se používá jako vstup pro klasifikaci objektů. Zjednodušeně řečeno se používá k rozdělení vstupních dat (či již nějakým způsobem upravených dat) do skupin, přičemž se očekává, že data, která budou náležet do stejné skupiny, budou představovat objekty (i když možná by bylo lepší říci informace) s podobnými vlastnostmi.

Obrázek 2: Výsledky modelu, který jsme natrénovali v rámci předchozího článku.

2. Příklad reálných dat, u nichž lze využít shlukovou analýzu

Poměrně dobrým příkladem toho, kde může být shluková analýza užitečná, mohou být data zjištěná o nějaké hvězdě (například informace o teplotě a zářivém výkonu nebo velikosti). Pokud vyneseme do jednoduchého dvourozměrného grafu hodnoty pro tyto dvě veličiny pro všechny pozorované (a změřené) hvězdy, získáme slavný Hertzsprungův–Russellův diagram, zkráceně též H–R diagram, který vypadá následovně:

Obrázek 3: Ukázka H–R diagramu.
Autor: Adam na projektu Wikipedie v jazyce čeština – Na Commons přeneseno z cs.wikipedia., Volné dílo, https://commons.wikimedia­.org/w/index.php?curid=2157609

Na H-R diagramu jsou jasně patrné oblasti, do nichž jsou sdruženy hvězdy s podobnými vlastnostmi (dokonce můžeme říci, že s podobným osudem). A úkolem shlukové analýzy (clusteringu) je tyto oblasti nalézt, resp. vypočítat. V tomto konkrétním případě je to relativně jednoduchá úloha, ovšem pochopitelně se v praxi ne vždy data shlukují takto „pěkným“ způsobem. Z tohoto důvodu existuje hned několik algoritmů pro shlukovou analýzu – a nejtěžším úkolem je vybrat si ten správný algoritmus a určit jeho parametry.

Poznámka: pochopitelně se však nemusíme orientovat pouze na astronomická data a astronomické objekty, protože shlukovou analýzu lze provádět i na mnoha dalších datech, typicky ve chvíli, kdy sice tušíme, že mezi jednotlivými veličinami (a požadovaným výsledkem) existuje nějaká korelace, ale nevíme jaká. A shluková analýza navíc není omezena pouze na dvě veličiny, tedy na dvourozměrný diagram. Počet shlukovaných veličin a tím pádem i počet dimenzí diagramu ve skutečnosti není omezen, i když vizualizace (ta je zde důležitá! – lidský mozek totiž dokáže výsledek velmi snadno verifikovat) bude mít praktický význam především pro diagramy se dvěma či třemi veličinami. I proto se v praxi při zpracování dat setkáme s redukcí počtu dimenzí (odstranění nějaké veličiny).

3. Příprava dat pro shlukovou analýzu

Dnes si otestujeme jeden konkrétní algoritmus určený pro provedení shlukové analýzy, který se nazývá K-means clustering. Ovšem nejprve si musíme připravit vhodná data, na kterých bude možné si vizuálně ověřit výsledky tohoto algoritmu. Jak již víme z předchozího textu, může být shluková analýza provedena v prakticky libovolném počtu dimenzí, ovšem pro jednoduchost začneme dvoudimenzionálními daty. V praxi se tedy bude jednat o sadu hodnot dvou veličin (nebo jedné veličiny) – například již zmíněné teploty a zářivém výkonu hvězdy. Pro každý objekt vyneseme jednu hodnotu na x-ovou osu a druhou hodnotu na y-ovou osu. Výsledkem bude bod v rovině.

Pro naše pokusy tedy potřebujeme získat sadu bodů v rovině, které však nebudou rozmístěny zcela náhodně (tam poněkud postrádá shluková analýza smysl). Existuje mnoho způsobů, jak body v rovině rozmístit. Pro velkou názornost jsem pro dnešní článek vybral metodu realizovanou ve funkci sklearn.datasets.make_blobs, která nejprve vybere n centrálních bodů a poté rozmisťuje další body okolo těchto center tak, že pravděpodobnost umístění bodu dále od centra klesá podle Gaussovy křivky (teoreticky tedy může být bod umístěn na ploše kdekoli, ovšem s větší vzdáleností od centrálních bodů pravděpodobnost jeho umístění do daného místa klesá – což si ověříme vizuálně).

Poznámka: poněkud podobný koncept, i když doplněný o nalezení izoplochy, je použit i v modelovací technice založené na metaballs, viz též poněkud starší článek Implicitní plochy (metaballs) v POV-Rayi.

4. Vygenerování sady bodů v rovině funkcí sklearn.datasets.make_blobs

Podívejme se nyní na praktické použití funkce sklearn.datasets.make_blobs. Této funkci musíme předat především počet bodů, které se mají vygenerovat (n_samples), počet centrálních bodů (centers) a další parametry ovlivňující generování bodů (směrodatná odchylka atd.). Výsledkem bude dvojice hodnot – pole se samotnými body a taktéž pole obsahující pro každý vygenerovaný bod index centrálního body (centroidu), který je k němu nejblíže (resp. přesněji index centrálního bodu, který byl použit v algoritmu výpočtu pseudonáhodné pozice):

# import funkce, která dokáže vygenerovat množinu bodů v rovině sdružených do oblastí
from sklearn.datasets import make_blobs
 
# testovací data
n_samples = 20
 
# počet oblastí, kam se budou data sdružovat
n_components = 6
 
# vygenerovat množinu bodů v rovině sdružených do oblastí
samples, labels = make_blobs(
    n_samples=n_samples, centers=n_components, cluster_std=0.60, random_state=0
)
 
print("Points:")
print(samples)
print()
 
print("Labels:")
print(labels)

Výsledky mohou pro daný počet dvaceti bodů vypadat následovně:

Before swap:
[[-1.45700306  7.9292694 ]
 [ 3.4171203   0.02504426]
 [-1.48065187  7.65407837]
 [-0.60723648  3.79949752]
 [ 5.29722082  0.81003989]
 [ 8.9674639  -2.5940142 ]
 [ 2.08272263  0.78535335]
 [-2.05957546  1.72940438]
 [ 4.86616205  0.45025423]
 [ 8.2494931  -1.16070439]
 [-0.51008137  8.55688792]
 [ 0.52347363  1.28983482]
 [ 1.87271752  4.18069237]
 [ 1.24258802  4.50399192]
 [ 5.08282355  1.04439261]
 [ 8.64412343 -3.18318039]
 [ 2.57392924  0.45236465]
 [ 1.43289271  4.37679234]
 [ 1.1641107   3.79132988]
 [-1.43393556  3.14477977]]
 
Labels:
[3 1 3 2 5 4 1 2 5 4 3 1 0 0 5 4 1 0 0 2]

5. Malá odbočka: prohození x-ových a y-ových souřadnic

Při vizualizaci bodů v rovině je mnohdy vhodné prohodit jejich x-ové a y-ové souřadnice (což může být případ výše zmíněného HR-diagramu). V praxi toho dosáhneme velmi snadno, protože knihovna NumPy u n-rozměrných polí přetěžuje jak operátor indexování, tak i operátor pro provedení řezu (slice). V případě dvourozměrného pole, kde souřadnice jsou uloženy ve dvou sloupcích, lze prohození sloupců s x-ovými a y-ovými souřadnicemi realizovat následovně:

samples = samples[:, ::-1]

Což si lze snadno otestovat:

# import funkce, která dokáže vygenerovat množinu bodů v rovině sdružených do oblastí
from sklearn.datasets import make_blobs
 
# testovací data
n_samples = 20
 
# počet oblastí, kam se budou data sdružovat
n_components = 6
 
# vygenerovat množinu bodů v rovině sdružených do oblastí
samples, labels = make_blobs(
    n_samples=n_samples, centers=n_components, cluster_std=0.60, random_state=0
)
 
print("Before swap:")
print(samples)
print()
 
samples = samples[:, ::-1]
print("After swap:")
print(samples)
print()
 
print("Labels:")
print(labels)

Po spuštění tohoto skriptu se opět vypíšou souřadnice dvaceti bodů. Tentokrát však budou odlišné od příkladu z předchozí kapitoly, protože došlo k prohození x-ových a y-ových souřadnic:

Before swap:
[[-1.45700306  7.9292694 ]
 [ 3.4171203   0.02504426]
 [-1.48065187  7.65407837]
 [-0.60723648  3.79949752]
 [ 5.29722082  0.81003989]
 [ 8.9674639  -2.5940142 ]
 [ 2.08272263  0.78535335]
 [-2.05957546  1.72940438]
 [ 4.86616205  0.45025423]
 [ 8.2494931  -1.16070439]
 [-0.51008137  8.55688792]
 [ 0.52347363  1.28983482]
 [ 1.87271752  4.18069237]
 [ 1.24258802  4.50399192]
 [ 5.08282355  1.04439261]
 [ 8.64412343 -3.18318039]
 [ 2.57392924  0.45236465]
 [ 1.43289271  4.37679234]
 [ 1.1641107   3.79132988]
 [-1.43393556  3.14477977]]
 
After swap:
[[ 7.9292694  -1.45700306]
 [ 0.02504426  3.4171203 ]
 [ 7.65407837 -1.48065187]
 [ 3.79949752 -0.60723648]
 [ 0.81003989  5.29722082]
 [-2.5940142   8.9674639 ]
 [ 0.78535335  2.08272263]
 [ 1.72940438 -2.05957546]
 [ 0.45025423  4.86616205]
 [-1.16070439  8.2494931 ]
 [ 8.55688792 -0.51008137]
 [ 1.28983482  0.52347363]
 [ 4.18069237  1.87271752]
 [ 4.50399192  1.24258802]
 [ 1.04439261  5.08282355]
 [-3.18318039  8.64412343]
 [ 0.45236465  2.57392924]
 [ 4.37679234  1.43289271]
 [ 3.79132988  1.1641107 ]
 [ 3.14477977 -1.43393556]]
 
Labels:
[3 1 3 2 5 4 1 2 5 4 3 1 0 0 5 4 1 0 0 2]

6. Korelační diagram (bodový graf)

Body vytvořené předchozími dvěma skripty je možné vykreslit (vizualizovat) několika způsoby, ovšem jediným typem grafu, se kterým se dnes seznámíme, je takzvaný korelační diagram, který je ovšem někdy známý i pod jménem bodový graf (scatter plot). Tento graf se v knihovně Matplotlib vykresluje funkcí matplotlib.pyplot.scatter, které se předají minimálně informace o x-ových a y-ových souřadnicích bodů, jež se mají do grafu vykreslit.

Všechny x-ové a všechny y-ové souřadnice získáme z našeho 2D pole (matice) takto:

xs = samples[:, 0]
ys = samples[:, 1]

7. Vykreslení bodů vygenerovaných funkcí sklearn.datasets.make_blobs

Ukažme si tedy, jakým způsobem je možné v praxi realizovat vykreslení bodů se souřadnicemi vygenerovanými funkcí sklearn.datasets.make_blobs. K tomuto účelu použijeme funkci nazvanou matplotlib.pyplot.scatter, které se v tom nejjednodušším případě předají dva vektory (jednorozměrná pole). V prvním vektoru budou uloženy x-ové souřadnice bodů, ve druhém vektoru pak jejich y-ové souřadnice. Souřadnice grafu budou upraveny takovým způsobem, aby se zobrazily všechny body a navíc aby byla plocha grafu vyplněna (bez prázdných oblastí):

# budeme provádět vykreslování de facto standardní knihovnou Matplotlib
import matplotlib.pyplot as plt
 
# import funkce, která dokáže vygenerovat množinu bodů v rovině sdružených do oblastí
from sklearn.datasets import make_blobs
 
# testovací data
n_samples = 100
 
# počet oblastí, kam se budou data sdružovat
n_components = 6
 
# vygenerovat množinu bodů v rovině sdružených do oblastí
samples, labels = make_blobs(
    n_samples=n_samples, centers=n_components, cluster_std=0.60, random_state=0
)
 
samples = samples[:, ::-1]
 
# vykreslení bodů v rovině
plt.scatter(samples[:, 0], samples[:, 1])
 
# uložení grafu do souboru
plt.savefig("scatter_1.png")
 
# vykreslení na obrazovku
plt.show()

Výsledek bude vypadat následovně:

Obrázek 4: Zobrazení 100 bodů rozmístěných v rovině pseudonáhodným způsobem.

8. Úprava skriptu pro vykreslení většího množství bodů v rovině

Výše uvedený příklad je vhodné použít ve chvíli, kdy se má zobrazit relativně malé množství bodů, odhadem několik set. Pokud je však bodů větší množství, budou se překrývat, protože jejich stopy v grafu jsou příliš velké. Ovšem je relativně snadné změnit jak tvar zobrazené stopy (parametr marker), tak i její velikost (parametr size). U velikosti je však vhodné si uvědomit, že je udávána nikoli v pixelech, ale v typografických bodech (1/72 palce), podobně jako na mnoha dalších místech Matplotlibu. Velikost je tedy poměrně špatné odhadnout a je tedy vhodné skript spustit s několika parametry a vybrat si tu „správnou“ velikost (záleží i na tom, jak se body shlukují apod.).

V dalším příkladu se pokusíme o zobrazení 1000 bodů, jejichž stopy však budou menší, než tomu bylo v předchozím demonstračním příkladu:

# budeme provádět vykreslování de facto standardní knihovnou Matplotlib
import matplotlib.pyplot as plt
 
# import funkce, která dokáže vygenerovat množinu bodů v rovině sdružených do oblastí
from sklearn.datasets import make_blobs
 
# testovací data
n_samples = 1000
 
# počet oblastí, kam se budou data sdružovat
n_components = 6
 
# vygenerovat množinu bodů v rovině sdružených do oblastí
samples, labels = make_blobs(
    n_samples=n_samples, centers=n_components, cluster_std=0.60, random_state=0
)
 
samples = samples[:, ::-1]
 
# vykreslení bodů v rovině
plt.scatter(samples[:, 0], samples[:, 1], marker=".", s=10)
 
# uložení grafu do souboru
plt.savefig("scatter_2.png")
 
# vykreslení na obrazovku
plt.show()

A takto by měl vypadat výsledek:

Obrázek 5: Zobrazení 1000 bodů rozmístěných v rovině pseudonáhodným způsobem.

9. Obarvení bodů na základě toho, do jakého clusteru patří

V předchozím textu jsme si řekli, že funkce sklearn.datasets.make_blobs vrací nejenom souřadnice bodů, ale i index (celé číslo) udávající oblast (řekněme poněkud nepřesně cluster), do které daný bod náleží. Tuto hodnotu můžeme využít ke dvěma účelům:

  1. Pro ověření algoritmu pro shlukovou analýzu
  2. Pro vizualizaci bodů, přesněji řečeno pro jejich obarvení podle oblasti

Vyzkoušejme si nyní obarvení bodů podle oblasti. Je to snadné. Vytvoříme si seznam (či n-tici) se šesti kódy barev:

colors = ["#4444cc", "#44bb44", "#cc4444", "#cccc44", "#44cccc", "#cc44cc"]

Dále budeme iterovat přes prvky této kolekce a vykreslíme pouze ty body, které mají shodnou hodnotu label s indexem barvy (0–5):

for i, color in enumerate(colors):
    ...
    ...
    ...

Jak se vlastně provádí výběr bodů z dvourozměrného pole? Nejprve si vytvoříme pomocný vektor obsahující pouze 0 a 1, kde 1 znamená, že se má příslušný bod vybrat. Tento vektor bude použit jako selektor do původního dvourozměrného pole se souřadnicemi:

for i, color in enumerate(colors):
    selector = labels == i

Zbývá nám jen selektor použít a vykreslit body zadanou barvou:

for i, color in enumerate(colors):
    selector = labels == i
    plt.scatter(samples[selector, 0], samples[selector, 1], c=color, marker=".", s=10)

Výsledek by měl vypadat následovně:

Obrázek 6: Obarvení bodů na základě toho, do jakého clusteru patří.

Pro jistotu si ukažme celý skript, který toto vykreslení provede:

# budeme provádět vykreslování de facto standardní knihovnou Matplotlib
import matplotlib.pyplot as plt
 
# import funkce, která dokáže vygenerovat množinu bodů v rovině sdružených do oblastí
from sklearn.datasets import make_blobs
 
# testovací data
n_samples = 1000
 
# počet oblastí, kam se budou data sdružovat
n_components = 6
 
# vygenerovat množinu bodů v rovině sdružených do oblastí
samples, labels = make_blobs(
    n_samples=n_samples, centers=n_components, cluster_std=0.60, random_state=0
)
 
samples = samples[:, ::-1]
 
# vykreslení bodů v rovině s jejich obarvením na základě labelu
plt.figure(1)
colors = ["#4444cc", "#44bb44", "#cc4444", "#cccc44", "#44cccc", "#cc44cc"]
 
for i, color in enumerate(colors):
    selector = labels == i
    plt.scatter(samples[selector, 0], samples[selector, 1], c=color, marker=".", s=10)
 
# uložení grafu do souboru
plt.savefig("scatter_3.png")
 
# vykreslení na obrazovku
plt.show()

10. Algoritmus K-means pro shlukovou analýzu

Vzhledem k tomu, že cíle shlukové analýzy jsou poměrně rozmanité a různé je i rozmístění bodů, pro které hledáme vhodné clustery, existuje minimálně několik desítek algoritmů, které shlukovou analýzu provádí. Tyto algoritmy se liší jak svou výpočetní složitostí, tak i způsobem vyhledání clusterů. Pro potřeby dnešního článku byl vybrán pravděpodobně nejznámější algoritmus shlukové analýzy, který se jmenuje K-means. Tento algoritmus je založen na předpokladu, že vstupní objekty jsou chápány jako skutečné body v nějakém eukleidovském prostoru a již dopředu víme, kolik shluků (clusterů) má existovat. Algoritmus nejprve nalezne takzvané centroidy a následně jsou vstupní objekty (body) zařazeny k tomu centroidu, který je k bodu nejblíže. Poté se provádí další iterace; centroidy se přesunou do těžiště clusteru a provede se nové přiřazení.

11. Nalezení centroidů algoritmem K-means

Prvním úkolem algoritmu K-means je nalezení centroidů, tedy centra oblastí, v nichž se body shlukují. V knihovně Scikit-learn můžeme tuto operaci provést s využitím funkce kmeans_plusplus (viz též tento článek). Tato funkce vrací ve své první návratové hodnotě dvourozměrné pole s nalezenými centry oblastí. Povšimněte si, že funkci kmeans_plusplus předáváme nejenom samotné pole se souřadnicemi bodů, ale i předpokládaný počet oblastí. Tuto hodnotu tedy musíme dopředu znát či ji odhadnout (a postupovat iterativním způsobem):

# nalézt centra oblastí
centers_init, indices = kmeans_plusplus(samples, n_clusters=6, random_state=0)

Získané dvourozměrné pole s centroidy lze vykreslit způsobem, který již dobře známe. Centroidů je nepatrný počet (6), takže je vykreslíme s využitím větších stop (s=50) a pro přehlednost použijeme červenou barvu:

plt.scatter(centers_init[:, 0], centers_init[:, 1], c="red", s=50)

Výsledek by měl vypadat následovně:

Obrázek 7: Vizualizace centroidů nalezených algoritmem K-means.

Opět si pro jistotu ukažme celý skript, který tuto operaci provádí:

# budeme provádět vykreslování de facto standardní knihovnou Matplotlib
import matplotlib.pyplot as plt
 
# funkce s implementací algoritmu pro clustering
from sklearn.cluster import kmeans_plusplus
 
# import funkce, která dokáže vygenerovat množinu bodů v rovině sdružených do oblastí
from sklearn.datasets import make_blobs
 
# testovací data
n_samples = 1000
 
# počet oblastí, kam se budou data sdružovat
n_components = 6
 
# vygenerovat množinu bodů v rovině sdružených do oblastí
samples, labels = make_blobs(
    n_samples=n_samples, centers=n_components, cluster_std=0.60, random_state=0
)
 
samples = samples[:, ::-1]
 
# nalézt centra oblastí
centers_init, indices = kmeans_plusplus(samples, n_clusters=6, random_state=0)
 
plt.scatter(centers_init[:, 0], centers_init[:, 1], c="red", s=50)
plt.title("K-Means++")
 
# uložení grafu do souboru
plt.savefig("k_means_1.png")
 
# vykreslení na obrazovku
plt.show()

12. Zobrazení centroidů společně se všemi body v rovině

Pro ověření, zda vypočtené centroidy skutečně leží (blízko) středů jednotlivých clusterů, je pochopitelně nejlepší si nechat na jednom grafu zobrazit jak vstupní body (vygenerované funkcí make_blobs), tak i vlastní centroidy. Tuto funkcionalitu knihovna Matplotlib podporuje, protože na jednu plochu výsledného grafu můžeme vložit větší množství korelačních diagramů (což jsme si již ostatně ukázali při „obarvování“ bodů). Skript pro výpočet centroidů a vizualizaci výsledků tedy můžeme upravit do následující podoby:

# budeme provádět vykreslování de facto standardní knihovnou Matplotlib
import matplotlib.pyplot as plt
 
# funkce s implementací algoritmu pro clustering
from sklearn.cluster import kmeans_plusplus
 
# import funkce, která dokáže vygenerovat množinu bodů v rovině sdružených do oblastí
from sklearn.datasets import make_blobs
 
# testovací data
n_samples = 1000
 
# počet oblastí, kam se budou data sdružovat
n_components = 6
 
# vygenerovat množinu bodů v rovině sdružených do oblastí
samples, labels = make_blobs(
    n_samples=n_samples, centers=n_components, cluster_std=0.60, random_state=0
)
 
samples = samples[:, ::-1]
 
# vykreslení bodů v rovině s jejich obarvením na základě labelu
plt.figure(1)
colors = ["#4444cc", "#44bb44", "#cc4444", "#cccc44", "#44cccc", "#cc44cc"]
 
for i, color in enumerate(colors):
    selector = labels == i
    plt.scatter(samples[selector, 0], samples[selector, 1], c=color, marker=".", s=10)
 
# nalézt centra oblastí
centers_init, indices = kmeans_plusplus(samples, n_clusters=6, random_state=0)
 
plt.scatter(centers_init[:, 0], centers_init[:, 1], c="red", s=50)
plt.title("K-Means++")
 
# uložení grafu do souboru
plt.savefig("k_means_2.png")
 
# vykreslení na obrazovku
plt.show()

A takto by měl vypadat výsledek:

Obrázek 8: Vizualizace centroidů nalezených algoritmem K-means zkombinovaná s původními vstupními body.

13. Výpočet clusterů

Pro výpočet clusterů, tedy pro rozřazení bodů do jednotlivých clusterů, se nepoužívá funkce kmeans_plusplus, protože ta „pouze“ nalezne centroidy. Musíme namísto ní použít nepatrně složitější způsob. Nejprve zkonstruujeme instanci třídy KMeans, které je opět nutné předat očekávaný počet clusterů. Následně výslednému objektu předáme vstupní body a zahájíme výpočet pro nalezení clusterů metodou fit:

# clustering
kmeans = KMeans(n_clusters=6, random_state=0, n_init="auto").fit(samples)

Objekt, který získáme po dokončení metody fit, obsahuje mj. i dvojici atributů nazvaných labels_ a centers_. V prvním z těchto atributů jsou uloženy indexy clusterů (číslují se od jedničky) pro všechny vstupní body (tedy například index 3 na pozici n znamená, že bod n patří ke clusteru číslo 3). A druhý atribut centers_ obsahuje souřadnice centroidů.

Oba atributy si necháme vypočítat a vypsat následujícím skriptem:

from sklearn.cluster import KMeans
from sklearn.datasets import make_blobs
 
# testovací data
n_samples = 1000
 
# počet oblastí, kam se budou data sdružovat
n_components = 6
 
samples, labels = make_blobs(
    n_samples=n_samples, centers=n_components, cluster_std=0.60, random_state=0
)
 
samples = samples[:, ::-1]
 
# clustering
kmeans = KMeans(n_clusters=6, random_state=0, n_init="auto").fit(samples)
 
print("Labels:")
print(kmeans.labels_)
print()
 
print("Centroids:")
print(kmeans.cluster_centers_)

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

Labels:
[4 1 0 5 4 2 4 0 1 2 3 3 4 0 3 0 1 3 3 4 3 3 0 3 3 4 1 2 1 2 0 2 2 5 3 4 1
 4 3 4 3 1 2 1 4 0 1 2 5 5 2 0 1 2 2 2 5 0 1 3 3 4 0 4 3 5 2 3 5 5 1 3 2 4
 4 4 1 3 3 4 4 4 3 5 0 5 4 0 4 4 5 3 1 1 0 2 5 4 3 3 1 5 0 5 3 0 3 5 0 1 2
 1 2 0 2 2 2 3 2 5 3 3 3 5 3 4 2 4 1 2 2 1 5 3 0 4 1 5 0 3 1 1 5 2 2 3 0 3
 3 5 3 4 4 4 2 0 5 3 4 2 0 3 2 4 5 3 0 3 4 0 3 4 3 1 4 0 4 3 1 3 3 3 5 2 4
 3 5 2 0 2 2 5 0 4 1 0 5 1 3 5 3 2 3 2 2 5 2 1 0 0 0 4 1 0 2 4 4 1 5 0 0 4
 2 3 0 3 1 1 2 0 5 1 5 2 3 1 0 4 2 3 0 4 0 0 1 2 1 0 3 0 0 2 0 3 2 4 1 4 5
 3 4 5 1 3 5 4 1 3 2 3 0 1 3 5 1 0 4 3 1 4 4 4 0 5 4 4 4 1 4 1 0 5 2 0 0 1
 4 4 3 1 4 1 5 0 4 2 2 0 0 0 4 0 2 4 5 2 1 0 3 5 3 3 3 4 0 4 3 1 2 3 4 3 2
 0 2 4 3 4 5 1 4 2 0 2 4 2 1 2 3 5 3 1 3 5 0 2 5 5 1 4 0 4 0 1 0 3 4 2 2 2
 0 3 3 0 5 4 2 1 4 1 3 4 0 2 5 4 5 1 0 4 4 4 3 5 5 2 0 5 4 1 2 4 2 4 5 4 2
 5 1 5 0 3 5 4 3 4 2 1 4 3 3 3 2 1 3 2 2 1 0 3 4 0 5 3 4 4 2 4 0 1 2 0 0 2
 2 4 5 4 0 1 3 5 4 5 4 1 2 2 0 5 3 3 3 1 5 3 4 1 5 2 0 4 0 0 2 5 0 2 4 5 5
 1 4 5 3 1 3 0 0 3 3 0 1 2 0 4 3 0 0 4 2 2 4 1 4 2 5 2 2 4 2 0 2 0 5 3 1 5
 1 0 4 4 5 5 1 2 4 0 1 3 3 1 5 0 5 3 2 2 2 1 2 2 1 2 3 4 5 5 0 0 3 5 3 1 2
 1 0 2 3 4 4 4 0 1 2 0 0 0 4 2 2 0 0 5 2 2 2 4 4 0 2 4 2 5 5 0 5 0 3 5 1 4
 2 0 3 3 1 2 3 1 0 1 3 1 2 1 1 4 4 5 5 3 2 0 1 1 0 5 3 1 1 3 5 2 2 3 0 0 2
 4 4 5 1 4 4 5 3 1 4 4 4 4 3 3 5 5 5 2 2 5 3 0 3 3 3 4 1 1 0 2 0 4 1 3 2 4
 3 1 1 4 3 5 2 2 3 2 2 4 3 1 2 5 0 5 4 5 1 3 5 4 0 0 0 2 2 0 1 1 4 0 1 4 5
 5 0 0 5 1 2 3 0 1 5 1 3 5 1 5 4 0 0 0 0 5 2 4 0 2 3 1 5 5 1 5 1 5 3 4 0 5
 2 3 3 1 0 3 0 5 2 0 3 4 2 5 1 1 5 1 5 0 3 2 5 2 0 2 5 5 0 5 3 1 1 5 5 1 4
 3 5 5 5 1 3 5 0 5 0 4 4 5 5 3 1 1 3 5 4 1 0 5 1 0 4 1 3 4 3 1 1 4 5 5 2 5
 1 1 3 0 4 3 0 0 2 0 2 1 0 2 1 0 1 0 1 2 1 5 0 2 0 0 0 5 1 5 2 2 3 2 5 5 4
 5 2 2 2 1 2 4 2 1 4 5 0 5 4 0 1 0 1 3 1 5 1 0 1 0 3 4 1 5 4 5 2 5 1 1 5 5
 0 3 5 2 1 3 0 2 1 5 3 4 5 1 4 4 0 0 3 0 2 4 3 3 5 2 3 5 1 1 3 1 0 5 1 5 3
 0 1 2 3 1 5 3 5 0 3 0 0 5 3 1 5 4 0 2 1 2 5 5 5 2 3 3 4 4 4 2 2 5 3 4 1 5
 0 4 1 4 2 3 2 1 1 4 4 3 1 2 3 4 2 2 2 5 2 5 0 2 5 2 5 1 5 4 1 1 4 1 1 5 1
 0]
 
Centroids:
[[ 0.81049056  1.983196  ]
 [-2.23667661  9.21962615]
 [ 7.77287664 -1.16476714]
 [ 4.33349676  0.90584235]
 [ 0.53153869  5.83908411]
 [ 2.92830112 -1.52190078]]

14. Vizualizace clusterů

Nyní již máme k dispozici všechny potřebné typy informací:

  1. Souřadnice vstupních bodů
  2. Souřadnice centroidů šesti clusterů (viz předchozí kapitolu)
  3. Určení, do kterého clusteru patří každý vstupní body (taktéž viz předchozí kapitolu)

Tyto tři typy informací nyní zkombinujeme a necháme si je zobrazit na jediném grafu. Jak se zobrazují souřadnice bodů i souřadnice centroidů již známe. Dokonce již víme, jak můžeme body obarvit. Ovšem tentokrát je budeme obarvovat na základě toho, do kterého clusteru byly body přiřazeny algoritmem K-means (budeme tedy ignorovat vektor labels). Pojďme na to:

# budeme provádět vykreslování de facto standardní knihovnou Matplotlib
import matplotlib.pyplot as plt
 
# třída s implementací algoritmu pro clustering
from sklearn.cluster import KMeans
 
# import funkce, která dokáže vygenerovat množinu bodů v rovině sdružených do oblastí
from sklearn.datasets import make_blobs
 
# testovací data
n_samples = 1000
 
# počet oblastí, kam se budou data sdružovat
n_components = 6
 
# vygenerovat množinu bodů v rovině sdružených do oblastí
samples, labels = make_blobs(
    n_samples=n_samples, centers=n_components, cluster_std=0.60, random_state=0
)
 
samples = samples[:, ::-1]
 
plt.figure(1)
colors = ["#4444cc", "#44bb44", "#cc4444", "#cccc44", "#44cccc", "#cc44cc"]
 
# clustering
kmeans = KMeans(n_clusters=6, random_state=0, n_init="auto").fit(samples)
 
#print(kmeans.labels_)
#print(kmeans.cluster_centers_)
 
# vykreslení bodů s jejich přiřazením ke clusteru
for i, color in enumerate(colors):
    selector = kmeans.labels_ == i
    plt.scatter(samples[selector, 0], samples[selector, 1], c=color, marker=".", s=1)
 
plt.scatter(kmeans.cluster_centers_[:, 0], kmeans.cluster_centers_[:, 1], c="red", s=50)
plt.title("K-Means++")
 
# uložení grafu do souboru
plt.savefig("k_means_3.png")
 
# vykreslení na obrazovku
plt.show()

Výsledek vypadá korektně:

Obrázek 9: Vstupní body, centroidy i rozdělení bodů do jednotlivých clusterů; vše na jediném grafu.

15. Clustering pro větší počet bodů v případě, kdy se oblasti centroidů překrývají

Změnou hodnoty směrodatné odchylky (viz zvýrazněný parametr funkce make_blobs) lze dosáhnout různé hustoty bodů, které budou posléze rozdělovány do clusterů (body mohou být více seskupeny okolo centra či naopak mohou být rozmístěny do větší plochy prostoru). Pokusme se tedy směrodatnou odchylku zvětšit, což by mělo vést k tomu, že se body rozmístí dále od center a navíc se budou jednotlivé oblasti centroidů více překrývat. Tím lépe pochopíme činnosti algoritmu K-means:

# budeme provádět vykreslování de facto standardní knihovnou Matplotlib
import matplotlib.pyplot as plt
 
# třída s implementací algoritmu pro clustering
from sklearn.cluster import KMeans
 
# import funkce, která dokáže vygenerovat množinu bodů v rovině sdružených do oblastí
from sklearn.datasets import make_blobs
 
# testovací data
n_samples = 10000
 
# počet oblastí, kam se budou data sdružovat
n_components = 6
 
# vygenerovat množinu bodů v rovině sdružených do oblastí
samples, labels = make_blobs(
    n_samples=n_samples, centers=n_components, cluster_std=2.00, random_state=0
)
 
samples = samples[:, ::-1]
 
plt.figure(1)
colors = ["#4444cc", "#44bb44", "#cc4444", "#cccc44", "#44cccc", "#cc44cc"]
 
# clustering
kmeans = KMeans(n_clusters=6, random_state=0, n_init="auto").fit(samples)
 
#print(kmeans.labels_)
#print(kmeans.cluster_centers_)
 
# vykreslení bodů s jejich přiřazením ke clusteru
for i, color in enumerate(colors):
    selector = kmeans.labels_ == i
    plt.scatter(samples[selector, 0], samples[selector, 1], c=color, marker=".", s=1)
 
plt.scatter(kmeans.cluster_centers_[:, 0], kmeans.cluster_centers_[:, 1], c="red", s=50)
plt.title("K-Means++")
 
# uložení grafu do souboru
plt.savefig("k_means_4.png")
 
# vykreslení na obrazovku
plt.show()

Na výsledném grafu jsou patrné hranice mezi clustery a výsledek (nikoli náhodou!) připomíná Voronoiův diagram:

Obrázek 10: Výsledek clusteringu pro překrývající se oblasti centroidů.

16. Pokus o nalezení clusterů v náhodných datech

Pro zajímavost se podívejme na to, jak bude shluková analýza provedená algoritmem K-means provedena na náhodných datech získaných funkcí numpy.random.rand namísto funkce sklearn.datasets.make_blobs. Upravíme tedy pouze jediný řádek ve skriptu a budeme vyžadovat rozdělení bodů do šesti clusterů:

# budeme provádět vykreslování de facto standardní knihovnou Matplotlib
import matplotlib.pyplot as plt
 
from sklearn.cluster import KMeans
import numpy as np
 
# testovací data
n_samples = 10000
 
# počet oblastí, kam se budou data sdružovat
n_components = 6
 
samples = np.random.rand(n_samples, 2)
 
samples = samples[:, ::-1]
 
plt.figure(1)
colors = ["#4444cc", "#44bb44", "#cc4444", "#cccc44", "#44cccc", "#cc44cc"]
 
# clustering
kmeans = KMeans(n_clusters=6, random_state=0, n_init="auto").fit(samples)
 
#print(kmeans.labels_)
#print(kmeans.cluster_centers_)
 
# vykreslení bodů s jejich přiřazením ke clusteru
for i, color in enumerate(colors):
    selector = kmeans.labels_ == i
    plt.scatter(samples[selector, 0], samples[selector, 1], c=color, marker=".", s=1)
 
plt.scatter(kmeans.cluster_centers_[:, 0], kmeans.cluster_centers_[:, 1], c="red", s=50)
plt.title("K-Means++")
 
# uložení grafu do souboru
plt.savefig(f"k_means_5.png")
 
# vykreslení na obrazovku
plt.show()

A takto bude vypadat výsledek:

Obrázek 11: Výsledek shlukové analýzy náhodných dat.

17. Limity standardního algoritmu K-means

Algoritmus K-means byl sice v dnešních demonstračních příkladech (až na příklad poslední) úspěšný, což ovšem neznamená, že je tento algoritmus použitelný pro všechny myslitelné účely. Například můžeme předpokládat (a příště si to ověříme), že nedokáže rozdělit hvězdy ve výše zmíněném HR diagramu do logických skupin, tedy do hvězd patřících do hlavní posloupnosti atd. K těmto účelům byly navrženy odlišné algoritmy shlukové analýzy a jedním z předpokladů úspěšné datové analýzy je tyto algoritmy znát (jménem a použitím, ne interní implementaci) a vybrat si ten správný.

bitcoin_skoleni

18. Obsah navazujícího článku

V navazujícím článku si nejprve ukážeme některé meze algoritmu K-means a posléze se budeme zabývat dalšími vybranými algoritmy, které dokážou provádět shlukové analýzy. Pro otestování chování těchto algoritmů použijeme další funkce pro vygenerování bodů v pseudonáhodných pozicích, tedy funkce obdobné dnes využité funkci make_blobs.

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

Všechny demonstrační příklady využívající knihovnu Scikit-learn, které jsme si popsali minule i dnes, lze nalézt v repositáři https://github.com/tisnik/most-popular-python-libs. Následují odkazy na jednotlivé příklady i na (Jupyter) diáře s postupem výpočtů a analýz:

# Příklad Stručný popis Adresa příkladu
1 01_show_matrix.py kooperace mezi knihovnami Matplotlib a NumPy: vizualizace obsahu 2D matice https://github.com/tisnik/most-popular-python-libs/blob/master/sklearn/01_show_ma­trix.py
2 02_get_digits.py datová množina obsahující naskenované ručně napsané číslice https://github.com/tisnik/most-popular-python-libs/blob/master/sklearn/02_get_di­gits.py
3 03_get_features.py další atributy datové množiny, které použijeme při trénování https://github.com/tisnik/most-popular-python-libs/blob/master/sklearn/03_get_fe­atures.py
4 04_get_images.py přečtení a následné vykreslení jednotlivých ručně nakreslených číslic https://github.com/tisnik/most-popular-python-libs/blob/master/sklearn/04_get_i­mages.py
5 05_show_grayscale_matrix.py odstranění umělé aplikované barvové palety (obrázky ve stupních šedi) https://github.com/tisnik/most-popular-python-libs/blob/master/sklearn/05_show_gra­yscale_matrix.py
6 06_grayscale_images.py vykreslení ručně nakreslených číslic ve formě obrázků ve stupních šedi https://github.com/tisnik/most-popular-python-libs/blob/master/sklearn/06_gra­yscale_images.py
7 07_multiplot.py rozdělení plochy grafu do oblastí; vykreslení více obrázků do jediného grafu https://github.com/tisnik/most-popular-python-libs/blob/master/sklearn/07_mul­tiplot.py
8 08_model_preperation1.py obrázky s jejich ohodnocením https://github.com/tisnik/most-popular-python-libs/blob/master/sklearn/08_mo­del_preperation1.py
9 09_training_set.py příprava dat pro trénink https://github.com/tisnik/most-popular-python-libs/blob/master/sklearn/09_tra­ining_set.py
10 10_classification.py klasifikace obrázků https://github.com/tisnik/most-popular-python-libs/blob/master/sklearn/10_clas­sification.py
11 11_results.py vykreslení obrázků společně s jejich klasifikací https://github.com/tisnik/most-popular-python-libs/blob/master/sklearn/11_results.py
12 12_change_training_set.py změna poměru rozdělení dat na tréninkovou a testovací množinu https://github.com/tisnik/most-popular-python-libs/blob/master/sklearn/12_chan­ge_training_set.py
       
13 13_blobs.py použití funkce make_blobs pro vygenerování sady bodů v rovině sdružených do oblastí https://github.com/tisnik/most-popular-python-libs/blob/master/sklearn/13_blobs.py
14 14_swap_coords.py úprava předchozího příkladu: prohození souřadnic na osách https://github.com/tisnik/most-popular-python-libs/blob/master/sklearn/14_swap_co­ords.py
15 15_blobs_scatter_plot.py základní podoba bodového diagramu (scatter plot) https://github.com/tisnik/most-popular-python-libs/blob/master/sklearn/15_blob­s_scatter_plot.py
16 16_blobs_scatter_plot.py úprava bodového diagramu při zobrazení většího množství bodů https://github.com/tisnik/most-popular-python-libs/blob/master/sklearn/16_blob­s_scatter_plot.py
17 17_colorized_blobs.py obarvení bodů podle oblastí https://github.com/tisnik/most-popular-python-libs/blob/master/sklearn/17_co­lorized_blobs.py
18 18_k-means.py základní použití algoritmu K-means pro clustering https://github.com/tisnik/most-popular-python-libs/blob/master/sklearn/18_k-means.py
19 19_combination.py zobrazení centroidů společně s původními body https://github.com/tisnik/most-popular-python-libs/blob/master/sklearn/19_com­bination.py
20 20_combinations.py vizualizace clusteringu původní množiny bodů https://github.com/tisnik/most-popular-python-libs/blob/master/sklearn/20_com­binations.py
21 21_other_settings.py vizualizace clusteringu původní množiny bodů pro odlišnou množinu https://github.com/tisnik/most-popular-python-libs/blob/master/sklearn/21_ot­her_settings.py
22 22_random_points.py clustering pro náhodná data https://github.com/tisnik/most-popular-python-libs/blob/master/sklearn/22_ran­dom_points.py
       
23 pyproject.toml projektový soubor (pro PDM) se všemi závislostmi https://github.com/tisnik/most-popular-python-libs/blob/master/sklearn/py­project.toml
       
24 pdm.lock lock soubor s konkrétními verzemi všech přímých i tranzitivních závislostí https://github.com/tisnik/most-popular-python-libs/blob/master/sklearn/pdm.lock
       
25 Rozpoznání_obrazu_scikit-learn.ipynb Jupyter notebook s celým postupem https://github.com/tisnik/most-popular-python-libs/blob/master/sklearn/Roz­poznání_obrazu_scikit-learn.ipynb

20. Odkazy na Internetu

  1. scikit-learn: Machine Learning in Python
    https://scikit-learn.org/stable/index.html
  2. Sklearn-pandas
    https://github.com/scikit-learn-contrib/sklearn-pandas
  3. sklearn-xarray
    https://github.com/phausamann/sklearn-xarray/
  4. Clustering
    https://scikit-learn.org/stable/modules/clus­tering.html
  5. Cluster analysis (Wikipedia)
    https://en.wikipedia.org/wi­ki/Cluster_analysis
  6. Shluková analýza (Wikipedia)
    https://cs.wikipedia.org/wi­ki/Shlukov%C3%A1_anal%C3%BDza
  7. K-means
    https://cs.wikipedia.org/wiki/K-means
  8. k-means clustering
    https://en.wikipedia.org/wiki/K-means_clustering
  9. Hertzsprungův–Russellův diagram
    https://cs.wikipedia.org/wi­ki/Hertzsprung%C5%AFv%E2%80%93Rus­sell%C5%AFv_diagram
  10. Using Machine Learning in an HR Diagram
    https://cocalc.com/share/pu­blic_paths/08b6e03583cbdef3cdb98­13a54ec68ff773c747f
  11. Gaia H-R diagrams: Querying Gaia data for one million nearby stars
    https://vlas.dev/post/gaia-dr2-hrd/
  12. The Hertzsprung–Russell diagram
    https://scipython.com/book2/chapter-9-data-analysis-with-pandas/problems/p92/the-hertzsprung-russell-diagram/
  13. Animated Hertzsprung-Russell Diagram with 119,614 datapoints
    https://github.com/zonination/h-r-diagram
  14. Neuraxle Pipelines
    https://github.com/Neuraxio/Neuraxle
  15. scikit-learn: Getting Started
    https://scikit-learn.org/stable/getting_started.html
  16. Support Vector Machines
    https://scikit-learn.org/stable/modules/svm.html
  17. Use Deep Learning to Detect Programming Languages
    http://searene.me/2017/11/26/use-neural-networks-to-detect-programming-languages/
  18. Natural-language processing
    https://en.wikipedia.org/wiki/Natural-language_processing
  19. THE MNIST DATABASE of handwritten digits
    http://yann.lecun.com/exdb/mnist/
  20. MNIST database (Wikipedia)
    https://en.wikipedia.org/wi­ki/MNIST_database
  21. MNIST For ML Beginners
    https://www.tensorflow.or­g/get_started/mnist/begin­ners
  22. Stránka projektu Torch
    http://torch.ch/
  23. Torch: Serialization
    https://github.com/torch/tor­ch7/blob/master/doc/seria­lization.md
  24. Torch: modul image
    https://github.com/torch/i­mage/blob/master/README.md
  25. Data pro neuronové sítě
    http://archive.ics.uci.edu/ml/in­dex.php
  26. Torch na GitHubu (několik repositářů)
    https://github.com/torch
  27. Torch (machine learning), Wikipedia
    https://en.wikipedia.org/wi­ki/Torch_%28machine_learnin­g%29
  28. Torch Package Reference Manual
    https://github.com/torch/tor­ch7/blob/master/README.md
  29. Torch Cheatsheet
    https://github.com/torch/tor­ch7/wiki/Cheatsheet
  30. Neural network containres (Torch)
    https://github.com/torch/nn/blob/mas­ter/doc/containers.md
  31. Simple layers
    https://github.com/torch/nn/blob/mas­ter/doc/simple.md#nn.Line­ar
  32. Transfer Function Layers
    https://github.com/torch/nn/blob/mas­ter/doc/transfer.md#nn.tran­sfer.dok
  33. Feedforward neural network
    https://en.wikipedia.org/wi­ki/Feedforward_neural_net­work
  34. Biologické algoritmy (4) – Neuronové sítě
    https://www.root.cz/clanky/biologicke-algoritmy-4-neuronove-site/
  35. Biologické algoritmy (5) – Neuronové sítě
    https://www.root.cz/clanky/biologicke-algoritmy-5-neuronove-site/
  36. Umělá neuronová síť (Wikipedia)
    https://cs.wikipedia.org/wi­ki/Um%C4%9Bl%C3%A1_neuronov%C3%A1_s%C3%AD%C5%A5
  37. PyTorch
    http://pytorch.org/
  38. JupyterLite na PyPi
    https://pypi.org/project/jupyterlite/
  39. JupyterLite na GitHubu
    https://github.com/jupyter­lite/jupyterlite
  40. Dokumentace k projektu JupyterLite
    https://github.com/jupyter­lite/jupyterlite
  41. Matplotlib Home Page
    http://matplotlib.org/
  42. Matplotlib (Wikipedia)
    https://en.wikipedia.org/wi­ki/Matplotlib
  43. Popis barvových map modulu matplotlib.cm
    https://gist.github.com/en­dolith/2719900#id7
  44. Ukázky (palety) barvových map modulu matplotlib.cm
    http://matplotlib.org/exam­ples/color/colormaps_refe­rence.html
  45. Galerie grafů vytvořených v Matplotlibu
    https://matplotlib.org/3.2.1/gallery/

Autor článku

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