Lineární transformace je proces, kterému se často říká lineární interpolace nebo "mapování rozsahu" (range mapping).
Tento postup vezme tvou hodnotu z jednoho "světa" (původní rozsah) a proporcionálně ji umístí do "světa" druhého (nový rozsah).
Obecný vzorec pro přepočet
Představme si, že máš hodnotu $x$ v rozsahu $[A, B]$ a chceš ji převést na hodnotu y v rozsahu [C, D]. Vzorec vypadá následovně:
Rozklad vzorce (proč to funguje):
(x - A): Hodnotu nejdříve "vynuluješ". Pokud je tvá spodní hranice 50 a ty máš 60, zbyde ti 10 (vzdálenost od startu).
\frac{D - C}{B - A}: Toto je poměr (škálovací faktor). Říká ti, kolik jednotek nového rozsahu připadá na jednu jednotku starého rozsahu.
+ C$: Nakonec výsledek posuneš o počáteční bod nového rozsahu, aby nezačínal od nuly, ale tam, kde potřebuješ.
Výpočet pro příklad:
Předpokládejme, že tvá hodnota 80 je z původního rozsahu 0 až 100 (nejčastější případ, např. procenta) a chceš ji převést do rozsahu 10 až 100.
x = 80
Původní rozsah: A = 0, B = 100
Nový rozsah: C = 10, D = 100
Dosazení:
Tvá hodnota 80 v novém měřítku odpovídá číslu 82.
Praktické využití
Tento vzorec je "denním chlebem" programátorů (např. mapování polohy kurzoru na velikost zvuku) nebo inženýrů při kalibraci senzorů.
Tip: Pokud bys převáděl hodnotu do rozsahu 0 až 1, mluvíme o normalizaci.
V Pythonu máš několik možností, jak to vyřešit. Protože se tento výpočet používá v různých oborech, najdeš ho v knihovnách pro matematiku, grafy i strojové učení.
Tady jsou 3 nejčastější způsoby, jak to udělat:
1. NumPy: np.interp (Standard pro vědce)
Knihovna NumPy je v Pythonu základem pro veškerou matematiku. Má funkci interp, která přesně implementuje lineární interpolaci.
import numpy as np
hodnota = 80
stary_rozsah = [0, 100]
novy_rozsah = [10, 100]
# Syntaxe: np.interp(hodnota, [původní_min, původní_max], [nový_min, nový_max])
vysledek = np.interp(hodnota, stary_rozsah, novy_rozsah)
print(vysledek) # Výstup: 82.0
Výhoda: Funguje bleskově i na celé seznamy (pole) čísel najednou.
2. Jednoduchá vlastní funkce (Bez knihoven)
Pokud nechceš instalovat NumPy (který je docela "těžký"), stačí ti tato jedna řádka kódu. Je to přesně ten vzorec, který jsme si ukázali:
def map_range(x, in_min, in_max, out_min, out_max):
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min
print(map_range(80, 0, 100, 10, 100)) # Výstup: 82.0
3. Scikit-learn: MinMaxScaler (Pro data science)
Pokud pracuješ s velkými tabulkami dat a chceš je "napasovat" do určitého rozsahu (často 0 až 1), používá se třída MinMaxScaler.
from sklearn.preprocessing import MinMaxScaler
import numpy as np
data = np.array([80]).reshape(-1, 1)
scaler = MinMaxScaler(feature_range=(10, 100))
scaler.fit([[0], [100]]) # Definujeme původní hranice
vysledek = scaler.transform(data)
print(vysledek) # Výstup: [[82.]]
Proč to Python nemá v "základu"?
Python v základní knihovně math tuto funkci nemá, protože je příliš specifická pro různé obory.
Grafici (knihovny jako Matplotlib nebo PyQt) mají své vlastní metody.
Tvůrci her (např. v Pygame) tomu často říkají
lerp(Linear Interpolation).Datoví analytici používají zmíněný NumPy.
Zajímavost: v Arduinu (C++) se tato funkce jmenuje přímo
map(). Je to úplně ten samý princip.
Arduinovská funkce map() je legendární svou jednoduchostí a v Pythonu ti pravděpodobně chybí právě tenhle přímočarý přístup.
Pokud si chceš v Pythonu vytvořit vlastní verzi, která se chová úplně stejně jako ta v C/C++, vypadala by takto:
def map_value(x, in_min, in_max, out_min, out_max):
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min
# Použití:
vysledek = map_value(80, 0, 100, 10, 100) # vrátí 82.0
Malý rozdíl mezi C a Pythonem (Integer vs. Float)
V klasickém C (Arduinu) pracuje map() standardně s celými čísly (integery), což občas vede k zaokrouhlovacím chybám (všechno za desetinou čárkou se prostě "uřízne").
V Pythonu tento vzorec automaticky pracuje s desetinnými čísly (floaty), což je pro grafy a přesné výpočty mnohem lepší. Pokud bys ale z nějakého důvodu trval na chování jako v C, stačí výsledek obalit funkcí int() nebo použít celočíselné dělení //.
Proč je to v grafech tak silný nástroj?
Když jsi v C používal map(), pravděpodobně jsi převáděl hodnotu z analogového pinu (0 až 1023) na svítivost LED (0 až 255). V grafech je to úplně to samé, jen ty cílové souřadnice jsou pixely.
Příklad s osou X v grafu:
Data: Máš časovou řadu od 0 do 60 sekund.
Obrazovka: Tvůj graf začíná na pixelu 50 a končí na pixelu 450 (šířka 400 px).
Výpočet pro sekundu 30:
map_value(30, 0, 60, 50, 450)ti vyplivne přesně prostředek grafu, tedy pixel 250.
Tip pro pokročilé: "Clamping"
Funkce map() v C i tento jednoduchý vzorec v Pythonu mají jednu vlastnost: nehlídají přetečení. Pokud do nich pošleš hodnotu 110 (mimo rozsah 0–100), vyhodí ti výsledek mimo cílový rozsah (109).
V grafech se proto často používá kombinace s funkcí clip (nebo constrain v Arduinu):
def map_with_limit(x, in_min, in_max, out_min, out_max):
# Výpočet
val = (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min
# Omezení (clamping)
return max(min(val, out_max), out_min) 
Žádné komentáře:
Okomentovat
Poznámka: Komentáře mohou přidávat pouze členové tohoto blogu.