10  NumPy pakkinn

10.1  Inngangur

NumPy pakkinn er ætlaður fyrir vinnu með vigra (vectors) og fylki (matrices) í Python og auk þess ýmsa tölulega reikninga (numerical computations) af einfaldara tagi. Pakkinn SciPy er síðan ætlaður fyrir allskyns sérhæfðari vísindalega reikninga.

10.1.1  Vigrar og fylki í stærðfræði

Nákvæmar skilgreingar á stærðfræðilegum vigrum og fylkjum og skyldum hugtökum eru umfjöllunarefni í námskeiðum um línuleg algebru. Hér látum við duga styttri útgáfur.

Vigur (vector) er runa af endanlega mörgum tölum sem gefið er nafn og notuð sem ein heild. Einstakar tölur nefnast stök (elements), þau eru oftast tölusett 1, 2, 3… en stundum 0, 1, 2…, og i-ta stak vigurs \(a\) er táknað \(a_i\). Vigur \(a\) með tölunum 1, 2 og 4 má rita

\[\begin{split}a=(1,2,4)\quad\text{eða}\quad a=\begin{pmatrix}1\\2\\4\end{pmatrix}\end{split}\]

Fylki (matrix) er tafla með tölum sem gefið er nafn og notuð sem heild. Tölur í fylki heita líka stök. Stakið í línu \(i\) og dálki \(j\) í fylki \(A\) er táknað \(a_{ij}\). Í stærðfræðilegri umfjöllun er oftast byrjað að telja í 1 svo stakið efst til vinstri er \(a_{11}\), en þegar verið er reikna í tölvum er oft byrjað að telja í 0. Fylki með \(m\) línum og \(n\) dálkum er kallað \(m\) sinnum \(n\) fylki, táknað \(m \times n\). Hér er dæmi um \(2 \times 3\) fylki:

\[\begin{split}A = \begin{pmatrix}1 & 2 & 3\\6 & 7 & 8\end{pmatrix}\end{split}\]

10.1.2  Um orðið Fylki

Skv. Tölvuorðasafninu hefur orðið fylki tvær merkingar á íslensku:

  1. [einkum í stærðfræði] rétthyrnd tafla af tölum (eða öðrum gildum) = e. matrix.

  2. [einkum í tölvufræði] samsteypa (aggregate) hluta af sama tagi þar sem hægt er að vísa í einstök stök með vísi eða vísum (index, indices) = e. array.

Í seinni merkingunni er fylki notað sem samheiti yfir vigra, tvívíð fylki, og margvíð fylki (þar sem vísarnir eru 3 eða fleiri). Stundum er talað um vigra sem einvíð fylki.

Aðvörun

Þar sem NumPy er beggja megin veggjar, í stærðfræði og í tölvufræði, verður orðið fylki notað í báðum merkingunum hér – lesandi þarf stundum af ráða af samhenginu við hvað er átt.

10.1.3  Import-skipanir fyrir NumPy

Í NumPy er undirpakki sem heitir numpy.linalg sem er talsvert notaður. Hann geymir meðal annars föll til að reikna norm vigurs og til að leysa jöfnuhneppi. Alsiða er að skammstafa numpy með np og numpy.linalg með la, sem sé að flytja inn með:

import numpy as np
import numpy.linalg as la

Síðan er sett np. eða la. á undan NumPy-skipunum og -föllum sem notuð eru.

10.2  Vigrar í NumPy

Vigur í NumPy er að mörgu leyti líkur venjulegum Python lista. Aðalmunurinn er sá að í vigrum verða öll stök að vera af sama gagnatagi, og með því sparast minnispláss og auk þess fæst mun hraðvirkari vinnsla þegar unnið er með mörg stök. Eins og við munum gildir slík skorða ekki um lista, þeir geta innihaldið blöndu gagnataga.

10.2.1  Vigur gefið gildi

NumPy er viðbót við Python, og öfugt við grunntögin sem öll gefa kost á að búa til fasta af hverju tagi (sjá kafla 4.1 og 5.1) er ekki boðið upp á vigur-fasta, heldur þarf að nota NumPy-föll til að búa til vigra.

Eitt þeirra er fallið np.array sem breytir venjulegum Python-lista af tölum í vigur. Til að búa til vigurinn \(x = (1,2,3,4,6)\) má nota skipunina:

x = np.array([1,2,3,4,6])

Svo eru til skipanir til að búa til ýmsa sérstaka vigra, til dæmis fæst núllvigur með 3 stökum, \(z = (0,0,0)\), með:

z = np.zeros(3)

Loks skal tekið fram að ef vigur er gefið gildi með öðrum vigri (t.d. y = x) er ekki tekið afrit heldur er búin til ný tilvísun í vigurinn, sbr. kafla 5.3.4 og 6.2. Til að taka afrit mætti nota:

y = x.copy()

10.2.2  Einstök stök og hlutvigrar

Vísað í einstök stök í vigrum með hornklofum, alveg eins og í listum, og fyrsta stakið er líka númer 0. Ef x er \((1,2,3,4,6)\) þá væri x[2] = 3, x[2:] væri NumPy vigurinn \((3,4,6)\) og x[::2] væri vigurinn \((1,3,6)\).

Æfing: Fyrsta NumPy-æfingin

Prófið skipanirnar í næsta kafla á undan (10.2.1) og þessum kafla

10.2.3  Einföld útprentun vigra

Til að prenta út vigur x er hægt að skrifa einfaldlega

print(x) eða print("x =", x)

Það er hægt að stjórna fjölda aukastafa sem print, sbr. kafla 10.6. Svo er hægt að prenta einstök stök í for-lykkju, og þá er hægt að nota f-strengi, sbr. eftirfarandi dæmi

a = np.array([5.55,7.77])
for i,ai in enumerate(a):
   print(f"a[{i}] = {ai}")

sem mundi prenta

a[0] = 5.55
a[1] = 7.77

10.2.4  Föll af vigrum

Föllin sem sagt var frá í köflum 6.2 og 6.3 og hægt er að nota á lista af tölum er líka hægt að nota á vigra. Það eru einkum len, min, max og sum sem eru nytsamleg. Með NumPy má auk þessara falla nota np.argmin(x) og np.argmax(x) sem skila sætisnúmeri minnsta eða stærsta staks x, np.mean(x) sem reiknar meðaltal stakanna og la.norm(x) sem skilar norminu af x, \(\|x\|\), en norm af n-staka vigur x er skilgreint með:

\[\text{norm(x)} = \|x\| = \sqrt{x_0^2 + x_1^2 + \ldots + x_{n-1}^2}\]

10.2.5  Aðferðir í stað falla

Öll föllin sem nefnd eru hér næst á undan nema len og norm eru líka til sem aðferðir. Ef x er vigur má sem sé skrifa x.sum() í stað np.sum(x) (og spara tvo stafi). Þetta er ekki hægt ef x er listi. Hér fylgir tafla yfir öll umrædd föll og aðferðir:

fall

aðferð

skilagildi

len(x)

fjöldi staka í x

sum(x)

x.sum()

summa stakanna í x

min(x)

x.min()

minnsta stak í x

max(x)

x.max()

stærsta stak í x

np.argmin(x)

x.argmin()

sæti minnsta staks í x

np.argmax(x)

x.argmax()

sæti stærsta staks í x

np.mean(x)

x.mean()

meðaltal stakanna í x

la.norm(x)

normið af x, \(\|x\|\)

Takið eftir að ekki þarf að setja np. fremst í fyrstu fjögur föllin, sem eru innbyggð í Python-málið.

Æfing: Nokkur NumPy föll

Búið til vigur \((2,3,5,7,11)\), prentið hann út, og prófið svo öll föllin sem hér voru talin upp

10.2.6  Föll til að búa til vigra

Nokkur þægileg og mikilvæg föll má nota til að búa til vigra. Við höfum þegar séð fallið zeros en auk þess er til ones og tvö föll til að búa til vigra með hlaupandi stökum:

kall

skilar

x = np.zeros(n)

vigur með n núllum

x = np.ones(n)

vigur með n ásum, \((1,1,1...)\)

x = np.linspace(a,b,n)

n-staka vigur jafnt dreifður á bilið \([a,b]\)

x = np.arange(a,b)

\((a, a+1, a+2,...,b-1)\)

x = np.arange(a,b,d)

\((a, a+d, a+2d,...,b-d)\)

Við tökum eftir að í arange er b ekki með í x, alveg eins og með innbyggða fallinu range.

Æfing: Vigratilbúningur

  • Búið til vigurinn \((3, 4, 5, 6, 7)\) bæði með linspace og arange

  • Búið til \((2.0, 2.1, 2.2,\ldots, 3.9, 4.0)\) með linspace og arange

10.3  Útreikningur með vigrum

10.3.1  Reiknað með vigrum í stærðfræði

Í stærðfræði, nánar tiltekið undirgrein hennar sem kallast línuleg algebra, er fjallað um ýmsa útreikninga með vigrum. Það er hægt að leggja saman vigra, draga þá hvern frá öðrum og margfalda þá með tölum. Það er líka hægt að reikna innfeldi (scalar product) tveggja vigra og fá út tölu, og svo má reikna norm vigurs með fyrrnefndu norm-falli

Sýnidæmi: Vigurreikningar

Lát \(x = (2,3,6)\) og \(y = (1,1,2)\). Þá gildir:

\[\begin{split}x + y &= (3,4,8)\\ x - y &= (1,2,4)\\ 2x &= (4,6,12)\\ \|x\| &= \sqrt{2^2 + 3^2 + 6^2} = \sqrt{49} = 7\\ x \cdot y &= 2\cdot 1 + 3\cdot 1 + 6\cdot 2 = 2 + 3 + 12 = 17\end{split}\]

Aðgerðin í neðstu línunni nefnist innfeldi, og það er skilgreint með:

\[x \cdot y = x_0 y_0 + x_1 y_1 + \ldots + x_{n-1} y_{n-1}\]

þar sem bæði \(x\) og \(y\) eru \(n\)-staka vigrar.

10.3.2  Plús, mínus, margföldun og deiling í NumPy

Rifjum upp þegar aðgerðunum plús og sinnum (+ og *) er beitt á lista þá skeyta þær saman listum eða fjölfalda þá. Hér skilur leiðir með listum og vigrum: Þegar þessar aðgerðir eru notaðar á vigra þá leggjast þeir saman eða margfaldast eins og í stærðfræði. Hér er tafla yfir helstu NumPy reikniaðgerðir, þar sem x, y og z eru vigrar en a er tala:

útreikningur

skilar

y = x*a

margfaldar öll stök í x með tölunni a

y = x/a

deilir með tölu a upp í öll stök x

z = x + a

leggur a við öll stök x

z = x - a

dregur a frá öllum stökum x

z = x + y

leggur saman tilsvarandi stök, z[i] = x[i]+y[i] fyrir öll i

z = x - y

dregur frá, z[i] = x[i]-y[i]

z = x*y

margfaldar tilsvarandi stök, z[i] = x[i]*y[i]

z = x@y

innfeldi x og y

Takið eftir margföldunarvirkinn * reiknar ekki innfeldi heldur margfaldast tilsvarandi stök saman. Slík notkun á margföldunarvirkja er ekki hefðbundin í stærðfræði. Í Python er notaður sérstakur virki sem reiknar innfeldi, @. Í stærðfræði er líka óhefðbundið að leggja tölu við vigur eða draga frá.

Æfing: Vigurreikningar

Búið til vigrana \(x = (2,4,6)\) og \(y = (3,4,5)\) og reiknið í framhaldi:

  • \(x + y\)

  • \(3x - 2y\)

  • innfeldið \(x\cdot y\)

  • vigurinn \(z\) sem gefinn er með \(z_i = 2x_iy_i + 3x_i\)

10.3.3  Venjulegum stærðfræðiföllum beitt á vigra

Í NumPy eru sérstakar útgáfur af venjulegu stærðfræðiföllunum, sqrt, exp, log, sin, cos o.s.frv. sem reikna fallsgildi í öllum stökum vigurs sem þeim er beitt á. Þannig gefur np.sqrt(np.array([4,9,25])) vigurinn \((2, 3, 5)\).

Æfing: Stærðfræðiföll af vigrum

Búið til vigurinn \((0, \pi/2, \pi, 3\pi/2, 2\pi)\) með því að byrja á að búa til \((0, 1, 2, 3, 4)\) með np.arange og margfalda hann svo með \(\pi\) og deila með tveimur. Reiknið svo sínus, kósínus og tangens af þessum vigri með np.-hornaföllunum.

10.4  Rökvigrar og rökvísun

10.4.1  Rökvigrar (logical vectors)

Við höfum aðeins fengið nasasjón af listum með rökgildum í kafla 6.3. Slíkir listar (reyndar vigrar) eru talsvert notaðir þegar unnið er með NumPy. Það eru nefnilega ekki bara reiknivirkjarnir +, – * og / og stærðfræðiföll sem virka stakvís á vigra heldur gildir það líka um samanburðarvirkjana, < > <= >= == og !=.

Ef x og y eru jafnlangir vigrar þá er x < y vigur af rökgildum með \(i\)-ta sæti satt (True) ef \(x_i < y_i\). Slíkan vigur má líka reikna með yfirgripi (comprehension) sbr. kafla 6.7 þannig að:

x < y er vigurinn np.array(xi < yi for (xi,yi) in zip(x,y)).

Svo mætti líka nota range og len til að fá sömu niðurstöðu með np.array([x[i] < y[i] for i in range(len(x))]). Í framhaldi má svo nota innbyggðu föllin \(any\) og \(all\) til að athuga hvort eitthvert eða öll stök rökvigranna séu sönn.

Sýnidæmi: Jákvæð stök og samanburður vigra

Lát \(x = (-1,3,-2,5)\) og \(y = (-2,2,3,4)\). Þá má gefa x og y gildi og finna út hvar stök \(x\) og \(y\) eru jákvæð og hvar \(x < y\) með Python forritsbútnum:

x = np.array([-1,3,-2,5])
y = np.array([-2,2,3,4])
xpos = x > 0
ypos = y > 0
xlessy = x < y

og svo má kanna t.d. hvort öll y-in séu jákvæð (sem þau eru ekki) með

if all(ypos):...   # eða:
if all(y > 0):...

10.4.2  Rökvísun (logical indexing)

Hægt er að nota rökvigur sem vísi í vigur. Ef x er einhver vigur af tölum og I er vigur með rökgildum þá er x(I) vigur með stökum í sætum þar sem tilsvarandi sæti í I er satt. Skoðum þetta betur í næsta sýnidæmi.

Sýnidæmi: Rökvísun

Reiknum í framhaldi af síðasta sýnidæmi:

z = x[x < 0]
w = y[ypos]

Þá verður z vigurinn \((-1,-2)\) og w verður \((-1,-2)\). Það mætti líka nota skilyrt yfirgrip til að búa til z:

z = np.array([xi for xi in x if xi < 0]

Það er líka hægt að nota svona rökvísun vinstra megin í gildisgjafarsetningu til að breyta hluta af stökum vigurs eins og gert er í sýnidæminu í næsta kafla.

10.4.3  Stakvísir rökvirkjar

Ef beita á rökaðgerðunum og/eða/ekki stakvíst (element-wise) á öll stök vigra er Python með sérstaka rökvirkja, nefnilega &, | og ~ í stað venjulegu virkjanna and, or og not. Ef x og y eru eins og í sýnidæminu í kafla 10.4.1 þá verður xpos & ypos rökvigur sem segir okkur hvar bæði x og y eru jákvæðir (sem sé (False, True, False, True)), xpos | ypos segir hvar annar hvor er jákvæður ((False, True, True, True) og ~xpos segir hvar x er ekki jákvæður ((True, False, True, False)).

Ath. ef formúla er með bæði samanburðarvirkja og stakvísan rökvirkja þarf að slá svigum utan um samanburðinn, t.d. bothpos = (x > 0) & (y > 0).

Hér er sýnidæmi sem sýnir hvernig má nota rökvísun og stakvísan ekki-virkja til að reikna vigur með gildum falls sem skilgreint er með gaffalformúlu:

Sýnidæmi:

Lát:

\[\begin{split}f(x) = \begin{cases} x^2 & \text{ef } x < 0 \\ x^3 & \text{ef x >= 0} \end{cases}\end{split}\]

og x vera vigur af þétt liggjandi gildum t.d. x = np.linspace(-2,2). Þá er hægt að reikna fallsgildi í öllum x-unum með

../_images/squarecube.jpg
I = x < 0
y = np.zeros(len(x))
y[I] = x[I]**2
y[~I] = x[~I]**3

Svo mætti teikna fallið með plt.plot(x,y), því að Matplotlib getur teiknað hvort sem er lista af tölum eða NumPy vigra.

Æfing: Rökvigrar

Látið \(x\) og \(y\) vera vigrana \((0, 14, 15, 16, 15, 13)\) og \((5,8,7,0,2,4)\). Finnið rökvigra sem svara til \(x_i \neq 0\), \(y_i \neq 0\) og í framhaldi vigur með True þar sem bæði \(x_i \neq 0\) og \(y_i \neq 0\). Búið svo til punktarit (scatter-plot) af jákvæðum pörum \((x_i,y_i)\).

10.5  Fylki í NumPy

10.5.1  Fylki er listi af listum

Í NumPy er fylki búið til með því að nota np.array fallið á lista af listum, sem hver um sig gefur eina línu í fylkinu, og NumPy skrifar líka fylki út með svipuðum hætti.

Sýnidæmi: Fyrsta NumPy fylkið

Hér er forrit sem býr til fylkið \(A\) í kafla 10.1 og skrifar það út:

A = np.array([[1,2,3],
              [6,7,8]])
print(A)

Forritið prentar út:

[[1 2 3]
 [6 7 8]]

Það væri líka hægt að búa A til á einni línu með A = np.array([[1,2,3],[6,7,8]]).

10.5.2  Stök fylkis, línur og dálkar

Hægt er að vísa í \(a_{ij}\) (þ.e.a.s. stakið í línu \(i\) og dálki \(j\)) með A[i,j]. Lína i fæst með A[i] eða A[i,:] og dálkur j fæst með A[:,j]. Eins og fyrir vigra þá byrjar Python að telja í 0 og þessar vísanir má líka nota vinstra megin í gildisgjöf til að breyta stökum, línum eða dálkum, t.d. A[0,0]=37, A[0,:]=[2,2,2] eða A[:,0]=0 (setur öll stök í fremsta dálki = 0)`.

Athugasemd: Gildisgjöf gefur tilvísun

Ef gefin er skipunin

a0 = A[0,:]

þá er búin til tilvísun í fyrstu línuna en ekki ný breyta með gildi hennar (sbr. kafla 5.3.4, 6.2 og 10.2.1). Í raun er verið að gefa línunni nafn, þannig að framhaldsskipunin a0 = [2,2,2] mundi breyta fyrstu línunni í A. Á sama hátt mundi skipunin

B = A

búa til tilvísun í fylkið. Til að taka afrit á nýjan stað í minni tölvnnar má nota aðferðina copy, t.d. a0 = A[0,:].copy() eða B = A.copy().

10.5.3  Núllfylki

Til að fá \(m \times n\) núllfylki má nota Z = np.zeros((m,n)) og Z = np.zeros((m,n),int) gefur heiltölu-núllfylki (það má líka skrifa np.zeros((m,n),dtype=int)).

Æfing: Margföldunartafla

  1. Búið til \(2 \times 2\) núllfylki og prentið það út

  2. Búið til fylkin

    \[\begin{split}C = \begin{pmatrix}1 & 2\\3 & 4\end{pmatrix}\;\text{ og }\; D = \begin{pmatrix}5 & 0\\0 & 5\end{pmatrix}\end{split}\]

    Leggið svo neðri línu \(D\) við efri línu \(C\) og prentið út nýja \(C\)-ið.

  3. Búið til \(10 \times 10\) fylki \(M\) með margföldunartöflu með því að byrja með heiltölu-núllfylki og reikna svo (í tvöfaldri for-lykkju):

    \[m_{ij} = (i+1)(j+1)\quad (i=0,\ldots,9, j=0,\ldots, 9)\]

10.5.4  Bylting

Bylting (transpose) fylkis fæst með því að skipta á línum og dálkum. Stærðfræðilegi rithátturinn fyrir byltingu fylkis \(A\) er \(A^T\), lesið „A bylt“. Í NumPy má rita A.T til að bylta fylki A, til dæmis:

import numpy as np
A = np.array([
    [3,2,1],
    [0,1,0],
    [2,2,2]])
B = A.T
print(B)

# Skrifar:
[[3 0 2]
 [2 1 2]
 [1 0 2]]

Athugasemd: Bylta fylkið er tilvísun

Fylkið B í dæminu hér að framan verður tilvísun í A bylt, sem uppfærist sjálfkrafa ef A breytist. Til að fá nýtt fylki mætti nota B = A.copy().T.

10.5.5  Afpökkun

Í ýmsu samhengi virkar fylki eins og samstæða af línum sínum, bæði þegar því er gefið gildi (eins og við höfum séð) en líka þegar það er notað til að gefa gildi. Ef A er 3 x 3 fylki má þannig skrifa

(u,v,w) = A

til að setja línur A inn í u, v og w. Það má líka sleppa svigunum: u,v,w = A. Hér virkar A eins og þrennd af línum sínum, og þegar þrennd er gefið gildi með A fer hver lína inn í sitt stak – eftir par = (3,4); (u,v) = par verður u=3 og v=4. Þetta er kallað afpökkun (unpacking).

Slík afpökkun er notuð í næsta kafla þegar lesið er inn í NumPy vigra úr línum eða dálkum í skrá, og byltingin úr síðasta kafla er líka notuð.

Æfing: Bylting og afpökkun

Búið til fylkið:

\[\begin{split}M = \begin{pmatrix}1 & 4\\2 & 5\\3 & 6\end{pmatrix}\end{split}\]

Náið svo í dálka þess inn í tvo vigra með því að bylta því fyrst og afpakka svo.

10.5.6  Föll af fylkjum

Numpy er með ýmis föll sem hafa fylki sem viðfang. Fyrst má nefna NumPy-útgáfur af venjulegum stærðfræðiföllum, sem beita má stakvís á fylki jafnt sem vigra, sbr. grein 10.3.3. Í grein 11.5 eru föllin np.det og np.inv, sem reikna ákveður og andhverfur, kynnt til sögunnar, og loks skulu nefnd föllin np.sum sem finnur summu allra staka í fylki og np.shape sem skilar tvennd með fjölda lína og dálka. Hér hafa aðeins verið talin örfá af þeim fjölda fylkjafalla sem NumPy hefur.

10.5.7  Föll til að búa til fylki

Við höfum þegar séð hvernig hægt er að búa til fylki með því að telja upp stökin í því og nota np.array, og líka hvernig búa má til núllfylki með fallinu np.zeros. Hér er tafla yfir þessi og fleiri föll til að búa til fylki, m.a. nokkur sem verður nánar lýst í seinni köflum.

np.array([L1,L2...])

Býr til nýtt fylki úr L1, L2,… sem hver um sig er listi af stökum til að setja í línur fylkisins (allir jafn langir)

np.zeros((m,n))

Skilar \(m \times n\) núllfylki

np.eye(n)

\(n \times n\) einingafylki, sjá kafla 11.2

np.diag(v)

(fyrir \(n\)-vigur v) skilar \(n \times n\) hornalínufylki með stökum v á
hornalínunni (sjá Valin efni í stærðfræði og reiknifræði)

rng.random((m,n))

\(m \times n\) slembifylki (sjá kafla 13.2)

np.c_[x,y]

(fyrir \(n\)-vigra x og y) skilar \(n \times 2\) fylki með dálka x og y

np.vstack((x,y))

(fyrir n-vigra x og y) skilar \(2 \times n\) fylki með línur x og y

Í kaflanum um slembitölur (13.2) er lýst fleiri föllum til að búa til slembifylki. Varðandi neðstu tvö föllin þá eru líka til np.r_ og np.hstack til að skeyta saman lóðrétt og lárétt en notkun þeirra er óþægilegri en þessara tveggja.

10.5.8  Breytt um lögun

Hægt er að breyta um lögun fylkja ef fjöldi staka helst óbreyttur, og sömuleiðis má breyta vigrum í fylki eða öfugt. Til dæmis má breyta \(3 \times 4\) fylki í \(6 \times 2\) fylki. Hér er tafla sem sýnir ýmsa möguleika. Í töflunni tákna A og B fylki og x vigur.

A = np.reshape(x, (m,n))
A = x.reshape(m,n)

breytir x í \(m \times n\) fylki; í efstu röð A koma x[0], x[1]\(\ldots\)
í þá næstu x[n], x[n+1]\(\ldots\) o.s.frv.

B = np.reshape(A, (m,n))
B = A.reshape(m,n)

tekur stök A röð fyrir röð og setur þau inn í nýtt \(m \times n\) fylki B

x = np.reshape(A, n)
x = A.reshape(n)
x = A.reshape(-1)
x = A.flatten()

Allar þessar skipanir taka stök A röð fyrir röð
og setja þau inn í nýjan n-vigur x (n er stakafjöldi A).

A = x[:, None]

Býr til \(n \times 1\) fylki úr \(n\text{-vigrinum}\) x (dálkfylki, column matrix)

A = x[None, :]

Býr til \(1 \times n\) fylki úr \(n\text{-vigrinum}\) x (línufylki, row matrix)

Í öllum tilvikum má ekkert stak ganga af, t.d. þarf fjöldi staka í x að vera \(mn\) efst í töflunni. Nota má -1 fyrir víddir sem hægt er að reikna út, t.d eru x.reshape(4,3) og x.reshape(-1,3) jafngildar ef x hefur 12 stök.

Til að fara í gegn um stökin dálk fyrir dálk má nota byltingu, t.d. x = A.T.flatten() eða B = A.T.reshape(m,n).T.

Sýnidæmi: Bankayfirlit

Fyrir liggja upplýsingar um heildarfærslur tveggja bankareikninga í fjórum dálkum sbr. eftirfarandi mynd. Til að sýna sömu upplýsingar með nýju skipulagi í tveimur dálkum má nota skipun:

nýtt = gamalt.reshape(4,2)

../_images/bankareikningar.jpg

Æfing: Bylting og ný lögun

Hægt er að nota byltingu og reshape saman til að gera frekari breytingar á skipulagi upplýsinga í töflum:

  1. Hvernig töflu mundi gamalt.T.reshape(2,4) skila?

  2. Búið til skipanir til að fá tvo dálka, einn fyrir hvort ár, og fjórar línur, fyrri tvær með innborgunum á A og B og seinni tvær með útborgunum.

10.6  Útskrift vigra og fylkja

10.6.1  Útprentun á skjá

Eins og sýnt hefur verið framar í þessum kafla má nota print-fallið til að prenta bæði vigra og fylki á skjá. Slík útprentun inniheldur hins vegar mismarga aukastafi, eftir því hve marga þarf til að sýna tölurnar með 8 stafa nákvæmni, og auk þess er bætt við hornklofum fremst og aftast. Úr þessum göllum má bæta með tvennu móti, með því að nota np.set_printoptions eða np.savetxt. Næstu greinar útskýra þessa kosti.

10.6.2  Stjórn útprentunar

Með fallinu np.set_printoptions má stjórna sniði útprentunar. Þetta fall hefur ýmsa stika en hér látum við duga að segja frá þremur: precision = n og floatmode = "fixed" gefur alltaf n aukastafi (án floatmode koma ≤ n stafir), og suppress = True þýðir að engar tölur eru prentaðar með tugveldistáknun (t.d. -6.33e08 eða 5.22e-12). Til að prenta öll stök vigurs eða fylkis án tugveldisvísis og með nákvæmlega þremur aukastöfum má nota:

np.set_printoptions(suppress=True, floatmode="fixed", precision=3)

Án suppress=True hefur eitt lítið stak í vigri áhrif á prentun allra stakanna, og án floatmode="fixed" getur vantað 0 aftast í aukastöfunum. Hér er dæmi til skýringar:

Lát x = np.array([0.000002, 1.25, 12.345]). Þá prentar print(x) út:
- með suppress=False:   [2.000e-06 1.250e+00 1.235e+01]
- með suppress=True:    [ 0.000  1.250 12.345]
- án floadmode="fixed": [ 0.     1.25  12.345]

10.6.3  Útprentun með savetxt

Annar möguleiki til að fá snyrtilega útprentun er að nota savetxt-fallið með sniði:

from sys import stdout
np.savetxt(stdout, fylki, fmt="%w.df")

þar sem w er breidd hvers sviðs (hverrar tölu) og d er fjöldi aukastafa (t.d. fmt="%6.2f"). Þá sleppur maður við hornklofana í útprentuninni, en hinsvegar verður að vita fyrirfram hver mesta breidd dálks þarf að vera.

10.6.4  Útskrift í skrá

Til að skrifa fylki í skrá má nota annaðhvort print eða savetxt:

      with open("skrá.txt", 'w') as f:
          print(fylki, file=f)
eða:
      np.savetxt("skrá.txt", fylki, fmt="%5.2f")

Svo væri líka hægt að nota lykkju og f-strengi, sbr. kafla 8.4 og 10.2.3.

10.7  Innlestur úr skrám

10.7.1  Talnalestur

Það er fremur auðvelt að lesa fylki úr textaskrá með fallinu loadtxt. Skráin verður að hafa jafnmörg svið (jafnmargar tölur) í hverri línu og sjálfgefið er að sviðin séu afmörkuð með bilum (sbr. kafla 8.3.4). Fyrir utan textaskrár sem geymdar eru í núverandi möppu getur loadtxt lesið skrár sem eru á netinu án vandkvæða. Hér eru nokkur dæmi sem skýra notkunina:

skrá = 'datafile.txt'                 #
A = np.loadtxt(skrá)                  # les alla skrána inn í eitt fylki A
x,y,z = np.loadtxt(skrá)              # les þriggja línu skrá inn í þrjá vigra
x,y,z = np.loadtxt(skrá).T            # les þriggja dálka skrá inn í þrjá vigra
A = np.loadtxt(skrá, skiprows=1)      # sleppir fyrstu línunni
A = np.loadtxt(skrá, usecols=(0,1))   # les bara fyrstu tvo dálkana
A = np.loadtxt(skrá, delimiter=',')   # les csv-skrá
url = 'https://cs.hi.is/python/'      #
netskrá = url + 'hiti-urkoma.txt'     # skrá á netinu
(ár,hiti,úrk) = np.loadtxt(netskrá).T # les hana inn í þrjá vigra

Þessi dæmi miðast við að skráin innihaldi bara tölur, en þó mega vera textastrengir í fyrstu línunni ef henni er sleppt með skiprows, og eins mega vera dálkar með strengjum ef þeim er sleppt með usecols. Takið sérstaklega eftir hvernig bylting er notuð í fjórðu línunni, og líka í neðstu línunni, til að breyta þremur dálkum í þrjár línur, sem svo er afpakkað inn í þrjá vigra. Í næsta kafla sjáum við hvernig hægt er að lesa inn textadálka.

Æfing: loadtxt

  1. Lesið skrána https://cs.hi.is/python/hiti-urkoma.txt og plottið svo ártal og úrkomu.

  2. Lesið https://cs.hi.is/python/malmar.txt og teiknið punktarit. Það þarf að sleppa bæði fyrstu línunni og fyrsta dálkinum.

10.7.2  Innlestur skráa með textadálkum

Í Numpy er hægt að lesa textafylki með því nota loadtxt(..., dtype=str). Síðan er hægt að nota aðferðirnar astype til að ná í talnadálka og tolist til að breyta textadálkum í lista af strengjum. Þetta er útskýrt með eftirfarandi dæmi.

Sýnidæmi: Lestur textadálka

Skráin https://cs.hi.is/python/malmar.txt er með streng í fyrsta dálki, kommutölu í öðrum dálki og heiltölu í þeim þriðja. Hún er líka með titillínu. Hér er forrit sem les skrána

skrá = "https://cs.hi.is/python/malmar.txt"
(m,e,b) = np.loadtxt(skrá, skiprows=1, dtype=str).T
málmur = m.tolist()
eðlisþ = e.astype(float)
bræðslum = b.astype(int)
print(málmur)
print(eðlisþ)
print(bræðslum)

Forritið prentar út

['Ál', 'Járn', 'Kopar', 'Gull']
[ 2.7   7.87  8.96 19.3 ]
[ 933 1538 1085 1064]

Það mætti líka lesa með A = np.loadtxt(skrá...).T og ná svo t.d. í málmanöfnin með málmur = A[0].tolist().

10.7.3  CSV-skár úr Excel

Til að lesa Excel-skrár eru tvær leiðir bestar:

  1. Að byrja að vista þær sem csv-skrár og lesa inn með NumPy

  2. Að nota Pandas

Lítum hér á fyrri möguleikann. Í skránni https://cs.hi.is/python/allir-malmar.xlsx eru upplýsingar um alla 70 málmana sem hafa sætistölu minni en 94 (plúton), nánar tiltekið (dæmi í svigum):

  • efnatákn (Fe)

  • íslenskt nafn (járn)

  • sætistala (26)

  • eðlisþyngd (7,87)

  • bræðslumark (1535)

  • enskt nafn (iron)

Í Excel er hægt að vista skrána sem csv-skrá með því að velja File – Save As…. Það hefur þegar verið gert og niðurstaðan skrifuð í

Þetta var gert með tölvuna stillta á íslensku og þá koma kommur í stað punkta í eðlisþyngdirnar. CSV-skrána má lesa inn með:

skrá = "https://cs.hi.is/python/allir-malmar.txt"
A = np.loadtxt(skrá, skiprows=1, delimiter=';', dtype='str').T
efnatákn    = A[0].tolist()
nafn        = A[1].tolist()
sætistala   = A[2].astype(int)
A3          = np.char.replace(A[3], ",", ".")
eðlisþyngd  = A3.astype(float)
bræðslumark = A[4].astype(int)
enskt_nafn  = A[5].tolist()

Hér hefur fallið np.char.replace verið notað til að breyta kommutölunum í enska útgáfu, sem er sú eina sem Python skilur.

Æfing: Allir málmar

  1. Smellið bæði á Excel-skrána (ef þið eruð með Excel) og CSV-skrána og skoðið innihaldið

  2. Lesið csv-skrána með því að keyra forritsbútinn hér á undan og búið svo til punktarit af sætistölu og eðlisþyngd.

  3. Búið til súlurit af bræðslumarki

  4. Opnið Excel-skrána og búið til csv-skrá (ef þið hafið Excel)

10.8  Listi yfir skrár á cs.hi.is/python

Eftirfarandi skrár sem koma við sögu ýmist í sýnidæmum, æfingum eða verkefnum í þessum fyrirlestrarnótum eru á https://cs.hi.is/python. Þær má nota til að prófa ýmsar aðferðir til að lesa skrár og vinna með gögn í NumPy (eða Pandas).

Aðferðirnar sem lýst er að framan duga ekki til að lesa skrána simaskra.txt, en hún er með dálka aðskilda með kommu og runu af bilum. Til að lesa hana má nota:

(nafn, sími, heimili) = np.genfromtxt(
    'https://cs.hi.is/python/simaskra.txt',
    skip_header = 1,
    delimiter = ',',
    autostrip = True,
    dtype = str).T.tolist()

Fallið genfromtxt er systurfall loadtxt með aðeins aðra valkosti, m.a. autostrip, auk þess sem skip_header kemur í stað skiprows. Vegna þess að það eru engir talnadálkar er hægt er að hengja .tolist() aftan á innlestrarskipunina til að breyta öllum dálkunum í venjulega lista af strengjum í einu lagi.

Önnur skrá sem er sérstök er flokksnofn.txt sem er með svið aðskilin með tab-táknum. Til að lesa hana með np.loadtxt dugar að setja delimiter = '\t'. Svo eru heldur engir talnadálkar í þeirri skrá svo það er líka hægt að hengja .T.tolist() aftan á loadtxt-kallið (sjá verkefni 16c).