Knihovna SciPy: interpolace a aproximace
Co se dozvíte v článku
- Knihovna SciPy: interpolace a aproximace
- Aproximační křivky
- Metoda nejmenších čtverců
- Lineární regrese
- Praktické využití lineární regrese
- Aproximační parametrické křivky nejenom pro počítačovou grafiku
- Požadované vlastnosti parametrických křivek
- Aproximace nebo interpolace bodů spline křivkou
- Ukázka aproximace bodů spline křivkou
- Vliv parametru lam na tvar křivky: od aproximace k interpolaci
- Interpolace bodů sérií kubických spline funkcí
- Výpočet a zobrazení křivky složené z kubických spline
- Výpočet a zobrazení n-té derivace křivky složené z kubických spline
- Rozdíl mezi křivkou vytvořenou pomocí make_smoothing_spline a CubicSpline
- Aproximace kružnice spline křivkami
- Aproximace s využitím libovolné Pythonovské funkce
- Aproximace přímkou a parabolou
- Aproximace křivkou založenou na goniometrických funkcích
- Repositář s demonstračními příklady
- Odkazy na Internetu
Ve druhém článku o knihovně SciPy se budeme zabývat problematikou interpolace a aproximace. Tyto techniky se v praxi poměrně často používají, a to v různých oblastech, ovšem nejlépe si je můžeme vyzkoušet v případě, že je zapotřebí proložit skupinu bodů v rovině nějakou vhodnou křivkou (přičemž přímka je pochopitelně speciálním případem křivky). Podle toho, zda křivka prochází všemi zadanými body, popř. zda tyto body „pouze“ ovlivňují tvar křivky, rozeznáváme křivky interpolační a aproximační.
V případě interpolačních křivek prochází vypočtená křivka předem zadanými (uzlovými) body. Typickou křivkou reprezentovanou polynomem, která do této skupiny náleží, je křivka vytvořená Lagrangeovým polynomem. Alternativně může být tato křivka složena z více oblouků (například z několika kubických spline). Naproti tomu u aproximačních křivek platí, že křivka obecně neprochází všemi zadanými uzlovými body. Příkladem takové křivky z oblasti matematiky resp. zpracování reálných dat je aproximační polynom (ve speciálním případě přímka) určený metodou nejmenších čtverců. Ovšem do této skupiny patří i křivky používané v počítačové grafice – Bézierova křivka, Coonsův oblouk, B-spline NURBS atd. Všemi těmito křivkami se pochopitelně budeme v tomto seriálu zabývat.
Obrázek 1: Parabola (tedy kuželosečka) je popsána polynomem druhého stupně.
Dnes si popíšeme aproximaci bodů přímkou (lineární regrese) a spline křivkou. Posléze si ukážeme interpolaci, a to opět spline křivkou, resp. přesněji řečeno několika na sebe navazujícími křivkami. Ovšem nezapomeneme ani na nejzajímavější techniku – aproximaci bodů libovolnou zvolenou funkcí. Může se jednat o polynom, sinusovku či o jakoukoli jinou funkci.
[project]
name = "scipy-lib"
version = "0.1.0"
description = "Scipy examples"
readme = "README.md"
requires-python = "==3.11.*"
dependencies = [
"matplotlib>=3.10.8",
"scipy>=1.17.1",
]
Aproximační křivky
Známým typem křivek, které se využívají v mnoha oborech (zdaleka tedy nejenom v počítačové grafice), jsou aproximační křivky. Tyto křivky obecně neprochází všemi zadanými body, ovšem tyto body nějakým způsobem ovlivňují tvar výsledných křivek. Typicky se jedná o křivky zadané polynomem, a to buď explicitně ve tvaru y=f(x) (proložení křivky naměřenými body, například s využitím dále zmíněné metody nejmenších čtverců) nebo parametricky ve tvaru bodové funkce x=f1(t), y=f2(t). Nejprve si ukážeme křivky zapsané explicitně a posléze se zaměříme na parametrické křivky, které jsou používány v počítačové grafice (a zhruba milion jich můžete vidět při pročítání tohoto článku, protože i znaky ve fontech jsou popsány křivkami).
Metoda nejmenších čtverců
V technické praxi (například při zpracování naměřených dat) velmi často stojíme před problémem nalezení vhodné křivky, která sice nemusí procházet přesně zadanými (změřenými) body, ale vystihuje závislost dvou veličin, jejichž hodnoty získáme měřením (popř. jedné veličiny, která se mění v čase). Tyto křivky získáváme takzvanou metodou nejmenších čtverců a nejčastěji je hledáme právě ve tvaru polynomu (relativně nízkého stupně), v tom nejjednodušším případě ve tvaru polynomu prvního stupně, tedy přímky.
Podívejme se na ilustrační příklad, ve kterém simulujeme chyby při měření veličiny. Body by měly napodobovat průběh, který je v čase je víceméně lineární. Průběh by tedy měl odpovídat lineární funkci neboli polynomu stupně jedna:
import numpy as np
import matplotlib.pyplot as plt
# hodnoty na x-ové ose
x = np.arange(0, 50)
# generátor pseudonáhodných dat
rng = np.random.default_rng(seed=42)
# hodnoty na y-ové ose
y = x + 10 * rng.random((len(x))) - 5
# rozměry grafu při uložení: 640x480 pixelů
fig, ax = plt.subplots(1, figsize=(6.4, 4.8))
# titulek grafu
fig.suptitle("Lineární regrese: úvod", fontsize=15)
# vrcholy na křivce
ax.plot(x, y, "go")
# popis os a legenda
plt.xlabel("x")
plt.ylabel("y")
plt.legend()
# uložení grafu do rastrového obrázku
plt.savefig("random_points.png")
# zobrazení grafu
plt.show()
Takto by měl vypadat výsledek po spuštění příkladu:
Obrázek 2: Pseudonáhodné body měřené veličiny, jejíž průběh v čase(nebo závislost na vstupu x) je víceméně lineární.
Lineární regrese
V knihovně SciPy nalezneme v balíčku scipy.stats funkci nazvanou linregress, která dokáže ze zadaných vstupních dat vypočítat přímku (resp. přesněji řečeno její sklon a posun od počátku). Pro výpočet se přitom používá metoda nejmenších čtverců. Tato metoda je založena na nalezení takové přímky, ve které je součet druhých mocnin vzdáleností od naměřených bodů nejmenší a tudíž výsledná přímka v nejlepší možné míře reprezentuje předpokládaný průběh vstupní veličiny. Výsledkem volání této funkce jsou koeficienty přímky, tj. již zmíněný sklon a posun:
Help on function linregress in module scipy.stats._stats_py:
linregress(x, y, alternative='two-sided', *, axis=0, nan_policy='propagate', keepdims=False)
Calculate a linear least-squares regression for two sets of measurements.
Parameters
----------
x, y : array_like
Two sets of measurements. Both arrays should have the same length N.
alternative : {'two-sided', 'less', 'greater'}, optional
Defines the alternative hypothesis. Default is 'two-sided'.
The following options are available:
* 'two-sided': the slope of the regression line is nonzero
* 'less': the slope of the regression line is less than zero
* 'greater': the slope of the regression line is greater than zero
.. versionadded:: 1.7.0
axis : int or None, default: 0
If an int, the axis of the input along which to compute the statistic.
The statistic of each axis-slice (e.g. row) of the input will appear in a
corresponding element of the output.
If ``None``, the input will be raveled before computing the statistic.
nan_policy : {'propagate', 'omit', 'raise'}
Defines how to handle input NaNs.
- ``propagate``: if a NaN is present in the axis slice (e.g. row) along
which the statistic is computed, the corresponding entry of the output
will be NaN.
- ``omit``: NaNs will be omitted when performing the calculation.
If insufficient data remains in the axis slice along which the
statistic is computed, the corresponding entry of the output will be
NaN.
- ``raise``: if a NaN is present, a ``ValueError`` will be raised.
Z vypočtených koeficientů přímky vytvoříme reprezentaci funkce, a to konkrétně zavoláním konstruktoru třídy numpy.poly1d:
Help on class poly1d in module numpy: class poly1d(builtins.object) | poly1d(c_or_r, r=False, variable=None) | | A one-dimensional polynomial class. | | .. note:: | This forms part of the old polynomial API. Since version 1.4, the | new polynomial API defined in `numpy.polynomial` is preferred. | A summary of the differences can be found in the | :doc:`transition guide </reference/routines.polynomials>`. | | A convenience class, used to encapsulate "natural" operations on | polynomials so that said operations may take on their customary | form in code (see Examples). | | Parameters | ---------- | c_or_r : array_like | The polynomial's coefficients, in decreasing powers, or if | the value of the second parameter is True, the polynomial's | roots (values where the polynomial evaluates to 0). For example, | ``poly1d([1, 2, 3])`` returns an object that represents | :math:`x^2 + 2x + 3`, whereas ``poly1d([1, 2, 3], True)`` returns | one that represents :math:`(x-1)(x-2)(x-3) = x^3 - 6x^2 + 11x -6`. | r : bool, optional | If True, `c_or_r` specifies the polynomial's roots; the default | is False. | variable : str, optional | Changes the variable used when printing `p` from `x` to `variable` | (see Examples).
Celý postup je tedy snadný:
coefficients = linregress(x, y) # konstrukce lineární funkce poly1d_fn = np.poly1d([coefficients.slope, coefficients.intercept])
Praktické využití lineární regrese
Ukažme si nyní základní způsob použití funkce scipy.stats.linregress pro proložení naměřených (resp. nasimulovaných) dat přímkou, tedy polynomem stupně jedna. Jedná se o klasickou lineární regresi používanou v mnoha technických oborech:
from scipy.stats import linregress
import numpy as np
import matplotlib.pyplot as plt
# hodnoty na x-ové ose
x = np.arange(0, 50)
# generátor pseudonáhodných dat
rng = np.random.default_rng(seed=42)
# hodnoty na y-ové ose
y = x + 10 * rng.random((len(x))) - 5
# výpočet lineární regrese
coefficients = linregress(x, y)
# koeficienty úsečky
print("Slope:", coefficients.slope)
print("Intercept:", coefficients.intercept)
# konstrukce lineární funkce
poly1d_fn = np.poly1d([coefficients.slope, coefficients.intercept])
# rozměry grafu při uložení: 640x480 pixelů
fig, ax = plt.subplots(1, figsize=(6.4, 4.8))
# titulek grafu
fig.suptitle("Lineární regrese", fontsize=15)
# vrcholy na křivce
ax.plot(x, y, "go")
# vykreslení interpolační křivky
plt.plot(poly1d_fn(np.arange(0, len(x))), "r-")
# popis os a legenda
plt.xlabel("x")
plt.ylabel("y")
plt.legend()
# uložení grafu do rastrového obrázku
plt.savefig("linear_regression_1.png")
# zobrazení grafu
plt.show()
Vypočtené parametry přímky (směrnice a posun):
Slope: 0.9675186980866821 Intercept: 1.14811922798118
Výsledek výpočtů bude vizualizován následovně:
Obrázek 3: Lineární regrese bodů, které jsou náhodně vzdáleny od úsečky se směrnicí 1
V případě, že je chyba měření menší, bude výsledná přímka lépe odpovídat předpokládanému průběhu:
from scipy.stats import linregress
import numpy as np
import matplotlib.pyplot as plt
# hodnoty na x-ové ose
x = np.arange(0, 50)
# generátor pseudonáhodných dat
rng = np.random.default_rng(seed=42)
# hodnoty na y-ové ose
y = x + 2 * rng.random((len(x))) - 1
# výpočet lineární regrese
coefficients = linregress(x, y)
# koeficienty úsečky
print("Slope:", coefficients.slope)
print("Intercept:", coefficients.intercept)
# konstrukce lineární funkce
poly1d_fn = np.poly1d([coefficients.slope, coefficients.intercept])
# rozměry grafu při uložení: 640x480 pixelů
fig, ax = plt.subplots(1, figsize=(6.4, 4.8))
# titulek grafu
fig.suptitle("Lineární regrese", fontsize=15)
# vrcholy na křivce
ax.plot(x, y, "go")
# vykreslení interpolační křivky
plt.plot(poly1d_fn(np.arange(0, len(x))), "r-")
# popis os a legenda
plt.xlabel("x")
plt.ylabel("y")
plt.legend()
# uložení grafu do rastrového obrázku
plt.savefig("linear_regression_2.png")
# zobrazení grafu
plt.show()
Výsledek výpočtů:
Obrázek 4: Menší odchylky bodů v rovině
Pro příliš malý počet naměřených bodů nebo pro velkou chybu měření přestává lineární regrese poskytovat dostatečně kvalitní a použitelné výsledky:
from scipy.stats import linregress
import numpy as np
import matplotlib.pyplot as plt
# hodnoty na x-ové ose
x = np.arange(0, 10)
# generátor pseudonáhodných dat
rng = np.random.default_rng(seed=42)
# hodnoty na y-ové ose
y = x + 20 * rng.random((len(x))) - 10
# výpočet lineární regrese
coefficients = linregress(x, y)
# koeficienty úsečky
print("Slope:", coefficients.slope)
print("Intercept:", coefficients.intercept)
# konstrukce lineární funkce
poly1d_fn = np.poly1d([coefficients.slope, coefficients.intercept])
# rozměry grafu při uložení: 640x480 pixelů
fig, ax = plt.subplots(1, figsize=(6.4, 4.8))
# titulek grafu
fig.suptitle("Lineární regrese", fontsize=15)
# vrcholy na křivce
ax.plot(x, y, "go")
# vykreslení interpolační křivky
plt.plot(poly1d_fn(np.arange(0, len(x))), "r-")
# popis os a legenda
plt.xlabel("x")
plt.ylabel("y")
plt.legend()
# uložení grafu do rastrového obrázku
plt.savefig("linear_regression_3.png")
# zobrazení grafu
plt.show()
Vypočtené parametry úsečky:
Slope: 0.46940694592616 Intercept: 4.316276172615453
Výsledek výpočtů:
Pro takto "rozházené" body nemá moc smysl se pokoušet provádět lineární regresi
Aproximační parametrické křivky nejenom pro počítačovou grafiku
V oblasti počítačové grafiky se nejčastěji setkáme s parametrickými křivkami, tedy s křivkami, jejichž tvar je určen bodovou funkcí závislou pouze na jediném parametru t, který typicky nabývá hodnot z rozsahu 0 až 1 (ovšem může tomu být i jinak, například rozsah může být 0 až 2π atd.). Tyto křivky jsou v mnoha případech založeny na polynomech nízkých stupňů, u nichž se nevyskytují velké zákmity, lze zajistit jejich hladké napojení (jak parametrické, tak i geometrické) a navíc je jejich výpočet většinou uspokojivě rychlý. Typicky se tak setkáme s polynomy druhého a třetího stupně, neboli s kvadrikami a kubikami.
V interaktivní počítačové grafice (což je podmnožina počítačové grafiky jako celku) se nejčastěji používají polynomy třetího stupně, a to z toho důvodu, že u polynomů nižších stupňů nelze vždy zaručit podmínky napojení (C2), což však nevadí například u fontů. Kubický polynom zaručuje spojitost C1 a C2 (a samozřejmě také G1 a G2) a poskytuje tak uspokojivé modelovací možnosti. Použití polynomů vyššího stupně vede ke zvýšení časové náročnosti výpočtů, možnému vzniku numerických chyb, případně i k nežádoucím oscilacím zmíněným výše.
Jen pro připomenutí si ukažme, jakým způsobem se vlastně vykresluje parametricky zadaná křivka. Využijeme knihovnu Matplotlib:
"""Parametrická křivka: kružnice."""
import numpy as np
import matplotlib.pyplot as plt
# hodnoty parametru t
t = np.arange(0, 2*np.pi, 0.1)
# poloměr kružnice
r = 2.0
# výpočet bodů ležících na kružnici
x = r*np.cos(t)
y = r*np.sin(t)
# rozměry grafu při uložení: 640x480 pixelů
fig, ax = plt.subplots(1, figsize=(6.4, 4.8))
# titulek grafu
fig.suptitle('Kružnice', fontsize=15)
# určení rozsahů na obou souřadných osách
ax.set_xlim(-4, 4)
ax.set_ylim(-3, 3)
# vrcholy na křivce pospojované úsečkami
ax.plot(x, y, 'g-')
# vrcholy na křivce (každý čtvrtý)
ax.plot(x[::4], y[::4], 'ro')
# uložení grafu do rastrového obrázku
plt.savefig("circle.png")
# zobrazení grafu
plt.show()
Požadované vlastnosti parametrických křivek
Mezi často požadované vlastnosti parametrických křivek (a i pro jejich zobecnění do ploch) patří:
- Invariance k lineárním transformacím popř. i k perspektivní projekci. Tato vlastnost zaručuje, že lineární transformace popř. projekce aplikované na řídící body parametrické plochy má stejný výsledek, jako aplikace této transformace na každý bod vygenerované plochy. Je zřejmé, že při splnění podmínky invariance je z výkonnostního hlediska výhodnější aplikovat transformace pouze na řídící body parametrické plochy. Většina používaných aproximačních ploch podmínku invariance splňuje vzhledem k lineárním transformacím, u racionálních křivek a ploch (mezi něž patří i NURBS) je současně splněna i invariance vzhledem k perspektivní projekci. Velmi často využívané Bézierovy křivky resp. Coonsovy oblouky však nejsou invariantní vzhledem k perspektivní projekci, což ovšem v 2D grafice nevadí.
- Vlastnost konvexní obálky zaručuje, že všechny body vygenerované parametrické křivky či plochy leží v konvexní obálce všech svých řídících bodů. Může se použít i slabší podmínka, kde pouze část plochy leží v konvexní obálce některých řídících bodů. Pokud zaručíme tuto vlastnost, zjednoduší se velké množství algoritmů prováděných s modely těles, například test na průnik dvou těles, vytváření obalových těles nebo test průsečíku paprsku s tělesem. Bézierovy křivky i plochy leží uvnitř konvexní obálky, stejně jako Coonsovy oblouky. Totéž platí pro racionální Bézierovy plochy a křivky, ovšem za předpokladu, že jsou váhy všech řídících bodů kladné.
- Lokalita změn zaručuje, že se změnou polohy jednoho řídícího bodu se změní tvar jen části křivky/plochy přiléhající k tomuto řídícímu bodu, nikoli plocha celá. Tato vlastnost je důležitá především při interaktivním modelování těles pomocí parametrických ploch, protože lze dopředu odhadnout vliv editačních operací na tvar výsledné plochy. Neméně důležitá je i v interaktivní 2D grafice – proto jsou zde ostatně často nasazovány Bézierovy křivky. Tuto vlastnost nepřímo využijeme při prokládání bodů spline křivkami, což je téma navazujících kapitol.
- Křivka nebo plocha může procházet krajními body svého řídícího polygonu. Tuto vlastnost, která je zaručena například u Bézierových křivek a ploch, lze využít pro snadné napojování jednotlivých plátů s dodržením požadované třídy spojitosti C0, C1, G1 nebo G2. Této vlastnosti lze dosáhnout i u B-spline ploch použitím takzvaných násobných řídících bodů, tj. bodů majících stejné souřadnice v prostoru. I s touto technikou se dnes setkáme. I tuto vlastnost využijeme v demonstračních příkladech popsaných níže.
Aproximace nebo interpolace bodů spline křivkou
V knihovně SciPy nalezneme v podbalíčku scipy.interpolate velmi jednoduše použitelnou funkci nazvanou make_smoothing_spline. Tuto funkci lze použít pro výpočet koeficientů (parametrické) spline křivky, jejíž tvar bude ovlivněn vstupními body. Nutno ovšem podotknout, že křivka nemusí těmito body procházet a proto se nejedná o interpolaci, ale o aproximaci (název balíčku může být v tomto případě poněkud matoucí):
Help on function make_smoothing_spline in module scipy.interpolate._bsplines:
make_smoothing_spline(x, y, w=None, lam=None, *, axis=0)
Create a smoothing B-spline satisfying the Generalized Cross Validation (GCV) criterion.
Compute the (coefficients of) smoothing cubic spline function using
``lam`` to control the tradeoff between the amount of smoothness of the
curve and its proximity to the data. In case ``lam`` is None, using the
GCV criteria [1] to find it.
A smoothing spline is found as a solution to the regularized weighted
linear regression problem:
.. math::
\sum\limits_{i=1}^n w_i\lvert y_i - f(x_i) \rvert^2 +
\lambda\int\limits_{x_1}^{x_n} (f^{(2)}(u))^2 d u
where :math:`f` is a spline function, :math:`w` is a vector of weights and
:math:`\lambda` is a regularization parameter.
If ``lam`` is None, we use the GCV criteria to find an optimal
regularization parameter, otherwise we solve the regularized weighted
linear regression problem with given parameter. The parameter controls
the tradeoff in the following way: the larger the parameter becomes, the
smoother the function gets.
Ukázka aproximace bodů spline křivkou
V následujícím příkladu je vypočtena spline křivka aproximující vstupní body. Ovšem obecně jimi neprochází, takže se nejedná o interpolaci. Tvar křivky je pozicí bodů pouze do jisté míry ovlivněn:
from scipy.interpolate import make_smoothing_spline
import numpy as np
import matplotlib.pyplot as plt
# hodnoty na x-ové ose
x = np.arange(0, 50)
# generátor pseudonáhodných dat
rng = np.random.default_rng(seed=42)
# hodnoty na y-ové ose
y = x + 10 * rng.random((len(x))) - 5
# vykreslení bodů
plt.plot(x, y, "go", label="data")
# vytvoření spline
spline = make_smoothing_spline(x, y)
# vykreslení křivky
plt.plot(x, spline(x), "r-", label="Spline")
# popis os a legenda
plt.xlabel("x")
plt.ylabel("y")
plt.legend()
# uložení grafu do rastrového obrázku
plt.savefig("smoothing_spline_1.png")
# zobrazení grafu
plt.show()
A takto by měl vypadat výsledný graf:
Obrázek 6: Aproximace bodů spline křivkou.
Body v ploše pochopitelně mohou být ještě více náhodně rozptýleny:
from scipy.interpolate import make_smoothing_spline
import numpy as np
import matplotlib.pyplot as plt
# hodnoty na x-ové ose
x = np.arange(0, 50)
# generátor pseudonáhodných dat
rng = np.random.default_rng(seed=42)
# hodnoty na y-ové ose
y = x + 20 * rng.random((len(x))) - 10
# vykreslení bodů
plt.plot(x, y, "go", label="data")
# vytvoření spline
spline = make_smoothing_spline(x, y)
# vykreslení křivky
plt.plot(x, spline(x), "r-", label="Spline")
# popis os a legenda
plt.xlabel("x")
plt.ylabel("y")
plt.legend()
# uložení grafu do rastrového obrázku
plt.savefig("smoothing_spline_2.png")
# zobrazení grafu
plt.show()
Nyní bude výsledek vypadat takto:
Obrázek 7: Aproximace bodů spline křivkou.
Vliv parametru lam na tvar křivky: od aproximace k interpolaci
Na tvar výsledné spline má největší vliv (samozřejmě kromě samotných bodů, které křivka aproximuje nebo interpoluje) parametr nazvaný lam. Výchozí hodnota tohoto parametru je None a značí, že se dopočítá automaticky, ovšem můžeme ho zvolit i explicitně při konstrukci spline. Může se jednat o jakékoli kladné číslo, ovšem rozumné hodnoty leží v rozsahu (0, 1>:
spline1 = make_smoothing_spline(x, y, lam=0) spline2 = make_smoothing_spline(x, y, lam=0.5) spline3 = make_smoothing_spline(x, y, lam=10)
Podívejme se na úplný zdrojový kód tohoto příkladu:
from scipy.interpolate import make_smoothing_spline
import numpy as np
import matplotlib.pyplot as plt
# hodnoty na x-ové ose
x = np.arange(0, 50)
# generátor pseudonáhodných dat
rng = np.random.default_rng(seed=42)
# hodnoty na y-ové ose
y = x + 20 * rng.random((len(x))) - 10
# vykreslení bodů
plt.plot(x, y, "go", label="data")
# vytvoření spline
spline1 = make_smoothing_spline(x, y, lam=0)
spline2 = make_smoothing_spline(x, y, lam=0.5)
spline3 = make_smoothing_spline(x, y, lam=10)
x = np.arange(0, 50, 0.5)
# vykreslení křivky
plt.plot(x, spline1(x), "r-", label="Spline 1")
plt.plot(x, spline2(x), "g-", label="Spline 2")
plt.plot(x, spline3(x), "b-", label="Spline 2")
# popis os a legenda
plt.xlabel("x")
plt.ylabel("y")
plt.legend()
# uložení grafu do rastrového obrázku
plt.savefig("smoothing_spline_3.png")
# zobrazení grafu
plt.show()
Povšimněte si, jak se odlišuje tvar jednotlivých spline – dokonce tak, že některé spline provádí „jen“ aproximaci a jiné interpolaci:
Obrázek 8: Aproximace a interpolace bodů různými spline křivkami.
Pro lepší náhled nad rozdíly tvarů spline je vhodnější zmenšit počet (řídicích) bodů, konkrétně v našem případě z padesáti na dvacet:
from scipy.interpolate import make_smoothing_spline
import numpy as np
import matplotlib.pyplot as plt
# hodnoty na x-ové ose
x = np.arange(0, 20)
# generátor pseudonáhodných dat
rng = np.random.default_rng(seed=42)
# hodnoty na y-ové ose
y = x + 20 * rng.random((len(x))) - 10
# vykreslení bodů
plt.plot(x, y, "go", label="data")
# vytvoření spline
spline1 = make_smoothing_spline(x, y, lam=0)
spline2 = make_smoothing_spline(x, y, lam=0.5)
spline3 = make_smoothing_spline(x, y, lam=10)
x = np.arange(0, 20, 0.25)
# vykreslení křivky
plt.plot(x, spline1(x), "r-", label="Spline 1")
plt.plot(x, spline2(x), "g-", label="Spline 2")
plt.plot(x, spline3(x), "b-", label="Spline 2")
# popis os a legenda
plt.xlabel("x")
plt.ylabel("y")
plt.legend()
# uložení grafu do rastrového obrázku
plt.savefig("smoothing_spline_4.png")
# zobrazení grafu
plt.show()
A takto bude vypadat výsledek:
Obrázek 9: Aproximace a interpolace bodů různými spline křivkami.
Interpolace bodů sérií kubických spline funkcí
V knihovně SciPy, konkrétně v modulu scipy.interpolate, nalezneme i třídu nazvanou CubicSpline. Ta slouží pro vytvoření série kubických oblouků, které na sebe hladce navazují (mají C2 spojitost). Výsledná křivka složená ze spline oblouků bude interpolovat původní sadu bodů v rovině:
Help on class CubicSpline in module scipy.interpolate._cubic: class CubicSpline(CubicHermiteSpline) | CubicSpline(x, y, axis=0, bc_type='not-a-knot', extrapolate=None) | | Piecewise cubic interpolator to fit values (C2 smooth). | | Interpolate data with a piecewise cubic polynomial which is twice | continuously differentiable [1]_. The result is represented as a `PPoly` | instance with breakpoints matching the given data. | | Parameters | ---------- | x : array_like, shape (n,) | 1-D array containing values of the independent variable. | Values must be real, finite and in strictly increasing order. | y : array_like | Array containing values of the dependent variable. It can have | arbitrary number of dimensions, but the length along ``axis`` | (see below) must match the length of ``x``. Values must be finite. | axis : int, optional | Axis along which `y` is assumed to be varying. Meaning that for | ``x[i]`` the corresponding values are ``np.take(y, i, axis=axis)``. | Default is 0. | bc_type : string or 2-tuple, optional | Boundary condition type. Two additional equations, given by the | boundary conditions, are required to determine all coefficients of | polynomials on each segment [2]_. | | If `bc_type` is a string, then the specified condition will be applied | at both ends of a spline. Available conditions are: | | * 'not-a-knot' (default): The first and second segment at a curve end | are the same polynomial. It is a good default when there is no
Výpočet a zobrazení křivky složené z kubických spline
Ukažme si nyní postup, který je nutné použít, pokud budeme chtít vypočítat a zobrazit interpolační křivku složenou z kubických spline. Zkonstruujeme instanci třídy CubicSpline. Výsledkem bude objekt, který se chová jako funkce (je volatelný, protože přetěžuje speciální metodu zajišťující tuto funkcionalitu). A jakmile máme k dispozici objekt chovající se jako funkce, můžeme ho použít při vykreslení křivky:
from scipy.interpolate import CubicSpline
import numpy as np
import matplotlib.pyplot as plt
# hodnoty na x-ové ose
x = np.arange(0, 10)
# generátor pseudonáhodných dat
rng = np.random.default_rng(seed=42)
# hodnoty na y-ové ose
y = x + 10 * rng.random((len(x))) - 5
# vykreslení bodů
plt.plot(x, y, "go", label="data")
# vytvoření spline
spline = CubicSpline(x, y)
x = np.arange(0, 10, 0.01)
# vykreslení křivky
plt.plot(x, spline(x), "r-", label="Spline")
# popis os a legenda
plt.xlabel("x")
plt.ylabel("y")
plt.legend()
# uložení grafu do rastrového obrázku
plt.savefig("cubic_spline_1.png")
# zobrazení grafu
plt.show()
Výsledná křivka bude vypadat následovně:
Obrázek 10: Interpolace deseti bodů spline křivkami.
Výpočet a zobrazení n-té derivace křivky složené z kubických spline
V některých situacích může být užitečná ještě jedna vlastnost třídy CubicSpline. Pomocí nepovinného parametru nu je možné zjistit n-tou derivaci spline. A derivace mají u křivek geometrický význam (spojitost, rychlost změny atd.). Použití je snadné:
from scipy.interpolate import CubicSpline
import numpy as np
import matplotlib.pyplot as plt
# hodnoty na x-ové ose
x = np.arange(0, 10)
# generátor pseudonáhodných dat
rng = np.random.default_rng(seed=42)
# hodnoty na y-ové ose
y = x + 10 * rng.random((len(x))) - 5
# vykreslení bodů
plt.plot(x, y, "go", label="data")
# vytvoření spline
spline = CubicSpline(x, y)
x = np.arange(0, 10, 0.01)
# vykreslení křivky
plt.plot(x, spline(x), "r-", label="Spline")
plt.plot(x, spline(x, nu=1), '--', label='1st derivative')
# popis os a legenda
plt.xlabel("x")
plt.ylabel("y")
plt.legend()
# uložení grafu do rastrového obrázku
plt.savefig("cubic_spline_2.png")
# zobrazení grafu
plt.show()
Tento příklad po svém spuštění vykreslí jak spline, tak i její první derivaci:
Obrázek 11: Interpolace deseti bodů spline křivkami. Zobrazení první derivace křivky.
Pochopitelně si můžeme nechat zobrazit i vyšší derivace:
plt.plot(x, spline(x), "r-", label="Spline") plt.plot(x, spline(x, nu=1), '--', label='1st derivative') plt.plot(x, spline(x, nu=2), '--', label='2nd derivative') plt.plot(x, spline(x, nu=3), '--', label='3rd derivative')
Výsledek:
Obrázek 12: Zobrazení první až třetí derivace spline.
from scipy.interpolate import CubicSpline
import numpy as np
import matplotlib.pyplot as plt
# hodnoty na x-ové ose
x = np.arange(0, 10)
# generátor pseudonáhodných dat
rng = np.random.default_rng(seed=42)
# hodnoty na y-ové ose
y = x + 10 * rng.random((len(x))) - 5
# vykreslení bodů
plt.plot(x, y, "go", label="data")
# vytvoření spline
spline = CubicSpline(x, y)
x = np.arange(0, 10, 0.01)
# vykreslení křivky
plt.plot(x, spline(x), "r-", label="Spline")
plt.plot(x, spline(x, nu=1), '--', label='1st derivative')
plt.plot(x, spline(x, nu=2), '--', label='2nd derivative')
plt.plot(x, spline(x, nu=3), '--', label='3rd derivative')
# popis os a legenda
plt.xlabel("x")
plt.ylabel("y")
plt.legend()
# uložení grafu do rastrového obrázku
plt.savefig("cubic_spline_3.png")
# zobrazení grafu
plt.show()
A nakonec se ještě přesvědčíme, že čtvrtá derivace kubické spline je (logicky) nulová:
from scipy.interpolate import CubicSpline
import numpy as np
import matplotlib.pyplot as plt
# hodnoty na x-ové ose
x = np.arange(0, 10)
# generátor pseudonáhodných dat
rng = np.random.default_rng(seed=42)
# hodnoty na y-ové ose
y = x + 10 * rng.random((len(x))) - 5
# vykreslení bodů
plt.plot(x, y, "go", label="data")
# vytvoření spline
spline = CubicSpline(x, y)
x = np.arange(0, 10, 0.01)
# vykreslení křivky
plt.plot(x, spline(x), "r-", label="Spline")
plt.plot(x, spline(x, nu=4), '--', label='4th derivative')
# popis os a legenda
plt.xlabel("x")
plt.ylabel("y")
plt.legend()
# uložení grafu do rastrového obrázku
plt.savefig("cubic_spline_4.png")
# zobrazení grafu
plt.show()
Výsledek:
Obrázek 13: Čtvrtá derivace kubické spline je nulová.
Rozdíl mezi křivkou vytvořenou pomocí make_smoothing_spline a CubicSpline
Název „spline“ může být matoucí, protože by se mohlo zdát, že křivky vytvořené pomocí make_smoothing_spline a CubicSpline budou totožné. Ve skutečnosti tomu tak není – jedna spline má n-tý řád zatímco druhá je tvořena kubickými oblouky. Ostatně o tom, že obě křivky mají odlišný průběh, se můžeme snadno přesvědčit jejich zobrazením do jediného grafu:
from scipy.interpolate import CubicSpline
from scipy.interpolate import make_smoothing_spline
import numpy as np
import matplotlib.pyplot as plt
# hodnoty na x-ové ose
x = np.arange(0, 10)
# generátor pseudonáhodných dat
rng = np.random.default_rng(seed=42)
# hodnoty na y-ové ose
y = x + 10 * rng.random((len(x))) - 5
# vykreslení bodů
plt.plot(x, y, "go", label="data")
# vytvoření spline
spline1 = CubicSpline(x, y)
spline2 = make_smoothing_spline(x, y, lam=0)
x = np.arange(0, 10, 0.01)
# vykreslení křivek
plt.plot(x, spline1(x), "r-", label="Cubic spline")
plt.plot(x, spline2(x), "b-", label="Smoothing spline #1")
# popis os a legenda
plt.xlabel("x")
plt.ylabel("y")
plt.legend()
# uložení grafu do rastrového obrázku
plt.savefig("comparison_1.png")
# zobrazení grafu
plt.show()
Z výsledného obrázku je patrné, že se obě křivky skutečně odlišují:
Obrázek 14: Rozdíly mezi kubickými křivkami a křivkou vytvořenou přes make_spline.
Můžeme se navíc pokusit o úpravu parametru lam, ovšem ani tak nedosáhneme shody:
from scipy.interpolate import CubicSpline
from scipy.interpolate import make_smoothing_spline
import numpy as np
import matplotlib.pyplot as plt
# hodnoty na x-ové ose
x = np.arange(0, 10)
# generátor pseudonáhodných dat
rng = np.random.default_rng(seed=42)
# hodnoty na y-ové ose
y = x + 10 * rng.random((len(x))) - 5
# vykreslení bodů
plt.plot(x, y, "go", label="data")
# vytvoření spline
spline1 = CubicSpline(x, y)
spline2 = make_smoothing_spline(x, y, lam=0)
spline3 = make_smoothing_spline(x, y, lam=0.5)
spline4 = make_smoothing_spline(x, y, lam=10)
x = np.arange(0, 10, 0.01)
# vykreslení křivek
plt.plot(x, spline1(x), "r-", label="Cubic spline")
plt.plot(x, spline2(x), "b-", label="Smoothing spline #1")
plt.plot(x, spline3(x), "g-", label="Smoothing spline #2")
plt.plot(x, spline4(x), "y-", label="Smoothing spline #3")
# popis os a legenda
plt.xlabel("x")
plt.ylabel("y")
plt.legend()
# uložení grafu do rastrového obrázku
plt.savefig("comparison_2.png")
# zobrazení grafu
plt.show()
Obrázek 15: Rozdíly mezi kubickými křivkami a křivkou vytvořenou přes make_spline.
Aproximace kružnice spline křivkami
Prozatím jsme třídu CubicSpline používali pro proložení bodů, které například vznikly měřením a jejich x-ové souřadnice se tedy odlišují o pevně daný offset. Ovšem naprosto stejným postupem můžeme namísto y-ových souřadnic předat dvojice [x, y] a nechat si tak propojit body, které jsou v rovině rozmístěny naprosto libovolným způsobem. Namísto původních x-ových souřadnic se v tomto případě předávají hodnoty parametru t – přecházíme tedy do oblasti klasických parametrických křivek.
Pokusme se aproximovat kružnici pomocí čtveřice oblouků kubické spline. Pokusíme se propojit body ležící na čtyřech místech na kružnici (sever, jih, západ, východ). Navíc bude křivka uzavřena a „periodická“:
from scipy.interpolate import CubicSpline
import numpy as np
import matplotlib.pyplot as plt
# hodnoty na x-ové ose
pts = np.array([[-1, 0],
[0, 1],
[1, 0],
[0, -1],
[-1, 0]])
t = np.arange(0, 5)
xs = np.linspace(0, 4, 100)
# vytvoření spline
spline = CubicSpline(t, pts, bc_type="periodic")
# rozměry grafu při uložení: 640x640 pixelů
fig, ax = plt.subplots(1, figsize=(6.4, 6.4))
# vykreslení bodů
ax.plot(pts[:, 0], pts[:, 1], 'o', label="data")
xs = np.linspace(0, 6.28, 100)
# vykreslení kružnice
ax.plot(np.cos(xs), np.sin(xs), "b-", label="circle");
# vykreslení křivky
ax.plot(spline(xs)[:, 0], spline(xs)[:, 1], "r-", label='spline')
plt.legend()
# uložení grafu do rastrového obrázku
plt.savefig("cubic_spline_5.png")
# zobrazení grafu
plt.show()
Výsledek:
Obrázek 16: Aproximace kružnice pomocí čtyř kubických spline oblouků.
Aproximace s využitím libovolné Pythonovské funkce
Poslední vlastností knihovny SciPy, kterou se dnes budeme zabývat, je technika, která se používá pro aproximaci bodů s využitím libovolné Pythonovské funkce. Může se jednat o funkci lineární, logaritmickou, goniometrickou, či o libovolný polynom. K tomuto účely slouží funkce nazvaná curve_fit, kterou ovšem nenajdeme v balíčku scipy.interpolate, ale scipy.optimize:
Help on function curve_fit in module scipy.optimize._minpack_py:
curve_fit(f, xdata, ydata, p0=None, sigma=None, absolute_sigma=False, check_finite=None, bounds=(-inf, inf), method=None, jac=None, *, full_output=False, nan_policy=None, **kwargs)
Use non-linear least squares to fit a function, f, to data.
Assumes ``ydata = f(xdata, *params) + eps``.
Parameters
----------
f : callable
The model function, f(x, ...). It must take the independent
variable as the first argument and the parameters to fit as
separate remaining arguments.
xdata : array_like
The independent variable where the data is measured.
Should usually be an M-length sequence or an (k,M)-shaped array for
functions with k predictors, and each element should be float
convertible if it is an array like object.
ydata : array_like
The dependent data, a length M array - nominally ``f(xdata, ...)``.
p0 : array_like, optional
Initial guess for the parameters (length N). If None, then the
initial values will all be 1 (if the number of parameters for the
function can be determined using introspection, otherwise a
ValueError is raised).
sigma : None or scalar or M-length sequence or MxM array, optional
Determines the uncertainty in `ydata`. If we define residuals as
``r = ydata - f(xdata, *popt)``, then the interpretation of `sigma`
depends on its number of dimensions:
Aproximace přímkou a parabolou
V dalším demonstračním příkladu se pokusíme o aproximaci sady bodů přímkou (tedy o lineární regresi). Použijeme přitom funkci, která skutečně dokáže vypočítat body na přímce se sklonem a a posunem b:
def func(x, a, b):
return a * x + b
Příklad bude vypadat takto:
from scipy.optimize import curve_fit
import numpy as np
import matplotlib.pyplot as plt
def func(x, a, b):
return a * x + b
# hodnoty na x-ové ose
x = np.arange(0, 50)
# generátor pseudonáhodných dat
rng = np.random.default_rng(seed=42)
# hodnoty na y-ové ose
y = x + 10 * rng.random((len(x))) - 5
# vykreslení bodů
plt.plot(x, y, "go", label="data")
# výpočet koeficientů a a b
popt, pcov = curve_fit(func, x, y)
# koeficienty úsečky
print("Slope:", popt[0])
print("Intercept:", popt[1])
# vykreslení křivky prokládající body
plt.plot(x, func(x, *popt), "r-", label="fit: a=%5.3f, b=%5.3f" % tuple(popt))
# popis os a legenda
plt.xlabel("x")
plt.ylabel("y")
plt.legend()
# uložení grafu do rastrového obrázku
plt.savefig("curve_fit_1.png")
# zobrazení grafu
plt.show()
Výsledek:
Obrázek 17: Aproximace bodů přímkou.
Ovšem stejně dobře lze provést aproximaci parabolou, tedy funkcí:
def func(x, a, b, c):
return a * x**2 + b*x + c
Upravený kód příkladu:
from scipy.optimize import curve_fit
import numpy as np
import matplotlib.pyplot as plt
def func(x, a, b, c):
return a * x**2 + b*x + c
# hodnoty na x-ové ose
x = np.linspace(0, 4, 50)
# generátor pseudonáhodných dat
rng = np.random.default_rng(seed=42)
# hodnoty na y-ové ose
y_orig = func(x, 1.0, -1.5, 0.5)
y_noise = 0.2 * rng.normal(size=x.size)
y = y_orig + y_noise
# vykreslení bodů
plt.plot(x, y, "go", label="data")
# výpočet koeficientů a a b
popt, pcov = curve_fit(func, x, y)
# vykreslení křivky prokládající body
plt.plot(x, func(x, *popt), "r-", label="fit: a=%5.3f, b=%5.3f, c=%5.3f" % tuple(popt))
# popis os a legenda
plt.xlabel("x")
plt.ylabel("y")
plt.legend()
# uložení grafu do rastrového obrázku
plt.savefig("curve_fit_2.png")
# zobrazení grafu
plt.show()
Výsledek:
Obrázek 18: Aproximace bodů parabolou.
Aproximace křivkou založenou na goniometrických funkcích
Můžeme ovšem provést i složitější aproximaci, například nějakou funkcí založenou na goniometrických funkcích (a tedy popř. i na funkci periodické). Dnes již poslední demonstrační příklad je zvolený tak, aby do určité míry napodobil základní myšlenku Fourierovy transformace:
def func(x, a, b, c):
return a * np.sin(x) + b * np.sin(2 * x) + c
from scipy.optimize import curve_fit
import numpy as np
import matplotlib.pyplot as plt
def func(x, a, b, c):
return a * np.sin(x) + b * np.sin(2 * x) + c
# hodnoty na x-ové ose
x = np.linspace(0, 4, 50)
# generátor pseudonáhodných dat
rng = np.random.default_rng(seed=42)
# hodnoty na y-ové ose
y_orig = func(x, 1.0, -1.5, 0.5)
y_noise = 0.2 * rng.normal(size=x.size)
y = y_orig + y_noise
# vykreslení bodů
plt.plot(x, y, "go", label="data")
# výpočet koeficientů a a b
popt, pcov = curve_fit(func, x, y)
# vykreslení křivky prokládající body
plt.plot(x, func(x, *popt), "r-", label="fit: a=%5.3f, b=%5.3f, c=%5.3f" % tuple(popt))
# popis os a legenda
plt.xlabel("x")
plt.ylabel("y")
plt.legend()
# uložení grafu do rastrového obrázku
plt.savefig("curve_fit_3.png")
# zobrazení grafu
plt.show()
Výsledek může vypadat takto:
Obrázek 19: Aproximace bodů funkcí odvozenou od goniometrických funkcí.
Repositář s demonstračními příklady
Odkazy na Internetu
- SciPy homepage
https://scipy.org/ - SciPy (Wikipedia)
https://en.wikipedia.org/wiki/SciPy - The Hertzsprung–Russell diagram
https://scipython.com/book2/chapter-9-data-analysis-with-pandas/problems/p92/the-hertzsprung-russell-diagram/ - Linear algebra (scipy.linalg)
https://docs.scipy.org/doc/scipy/reference/linalg.html - Frequently Asked Questions – SciPy
https://scipy.org/faq/ - SciPy – Introduction
https://www.tutorialspoint.com/scipy/scipy_introduction.htm - LAPACK — Linear Algebra PACKage
https://www.netlib.org/lapack/ - LAPACK (Wikipedia)
https://en.wikipedia.org/wiki/LAPACK - LAPACK na GitHubu
https://github.com/Reference-LAPACK/lapack - SciPy in Python
https://pythonguides.com/scipy/ - scipy.linalg.det
https://docs.scipy.org/doc/scipy/reference/generated/scipy.linalg.det.html#scipy.linalg.det - scipy.linalg.inv
https://docs.scipy.org/doc/scipy/reference/generated/scipy.linalg.inv.html#scipy.linalg.inv - scipy.linalg.solve
https://docs.scipy.org/doc/scipy/reference/generated/scipy.linalg.inv.html#scipy.linalg.inv - Algebra
https://cs.wikipedia.org/wiki/Algebra - Lineární algebra
https://cs.wikipedia.org/wiki/Line%C3%A1rn%C3%AD_algebra - Lineární rovnice
https://cs.wikipedia.org/wiki/Line%C3%A1rn%C3%AD_rovnice - Soustava lineárních rovnic
https://cs.wikipedia.org/wiki/Soustava_line%C3%A1rn%C3%ADch_rovnic - Norma matice
https://cs.wikipedia.org/wiki/Norma_matice - Matrix norm
https://en.wikipedia.org/wiki/Matrix_norm - Norma (vektoru)
https://cs.wikipedia.org/wiki/Norma_(matematika) - Frobeniův skalární součin
https://cs.wikipedia.org/wiki/Frobeni%C5%AFv_skal%C3%A1rn%C3%AD_sou%C4%8Din - BLAS (Basic Linear Algebra Subprograms)
https://www.netlib.org/blas/ - Basic Linear Algebra Subprograms (Wikipedia)
https://en.wikipedia.org/wiki/Basic_Linear_Algebra_Subprograms - Operace s daty uloženými v binárních souborech v knihovnách NumPy a Pandas
https://www.root.cz/clanky/operace-s-daty-ulozenymi-v-binarnich-souborech-v-knihovnach-numpy-a-pandas/ - Operace s daty uloženými v binárních souborech v knihovnách NumPy a Pandas (dokončení)
https://www.root.cz/clanky/operace-s-daty-ulozenymi-v-binarnich-souborech-v-knihovnach-numpy-a-pandas-dokonceni/ - NumPy Home Page
http://www.numpy.org/ - NumPy v1.10 Manual
http://docs.scipy.org/doc/numpy/index.html - NumPy (Wikipedia)
https://en.wikipedia.org/wiki/NumPy - OpenBLAS: An optimized BLAS library
https://www.openblas.net/ - Integrovaná vývojová prostředí ve Fedoře: IPython a IPython Notebook
http://mojefedora.cz/integrovana-vyvojova-prostredi-ve-fedore-ipython-a-ipython-notebook/ - Integrovaná vývojová prostředí ve Fedoře: praktické použití IPython Notebooku a knihovny Numpy
http://mojefedora.cz/integrovana-vyvojova-prostredi-ve-fedore-prakticke-pouziti-ipython-notebooku-a-knihovny-numpy/ - Integrovaná vývojová prostředí ve Fedoře: praktické použití IPython Notebooku a knihovny Numpy (2.část)
http://mojefedora.cz/integrovana-vyvojova-prostredi-ve-fedore-prakticke-pouziti-ipython-notebooku-a-knihovny-numpy-2-cast/ - Integrovaná vývojová prostředí ve Fedoře: vykreslování grafů s využitím knihoven Numpy a matplotlib
http://mojefedora.cz/integrovana-vyvojova-prostredi-ve-fedore-vykreslovani-grafu-s-vyuzitim-knihoven-numpy-a-matplotlib/ - Integrovaná vývojová prostředí ve Fedoře: vykreslování grafů s využitím knihoven Numpy a matplotlib (2.část)
http://mojefedora.cz/integrovana-vyvojova-prostredi-ve-fedore-vykreslovani-grafu-s-vyuzitim-knihoven-numpy-a-matplotlib-2-cast/ - Piecewise linear interpolation
https://docs.scipy.org/doc//scipy/tutorial/interpolate/1D.html - Statistical functions (scipy.stats)
https://docs.scipy.org/doc/scipy/reference/stats.html - scipy.stats.linregress
https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.linregress.html - numpy.poly1d
https://numpy.org/doc/stable/reference/generated/numpy.poly1d.html - scipy.optimize.curve_fit
https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.curve_fit.html#scipy.optimize.curve_fit - scipy.interpolate.make_smoothing_spline
https://docs.scipy.org/doc/scipy/reference/generated/scipy.interpolate.make_smoothing_spline.html#scipy.interpolate.make_smoothing_spline - Famous Curves Index
https://mathshistory.st-andrews.ac.uk/Curves/ - Curve (Wikipedia)
https://en.wikipedia.org/wiki/Curve - Mathematical curves
https://www.2dcurves.com/index.html - Curves (Wolfram MathWorld)
https://mathworld.wolfram.com/topics/Curves.html - Smooth Curve (Wolfram MathWorld)
https://mathworld.wolfram.com/SmoothCurve.html - Spirals (Wolfram MathWorld)
https://mathworld.wolfram.com/topics/Spirals.html - An Interactive Introduction to Splines
https://ibiblio.org/e-notes/Splines/Intro.htm - Algebraic curve
https://en.wikipedia.org/wiki/Algebraic_curve - Transcendental curve
https://en.wikipedia.org/wiki/Transcendental_curve - Algebraic Curves
https://mathworld.wolfram.com/topics/AlgebraicCurves.html - Elliptic Curves
https://mathworld.wolfram.com/topics/EllipticCurves.html - Curves we (mostly) don't learn in high school (and applications)
https://www.youtube.com/watch?v=3izFMB91K_Q - Catenary arch
https://en.wikipedia.org/wiki/Catenary_arch - Parabolic arch
https://en.wikipedia.org/wiki/Parabolic_arch - Wattova křivka
https://www.geogebra.org/m/gNh4bW9r - Fifty Famous Curves, Lots of Calculus Questions, And a Few Answers
https://elepa.files.wordpress.com/2013/11/fifty-famous-curves.pdf - Faux, I.D. a Pratt, M.J.: Computational Geometry for Design and Manufacture,
Ellis Horwood Ltd., Wiley & Sons, 1979 - Wallace A.: Differential Topology,
Benjamin/Cummings Co., Reading, Massachussetts, USA, 1968 - Glossary of Bridge Terminology
http://sdrc.lib.uiowa.edu/eng/bridges/WaddellGlossary/GlossC.htm - Brachistochrona
https://cs.wikipedia.org/wiki/Brachistochrona - Missions: Cassini
https://solarsystem.nasa.gov/missions/cassini/overview/ - Giovanni Domenico Cassini
https://en.wikipedia.org/wiki/Giovanni_Domenico_Cassini - Cassini Ovals
https://mathworld.wolfram.com/CassiniOvals.html - Geocentrismus
https://cs.wikipedia.org/wiki/Geocentrismus - Who was Giovanni Cassini?
https://www.universetoday.com/130823/who-was-giovanni-cassini/ - Special plane curves
http://xahlee.info/SpecialPlaneCurves_dir/ConicSections_dir/conicSections.html - Interpolace
https://mathonline.fme.vutbr.cz/pg/Algoritmy/05_APROX_KRIVKY.htm - Lagrange Polynomial Interpolation
https://pythonnumericalmethods.berkeley.edu/notebooks/chapter17.04-Lagrange-Polynomial-Interpolation.html - Python Program for Lagrange Interpolation Method (with Output)
https://www.codesansar.com/numerical-methods/python-program-lagrange-interpolation-method.htm - Smooth Paths Using Catmull-Rom Splines
https://qroph.github.io/2018/07/30/smooth-paths-using-catmull-rom-splines.html - Lecture 11: Linear Interpolation Again – Bézier Curves
http://www.math.kent.edu/~reichel/courses/intr.num.comp.1/fall09/lecture12/bez.pdf - Geometrie/Úvod do křivek
https://cs.wikibooks.org/wiki/Geometrie/%C3%9Avod_do_k%C5%99ivek - B-Spline Curves and Surfaces (1)
http://www.cad.zju.edu.cn/home/zhx/GM/006/00-bscs1.pdf - Praktické ukázky možností aplikace Mandelbulber při tvorbě animací
https://www.root.cz/clanky/prakticke-ukazky-moznosti-aplikace-mandelbulber-pri-tvorbe-animaci/ - Kochanek–Bartels spline
https://en.wikipedia.org/wiki/Kochanek%E2%80%93Bartels_spline - class KochanekBartels
https://splines.readthedocs.io/en/latest/_modules/splines.html#KochanekBartels
