Kurzusok
/
Julia
Julia
/
Tanterv
/
Többdimenziós tömbök
Tö

Többdimenziós tömbök ebben a kurzusban: Julia

2 feladat

A(z) Többdimenziós tömbök fogalomról

Régen, a Vektorok fogalomban megjegyeztük, hogy „a tömbök tetszőleges méretűek lehetnek (csak a hardvered memóriakorlátai szabnak határt nekik), és tetszőleges számú dimenziójuk lehet.”

Azóta nagyrészt figyelmen kívül hagytuk az egynél több dimenziós tömböket, hogy egyszerű maradjon a dolog. Ez a döntés sokkal érthetőbb lesz, ha megpróbálod elolvasni a Julia referenciadokumentumait, teljes bonyolultságukban.

A magasabb dimenziós tömbök azonban nagyon-nagyon fontosak a tudományos számításokban, ezért meg kell értenünk őket.

Nevezéktan: A matematika évszázados hagyományát követve az egydimenziós tömböket Vectors, a kétdimenziósakat Matrices néven emlegetjük.

A dokumentumban szereplő példák többnyire mátrixok lesznek. A 3 vagy több dimenzióval való munka szintaktikailag szinte ugyanaz, de a kimenetet nehéz és zavaró olvasni (egy kétdimenziós képernyőn).

Egy N-dimenziós, T eltype-ű tömb type-ja Array{T, N}.

A kényelem és a matematikai nevezéktannal való összhang kedvéért a Julia definiál néhány típusáltnevet: Vector{T} a Array{T, 1} helyett és Matrix{T} a Array{T, 2} helyett.

# 3-D array
julia> m3 = ones(2, 3, 4);

julia> typeof(m3)
Array{Float64, 3}

# Vector
julia> v = [1, 2]
2-element Vector{Int64}:
 1
 2

julia> typeof(v) == Array{Int64, 1}
true

# Matrix
julia> m
2×3 Matrix{Int64}:
 1  2  3
 4  5  6

julia> typeof(m)
Matrix{Int64} (alias for Array{Int64, 2})

julia> typeof(m) == Array{Int64, 2}
true

Tömbök létrehozása

Sok vektort hoztunk már létre úgy, hogy egy vesszővel elválasztott listát tettünk szögletes zárójelbe. Elválasztóként pontosvessző is használható.

julia> v = [1, 2, 3]
3-element Vector{Int64}:
 1
 2
 3

julia> w = [1; 2; 3]
3-element Vector{Int64}:
 1
 2
 3

julia> v == w
true

Ha szóközt (vagy tabulátort) használunk elválasztóként, az eredmény más lesz.

julia> u = [1 2 3]
1×3 Matrix{Int64}:
 1  2  3

A Julia ezt most egy 1×3-as mátrixként definiálja (más összefüggésben row vector-nak neveznénk).

Általában a szóközök vízszintesen kapcsolnak össze dolgokat, a pontosvesszők (vagy a sortörések) pedig függőlegesen.

A »dolgok« említése szándékosan homályos, mert a Julia megpróbál dolgozni azzal, amit adsz neki.

julia> [v 2v]
3×2 Matrix{Int64}:
 1  2
 2  4
 3  6

julia> [v; 2v]
6-element Vector{Int64}:
 1
 2
 3
 2
 4
 6

Léteznek hcat() és vcat() függvények, amelyek ugyanezt teszik, és egyértelműbbé teszik, hogy ezek vízszintes és függőleges összefűzések. A magasabb dimenziós általánosítás a cat() függvény.

jjulia> hcat(v, 2v)
3×2 Matrix{Int64}:
 1  2
 2  4
 3  6

julia> vcat(v, 2v)
6-element Vector{Int64}:
 1
 2
 3
 2
 4
 6

A kifejezett mátrixokat kényelmesen sorfolytonos sorrendben írjuk le, mert ez illeszkedik az emberi intuícióhoz (könnyebb ránézni, a vízszintes szöveget használó kultúrákban):

julia> m = [1 2 3; 4 5 6]
2×3 Matrix{Int64}:
 1  2  3
 4  5  6

Jegyezd meg azonban, hogy a Julia (a Fortranhoz, R-hez és Matlabhoz hasonlóan, de a C/C++-tól vagy a NumPy-tól eltérően) oszlopfolytonos sorrendben tárolja az N-dimenziós tömböket, és ez óriási teljesítménykülönbséget jelenthet, ha végigiterálsz az elemeken. Segíts a CPU-gyorsítótáradnak, hogy segíthessen neked!

# put these integers in 2 rows and 3 columns

julia> reshape(collect(1:6), 2, 3)
2×3 Matrix{Int64}:
 1  3  5
 2  4  6

A fenti példa az 1-től 6-ig terjedő egészeket veszi, és oszloponként tölti fel velük a 2×3-as mátrixot.

Több segédfüggvény is létezik a gyakori tömbtípusok létrehozására (egyenletes vagy véletlen).

julia> zeros(2, 3)  # see also ones()
2×3 Matrix{Float64}:
 0.0  0.0  0.0
 0.0  0.0  0.0

julia> falses(2, 2)  # booleans, see also trues()
2×2 BitMatrix:
 0  0
 0  0

julia> rand(Float32, 2, 3)  # random numbers in the interval [0, 1)
2×3 Matrix{Float32}:
 0.768823  0.169633  0.632565
 0.388451  0.109176  0.850381

Egyenletesen elosztott értékek generálása

A NumPy rajongói tudják, hogy a np.linspace széles körben használt függvény meghatározott hosszúságú, meghatározott végpontokkal és egyenletesen elosztott értékekkel rendelkező tömb létrehozására. Ezt jellemzően egy grafikon x-tengelyeként vagy lineáris modellek független változójaként használják.

A Juliának nincs pontos megfelelője, de a nagyon rugalmas range() függvény utánozhatja, ha megadjuk az alsó és felső korlátot, valamint a length kulcsszóargumentumot.

Aki helyben dolgozik, és hozzáfér a Plots csomaghoz, lefuttathatja ezt a kódot:

julia> using Plots

julia> x = range(0, 2π; length=100)
0.0:0.06346651825433926:6.283185307179586

julia> typeof(x)
StepRangeLen{Float64, Base.TwicePrecision{Float64}, Base.TwicePrecision{Float64}, Int64}

julia> vals = [x sin.(x) cos.(x)]
100×3 Matrix{Float64}:
 0.0         0.0           1.0
 0.0634665   0.0634239     0.997987
 0.126933    0.126592      0.991955
 0.1904      0.189251      0.981929
(...truncated)

julia> plot(vals[:, 1], vals[:, 2:3])

A StepRangeLen úgy használható, mint egy vektor, beleértve a mátrixoszloppá alakítást is.

Lásd még a megfelelő logrange() függvényt, amellyel egy logaritmikus tengelyen egyenletesen elosztott értékeket generálhatsz.

Indexelés

Egy kétdimenziós tömbnél általában két indexet használunk, [row, col] sorrendben.

julia> m
2×3 Matrix{Int64}:
 1  2  3
 4  5  6

julia> m[1, 2] # row 1, col 2
2

# Stay within bounds! There is no row 3.
julia> m[3, 1]
ERROR: BoundsError: attempt to access 2×3 Matrix{Int64} at index [3, 1]

julia> m[3]
2

Az utolsó példa talán meglepő: egy egyszerű index nem hiba, és egyetlen elemet ad vissza.

A magyarázat visszavezet az oszlopfolytonos sorrendről szóló megjegyzéshez: a Julia végigmegy az 1. oszlopon, majd a 2. oszlopon, amíg meg nem találja a 3. elemet a memóriában.

Vigyázz: ennek van néhány haszna általános célú könyvtárak írásakor, de valószínűbb, hogy összezavar!

Hasonló probléma merül fel, amikor egy tömb méretét kérdezzük le. A length() az elemek teljes számát adja, a size() pedig egy ndims() elemű tuple-t ad, amely az egyes dimenziók hosszát tartalmazza.

julia> m
2×3 Matrix{Int64}:
 1  2  3
 4  5  6

julia> length(m)
6

julia> size(m)  # 2 rows, 3 cols
(2, 3)

julia> ndims(m)  # how many dimensions? Like `m |> size |> length`
2

Indexelés tartományokkal és tömbökkel

Könnyedén másolhatunk egy részmátrixot.

Az alábbi példában a reshape() függvény a megadott dimenziókra kényszeríti a tömböt úgy, hogy oszloponként tölti fel, majd felszeleteljük.

julia> m12 = reshape(collect(1:12), 4, 3)
4×3 Matrix{Int64}:
 1  5   9
 2  6  10
 3  7  11
 4  8  12

julia> m12[2:4, 1:2]
3×2 Matrix{Int64}:
 2  6
 3  7
 4  8

# some rows, all columns
julia> m12[2:4, :]
3×3 Matrix{Int64}:
 2  6  10
 3  7  11
 4  8  12

Egy önmagában álló : azt jelenti, hogy »másolj le mindent ebben a dimenzióban«.

Nem egymást követő sorokhoz/oszlopokhoz vektorokat használj:

julia> m12[[1, 3], :]  # rows 1 and 3
2×3 Matrix{Int64}:
 1  5   9
 3  7  11

Egy önmagában álló : bármely tömböt egy vektorrá lapít, oszloponként.

julia> m
2×3 Matrix{Int64}:
 1  2  3
 4  5  6

julia> m[:]
6-element Vector{Int64}:
 1
 4
 2
 5
 3
 6

Függvények alkalmazása egy tömbre

Korábban már láttunk olyan aggregációs függvényeket, mint a sum() és a maximum(), egydimenziós gyűjteményekre alkalmazva, ahol az összes elemen működnek, és skaláris eredményt adnak vissza.

Ez magasabb dimenziókban is működik. Előfordulhat azonban, hogy a függvényt csak egy dimenzióra szeretnénk alkalmazni, például lefelé vagy keresztben összegezve, hogy egy 1 méretű singleton dimension-nel rendelkező tömböt kapjunk.

Erre szolgál az opcionális dims kulcsszóargumentum.

julia> m
2×3 Matrix{Int64}:
 1  2  3
 4  5  6

julia> sum(m)  # sum everything
21

julia> sum(m; dims=1)  # sum down
1×3 Matrix{Int64}:
 5  7  9

julia> sum(m; dims=2)  # sum across
2×1 Matrix{Int64}:
  6
 15

# other aggregation functions are similar
julia> maximum(m; dims=2)
2×1 Matrix{Int64}:
 3
 6

A dims értéke az a dimenzió, amely singleton lesz, tehát a fenti példákban dims=1 => 1×3 és dims=2 => 2×1.

Általánosítva, a magasabb dimenziós tömbök több dimenziót is redukálhatnak, ha a dims értéke egy tömb vagy tartomány (a kimenet megértése ismét némi gondolkodást igényelhet!).

Dimenziótudatos függvények írása

A dims kulcsszóargumentum gyakori a beépített függvényekben, mint a sum(), de hogyan írjunk valami egyenértékűt a saját kódunkban?

Az egyik jó válasz a magasabb rendű függvények használata, mint a reduce(), amit egy későbbi fogalomban részletesebben is tárgyalunk.

Alternatívaként a Julia több olyan függvényt kínál, amelyek segítségével az N-dimenziós tömböket úgy kezelhetjük, mintha vektorok egymásba ágyazott vektorai lennének.

Mátrixokhoz a eachrow() és a eachcol() kényelmes, de az általánosabb függvény az eachslice(), amely tetszőleges dimenziókat kezel.

# m is as in the previous examples

julia> eachrow(m)
2-element RowSlices{Matrix{Int64}, Tuple{Base.OneTo{Int64}}, SubArray{Int64, 1, Matrix{Int64}, Tuple{Int64, Base.Slice{Base.OneTo{Int64}}}, true}}:
 [1, 2, 3]
 [4, 5, 6]

julia> eachcol(m)
3-element ColumnSlices{Matrix{Int64}, Tuple{Base.OneTo{Int64}}, SubArray{Int64, 1, Matrix{Int64}, Tuple{Base.Slice{Base.OneTo{Int64}}, Int64}, true}}:
 [1, 4]
 [2, 5]
 [3, 6]

A típus kissé ijesztő, de csak azért, mert ez egy nézet az eredeti tömbre, ami lehetővé teszi, hogy másolás nélkül dolgozzunk rajta. A Julia tömbök akár terabájt méretűek is lehetnek, ezért a másolás potenciális teljesítményrémálom lehet!

Egy ilyen nézet használható ciklusokhoz, broadcastinghoz vagy bármely más művelethez, amit eddig a tantervben láttunk.

Emellett a comprehensionök hatékonyak és sokoldalúak lehetnek tömbbemenettel. Az egyszerű eseteket a Ciklusok fogalomban említettük, de egy későbbi fogalomban bővebben is kitérünk rájuk.

Broadcasting több dimenzióban

Az egydimenziós esetet a Vektorműveletek fogalomban tárgyaltuk.

julia> v = [1.2, 1.5, 1.7]
3-element Vector{Float64}:
 1.2
 1.5
 1.7

julia> v .- 0.5
3-element Vector{Float64}:
 0.7
 1.0
 1.2

A pontozott .- operátor az egyetlen 0.5-öt egyenértékűnek tekinti a [0.5, 0.5, 0.5]-tel, mivel broadcasting útján a dimenziókhoz igazítja, majd elemenként elvégzi a kivonást.

Ennek kiterjesztése magasabb dimenziókra valójában csak ugyanaz még egyszer.

Például amikor egy 2x1-es sorvektor [1.0 1.5 2.0] és a 2x3-as mátrix [1 2 3; 4 5 6] szorzását broadcasteljük, az egyenértékű azzal, mintha [1.0 1.5 2.0]-t [1.0 1.5 2.0; 1.0 1.5 2.0]-ra broadcastelnénk, majd a szorzást elemenként végeznénk el.

julia> m
2×3 Matrix{Int64}:
 1  2  3
 4  5  6

julia> m .* [1.0 1.5 2.0]  # broadcast a 1x3 row vector down
2×3 Matrix{Float64}:
 1.0  3.0   6.0
 4.0  7.5  12.0

julia> m .* [1.0 1.5 2.0; 1.0 1.5 2.0]  # 2x3 elementwise multiplication
2×3 Matrix{Float64}:
 1.0  3.0   6.0
 4.0  7.5  12.0

julia> m ./ [1, 2]  # broadcast a 2x1 column vector across
2×3 Matrix{Float64}:
 1.0  2.0  3.0
 2.0  2.5  3.0

julia> m ./ [1 1 1; 2 2 2]  # 2x3 elementwise division
2×3 Matrix{Float64}:
 1.0  2.0  3.0
 2.0  2.5  3.0

Megjegyzés: A broadcastinghez a nem singleton dimenzióknak kell azonos méretűnek lenniük (pl. 2x3 Matrix .* 2x1 Matrix).

Továbbá egy függvény pontosan ugyanúgy broadcastelhető egy Matrix minden elemére, ahogy egy Vector esetében is történik.

julia> m
2×3 Matrix{Int64}:
 1  2  3
 4  5  6

julia> (x -> x^2).(m)  # broadcast a function to all elements
2×3 Matrix{Int64}:
  1   4   9
 16  25  36

Eleinte úgy érezheted, hogy a programozói összezavarodás lehetősége exponenciálisan nő a dimenziók számával, de a gyakorlás sokat segít. Ráadásul a NumPy tömbökkel való jártasság meglehetősen jól átvihető a Juliára, az eltérő szintaxis ellenére is.

További olvasnivaló

A vektorokon és mátrixokon végzett sok gyakori művelet a lineáris algebra általános körébe tartozik.

(Jelenleg) két további fogalom található az Exercism weboldalán, amelyek csak dokumentum formájában léteznek, amíg nem tudunk hozzájuk feladatokat tervezni.

  1. A Lineáris algebra alapjai elsősorban a szorzás különböző típusaival foglalkozik.
  2. A Lineáris egyenletrendszerek megoldása a lineáris egyenletrendszerekkel és azok hatékony megoldásával foglalkozik.
Advanced

A jövőben lehet egy harmadik fogalom is a mátrixfaktorizációkról.

Ne feledd azonban, hogy a Lineáris algebra alapjai meglehetősen matematikai jellegű, és nem biztos, hogy minden tanulónak megfelel.

A későbbi fogalmak (egyenletmegoldás és faktorizációk) fejlettebbek, és valójában csak azoknak szólnak, akik otthonosan mozognak az egyetemi szintű matematikában.

Elnézést, de ilyen a téma természete, és ez a Julia egyik fontos felhasználási esete.

Szerkesztés GitHubon A hivatkozás új ablakban vagy lapon nyílik meg

Tanuld meg a(z) Többdimenziós tömbök fogalmat

A gyakorlás zárolva

Oldj fel még 1 feladatot, hogy gyakorolhasd a(z) Többdimenziós tömbök fogalmat