Introduktion till NumPy A

I denna uppgift ska vi titta lite grann på biblioteket NumPy.

NumPy stödjer array-er som liknar listor men som är bättre lämpade för numeriska beräkningar. Numeriska beräkningar på arrayer är både smidigare och snabbare än på listor. En array tar dessutom mycket mindre plats i datorns minne än motsvarande lista.

När vi skall hantera stora datamängder, som t.ex. satellitbilder, så behöver vi NumPys arrayer. Listor skulle bli alldeles för långsamt. Många Python-bibliotek använder NumPy, inklusive matplotlib som du redan känner till.

Listor är däremot bra när antalet element ändrar sig medan programmet kör. T.ex. om vi läser in data och inte vet hur stor listan kommer att bli. Att lägga till ett element i en lista är en snabb operation. När vi använder arrayer måste vi veta från början hur stor arrayen skall vara.

Denna uppgift är tänkt att köras på https://repl.it/languages/python3

1. Din första NumPy array

I sin enklaste form är arrayer väldigt lika vanliga listor. För att använda arrayer måste vi importera NumPy-biblioteket:

import numpy as np

Vi kan nu skapa en array och printa den genom att skriva:

a = np.array([1,2,3])
print(a)

Som du ser så kan vi använda en vanlig lista som argument till array(). Det gör att vi enkelt kan omvandla en färdig lista till en array.

Uppdrag 1 a: Skapa på liknande sätt en array x som innehåller värdena 1, 2, 3, 4, 5. Skriv ut x så du ser att den har rätt innehåll.

Lösning

import numpy as np
x = np.array([1,2,3,4,5])
print(x)

Observera att en array skrivs ut på liknande sätt som en lista, men utan komma-tecken mellan värdena.

Vi kan indexera arrayer på samma sätt som listor. Till exempel betyder a[0] det första elementet i a.

Uppdrag 1 b: Skriv ut det sista elementet i x.

Lösning

print(x[4])

Alternativt kan vi skriva

print(x[-1])

2. Plotta kurva med NumPy

När vi plottar med MatPlotLib går det lika bra att använda NumPy-arrayer som listor.

Uppdrag 2: Skapa en numpy-array y som innehåller värdena 1, 4, 9, 16, 25. Plotta en kurva med matplotlib där du använder x som x-värden och y som y-värden.

Lösning

import matplotlib.pyplot as plt
import numpy as np
plt.ion()
x = np.array([1, 2, 3, 4, 5])
y = np.array([1, 4, 9, 16, 25])
plt.plot(x,y)
plt.savefig("test.png")

3. Fler-dimensionell array

Du kan nu ta bort eller kommentera bort koden du skrivit hittills, eller starta ett nytt fönster för https://repl.it/languages/python3.

En trevlig sak med arrayer är att de kan ha flera dimensioner. En 1-dimensionell array motsvarar en lista av värden. En 2-dimensionell array motsvarar ett rutnät av värden, eller en lista av listor. En 3-dimensionell array motsvarar en lista av listor av listor, osv.

En 2-dimensionell array kallas ofta matris, och passar bra för att representera en 2D-bild av pixlar.

Vi kan t.ex. skapa en matris m genom att skapa en lista av listor, och omvandla till en numpy array så här:

m = np.array(
  [[0, 1, 0, 1, 0],
   [1, 0, 1, 0, 1],
   [0, 1, 0, 1, 0],
   [1, 0, 1, 0, 1],
   [0, 1, 0, 1, 0]])

Uppdrag 3 a: Skapa matrisen m ovan, och gör sedan en bildplot av den på följande sätt: (Glöm inte att du behöver importera numpy och matplotlb som vanligt.)

plt.imshow(m)
plt.savefig("matrix.png")

Att observera:

  • Varje värde i matrisen motsvarar en färg. Matplotlib hittar själv på färger för de olika värdena.
  • imshow står för image show, alltså “visa bild”.
  • Plotten har en given storlek, och eftersom matrisen är så liten (bara 5x5 värden) så blir pixlarna motsvarande stora.

Uppdrag 3 b: Ändra matrisen m så att du byter ut några av ettorna eller nollorna mot andra värden, t.ex. 2 eller 5. Hur blir bilden?

Uppdrag 3 c: Ändra matrisen m så att du bara använder två färger och försöker få en bild som ser ut ungefär som bokstaven “A” eller någon annan bokstav. Lyckas du?

Lösningsförslag

import matplotlib.pyplot as plt
import numpy as np
plt.ion()
m = np.array(
  [[0, 1, 1, 1, 0],
   [1, 0, 0, 0, 1],
   [1, 1, 1, 1, 1],
   [1, 0, 0, 0, 1],
   [1, 0, 0, 0, 1]])
plt.imshow(m)
plt.savefig("matrix.png")

4. Fler sätt att skapa arrayer

Du kan nu rensa bort tidigare kod.

Det finns många sätt att skapa nya arrayer. Om man till exempel vill ha en array med tio nollor kan man skriva:

a = np.zeros(10)

På liknande sätt kan man skapa en array med åtta ettor på följande sätt:

a = np.ones(8)

Uppdrag 4: Skapa en array med 6 nollor och en annan med 6 ettor, och skriv ut dem med print för att se hur de ser ut.

Lösning

a = np.zeros(6)
b = np.ones(6)
print(a)
print(b)

Observera att elementen blir decimaltal. Det går att skapa heltal i stället genom att ange ett extra argument dtype=int till zeros eller ones.

5. Matematiska operationer på arrayer

Du kan nu rensa bort tidigare kod.

Det är enkelt att göra addition och andra matematiska operationer på elementen i en array. Vi ska se på några exempel.

Uppdrag 5 a: Om vi har två arrayer, så kan vi addera dem elementvis. Prova följande kod. Vad skrivs ut?

npa = np.array([1, 2, 3])
npb = np.array([4, 5, 6])
print(npa + npb)
Svar

Vi får en array [5 7 9] som resultat. Varje element i resultatet är summan av motsvarande element i de två adderade arrayerna.

Uppdrag 5 b: Prova andra operationer än +. Vilket resultat får du om du gör *, \ eller - i stället?

Observera att även här görs operationerna elementvis.

Uppdrag 5 c: Man kan också addera en konstant till varje element i en array. Prova följande kod. Vad skrivs ut?

print(npa + 5)
Svar

Vi får en array [6 7 8] som resultat. Varje element i npa har ökats med 5.

Uppdrag 5 d: Prova att multiplicera npamed en konstant i stället. Vilket resultat får du?

Uppdrag 5 e: För att kunna addera två arrayer måste de ha samma “form” (lika många dimensioner och samma storlek i varje dimension). Prova följande kod. Vilket fel får du?

npa = np.array([1, 2, 3, 7])
npb = np.array([4, 5, 6])
print(npa + npb)
Svar

Det blir ett ValueError. I felmeddelandet står det att formen ("shape") på de två operanderna inte passar.

6. Jämförelse mellan listor och arrayer

Du kan nu rensa bort tidigare kod.

Som vi nämnt är listor inte lika smidiga som arrayer för matematiska operationer.

Uppdrag 6 a: Vad skulle hänt om du hade försökt addera två listor i stället för två arrayer? Prova följande kod:

a = [1, 2, 3]
b = [4, 5, 6]
print(a + b)
Svar

Resultatet blir en ny lista med 6 element (a-elementen följt av b-elementen).

Observera att addition för listor inte är matematisk addition, utan i stället konkatenering (“hopkedjning”). Det samma gäller strängar. T.ex. blir "abc" + "def" en ny sträng "abcdef".

Om vi skulle vilja addera elementen i listorna a och b så skulle vi i stället behöva skriva någon typ av loop, t.ex.:

c = [0, 0, 0]
for i in range(len(a)):
  c[i] = a[i] + b[i]
print(c)

Uppdrag 6 b: Skapa en lista [1, 3, 5] och en annan lista [10, 11, 12] och addera dem genom att skriva en loop, som i exemplet ovan. Resultatet borde bli en lista [11, 14, 17].

Lösning

a = [1, 3, 5]
b = [10, 11, 12]
c = [0, 0, 0]
for i in range(len(a)):
  c[i] = a[i] + b[i]
print(c)

Vi kan inte multiplicera två listor - då får vi ett fel. Men vi kan faktiskt multiplicera en lista med en konstant! Vad kan det betyda?

Uppdrag 6 c: Provkör koden nedan. Vad blir resultatet?

print(a*3)

Vi ser att det blir en ny lista som repeteras 3 gånger i följd. a*3 blir alltså samma sak som a+a+a, vilket ju är logiskt!

Men man kan ju undra vilken nytta man kan ha av detta? En sak vi kan göra är att skapa en lista av samma värde på ett enkelt sätt.

Uppdrag 6 d: Provkör koden nedan. Vad blir resultatet?

print([1]*10)

Vi ser att vi får en lista med 10 ettor. Detta hade vi kunnat utnyttja i uppdrag 6b genom att skriva

c = [0]*3

i stället för

c = [0, 0, 0]

7. Effektivitet hos numpy (extrauppgift)

Vi har nämnt tidigare att numpy arrayer är mycket mer effektiva än listor.

Nedan ser du ett exempelprogram där vi har en funktion time_for_adding_lists som tar tiden för att skapa två stora listor och addera dem, och en motsvarande funktion time_for_adding_arrays som skapar två arrayer och adderar dem.

import time
import numpy as np

size = 10000

def time_for_adding_lists():
    t = time.time()
    a = range(size)
    b = range(size)
    c = [0]*size
    for i in range(len(a)):
      c = a[i] + b[i]
    return time.time() - t

def time_for_adding_arrays():
    t = time.time()
    a = np.arange(size)
    b = np.arange(size)
    c = a + b
    return time.time() - t

t_lists = time_for_adding_lists()
t_arrays = time_for_adding_arrays()
print("Addition av listor med", size, "element tog",round(t_lists*1000,2), "ms")
print("Addition av arrayer med", size, "element tog", round(t_arrays*1000,2), "ms")
print("Addition av arrayer gick", round(t_lists/t_arrays, 1), "gånger så snabbt")

Några saker att observera om koden:

  • Tiden mäts genom att läsa av tiden före och efter operationerna, och subtrahera.
  • Uttrycket [0]*size ger en lista av nollor som är size lång.
  • Funktionen range(size) ger en lista [0, 1, 2, … size-1]. Funktionen np.arange(size) fungerar på motsvarande sätt, men för arrayer.

Uppdrag 7: Provkör koden ovan. Hur mycket snabbare är arrayer än listor? Kör gärna några gånger, för resultaten kan variera väldigt mycket. Du bör dock kunna se att arrayer oftast är mycket snabbare än listor.

En anledning till att resultaten varierar mycket är att datorn gör många andra saker samtidigt som den kör ditt program. Din laptop kör t.ex. typiskt mail, webbrowser och kalender samtidigt, och dessutom många program som har med datorns operativ-system att göra. Om du kör på repl.it så kör du egentligen på en server där många olika användare kör sina repl.it-program samtidigt.

8. Quiz

Fråga 1

Vilka anledningar kan finnas att använda NumPy arrayer i stället för listor?

  • a) De tar mindre plats i datorns minne.
  • b) Numeriska beräkningar som addition och multiplikation är både smidigare och snabbare.
  • c) Arrayen kan göras större eller mindre genom att lägga till eller ta bort element.
Svar

a) och b) är korrekta, men c) är fel. Man kan inte ändra storleken på en array. Det kan man däremot med en lista. Man kan t.ex. lägga till ett element i en lista med funktionen append, vilket är en snabb operation. Om man gör append på en array så ändras inte arrayen, utan man får en ny array som är större och där elementen i den gamla har kopierats till den nya. Detta kan ta lång tid om arrayen är stor.

Fråga 2

Vilket av dessa uttryck skapar en NumPy array med elementen 1, 4, 7?

  • np.array([1 4 7])
  • np.array([1, 4, 7])
  • np.array(1, 4, 7)
Svar

np.array([1, 4, 7])
Först skapas listan [1, 4, 7]. Sedan skapas en array från denna lista.

Fråga 3

Vilket anrop plottar en bild?

  • plt.implot(...)
  • plt.imshow(...)
  • plt.figplot(...)
Svar

plt.imshow(...)

Fråga 4

Vad blir resultatet av uttrycket np.ones(3) + np.ones(3)?

  • En array [2 2 2]
  • En array [2. 2. 2.]
  • En array [1 1 1 1 1 1]
Svar

En array [2. 2. 2.]

Fråga 5

Vad blir resultatet av uttrycket [1]*3 + [1]*3

  • En lista [2, 2, 2]
  • En lista [2., 2., 2.]
  • En lista [1, 1, 1, 1, 1, 1]
Svar

En lista [1, 1, 1, 1, 1, 1]