umz21/lab/01_Python.ipynb
2021-03-10 07:58:23 +01:00

40 KiB
Raw Blame History

Uczenie maszynowe 2020/2021 laboratoria

3 marca 2021

1. Python listy składane, indeksowanie, biblioteka _NumPy

Listy składane (_List comprehension)

lista = []
for x in range(1, 11):
    lista.append(x ** 2)
    
print(lista)
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
lista = [x ** 2 for x in range(1, 11)]
print(lista)
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

Przypuśćmy, że mamy dane zdanie i chcemy utworzyć listę, która będzie zawierać długości kolejnych wyrazów tego zdania. Możemy to zrobić w następujący sposób:

lista = []
for i in range(1, 11):
    if i % 2 == 0:
        lista.append(i ** 2)
    
print(lista)
[4, 16, 36, 64, 100]
lista = [i ** 2 for i in range(1, 11) if i % 2 == 0]
print(lista)
[4, 16, 36, 64, 100]
list(range(1, 11))
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
zdanie = 'tracz tarł tarcicę tak takt w takt jak takt w takt tarcicę tartak tarł'
print(zdanie)
tracz tarł tarcicę tak takt w takt jak takt w takt tarcicę tartak tarł
wyrazy = zdanie.split()
print(wyrazy)
['tracz', 'tarł', 'tarcicę', 'tak', 'takt', 'w', 'takt', 'jak', 'takt', 'w', 'takt', 'tarcicę', 'tartak', 'tarł']
dlugosci_wyrazow = []
for wyraz in wyrazy:
    dlugosci_wyrazow.append(len(wyraz))
    
print(dlugosci_wyrazow)
[5, 4, 7, 3, 4, 1, 4, 3, 4, 1, 4, 7, 6, 4]
dlugosci_wyrazow = [len(wyraz) for wyraz in wyrazy]
print(dlugosci_wyrazow)
[5, 4, 7, 3, 4, 1, 4, 3, 4, 1, 4, 7, 6, 4]
zdanie = 'tracz tarł tarcicę tak takt w takt jak takt w takt tarcicę tartak tarł'
wyrazy = zdanie.split()
dlugosci_wyrazow = []
for wyraz in wyrazy:
    dlugosci_wyrazow.append(len(wyraz))
    
print(dlugosci_wyrazow)
[5, 4, 7, 3, 4, 1, 4, 3, 4, 1, 4, 7, 6, 4]

Możemy to też zrobić bardziej „pythonicznie”, przy użyciu list składanych:

zdanie = 'tracz tarł tarcicę tak takt w takt jak takt w takt tarcicę tartak tarł'
dlugosci_wyrazow = [len(wyraz) for wyraz in zdanie.split()]

print(dlugosci_wyrazow)
[5, 4, 7, 3, 4, 1, 4, 3, 4, 1, 4, 7, 6, 4]

Jeżeli chcemy, żeby był sprawdzany dodatkowy warunek, np. chcemy pomijać wyraz „takt”, to wciąż możemy użyć list składanych:

zdanie = 'tracz tarł tarcicę tak takt w takt jak takt w takt tarcicę tartak tarł'
wyrazy = zdanie.split()
dlugosci_wyrazow = [len(wyraz) for wyraz in wyrazy if wyraz != 'takt']
print(dlugosci_wyrazow)
[5, 4, 7, 3, 1, 3, 1, 7, 6, 4]

Indeksowanie

Wszystkie listy i krotki w Pythonie, w tym łańcuchy (które trakowane są jak krotki znaków), są indeksowane od 0:

napis = 'abcde'
print(napis[0])  # 'a'
print(napis[4])  # 'e'
a
e

Indeksy możemy liczyć również „od końca”:

napis = 'abcde'
print(napis[-1])  # 'e' („ostatni”)
print(napis[-2])  # 'd' („drugi od końca”)
print(napis[-5])  # 'a' („piąty od końca”)
e
d
a

Łańcuchy możemy też „kroić na plasterki” (_slicing):

napis = 'abcde'
print(napis[1:4])  # 'bcd' („znaki od 1. włącznie do 4. wyłącznie”)
print(napis[1:2])  # 'b' (to samo co `napis[1]`)
print(napis[-3:-1])  # 'cd' (kroić można też stosując indeksowanie od końca)
print(napis[1:-1])  # 'bcd' (możemy nawet mieszać te dwa sposoby indeksowania)
print(napis[3:])  # 'cde' (jeżeli koniec przedziału nie jest podany, to kroimy do samego końca łańcucha)
print(napis[:3])  # 'ab' (jeżeli początek przedziału nie jest podany, to kroimy od początku łańcucha)
print(napis[:])  # 'abcde' (kopia całego napisu)
bcd
b
cd
bcd
de
abc
abcde
print(napis[:])
abcde

Biblioteka _NumPy

Tablice

Głównym obiektem w NumPy jest jednorodna, wielowymiarowa tablica. Przykładem takiej tablicy jest macierz x.

Macierz $x = \begin{pmatrix} 1 & 2 & 3 \\ 4 & 5 & 6 \\ 7 & 8 & 9 \end{pmatrix}$ można zapisać jako:

import numpy as np

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

print(A)
[[1 2 3]
 [4 5 6]
 [7 8 9]]
import numpy as np

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

y = np.array([[1, 2, 3, -4.5, 5], [10, 9, -8, -13, 0.39]])
print(y)
[[  1.     2.     3.    -4.5    5.  ]
 [ 10.     9.    -8.   -13.     0.39]]
print(x.shape)
(3, 3)
print(y.shape)
(2, 5)
z = y.reshape(5, 2)
print(z)
[[  1.     2.  ]
 [  3.    -4.5 ]
 [  5.    10.  ]
 [  9.    -8.  ]
 [-13.     0.39]]
print(z.shape)
(5, 2)
w = z.reshape(2, 5)
print(w)
[[  1.     2.     3.    -4.5    5.  ]
 [ 10.     9.    -8.   -13.     0.39]]

Najczęsciej używane metody tablic typu array:

print(y.shape)

z = y.reshape((5, 2))
print(z)
print(z.shape)
(2, 5)
[[  1.     2.  ]
 [  3.    -4.5 ]
 [  5.    10.  ]
 [  9.    -8.  ]
 [-13.     0.39]]
(5, 2)
print(z)
[[  1.     2.  ]
 [  3.    -4.5 ]
 [  5.    10.  ]
 [  9.    -8.  ]
 [-13.     0.39]]
print(z.sum(axis=1))
[  3.    -1.5   15.     1.   -12.61]
print(x)
print(x.mean(axis=1))
[[1 2 3]
 [4 5 6]
 [7 8 9]]
[2. 5. 8.]
lista = list(range(1, 11))
A = np.array(lista)
print(A)
[ 1  2  3  4  5  6  7  8  9 10]
lista = list(range(1, 11))
print(lista)
A = np.array(lista)
print(A)
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[ 1  2  3  4  5  6  7  8  9 10]
A = np.arange(1, 11)
print(A)
[ 1  2  3  4  5  6  7  8  9 10]
x.sum(axis=0)
array([12, 15, 18])
x.mean(axis=1)
array([2., 5., 8.])

Do tworzenia sekwencji liczbowych jako obiekty typu array należy wykorzystać funkcję arange.

np.arange(10)
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
np.arange(5, 15)
array([ 5,  6,  7,  8,  9, 10, 11, 12, 13, 14])
np.arange(5, 15, 0.1).reshape(10, 10)
array([[ 5. ,  5.1,  5.2,  5.3,  5.4,  5.5,  5.6,  5.7,  5.8,  5.9],
       [ 6. ,  6.1,  6.2,  6.3,  6.4,  6.5,  6.6,  6.7,  6.8,  6.9],
       [ 7. ,  7.1,  7.2,  7.3,  7.4,  7.5,  7.6,  7.7,  7.8,  7.9],
       [ 8. ,  8.1,  8.2,  8.3,  8.4,  8.5,  8.6,  8.7,  8.8,  8.9],
       [ 9. ,  9.1,  9.2,  9.3,  9.4,  9.5,  9.6,  9.7,  9.8,  9.9],
       [10. , 10.1, 10.2, 10.3, 10.4, 10.5, 10.6, 10.7, 10.8, 10.9],
       [11. , 11.1, 11.2, 11.3, 11.4, 11.5, 11.6, 11.7, 11.8, 11.9],
       [12. , 12.1, 12.2, 12.3, 12.4, 12.5, 12.6, 12.7, 12.8, 12.9],
       [13. , 13.1, 13.2, 13.3, 13.4, 13.5, 13.6, 13.7, 13.8, 13.9],
       [14. , 14.1, 14.2, 14.3, 14.4, 14.5, 14.6, 14.7, 14.8, 14.9]])

Kształt tablicy można zmienić za pomocą metody reshape:

x = np.arange(1, 13)
print(x)
y = x.reshape(3, 4)
print(y)
[ 1  2  3  4  5  6  7  8  9 10 11 12]
[[ 1  2  3  4]
 [ 5  6  7  8]
 [ 9 10 11 12]]

Funkcją podobną do arange jest linspace, która wypełnia wektor określoną liczbą elementów z przedziału o równych automatycznie obliczonych odstępach (w arange należy podać rozmiar kroku):

x = np.linspace(0, 2, 10)
print(x)
[0.         0.22222222 0.44444444 0.66666667 0.88888889 1.11111111
 1.33333333 1.55555556 1.77777778 2.        ]

Dodatkowe informacje o funkcjach NumPy uzyskuje się za pomocą polecenia help(nazwa_funkcji):

help(np.shape)
Help on function shape in module numpy:

shape(a)
    Return the shape of an array.
    
    Parameters
    ----------
    a : array_like
        Input array.
    
    Returns
    -------
    shape : tuple of ints
        The elements of the shape tuple give the lengths of the
        corresponding array dimensions.
    
    See Also
    --------
    alen
    ndarray.shape : Equivalent array method.
    
    Examples
    --------
    >>> np.shape(np.eye(3))
    (3, 3)
    >>> np.shape([[1, 2]])
    (1, 2)
    >>> np.shape([0])
    (1,)
    >>> np.shape(0)
    ()
    
    >>> a = np.array([(1, 2), (3, 4)], dtype=[('x', 'i4'), ('y', 'i4')])
    >>> np.shape(a)
    (2,)
    >>> a.shape
    (2,)

Tablice mogą składać się z danych różnych typów (ale tylko jednego typu danych równocześnie, stąd jednorodność).

x = np.array([1, 2, 3, 0.5])
print(x.dtype)
x = np.array([0.1, 0.2, 0.3])
print(x)
print(x.dtype)
x = np.array([1, 2, 3], dtype='float64')
print(x.dtype)
float64
[0.1 0.2 0.3]
float64
float64

Tworzenie tablic składających się z samych zer lub jedynek umożliwiają funkcje zeros oraz ones:

x = np.zeros([3,4])
print(x)
[[0. 0. 0. 0.]
 [0. 0. 0. 0.]
 [0. 0. 0. 0.]]
y = np.ones([3,4])
print(y)
[[1. 1. 1. 1.]
 [1. 1. 1. 1.]
 [1. 1. 1. 1.]]
print(x + y)
[[1. 1. 1. 1.]
 [1. 1. 1. 1.]
 [1. 1. 1. 1.]]
x = np.arange(12).reshape(4, 3)
print(x)
[[ 0  1  2]
 [ 3  4  5]
 [ 6  7  8]
 [ 9 10 11]]
y = np.arange(5, 17).reshape(4, 3)
print(y)
[[ 5  6  7]
 [ 8  9 10]
 [11 12 13]
 [14 15 16]]
print(x * y)
[[  0   6  14]
 [ 24  36  50]
 [ 66  84 104]
 [126 150 176]]
A = np.arange(8).reshape(2, 4)
print(A)
[[0 1 2 3]
 [4 5 6 7]]
B = np.arange(1, 13).reshape(4, 3)
print(B)
[[ 1  2  3]
 [ 4  5  6]
 [ 7  8  9]
 [10 11 12]]
print(A.shape, B.shape)
(2, 4) (4, 3)
C = np.matmul(A, B)
print(C)
print(C.shape)
[[ 48  54  60]
 [136 158 180]]
(2, 3)
A * B
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-51-a4cedde81ed0> in <module>
----> 1 A * B

ValueError: operands could not be broadcast together with shapes (2,4) (4,3) 
np.matmul(A, C)

Podstawowe operacje arytmetyczne

Operatory arytmetyczne na tablicach w NumPy działają element po elemencie.

A = np.array([[1, 2, 3], [1, 2, 3]])
B = np.array([[4, 5, 6], [7, 8, 9]])
              
print(A)
print(B)
print(A - B)
print(B - A)
print(A * B)
np.matmul(A, B)
print(A.shape)
B = np.array([[1, 2, 3, 4], [9, 8, 7, 6], [2, 4, 6, 7]])
print(B.shape)

print()

print(A)
print(B)
C = np.matmul(A, B)
print(C)

print()

print(A.shape, B.shape)
print(C.shape)
np.dot(A, B)
import numpy as np

a = np.array([3, 4, 5])
b = np.ones(3)
print(a - b)

Za mnożenie macierzy odpowiada funkcja dot (nie operator *):

a = np.array([[1, 2], [3, 4]])
print(a)
b = np.array([[1, 2], [3, 4]])
print(b)
a * b
np.dot(a,b)

Przykłady innych operacji dodawania i mnożenia:

a = np.zeros((2, 2), dtype='float')
a += 5
a
a *= 5
a
a + a

Sklejanie tablic:

a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
c = np.array([7, 8, 9])
np.hstack([a, b, c])
np.vstack([a, b, c])

Typowe funkcje matematyczne:

x = np.arange(1, 5)
np.sqrt(x) * np.pi
2**4
np.power(2, 4)
np.log(np.e)
x = np.arange(5)
x.max() - x.min()

Indeksy i zakresy

Tablice jednowymiarowe zachowują sie podobnie do zwykłych list pythonowych.

a = np.arange(10)
a[2:4]
a[:10:2]
a[::-1]

Tablice wielowymiarowe mają po jednym indeksie na wymiar:

x = np.arange(12).reshape(3, 4)
x
x[2, 3]
x[:, 1]
x[1, :]
x[1:3, :]

Warunki

Warunki pozwalają na selekcję elementów tablicy.

a = np.array([1, 1, 1, 2, 2, 2, 3, 3, 3])
a[a > 1]
a[a == 3]
np.where(a < 3)
np.where(a < 3)[0]
np.where(a > 9)

Pętle i wypisywanie

for row in x:
    print row
for element in x.flat:
    print element, 

Liczby losowe

np.random.randint(0, 10, 5)
np.random.normal(0, 1, 5) 
np.random.uniform(0, 2, 5)

Macierze

NumPy jest pakietem wykorzystywanym do obliczeń w dziedzinie algebry liniowej, co jeszcze szczególnie przydatne w uczeniu maszynowym.

Wektor o wymiarach $1 \times N$ $$ x = \begin{pmatrix} x_{1} \\ x_{2} \\ \vdots \\ x_{N} \end{pmatrix} $$

i jego transpozycję $x^\top = (x_{1}, x_{2},\ldots,x_{N})$ można wyrazić w Pythonie w następujący sposób:

import numpy as np

x = np.array([[1, 2, 3]]).T
x.shape
xt = x.T
xt.shape

Macierz kolumnowa w NumPy. $$X = \begin{pmatrix} 3 \\ 4 \\ 5 \\ 6
\end{pmatrix}$$

x = np.array([[3,4,5,6]]).T
x

Macierz wierszowa w NumPy. $$ X = \begin{pmatrix} 3 & 4 & 5 & 6 \end{pmatrix}$$

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

Oprócz obiektów typu array istnieje wyspecjalizowany obiekt matrix, dla którego operacje * (mnożenie) oraz **-1 (odwracanie) są określone w sposób właściwy dla macierzy (w przeciwieństwie do operacji elementowych dla obiektów array).

x = np.array([1,2,3,4,5,6,7,8,9]).reshape(3,3)
x
X = np.matrix(x)
X

Wyznacznik macierzy

a = np.array([[3,-9],[2,5]])
np.linalg.det(a)

Macierz odwrotna

A = np.array([[-4,-2],[5,5]])
A
invA = np.linalg.inv(A)
invA
np.round(np.dot(A, invA))

(ponieważ $AA^{-1} = A^{-1}A = I$).

Wartości i wektory własne

a = np.diag((1, 2, 3))
a
w, v = np.linalg.eig(a)
print(w)  # wartości własne
print(v)  # wektory własne

Zadania

Zadanie 1.1 (1 pkt)

Dla danej listy input_list zawierającej liczby utwórz nową listę output_list, która będzie zawierała kwadraty liczb dodatnich z input_list. Użyj _list comprehension!

# Przykładowe dane

input_list = [34.6, -203.4, 44.9, 68.3, -12.2, 44.6, 12.7]

Zadanie 1.2 (1 pkt)

Za pomocą jednowierszowego polecenia utwórz następującą macierz jako obiekt typu array: $$A = \begin{pmatrix} 1 & 2 & \cdots & 10 \\ 11 & 12 & \cdots & 20 \\ \vdots & \ddots & \ddots & \vdots \\ 41 & 42 & \cdots & 50 \end{pmatrix}$$

Zadanie 1.3 (1 pkt)

Dla macierzy $A$ z zadania 1.2:

  • określ liczbę elementów, kolumn i wierszy,

  • stwórz wektory średnich po wierszach oraz po kolumnach,

  • wypisz jej trzecią kolumnę,

  • wypisz jej czwarty wiersz.

    Użyj odpowiednich metod obiektu array.

Zadanie 1.4 (1 pkt)

Utwórz macierze $$ A = \begin{pmatrix} 0 & 4 & -2 \\ -4 & -3 & 0 \end{pmatrix} $$ $$ B = \begin{pmatrix} 0 & 1 \\ 1 & -1 \\ 2 & 3 \end{pmatrix} $$ oraz wektor $$ x = \begin{pmatrix} 2 \\ 1 \\ 0 \end{pmatrix} $$

Oblicz:

  • iloczyn macierzy $A$ z wektorem $x$
  • iloczyn macierzy $A \cdot B$
  • wyznacznik $\det(A \cdot B)$
  • wynik działania $(A \cdot B)^\top - B^\top \cdot A^\top$

Zadanie 1.5 (1 pkt)

Czym różni się operacja A**-1 dla obiektów typu array i matrix? Pokaż na przykładzie.

Zadanie 1.6 (1 pkt)

Dla macierzy $X = \left[ \begin{array}{rrr} 1 & 2 & 3\\ 1 & 3 & 6 \\ \end{array} \right]$ oraz wektora $y = \left[ \begin{array}{r} 5 \\ 6 \\ \end{array} \right]$ oblicz wynikowy wektor: $$ \theta = (X^\top , X)^{-1} , X^\top , y , . $$ Wykonaj te same obliczenia raz na obiektach typu array, a raz na obiektach typu matrix. W przypadku obiektów typu matrix zastosuj możliwie krótki zapis.