Vydělávej až 160.000 Kč měsíčně! Akreditované rekvalifikační kurzy s garancí práce od 0 Kč. Více informací.
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

Práce s maticemi a vektory II

Způsob práce s vektory a maticemi v MATLABu je natolik zásadní téma, že je rozděleno do třech lekcí. Informace v jednotlivých částech se překrývají a pokaždé jsou podány trochu jiným způsobem. Umožní tak nahlédnout na problematiku z různých úhlů.

V první části jsme si vysvětlili indexování a práci s buňkami. Tato část se dopodrobna zabývá tvorbou vektorů, přístupem k jednotlivým prvkům a jejich filtrováním. Podobná témata jsou vysvětlena v třetí části, pouze s pomocí matic a na praktické ukázce úpravy fotografie.

Matice

Druhou nezbytnou záležitostí k ovládnutí MATLABu je práce s maticemi. Jeden příklad za všechny: Máme definované dvourozměrné pole (matici). Úkolem je ke každému prvku pole přičíst hodnotu 20.

Pokud umíte programovat v jiném jazyce, možná by vás napadlo použít cykly. Např. v C# bychom to udělali takto:

var pole = new int[,]{ {5, 6},{9, 8},{6, 1} };
for(int k=0;k < pole.GetLength(0);k++)
  for(int l=0;l < pole.GetLength(1);l++)
    pole[k,l]+=20;

Ekvivalentem v MATLABu ale je:

pole = [5 6; 9 8; 6 1];
pole = pole + 20;

Žádný dvojitý cyklus, pouze jednoduché přičtení. MATLAB zde pochopil, že ke každému prvku z matice chceme přičíst 20. Pokud bychom to udělali takto: pole = pole + [10 20], tedy snažili se přiřadit k matici vektor, zahlásí nám prostředí chybu, protože už neví, jak si s tím poradit (což by se také dalo čekat).

Typování

MATLAB je slabě typovaný jazyk (stejně jako např. JavaScript nebo Python), nemusíme mu definovat, že číslo je int nebo double. Tato vlastnost je skvělá pro rychlé psaní skriptů. Při tvorbě komplexnějšího programu trochu překáží, respektive nepohlídá za nás některé hloupé chyby. Můžeme totiž napsat něco jako:

promenna = 21;
promenna = "teď je tu string a nikdo si nestěžuje"

Nikdo si sice nestěžuje, ale až se o pár desítek řádků kódu dále budeme snažit přičíst k proměnné nějaké číslo, bude to již problém. A pro zjištění jeho příčiny se budeme muset prokousat o ty desítky řádků zpět (a nevíme jak moc zpět).

Tvorba vektorů

Po teoretické odbočce zpět k maticím. Z definice proměnné pole = [5 6; 9 8; 6 1]; je zřetelné, jak se definuje matice. Je to tedy v hranatých závorkách. Oddělovačem čísel je mezera (nebo čárka), oddělovačem řádků je středník. Finální středník je zde proto, aby se zamezilo vypsání do konzole.

Chceme-li vytvořit vektor obsahující čísla například o 1 do 100, lze to pomocí dvojtečky:

vektor = 1:100;

Zkusme si zahrnout každé druhé číslo od 1 do 100:

vektor = 1:2:100;

Vložením dalšího čísla mezi dvojtečky se tedy definuje krok. Funguje to i s desetinnými čísly:

vektor = 1:0.1:100;

nebo bez té nuly:

vektor = 1:.1:100;

Funguje to též pozpátku, jen je nutné krokovat zápornou hodnotou:

vektor = 100:-.5:1;

Přístup k prvkům vektoru

Chceme-li zjistit, co se ve vektoru skrývá, tedy přistoupit k prvkům pole, využijeme index a kulaté závorky. Zde přiřazujeme do proměnné v dvacáté číslo vektoru:

v = vektor(20)

Ekvivalentem v C# by bylo:

int v = vektor[19];

Ano, MATLAB, na rozdíl od většiny používaných jazyků, indexuje od 1. Chceme-li vytáhnout více prvků z vektoru, stačí ho naindexovat vektorem. 20. ,21., a 22. prvek vektoru získáme:

prvky20_22 = vektor([20 21 22]);

Zde je částečné vysvětlení toho, proč se k přístupům prvků nevyužívají hranaté závorky – ty se používají k definici vektorů a matic.

Pokud by prvků bylo více, nebudeme je v indexačním vektoru vypisovat všechny, ale zas využijeme dvojtečky:

prvky20_50 = vektor(20:50)

Zde již nepotřebujeme hranatou závorku, vektor se prozradil právě onou dvojtečkou. Dalším způsobem, jak vytáhnout prvky vektoru, je pomocí binárního vektoru:

vektor = [1 5 6 9 8 3];
pouze_neco_z_vektoru = vektor([0 0 1 1 0 0])

Tímto zápisem říkáme: První prvek neber, druhý také ne, třetí ano, čtvrtý ano, pátý ne, šestý také ne... Výsledkem je [6 9]. Binární vektor musí mít stejnou délku jako vektor původní (jinak by to nedávalo smysl). K čemu je to dobré?

Podmínky při výběru prvků vektoru

Z vektoru chceme pouze čísla větší než 6:

vetsi_nez_6 = vektor(vektor > 6)

Výsledek je [9 8].

Tento na první pohled trochu krkolomný zápis obsahuje dva kroky. Ten vnitřní je tvorba binárního vektoru. Tážeme-li se, zdali je vektor větší než skalár (vektor > 6), MATLAB to pochopí a proiteruje podmínkou celý vektor. Tam, kde je splněna, zaznamená jedničku, tam, kde nikoliv, nulu. No a tento binární vektor je následně využit k indexování samotného vektoru.

C# ekvivalentem bylo toto:

var vetsi_nez_6  = new List<int>();
for(int k = 0; k < pole.GetLength(0); k++)
  if(pole[k] > 6)
    vetsi_nez_6.Add(pole[k]);

Anebo zjednodušeně převodem na List a za použití LINQu:

var pole = new int[] { 1, 5, 6, 9, 8, 3 };
var vetsi_nez_6 = (new List<int>(pole)).Where(x => x > 6);

Vícero podmínek při výběru prvků

Podmínky pro získávání prvků z vektoru se pochopitelně dají kombinovat. Chceme-li tedy z vektoru získat pouze prvky větší než 6, které jsou navíc sudé, použijeme následující zápis:

vetsi_nez_6_sude = vektor((vektor > 6) & (~mod(vektor, 2)))

První část výrazu již známe, za ní následuje logické AND (tedy jednička vznikne pouze v případě, že na obou stranách budou jedničky). Druhá část je zodpovědná za určení, zda je číslo ve vektoru sudé. Děje se tak s pomocí funkce mod() (MATLAB nemá operátor modulo, jako třeba C# má %. Procento je v MATLABu používáno pro komentáře). Modulo nějakého čísla je zbytek po celočíselném dělení a může nabývat jakékoliv hodnoty od 0 do čísla - 1. V případě, že "modulujeme" s pomocí dvojky, jsou možnosti buď 0 nebo 1. Zde se projevuje výhoda slabé typovosti. Výstupem funkce mod() je v tomto případě pouze binární vektor. Vlnovka ~ je operátorem negace (v C# vykřičník !). Využíváme ji proto, aby se z jedniček, které vzniknou po "modulování" dvojkou u lichých čísel, staly nuly, a tím pádem výsledné číslo nebylo vybráno.

Stejná pravidla jako pro indexování vektorů platí i pro indexování matic. Chceme-li získat z matice M pouze prvky, které jsou dělitelné 3, uděláme to následovně:

M = [1 3 6 8; 5 6 9 3; 2 1 2 9];
delitelne_3 = M(mod(M, 3)==0) % 1. řešení
delitelne_3_alt = M(~mod(M, 3)) % 2. řešení

Zde jsme vytvořili 2 řešení, první porovnává výsledek modula s nulou, která vznikne v případě dělitelnosti třemi. Alternativní zápis využívá opět operátoru negace. Ten ze všech čísel, která nejsou 0, udělá právě 0. Výsledky jsou stejné.

Zápis je zcela totožný tomu, jako bychom operaci prováděli s vektory. Výstupem je též vektor, také z toho důvodu, že předem nevíme, jaký výsledek bude a jestli by se vůbec do matice poskládal (jak vytvoříte dvourozměrnou matici o třech prvcích...).

MATLAB iteruje maticí podobně jako vektorem. Nemusíme k tomu používat cykly a tento funkcionální a maticový přístup je doporučený způsob práce. Také je to způsob, který spoustě lidem dělá problém řádně uchopit, zvlášť když za vším vidí cyklus (a on tam ve skutečnosti je). Nicméně komu se to podaří, má nad MATLABem zpola vyhráno. A nejen to. Funkcionální přístup se promítá v mnoha jiných jazycích: R, Haskell, Scala, F#, a mnohé další. Koneckonců i C#, především od své 7. verze, přidává způsoby, jak řešit problémy funkcionálně (například výše zmíněný kód pro filtrování čísel větších než 6).

Nahrazení prvků splňujících podmínku

Již víme, jak z vektoru (nebo matice) získáme prvky splňující podmínku. Nyní tyto prvky nahradíme jiným číslem:

vektor = [1 5 6 9 8 3];
vektor(vektor > 6) = 0;

Tímto kusem kódu jsme čísla přesahující 6 nahradili nulou. Výběr prvků se nám přesunul na levou stranu rovnítka.

Editace prvků splňujících podmínku

Předchozí příklad čísla zcela nahradil, nyní prvky jen trochu upravíme:

vektor(vektor > 6) = vektor(vektor > 6) + 3;

Je to zas kombinace již známých postupů. Podmínka je na obou stranách. Tento zápis bych osobně nejraději zjednodušil následovně:

%Tento kód není validní
vektor(vektor > 6) +=3;

tak, jak to známe z mnoha jiných jazyků. Nicméně v MATLABu to nelze. Musíme se tedy spokojit s podmínkou na obou stranách. Odporuje to principu DRY (don't repeat yourself). Můžeme s tím provést jen toto:

podminka = vektor > 6;
vektor(podminka) = vektor(podminka) + 3;

Kód nevypadá moc elegantněji, avšak pokud je podmínka náročnější (delší), vyplatí se ji takto odstrčit do proměnné. podminka bude obsahovat pouze 0 a 1.

Cvičení

Na závěr následuje pár příkladů, jejichž řešení naleznete na konci přiloženého souboru. Soubor obsahuje i veškerý kód z tohoto článku.

Vytvořte vektor f s celými čísly od 101 do 202 a z něj získejte:

  • první a poslední prvek.
  • prvních 12 prvků.
  • všechny prvky dělitelné 9.
  • každý 4. prvek.
  • vektor, jehož hodnoty mají poloviční hodnotu.
  • vektor, jehož hodnoty jsou o 7 větší.
  • posledních 13 prvků.
  • posledních 10 prvků dělitelných 2.
  • sedmý až devatenáctý prvek.
  • prvky dělitelné pěti, jejichž poslední číslovka není 0.

Příště, v lekci , se podrobněji podíváme na matice a konečně si řekneme, k čemu je to všechno dobré :)


 

Stáhnout

Stažením následujícího souboru souhlasíš s licenčními podmínkami

Staženo 14x (1.3 kB)
Aplikace je včetně zdrojových kódů

 

Předchozí článek
Zobrazení grafů v MATLABu
Všechny články v sekci
Matlab
Článek pro vás napsal tesař.tech
Avatar
Uživatelské hodnocení:
Ještě nikdo nehodnotil, buď první!
Autor se věnuje dýchání přibližně celý život
Aktivity