Biblioteka numpy¶

In [1]:
import numpy as np
# scipy - jeszcze dużo dodatków
In [2]:
np.__version__
Out[2]:
'1.26.2'
In [3]:
tablica = np.array([10, 15, 20])
In [4]:
tablica
Out[4]:
array([10, 15, 20])
In [5]:
np.arange(10, 20)
Out[5]:
array([10, 11, 12, 13, 14, 15, 16, 17, 18, 19])
In [6]:
np.arange(10, 20, 1.5)
Out[6]:
array([10. , 11.5, 13. , 14.5, 16. , 17.5, 19. ])
In [7]:
np.arange(1, 20, 2).sum()
Out[7]:
100
In [8]:
# Aby wyliczyć sobie potęgi dwójki:
[2**x for x in range(65)]
Out[8]:
[1,
 2,
 4,
 8,
 16,
 32,
 64,
 128,
 256,
 512,
 1024,
 2048,
 4096,
 8192,
 16384,
 32768,
 65536,
 131072,
 262144,
 524288,
 1048576,
 2097152,
 4194304,
 8388608,
 16777216,
 33554432,
 67108864,
 134217728,
 268435456,
 536870912,
 1073741824,
 2147483648,
 4294967296,
 8589934592,
 17179869184,
 34359738368,
 68719476736,
 137438953472,
 274877906944,
 549755813888,
 1099511627776,
 2199023255552,
 4398046511104,
 8796093022208,
 17592186044416,
 35184372088832,
 70368744177664,
 140737488355328,
 281474976710656,
 562949953421312,
 1125899906842624,
 2251799813685248,
 4503599627370496,
 9007199254740992,
 18014398509481984,
 36028797018963968,
 72057594037927936,
 144115188075855872,
 288230376151711744,
 576460752303423488,
 1152921504606846976,
 2305843009213693952,
 4611686018427387904,
 9223372036854775808,
 18446744073709551616]

Typy numpy¶

numpy definiuje typy liczbowe, które odpowiadają tym używanym przez procesor. Odpowiadają one typom short / int / long / float / double z innych języków programowania (C, Java, C#).

Liczba w nazwie typu to jej rozmiar w bitach (bajt = 8 bitów). Przykładowo tablica miliarda liczb typu int32 będzie zajmować 4 GB pamięci.

  • int8 - jak byte w Javie lub signed char w C, zakres od -128 do +127
  • int16 - jak short, zakres od -32768 do +32767
  • int32 - jak int, zakres od -2_147_483_648 do +2_147_483_647
  • int64 - jak long, zakres do 9223372036854775807 (19 cyfr)
  • float16 - liczby zmiennoprzecinkowe o małej precyzji, tylko ok 4-5 cyfr dziesiętnych
  • float32 - jak float w C i Javie; średnia precyzja, ok 7-8 cyfr
  • float64 - jak double w C i Javie, i jak zwykły float w Pythonie; duża precyzja, ok 14-15 cyfr
In [9]:
x = np.int32(5)
y = np.int32(1000_000_000)
x, y
Out[9]:
(5, 1000000000)

Używając liczb takiego typu ryzykujemy, że wynik nie zmieści się w zakresie. Dla intów 32-bitowych zakresem jest od ok -2mld do ok +2mld i wynik rzędu 5mld nie mieści się w zakresie. Dochodzi do błędu integer overflow.

(dla chętnych - poszukać na YT 'Ariane 5' - efekt integer overflow w praktyce)

In [10]:
x * y
/tmp/ipykernel_22066/4205270810.py:1: RuntimeWarning: overflow encountered in scalar multiply
  x * y
Out[10]:
705032704
In [11]:
x = np.int64(5)
y = np.int64(1000_000_000)
x, y
Out[11]:
(5, 1000000000)
In [12]:
x * y
Out[12]:
5000000000

Tablice w numpy¶

Podstawy¶

In [13]:
a = np.array([100, 110, 120, 130, 140])
b = np.array([1, 2, 3, 4, 5])
In [14]:
a
Out[14]:
array([100, 110, 120, 130, 140])
In [15]:
b
Out[15]:
array([1, 2, 3, 4, 5])
In [16]:
type(a)
Out[16]:
numpy.ndarray
In [17]:
# Można wybierać pojdeyncze elementy
a[2]
Out[17]:
120
In [18]:
a[2] * 1000
Out[18]:
120000
In [19]:
# Można iterować za pomocą pętli for
for x in a:
    print(x)
100
110
120
130
140
In [20]:
# Można operaować na całych tablicach
# Zasadniczo działają wtedy zasady jak dla wektotów / macierzy w matematyce
a + b
Out[20]:
array([101, 112, 123, 134, 145])
In [21]:
a * b
Out[21]:
array([100, 220, 360, 520, 700])
In [22]:
a / b
Out[22]:
array([100. ,  55. ,  40. ,  32.5,  28. ])
In [23]:
a // b
Out[23]:
array([100,  55,  40,  32,  28])
In [24]:
b * 10
Out[24]:
array([10, 20, 30, 40, 50])

Informacje o tablicach¶

In [25]:
t = np.array([
    [10, 11, 12, 13, 14],
    [50, 51, 52, 53, 54],
    [70, 71, 72, 73, 74],
])
In [26]:
t
Out[26]:
array([[10, 11, 12, 13, 14],
       [50, 51, 52, 53, 54],
       [70, 71, 72, 73, 74]])
In [27]:
print(t)
[[10 11 12 13 14]
 [50 51 52 53 54]
 [70 71 72 73 74]]
In [28]:
# size - ilość wszystkich elementów
t.size
Out[28]:
15
In [29]:
# len odczyta (w tym przypadku) ilość wierszy
# generalnie zwraca rozmiar mierzony w najwyższym wymiarze (tym, który w indeksach podaje się najbardziej z lewej)
len(t)
Out[29]:
3
In [30]:
# liczba wymiarów - w tym przypadku 2
t.ndim
Out[30]:
2
In [31]:
a.ndim
Out[31]:
1
In [32]:
# shape - kształt, czyli informacja o rozmiarze w każdym wymiarze
# czyli "macierz 3 × 5"
t.shape
Out[32]:
(3, 5)
In [33]:
type(t)
Out[33]:
numpy.ndarray
In [34]:
# typ elementu tablicy (data type)
t.dtype
Out[34]:
dtype('int64')
In [35]:
# elementami tablic nie muszą być liczby, chociaż tablice napisów i innych obiektów są bardzo rzadko używane
imiona = np.array(['Ala', 'Ola', 'Anna', 'Ela', 'Ula'])
imiona
Out[35]:
array(['Ala', 'Ola', 'Anna', 'Ela', 'Ula'], dtype='<U4')
In [36]:
imiona.dtype
Out[36]:
dtype('<U4')
In [37]:
# W takiej sytuacji zamieni liczby na stringi
np.array([10, 3.14, 'Ala'])
Out[37]:
array(['10', '3.14', 'Ala'], dtype='<U32')

Sposoby tworzenia tablic¶

In [38]:
# array - tworzenie na podstawie listy lub innego źródła
a = np.array([100, 110, 120, 130, 140])
b = np.array([1, 2, 3, 4, 5])
t = np.array([
    [10, 11, 12, 13, 14],
    [50, 51, 52, 53, 54],
    [70, 71, 72, 73, 74],
])
In [39]:
# Jeśli chchemy wymusić użycie określonego typu dla elementów, robimy to parametrem dtype
c = np.array([100, 110, 120, 130, 140], dtype=np.int16)
c
Out[39]:
array([100, 110, 120, 130, 140], dtype=int16)
In [40]:
c.dtype
Out[40]:
dtype('int16')
In [41]:
# Typ można wskazywać bezpośrednio z biblioteki, ale można też podać tekstowo
d = np.array([1, 2, 3, 4, 5], dtype='float16')
d
Out[41]:
array([1., 2., 3., 4., 5.], dtype=float16)

Tworzenie tablic z automatycznie generowaną zawartością¶

In [42]:
# full - tablica wypełniona jednakowymi wartościami
# full(kształt, wartość)
np.full(10, 44)
Out[42]:
array([44, 44, 44, 44, 44, 44, 44, 44, 44, 44])
In [43]:
# jeśli ma być wielowymiarowa, to jako kształt należy przekazać tuplę (lub inną sekwencję, np. listę) z wymiarami
np.full((3, 2), 2.5)
Out[43]:
array([[2.5, 2.5],
       [2.5, 2.5],
       [2.5, 2.5]])
In [44]:
np.full([3, 4, 5], 7, dtype='float32')
Out[44]:
array([[[7., 7., 7., 7., 7.],
        [7., 7., 7., 7., 7.],
        [7., 7., 7., 7., 7.],
        [7., 7., 7., 7., 7.]],

       [[7., 7., 7., 7., 7.],
        [7., 7., 7., 7., 7.],
        [7., 7., 7., 7., 7.],
        [7., 7., 7., 7., 7.]],

       [[7., 7., 7., 7., 7.],
        [7., 7., 7., 7., 7.],
        [7., 7., 7., 7., 7.],
        [7., 7., 7., 7., 7.]]], dtype=float32)
In [45]:
# Dedykowane wersje dla zer i jedynek
np.zeros(5)
Out[45]:
array([0., 0., 0., 0., 0.])
In [46]:
np.zeros((5, 2), dtype='int32')
Out[46]:
array([[0, 0],
       [0, 0],
       [0, 0],
       [0, 0],
       [0, 0]], dtype=int32)
In [47]:
np.ones(100, dtype='int16')
Out[47]:
array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], dtype=int16)

arange jest czymś analogicznym do range z Pythona.

Te same zasady (start, stop, step), przy czym w arange można uzywać liczb niecałkowitych.

In [48]:
np.arange(10)
Out[48]:
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
In [49]:
np.arange(5, 20)
Out[49]:
array([ 5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19])
In [50]:
np.arange(5, 25, 3)
Out[50]:
array([ 5,  8, 11, 14, 17, 20, 23])
In [51]:
np.arange(5, 10, 0.5)
Out[51]:
array([5. , 5.5, 6. , 6.5, 7. , 7.5, 8. , 8.5, 9. , 9.5])
In [52]:
np.arange(2.0, 1.0, -0.1)
Out[52]:
array([2. , 1.9, 1.8, 1.7, 1.6, 1.5, 1.4, 1.3, 1.2, 1.1])

Podział przedziału na równe części - linspace Wygeneruj tablicę N liczb między X i Y, rozmieszczając je równomiernie.

Ostatni argument oznacza liczbę generownaych wartości (punktów), a nie liczbę przedziałów.

In [53]:
np.linspace(20, 40, 5)
Out[53]:
array([20., 25., 30., 35., 40.])
In [54]:
np.linspace(20, 40, 5, dtype='int32')
Out[54]:
array([20, 25, 30, 35, 40], dtype=int32)
In [55]:
# Gdy podamy endpoint=False, to prawy zakres nie jest brany do wynikowej tablicy
# Tutaj 40 jest jakby szótym punktem, który nie jest już uwzględniany w wynikach
np.linspace(20, 40, 5, endpoint=False)
Out[55]:
array([20., 24., 28., 32., 36.])
In [56]:
# Praktyczne zastosowanie - podziel równo przedziały, gdy znasz początek i koniec, a nie znasz (i nie chcesz wyliczać) wielkości kroku.
n = 127
maks = 1200
arr = np.linspace(0, maks, n)
arr
Out[56]:
array([   0.        ,    9.52380952,   19.04761905,   28.57142857,
         38.0952381 ,   47.61904762,   57.14285714,   66.66666667,
         76.19047619,   85.71428571,   95.23809524,  104.76190476,
        114.28571429,  123.80952381,  133.33333333,  142.85714286,
        152.38095238,  161.9047619 ,  171.42857143,  180.95238095,
        190.47619048,  200.        ,  209.52380952,  219.04761905,
        228.57142857,  238.0952381 ,  247.61904762,  257.14285714,
        266.66666667,  276.19047619,  285.71428571,  295.23809524,
        304.76190476,  314.28571429,  323.80952381,  333.33333333,
        342.85714286,  352.38095238,  361.9047619 ,  371.42857143,
        380.95238095,  390.47619048,  400.        ,  409.52380952,
        419.04761905,  428.57142857,  438.0952381 ,  447.61904762,
        457.14285714,  466.66666667,  476.19047619,  485.71428571,
        495.23809524,  504.76190476,  514.28571429,  523.80952381,
        533.33333333,  542.85714286,  552.38095238,  561.9047619 ,
        571.42857143,  580.95238095,  590.47619048,  600.        ,
        609.52380952,  619.04761905,  628.57142857,  638.0952381 ,
        647.61904762,  657.14285714,  666.66666667,  676.19047619,
        685.71428571,  695.23809524,  704.76190476,  714.28571429,
        723.80952381,  733.33333333,  742.85714286,  752.38095238,
        761.9047619 ,  771.42857143,  780.95238095,  790.47619048,
        800.        ,  809.52380952,  819.04761905,  828.57142857,
        838.0952381 ,  847.61904762,  857.14285714,  866.66666667,
        876.19047619,  885.71428571,  895.23809524,  904.76190476,
        914.28571429,  923.80952381,  933.33333333,  942.85714286,
        952.38095238,  961.9047619 ,  971.42857143,  980.95238095,
        990.47619048, 1000.        , 1009.52380952, 1019.04761905,
       1028.57142857, 1038.0952381 , 1047.61904762, 1057.14285714,
       1066.66666667, 1076.19047619, 1085.71428571, 1095.23809524,
       1104.76190476, 1114.28571429, 1123.80952381, 1133.33333333,
       1142.85714286, 1152.38095238, 1161.9047619 , 1171.42857143,
       1180.95238095, 1190.47619048, 1200.        ])
In [57]:
# Teraz różnice pomiędzy kolejnymi punktami powinny być jednakowe.
arr[3] - arr[2]
Out[57]:
9.523809523809522
In [58]:
arr[10] - arr[9]
Out[58]:
9.523809523809533
In [59]:
# logspace - liczby rozłożone równomiernie w przestrzeni logarytmicznej.
# Czyli proporcje (ilorazy) między kolejnymi elementami są jednakowe.
# Pierwszy i drugi argument to są wykładniki, domyślną podstawą jest 10,
# czyli tu generuję liczby od 10**0 do 10**7
np.logspace(0, 7, 8, dtype='int64')
Out[59]:
array([       1,       10,      100,     1000,    10000,   100000,
        1000000, 10000000])
In [60]:
np.logspace(4, 20, 17, base=2, dtype='int64')
Out[60]:
array([     16,      32,      64,     128,     256,     512,    1024,
          2048,    4096,    8192,   16384,   32768,   65536,  131072,
        262144,  524288, 1048576])
In [61]:
# Gdybyśmy wiedzieli, że początkiem przedziału jest 100, a końcem 1500 i chcieli podzielić to 4 równe przedziały (czyli 5 punktów) w skali logarytmicznej,
# to np tak:
x = 100.0
y = 1500.0
dane = np.logspace(np.log10(x), np.log10(y), 5)
dane
Out[61]:
array([ 100.        ,  196.79896713,  387.29833462,  762.19912223,
       1500.        ])
In [62]:
# Teraz proporcje między kolejnymi elementami są jednakowe
dane[1] / dane[0]
Out[62]:
1.9679896712654306
In [63]:
dane[2] / dane[1]
Out[63]:
1.9679896712654306
In [64]:
a
Out[64]:
array([100, 110, 120, 130, 140])
In [65]:
np.diag(a)
Out[65]:
array([[100,   0,   0,   0,   0],
       [  0, 110,   0,   0,   0],
       [  0,   0, 120,   0,   0],
       [  0,   0,   0, 130,   0],
       [  0,   0,   0,   0, 140]])
In [66]:
macierz_identycznosciowa = np.diag(np.ones(10))
macierz_identycznosciowa
Out[66]:
array([[1., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 1., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 1., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 1., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 1., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 1., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 1., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 1., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 1., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 1.]])
In [67]:
# Ale na to jest prostszy sposób
np.eye(7)
Out[67]:
array([[1., 0., 0., 0., 0., 0., 0.],
       [0., 1., 0., 0., 0., 0., 0.],
       [0., 0., 1., 0., 0., 0., 0.],
       [0., 0., 0., 1., 0., 0., 0.],
       [0., 0., 0., 0., 1., 0., 0.],
       [0., 0., 0., 0., 0., 1., 0.],
       [0., 0., 0., 0., 0., 0., 1.]])
In [68]:
# Z tym, że tutaj można podać nietypowe wymiary - i gdy to nie będzie kwadrat, to już nie będzie to macierz ident.
np.eye(10, 5)
Out[68]:
array([[1., 0., 0., 0., 0.],
       [0., 1., 0., 0., 0.],
       [0., 0., 1., 0., 0.],
       [0., 0., 0., 1., 0.],
       [0., 0., 0., 0., 1.],
       [0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0.]])
In [69]:
np.eye(10, dtype='int32')
Out[69]:
array([[1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
       [0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 1, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 1, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 1, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 1, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0, 1, 0, 0],
       [0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
       [0, 0, 0, 0, 0, 0, 0, 0, 0, 1]], dtype=int32)
In [70]:
# Wreszcie, konkretnie dla macierzy identycznościowej jej dedykowana funkcja.
np.identity(10)
Out[70]:
array([[1., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 1., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 1., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 1., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 1., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 1., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 1., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 1., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 1., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 1.]])
In [71]:
np.identity(5, dtype='int16')
Out[71]:
array([[1, 0, 0, 0, 0],
       [0, 1, 0, 0, 0],
       [0, 0, 1, 0, 0],
       [0, 0, 0, 1, 0],
       [0, 0, 0, 0, 1]], dtype=int16)

reshape¶

Tworzy nową tablicę z takimi samymi elementami, ale rozmieszczonymi w innym kształcie.

Wymary muszą być tak dobrane, aby liczba elementów się zgadzała; inaczej będzie błąd.

In [72]:
c = np.arange(24)
c
Out[72]:
array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16,
       17, 18, 19, 20, 21, 22, 23])
In [73]:
d = c.reshape(4, 6)
d
Out[73]:
array([[ 0,  1,  2,  3,  4,  5],
       [ 6,  7,  8,  9, 10, 11],
       [12, 13, 14, 15, 16, 17],
       [18, 19, 20, 21, 22, 23]])
In [74]:
c.shape
Out[74]:
(24,)
In [75]:
d.shape
Out[75]:
(4, 6)
In [76]:
c.reshape(4, 3, 2)
Out[76]:
array([[[ 0,  1],
        [ 2,  3],
        [ 4,  5]],

       [[ 6,  7],
        [ 8,  9],
        [10, 11]],

       [[12, 13],
        [14, 15],
        [16, 17]],

       [[18, 19],
        [20, 21],
        [22, 23]]])
In [77]:
#ERR c.reshape(5, 5)
In [78]:
# Jako jeden z wymiarów można podać -1 i wtedy zostanie on dobrany automatycznie.
c.reshape(3, -1)
Out[78]:
array([[ 0,  1,  2,  3,  4,  5,  6,  7],
       [ 8,  9, 10, 11, 12, 13, 14, 15],
       [16, 17, 18, 19, 20, 21, 22, 23]])
In [79]:
#ERR c.reshape(5, -1)
In [80]:
c.reshape(-1, 4)
Out[80]:
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11],
       [12, 13, 14, 15],
       [16, 17, 18, 19],
       [20, 21, 22, 23]])
In [81]:
c.reshape(4, -1, 3)
Out[81]:
array([[[ 0,  1,  2],
        [ 3,  4,  5]],

       [[ 6,  7,  8],
        [ 9, 10, 11]],

       [[12, 13, 14],
        [15, 16, 17]],

       [[18, 19, 20],
        [21, 22, 23]]])
In [82]:
d
Out[82]:
array([[ 0,  1,  2,  3,  4,  5],
       [ 6,  7,  8,  9, 10, 11],
       [12, 13, 14, 15, 16, 17],
       [18, 19, 20, 21, 22, 23]])
In [83]:
d.reshape(3, -1)
Out[83]:
array([[ 0,  1,  2,  3,  4,  5,  6,  7],
       [ 8,  9, 10, 11, 12, 13, 14, 15],
       [16, 17, 18, 19, 20, 21, 22, 23]])

Funkcje losujące w numpy¶

Kolejnym sposobem generowania tablic, jest tworzenie tablic z (pesudo)losowymi wartościami.

O ile w innych sytuacjach w programowaniu często losuje się pojedyncze liczby, to w numpy zazwyczaj pobieramy od razu całe tablice. W tej sytuacji szczególnie dobrze widać jaki jest rozkład pobranych wartości.

In [84]:
np.random.rand(100)
Out[84]:
array([3.31453055e-01, 8.24259722e-01, 2.84469468e-01, 9.86221550e-01,
       5.47865941e-01, 4.13916466e-01, 7.04353055e-01, 8.34806951e-01,
       4.86295852e-01, 2.23872612e-01, 3.90999229e-01, 3.90934657e-01,
       3.65957436e-01, 9.08766202e-01, 7.63272850e-01, 3.25544227e-01,
       8.31738885e-01, 1.47934369e-01, 2.50951681e-01, 7.71858950e-01,
       1.24314380e-01, 4.17359850e-01, 3.19560772e-01, 9.87871625e-01,
       8.26386055e-01, 1.10428746e-01, 3.67436231e-01, 6.37947354e-01,
       5.53297819e-01, 4.95815980e-01, 2.22976582e-01, 9.24756933e-01,
       7.30663891e-01, 3.87698708e-01, 8.49520272e-01, 5.53153146e-01,
       3.36838851e-01, 8.17695998e-01, 6.00914159e-01, 6.25865187e-01,
       3.60821285e-01, 3.06951483e-01, 7.05864240e-01, 7.29799061e-02,
       9.87776240e-02, 6.34720614e-04, 1.02279020e-01, 6.61337514e-04,
       7.27813279e-01, 8.42359704e-01, 9.86165351e-01, 3.23198045e-01,
       4.55718561e-01, 7.39777126e-01, 7.17044654e-01, 9.73983557e-01,
       2.00385705e-01, 9.27574301e-01, 3.68896021e-01, 8.45257042e-01,
       8.08767077e-01, 1.38596360e-01, 3.21125041e-01, 2.88418250e-01,
       4.60653716e-01, 8.09173841e-01, 9.36020750e-01, 3.39259026e-01,
       6.44673787e-03, 8.30777393e-02, 3.56994285e-01, 4.22344623e-01,
       2.70245866e-01, 9.68793420e-01, 4.13488822e-01, 8.12963482e-01,
       8.87157082e-01, 9.18304746e-01, 1.42467560e-02, 6.00450932e-01,
       4.67951510e-01, 8.23261599e-01, 3.09395538e-01, 6.61861442e-01,
       3.35802028e-02, 5.41616701e-01, 4.53689404e-01, 8.94697546e-01,
       3.15462382e-01, 1.62058578e-01, 9.47238434e-01, 5.93645270e-01,
       3.24289977e-01, 5.20944279e-01, 7.86686487e-01, 5.12723192e-01,
       2.37338358e-02, 7.17543561e-01, 4.85529644e-01, 1.21258614e-01])

Generator liczb pseudolosowych deterministycznie zwraca kolejne liczby z pewnego ciągu, który „na oko” ma dobry rozkład. Aby w różnych uruchomieniach pojawiały się różne wartości, jest o inicjalizowany bieżącym odczytem zegara.

Jeśli chcemy, aby notatnik uruchamiany wielokrotnie lub na różnych komputerach przez różne osoby zawierał te same liczby pseudolosowe, możemy zainicjalizować generator wybraną konkretną liczbą. Robi się to za pomocą funkcji seed.

In [85]:
np.random.seed(100)
In [86]:
# Liczby float z zakresu od 0 do 1, rozkład jednostajny.
np.random.rand(10)
Out[86]:
array([0.54340494, 0.27836939, 0.42451759, 0.84477613, 0.00471886,
       0.12156912, 0.67074908, 0.82585276, 0.13670659, 0.57509333])
In [87]:
# Generowanie liczb całkowitych, tutaj liczby z zakresu [100, 200)
np.random.randint(100, 200, 10)
Out[87]:
array([160, 158, 116, 109, 193, 186, 102, 127, 104, 131])
In [88]:
jednostajny = np.random.rand(1000)
In [89]:
import matplotlib.pyplot as plt
%matplotlib inline
In [90]:
plt.hist(jednostajny, bins=10)
Out[90]:
(array([103.,  99., 103.,  97.,  99., 116.,  91.,  98.,  91., 103.]),
 array([4.86759475e-04, 1.00385999e-01, 2.00285238e-01, 3.00184477e-01,
        4.00083716e-01, 4.99982955e-01, 5.99882194e-01, 6.99781434e-01,
        7.99680673e-01, 8.99579912e-01, 9.99479151e-01]),
 <BarContainer object of 10 artists>)
No description has been provided for this image
In [91]:
# Rozkład normalny („Gaussowski”)
normalny = np.random.randn(1000)
In [92]:
plt.hist(normalny, bins=10)
Out[92]:
(array([  1.,   3.,   6.,  61., 184., 257., 254., 147.,  65.,  22.]),
 array([-4.24242115, -3.54206947, -2.84171779, -2.14136612, -1.44101444,
        -0.74066276, -0.04031108,  0.6600406 ,  1.36039228,  2.06074396,
         2.76109563]),
 <BarContainer object of 10 artists>)
No description has been provided for this image
In [93]:
plt.hist(np.random.randn(10000)*25 + 50, bins=100)
plt.show()
No description has been provided for this image

Operacje na tablicach¶

In [94]:
a = np.arange(10, 20)
b = np.arange(10, 0, -1)
t = np.arange(40).reshape(5, 8)
v = np.arange(50, 90).reshape(5, 8)
w = v.T
In [95]:
a
Out[95]:
array([10, 11, 12, 13, 14, 15, 16, 17, 18, 19])
In [96]:
b
Out[96]:
array([10,  9,  8,  7,  6,  5,  4,  3,  2,  1])
In [97]:
t
Out[97]:
array([[ 0,  1,  2,  3,  4,  5,  6,  7],
       [ 8,  9, 10, 11, 12, 13, 14, 15],
       [16, 17, 18, 19, 20, 21, 22, 23],
       [24, 25, 26, 27, 28, 29, 30, 31],
       [32, 33, 34, 35, 36, 37, 38, 39]])
In [98]:
v
Out[98]:
array([[50, 51, 52, 53, 54, 55, 56, 57],
       [58, 59, 60, 61, 62, 63, 64, 65],
       [66, 67, 68, 69, 70, 71, 72, 73],
       [74, 75, 76, 77, 78, 79, 80, 81],
       [82, 83, 84, 85, 86, 87, 88, 89]])
In [99]:
w
Out[99]:
array([[50, 58, 66, 74, 82],
       [51, 59, 67, 75, 83],
       [52, 60, 68, 76, 84],
       [53, 61, 69, 77, 85],
       [54, 62, 70, 78, 86],
       [55, 63, 71, 79, 87],
       [56, 64, 72, 80, 88],
       [57, 65, 73, 81, 89]])

Generalnie można stosować operacje arytmetyczne między tablicami. Jeśli wymiary tablic się zgadzają, operacje są wykonywane „per element”, na zgodnych pozycjach.

In [100]:
a + b
Out[100]:
array([20, 20, 20, 20, 20, 20, 20, 20, 20, 20])
In [101]:
a - b
Out[101]:
array([ 0,  2,  4,  6,  8, 10, 12, 14, 16, 18])
In [102]:
a * b
Out[102]:
array([100,  99,  96,  91,  84,  75,  64,  51,  36,  19])
In [103]:
a / b
Out[103]:
array([ 1.        ,  1.22222222,  1.5       ,  1.85714286,  2.33333333,
        3.        ,  4.        ,  5.66666667,  9.        , 19.        ])
In [104]:
a // b
Out[104]:
array([ 1,  1,  1,  1,  2,  3,  4,  5,  9, 19])
In [105]:
a % b
Out[105]:
array([0, 2, 4, 6, 2, 0, 0, 2, 0, 0])
In [106]:
t + v
Out[106]:
array([[ 50,  52,  54,  56,  58,  60,  62,  64],
       [ 66,  68,  70,  72,  74,  76,  78,  80],
       [ 82,  84,  86,  88,  90,  92,  94,  96],
       [ 98, 100, 102, 104, 106, 108, 110, 112],
       [114, 116, 118, 120, 122, 124, 126, 128]])
In [107]:
t * v
Out[107]:
array([[   0,   51,  104,  159,  216,  275,  336,  399],
       [ 464,  531,  600,  671,  744,  819,  896,  975],
       [1056, 1139, 1224, 1311, 1400, 1491, 1584, 1679],
       [1776, 1875, 1976, 2079, 2184, 2291, 2400, 2511],
       [2624, 2739, 2856, 2975, 3096, 3219, 3344, 3471]])

Mnożenie macierzy oznaczane jest symbolem @ i wymaga użycia tablic pasujących wielkością.

In [108]:
v @ w
Out[108]:
array([[22940, 26364, 29788, 33212, 36636],
       [26364, 30300, 34236, 38172, 42108],
       [29788, 34236, 38684, 43132, 47580],
       [33212, 38172, 43132, 48092, 53052],
       [36636, 42108, 47580, 53052, 58524]])
In [109]:
w @ v
Out[109]:
array([[22420, 22750, 23080, 23410, 23740, 24070, 24400, 24730],
       [22750, 23085, 23420, 23755, 24090, 24425, 24760, 25095],
       [23080, 23420, 23760, 24100, 24440, 24780, 25120, 25460],
       [23410, 23755, 24100, 24445, 24790, 25135, 25480, 25825],
       [23740, 24090, 24440, 24790, 25140, 25490, 25840, 26190],
       [24070, 24425, 24780, 25135, 25490, 25845, 26200, 26555],
       [24400, 24760, 25120, 25480, 25840, 26200, 26560, 26920],
       [24730, 25095, 25460, 25825, 26190, 26555, 26920, 27285]])

Jeśli rozmiary tablic się nie zgadzają (np. krótsza i dłuższa jednowymiarowa tablica), powoduje to błąd.

In [110]:
# ValueError
# np.array([1, 2, 3]) * np.array([11, 12, 13, 14])
In [111]:
# ValueError
# t + w

Broadcasting¶

Jeśli jednak argumenty mają różną liczbę wymiarów, numpy podejmuje próbę przeprowadzenia operacji broadcasting, która polega na powieleniu danych, aby uzupełnić brakujący wymiar.

Najlepszym przykładem jest powielenie jednego wiersza, aby uzyskać tyle wierszy, ile posiada druga tablica.

In [112]:
g = np.array([1, 2, 3, 4, 5])
h = np.array([
    [10, 20, 30, 40, 50],
    [100, 200, 300, 400, 500],
    [1000, 2000, 3000, 4000, 5000],
])
g + h
Out[112]:
array([[  11,   22,   33,   44,   55],
       [ 101,  202,  303,  404,  505],
       [1001, 2002, 3003, 4004, 5005]])

Ale działa to w różne strony, nawet w kilka jednocześnie:

In [113]:
g = np.array([[1],
              [2],
              [3]])
h = np.array([
    [10, 20, 30, 40, 50],
    [100, 200, 300, 400, 500],
    [1000, 2000, 3000, 4000, 5000],
])
g + h
Out[113]:
array([[  11,   21,   31,   41,   51],
       [ 102,  202,  302,  402,  502],
       [1003, 2003, 3003, 4003, 5003]])
In [114]:
g = np.array([1, 2, 3, 4, 5])
h = np.array([[100],
              [200],
              [300]])
g + h
Out[114]:
array([[101, 102, 103, 104, 105],
       [201, 202, 203, 204, 205],
       [301, 302, 303, 304, 305]])
In [115]:
%pprint
Pretty printing has been turned OFF
In [116]:
d3 = np.random.rand(5*10*3).reshape(5, 10, 3) * 100.0
d3
Out[116]:
array([[[20.77468354, 52.59137503, 97.48647409],
        [62.42582292, 17.12401438, 10.09106112],
        [82.90940154, 96.67908726, 93.55647434],
        [57.12906566, 65.30888178, 33.12558963],
        [97.06466058,  4.44342006, 37.77203671],
        [85.8368438 , 38.61150895, 47.53379762],
        [ 9.78858587,  2.98857293, 50.67247657],
        [98.66142583, 46.91103274, 36.17638063],
        [ 7.95545013, 72.22736441, 85.03220205],
        [ 2.01537568, 24.57806507, 24.60706984]],

       [[60.26382953, 39.21532521, 69.45802257],
        [64.91502955, 22.90854365, 63.33072236],
        [62.17703359, 89.57509609, 72.36648606],
        [47.24628802, 75.45986122, 71.42303184],
        [74.88592042, 84.24075714, 43.08310947],
        [89.73476707, 29.78942389,  8.13245342],
        [58.9916479 , 57.5727015 , 53.74846818],
        [ 8.52789782, 27.45866237, 85.40632302],
        [80.4840114 ,  3.72941453, 33.67676857],
        [30.60508003, 65.10202971, 15.0767815 ]],

       [[22.91796387, 33.66468471, 11.58284787],
        [82.70486494, 25.58862713,  8.85710901],
        [96.05458196, 22.27216592, 86.88676278],
        [40.6976419 , 35.09643016, 64.19349516],
        [15.06812199, 37.3072805 , 78.44813408],
        [54.41629057, 87.58040307, 84.97357785],
        [ 9.88116531, 29.22518685, 35.96513353],
        [10.03072168, 58.01284743, 81.57909284],
        [40.38913993, 79.17767712, 46.06589119],
        [81.42298256, 43.49854046, 16.65471239]],

       [[ 7.74014755, 62.04422949, 60.73016565],
        [25.61369746, 99.45251398, 13.43522669],
        [34.36105221, 41.31203172, 12.54333236],
        [48.54043717, 77.55004905,  8.4928454 ],
        [17.09838706, 30.28284352, 48.27583824],
        [ 6.20668772, 79.71543193, 87.13417147],
        [ 7.12655683, 88.55698287, 15.00428966],
        [29.76900956,  6.70504356, 71.84614958],
        [ 7.22440379, 10.62873949, 33.1026422 ],
        [24.68642182, 24.83172962, 86.29812893]],

       [[73.92148741, 93.42716828, 45.54270843],
        [21.79758124, 67.65624339, 39.48641364],
        [79.33790855, 54.26892985, 45.31459653],
        [99.93228467, 59.80322076, 29.95309268],
        [43.03292894, 32.50722983, 82.15139342],
        [91.38581803, 12.12643529, 77.38812407],
        [60.57227984,  0.41180725,  5.72317641],
        [45.951643  , 73.32741302, 64.59881914],
        [69.59889213, 41.58058327, 74.45956073],
        [90.87358269, 30.48219785, 46.80896599]]])
In [117]:
d3 *= np.array([0.5, 0, 1])
In [118]:
d3
Out[118]:
array([[[10.38734177,  0.        , 97.48647409],
        [31.21291146,  0.        , 10.09106112],
        [41.45470077,  0.        , 93.55647434],
        [28.56453283,  0.        , 33.12558963],
        [48.53233029,  0.        , 37.77203671],
        [42.9184219 ,  0.        , 47.53379762],
        [ 4.89429294,  0.        , 50.67247657],
        [49.33071291,  0.        , 36.17638063],
        [ 3.97772506,  0.        , 85.03220205],
        [ 1.00768784,  0.        , 24.60706984]],

       [[30.13191476,  0.        , 69.45802257],
        [32.45751477,  0.        , 63.33072236],
        [31.0885168 ,  0.        , 72.36648606],
        [23.62314401,  0.        , 71.42303184],
        [37.44296021,  0.        , 43.08310947],
        [44.86738354,  0.        ,  8.13245342],
        [29.49582395,  0.        , 53.74846818],
        [ 4.26394891,  0.        , 85.40632302],
        [40.2420057 ,  0.        , 33.67676857],
        [15.30254001,  0.        , 15.0767815 ]],

       [[11.45898193,  0.        , 11.58284787],
        [41.35243247,  0.        ,  8.85710901],
        [48.02729098,  0.        , 86.88676278],
        [20.34882095,  0.        , 64.19349516],
        [ 7.53406099,  0.        , 78.44813408],
        [27.20814529,  0.        , 84.97357785],
        [ 4.94058266,  0.        , 35.96513353],
        [ 5.01536084,  0.        , 81.57909284],
        [20.19456996,  0.        , 46.06589119],
        [40.71149128,  0.        , 16.65471239]],

       [[ 3.87007377,  0.        , 60.73016565],
        [12.80684873,  0.        , 13.43522669],
        [17.18052611,  0.        , 12.54333236],
        [24.27021858,  0.        ,  8.4928454 ],
        [ 8.54919353,  0.        , 48.27583824],
        [ 3.10334386,  0.        , 87.13417147],
        [ 3.56327841,  0.        , 15.00428966],
        [14.88450478,  0.        , 71.84614958],
        [ 3.61220189,  0.        , 33.1026422 ],
        [12.34321091,  0.        , 86.29812893]],

       [[36.9607437 ,  0.        , 45.54270843],
        [10.89879062,  0.        , 39.48641364],
        [39.66895428,  0.        , 45.31459653],
        [49.96614234,  0.        , 29.95309268],
        [21.51646447,  0.        , 82.15139342],
        [45.69290901,  0.        , 77.38812407],
        [30.28613992,  0.        ,  5.72317641],
        [22.9758215 ,  0.        , 64.59881914],
        [34.79944606,  0.        , 74.45956073],
        [45.43679134,  0.        , 46.80896599]]])

Szczególnym przypadkiem broadcastingu tak naprawdę jest operacja arytmetyczna łącząca tablicę i wartość skalarną: wartość skalarna jest „rozmnażana” tyle razy, iel trzeba, aby dopasować ją do tablicy.

In [119]:
a + 1000
Out[119]:
array([1010, 1011, 1012, 1013, 1014, 1015, 1016, 1017, 1018, 1019])
In [120]:
t * (-1)
Out[120]:
array([[  0,  -1,  -2,  -3,  -4,  -5,  -6,  -7],
       [ -8,  -9, -10, -11, -12, -13, -14, -15],
       [-16, -17, -18, -19, -20, -21, -22, -23],
       [-24, -25, -26, -27, -28, -29, -30, -31],
       [-32, -33, -34, -35, -36, -37, -38, -39]])

Wybieranie elementów i zakresów¶

Indeksy i wycinanki (slices), podobnie jak dla list, ale tutaj może to być wielowymiarowe.

In [121]:
a = np.arange(10, 20)
a
Out[121]:
array([10, 11, 12, 13, 14, 15, 16, 17, 18, 19])
In [122]:
t = np.arange(50).reshape(5, 10)
t
Out[122]:
array([[ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9],
       [10, 11, 12, 13, 14, 15, 16, 17, 18, 19],
       [20, 21, 22, 23, 24, 25, 26, 27, 28, 29],
       [30, 31, 32, 33, 34, 35, 36, 37, 38, 39],
       [40, 41, 42, 43, 44, 45, 46, 47, 48, 49]])
In [123]:
t.shape
Out[123]:
(5, 10)
In [124]:
# Dla jednowymiarowej tablicy jest tak jak dla listy
a[5]
Out[124]:
15
In [125]:
a[2:7]
Out[125]:
array([12, 13, 14, 15, 16])
In [126]:
a[2:7:2]
Out[126]:
array([12, 14, 16])
In [127]:
# Można pomijać współrzędne - wtedy przyjmowane są:
# - początek 0
# - koniec size
# - krok 1
# od 3 do końca
a[3:]
Out[127]:
array([13, 14, 15, 16, 17, 18, 19])
In [128]:
# od początku do 7 (wyłączając)
a[:7]
Out[128]:
array([10, 11, 12, 13, 14, 15, 16])
In [129]:
a[3::]
Out[129]:
array([13, 14, 15, 16, 17, 18, 19])
In [130]:
a[:7:]
Out[130]:
array([10, 11, 12, 13, 14, 15, 16])
In [131]:
a[::-1]
Out[131]:
array([19, 18, 17, 16, 15, 14, 13, 12, 11, 10])
In [132]:
# Gdy tablica jest wielowymiarowa, to podanie współrzędnych w jednym wymiarze wybiera np. pojedynczy wiersz
t[2]
Out[132]:
array([20, 21, 22, 23, 24, 25, 26, 27, 28, 29])
In [133]:
t[2:4]
Out[133]:
array([[20, 21, 22, 23, 24, 25, 26, 27, 28, 29],
       [30, 31, 32, 33, 34, 35, 36, 37, 38, 39]])
In [134]:
# Aby wskazać konkretny element, używa się indeksowania wielopoziomowego, gdzie współrzędne rozdziela się przecinkiem

# wiersz nr 2, kolumna nr 4 (przy czym numeracja jest od zera)
t[2,4]
Out[134]:
24
In [135]:
i = 1
j = 6
t[i, j]
Out[135]:
16
In [136]:
# Może to nie jest typowe dla numpy, ale zobaczmy, że można iterować za pomocą pętli
# Styl języka C:
for i in range(len(t)):
    for j in range((len(t[i]))):
        print(t[i,j], end=' ')
    print()
0 1 2 3 4 5 6 7 8 9 
10 11 12 13 14 15 16 17 18 19 
20 21 22 23 24 25 26 27 28 29 
30 31 32 33 34 35 36 37 38 39 
40 41 42 43 44 45 46 47 48 49 
In [137]:
# Tworze kopię tej tablicy, aby w niej coś mzienić, a nie psuć oryginału
tab = t.copy()
In [138]:
tab
Out[138]:
array([[ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9],
       [10, 11, 12, 13, 14, 15, 16, 17, 18, 19],
       [20, 21, 22, 23, 24, 25, 26, 27, 28, 29],
       [30, 31, 32, 33, 34, 35, 36, 37, 38, 39],
       [40, 41, 42, 43, 44, 45, 46, 47, 48, 49]])
In [139]:
# Tablice numpy są mutowalne. Zmiana wygląda podonie jak zmiana wartości w liście:
a[1] = 91
a
Out[139]:
array([10, 91, 12, 13, 14, 15, 16, 17, 18, 19])
In [140]:
tab[1,3] = 777
tab
Out[140]:
array([[  0,   1,   2,   3,   4,   5,   6,   7,   8,   9],
       [ 10,  11,  12, 777,  14,  15,  16,  17,  18,  19],
       [ 20,  21,  22,  23,  24,  25,  26,  27,  28,  29],
       [ 30,  31,  32,  33,  34,  35,  36,  37,  38,  39],
       [ 40,  41,  42,  43,  44,  45,  46,  47,  48,  49]])
In [141]:
# Takiej pętli można użyć, aby indywidualnie zmieniać wartości poszczególnych pól.
# Np. tutaj do liczby dodam numer kolumny
for i in range(len(tab)):
    for j in range((len(tab[i]))):
        tab[i,j] += j
In [142]:
tab
Out[142]:
array([[  0,   2,   4,   6,   8,  10,  12,  14,  16,  18],
       [ 10,  12,  14, 780,  18,  20,  22,  24,  26,  28],
       [ 20,  22,  24,  26,  28,  30,  32,  34,  36,  38],
       [ 30,  32,  34,  36,  38,  40,  42,  44,  46,  48],
       [ 40,  42,  44,  46,  48,  50,  52,  54,  56,  58]])
In [143]:
# Jeśli chcemy w pętli tylko odczytać elementy, to można prościej:
for wiersz in tab:
    for element in wiersz:
        print(element, end='; ')
    print()
0; 2; 4; 6; 8; 10; 12; 14; 16; 18; 
10; 12; 14; 780; 18; 20; 22; 24; 26; 28; 
20; 22; 24; 26; 28; 30; 32; 34; 36; 38; 
30; 32; 34; 36; 38; 40; 42; 44; 46; 48; 
40; 42; 44; 46; 48; 50; 52; 54; 56; 58; 
In [144]:
# Wracając do indeksów
t
Out[144]:
array([[ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9],
       [10, 11, 12, 13, 14, 15, 16, 17, 18, 19],
       [20, 21, 22, 23, 24, 25, 26, 27, 28, 29],
       [30, 31, 32, 33, 34, 35, 36, 37, 38, 39],
       [40, 41, 42, 43, 44, 45, 46, 47, 48, 49]])
In [145]:
t[2,4]
Out[145]:
24
In [146]:
# Gdy w przypadku wielowymiarowym podajemy zakresy, to w wyniku dostajemy fragment tablicy
t[1:3, 5:8]
Out[146]:
array([[15, 16, 17],
       [25, 26, 27]])

Dzięki zostawianiu przedziałów otwartych, łatwo wyciąć dane tylko w jednym wymiarze

In [147]:
# Wybrane wiersze i wszystkie kolumny
t[1:3, :]
Out[147]:
array([[10, 11, 12, 13, 14, 15, 16, 17, 18, 19],
       [20, 21, 22, 23, 24, 25, 26, 27, 28, 29]])
In [148]:
# To można było uzyskać także w ten sposób
t[1:3]
Out[148]:
array([[10, 11, 12, 13, 14, 15, 16, 17, 18, 19],
       [20, 21, 22, 23, 24, 25, 26, 27, 28, 29]])
In [149]:
# Wszystkie wiersze i wybrane kolumny
t[:, 5:8]
Out[149]:
array([[ 5,  6,  7],
       [15, 16, 17],
       [25, 26, 27],
       [35, 36, 37],
       [45, 46, 47]])
In [150]:
d = np.arange(60).reshape(3, 4, 5)
d
Out[150]:
array([[[ 0,  1,  2,  3,  4],
        [ 5,  6,  7,  8,  9],
        [10, 11, 12, 13, 14],
        [15, 16, 17, 18, 19]],

       [[20, 21, 22, 23, 24],
        [25, 26, 27, 28, 29],
        [30, 31, 32, 33, 34],
        [35, 36, 37, 38, 39]],

       [[40, 41, 42, 43, 44],
        [45, 46, 47, 48, 49],
        [50, 51, 52, 53, 54],
        [55, 56, 57, 58, 59]]])
In [151]:
d[1, 0, 3]
Out[151]:
23
In [152]:
d[1, :, 3]
Out[152]:
array([23, 28, 33, 38])
In [153]:
d[1, :, :]
Out[153]:
array([[20, 21, 22, 23, 24],
       [25, 26, 27, 28, 29],
       [30, 31, 32, 33, 34],
       [35, 36, 37, 38, 39]])
In [154]:
# Jeśli we wszystkich pozostałych wymiarach stosuję wildcard "weź wszystko", to można jeszcze krócej:
d[1, ...]
Out[154]:
array([[20, 21, 22, 23, 24],
       [25, 26, 27, 28, 29],
       [30, 31, 32, 33, 34],
       [35, 36, 37, 38, 39]])
In [155]:
d[..., 3]
Out[155]:
array([[ 3,  8, 13, 18],
       [23, 28, 33, 38],
       [43, 48, 53, 58]])
In [156]:
t
Out[156]:
array([[ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9],
       [10, 11, 12, 13, 14, 15, 16, 17, 18, 19],
       [20, 21, 22, 23, 24, 25, 26, 27, 28, 29],
       [30, 31, 32, 33, 34, 35, 36, 37, 38, 39],
       [40, 41, 42, 43, 44, 45, 46, 47, 48, 49]])
In [157]:
# Aby odczytać macierz transponowaną (zamiana znaczenia wierszy z kolumnami), możemy użyć specjalnego trybutu .T
t.T
Out[157]:
array([[ 0, 10, 20, 30, 40],
       [ 1, 11, 21, 31, 41],
       [ 2, 12, 22, 32, 42],
       [ 3, 13, 23, 33, 43],
       [ 4, 14, 24, 34, 44],
       [ 5, 15, 25, 35, 45],
       [ 6, 16, 26, 36, 46],
       [ 7, 17, 27, 37, 47],
       [ 8, 18, 28, 38, 48],
       [ 9, 19, 29, 39, 49]])
In [158]:
# reshape robi coś innego
t.reshape(10, 5)
Out[158]:
array([[ 0,  1,  2,  3,  4],
       [ 5,  6,  7,  8,  9],
       [10, 11, 12, 13, 14],
       [15, 16, 17, 18, 19],
       [20, 21, 22, 23, 24],
       [25, 26, 27, 28, 29],
       [30, 31, 32, 33, 34],
       [35, 36, 37, 38, 39],
       [40, 41, 42, 43, 44],
       [45, 46, 47, 48, 49]])

Warunki i filtrowanie¶

In [159]:
np.random.seed(0)
a = np.random.randint(-5, 10, 20)
a
Out[159]:
array([ 7,  0, -5, -2,  6, -2,  2,  4, -2,  0, -3, -1,  2,  1,  3,  3,  7,
        5, -4,  1])
In [160]:
b = [10, 11, 12, 13, 14]
In [161]:
# Co się stanie, gdy porównamy tablicę z liczbą.
# Powstaje tablica wartości logicznych. Na danej pozycji jest info czy element spełniał warunek
a > 0
Out[161]:
array([ True, False, False, False,  True, False,  True,  True, False,
       False, False, False,  True,  True,  True,  True,  True,  True,
       False,  True])

Z kolei operacja indeksowania pozwala przekazać matrycę wartości True/False decydującą, które elementy zostaną zwrócone w wyniku

In [162]:
# Jeśli wynik porównania zapiszemy sobie do zmiennej:
maska = a > 0
In [163]:
# wygląda to tak:
maska
Out[163]:
array([ True, False, False, False,  True, False,  True,  True, False,
       False, False, False,  True,  True,  True,  True,  True,  True,
       False,  True])
In [164]:
# To teraz mogę wybrać elementy z tablicy a, dla których w masce jest True
a[maska]
Out[164]:
array([7, 6, 2, 4, 2, 1, 3, 3, 7, 5, 1])
In [165]:
# W praktyce zapisuje się to bezpośrednio i wygląda to bardzo przejrzyście:
a[a>0]
# Wybierz z tablicy a takie elementy, które są dodatnie
Out[165]:
array([7, 6, 2, 4, 2, 1, 3, 3, 7, 5, 1])

Aby połączyć kilka warunków, możemy użyć spójników logicznych, ale tym razem nie pisanych jako and or, tylko za pomocą znaczków & |

Bo technicznie wykonujemy tutaj operacje na bitach w tych maskach opisujących gdzie była pracwa, a gdzie fałsz.

In [166]:
a[(a >= 0) & (a < 5)]
Out[166]:
array([0, 2, 4, 0, 2, 1, 3, 3, 1])
In [167]:
# Jak to działa
maska1 = a >= 0
maska1
Out[167]:
array([ True,  True, False, False,  True, False,  True,  True, False,
        True, False, False,  True,  True,  True,  True,  True,  True,
       False,  True])
In [168]:
maska2 = a < 5
maska2
Out[168]:
array([False,  True,  True,  True, False,  True,  True,  True,  True,
        True,  True,  True,  True,  True,  True,  True, False, False,
        True,  True])
In [169]:
maska1 & maska2
Out[169]:
array([False,  True, False, False, False, False,  True,  True, False,
        True, False, False,  True,  True,  True,  True, False, False,
       False,  True])
In [170]:
# Inny przykład: wybierzmy liczby dodatnie, które jednocześnie są parzyste
a[(a > 0) & (a % 2 == 0)]
Out[170]:
array([6, 2, 4, 2])
In [171]:
# Lub to jest |
a[(a > 0) | (a % 2 == 0)]
Out[171]:
array([ 7,  0, -2,  6, -2,  2,  4, -2,  0,  2,  1,  3,  3,  7,  5, -4,  1])
In [172]:
# Negacja jako ~
a[~maska]
Out[172]:
array([ 0, -5, -2, -2, -2,  0, -3, -1, -4])
In [173]:
# where zwraca info o pozycjach, na których warunek jest prawdziwy
np.where(a % 2 == 0)
Out[173]:
(array([ 1,  3,  4,  5,  6,  7,  8,  9, 12, 18]),)
In [174]:
min(a), max(a)
Out[174]:
(-5, 7)
In [175]:
# Czy jakikolwiek element spełnia warunek
(a > 6).any()
Out[175]:
True
In [176]:
(a > 8).any()
Out[176]:
False
In [177]:
# Czy wszystkie elementy spełniają warunek
(a > 0).all()
Out[177]:
False

Funkcje agregujące¶

Zasadniczo funkcje agregujace zwracają pojedynczy wynik na podstawie całej tabeli.

In [178]:
t
Out[178]:
array([[ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9],
       [10, 11, 12, 13, 14, 15, 16, 17, 18, 19],
       [20, 21, 22, 23, 24, 25, 26, 27, 28, 29],
       [30, 31, 32, 33, 34, 35, 36, 37, 38, 39],
       [40, 41, 42, 43, 44, 45, 46, 47, 48, 49]])
In [179]:
# Większosć funkcji agregujących jest dostępna jako metody w obiektach array:
t.sum()
Out[179]:
1225
In [180]:
# Nietypowa nazwa: średnia to jest mean a nie avg
t.mean()
Out[180]:
24.5
In [181]:
t.std()
Out[181]:
14.430869689661812
In [182]:
t.size, t.sum(), t.mean(), t.min(), t.max(), t.std()
Out[182]:
(50, 1225, 24.5, 0, 49, 14.430869689661812)
In [183]:
# Mediana jest dostępna jako funkcja w bibliotece numpy, a nie metoda obiektu
np.median(t)
Out[183]:
24.5
In [184]:
# W przypadku tablic wielowymiarowych można też agregować dane "wierszami" albo "kolumnami":
t.sum()
Out[184]:
1225
In [185]:
# średnia z każdej kolumny
t.sum(axis=0)
Out[185]:
array([100, 105, 110, 115, 120, 125, 130, 135, 140, 145])
In [186]:
# średnia z każdego wiersza
t.sum(axis=1)
Out[186]:
array([ 45, 145, 245, 345, 445])
In [187]:
# w parametrze axis podaje się numer tego wymiaru, który znika, który jest redukowany, wzdłuż którego wyliczane są agregaty
In [188]:
t.mean(axis=1).reshape(-1, 1)
Out[188]:
array([[ 4.5],
       [14.5],
       [24.5],
       [34.5],
       [44.5]])

Rozwiązywanie układów równań liniowych¶

Powiedzmy, że mamy układ równań: 2x + y = 5 3x - y = 5

In [189]:
a = np.array([[2, 1],
              [3, -1]])
b = np.array([5, 5])
In [190]:
np.linalg.solve(a, b)
Out[190]:
array([2., 1.])
In [191]:
# Przykład sprytnego odczytu wyników dzięki technice rozpakowywania:
x, y = np.linalg.solve(a, b)
print(f'Rozwiązania równania: x = {x}, y = {y}')
Rozwiązania równania: x = 2.0, y = 1.0000000000000002

Wczytywanie danych z pliku¶

Dosyć łatwo wczytuje się dane tabelaryczne z plików tekstowych o formacie przypominającym CSV: wartości rozdzielone separatorem. Służy do tego funkcja genfromtxt.

Przy domyślnych ustawieniach wczytywana jest dwuwymiarowa tablica float64, za separator uznawane są dowolne ciągi białych znaków.

In [192]:
liczby = np.genfromtxt('pliki/liczby.txt')
In [193]:
liczby
Out[193]:
array([[10.  , 11.  , 12.  , 13.  , 14.  , 15.  , 16.  , 17.  , 18.  ,
        19.  ],
       [10.  ,  9.  ,  8.  ,  7.  ,  6.  ,  5.  ,  4.  ,  3.  ,  2.  ,
         1.  ],
       [ 0.  ,  0.  ,  0.  ,  0.  ,  0.  ,  0.  ,  0.  ,  0.  ,  0.  ,
         0.  ],
       [ 1.1 ,  1.25,  1.44,  1.75,  2.  ,  1.66,  1.5 ,  1.33,  1.2 ,
         1.  ],
       [ 5.  ,  4.  ,  6.  ,  3.  ,  7.  ,  2.  ,  8.  ,  1.  ,  9.  ,
         0.  ]])
In [194]:
liczby.dtype, liczby.shape
Out[194]:
(dtype('float64'), (5, 10))
In [195]:
liczby *= 5
liczby -= 1.5
liczby
Out[195]:
array([[48.5 , 53.5 , 58.5 , 63.5 , 68.5 , 73.5 , 78.5 , 83.5 , 88.5 ,
        93.5 ],
       [48.5 , 43.5 , 38.5 , 33.5 , 28.5 , 23.5 , 18.5 , 13.5 ,  8.5 ,
         3.5 ],
       [-1.5 , -1.5 , -1.5 , -1.5 , -1.5 , -1.5 , -1.5 , -1.5 , -1.5 ,
        -1.5 ],
       [ 4.  ,  4.75,  5.7 ,  7.25,  8.5 ,  6.8 ,  6.  ,  5.15,  4.5 ,
         3.5 ],
       [23.5 , 18.5 , 28.5 , 13.5 , 33.5 ,  8.5 , 38.5 ,  3.5 , 43.5 ,
        -1.5 ]])

Aby zapisać tablicę do pliku, można użyć savetxt:

In [196]:
np.savetxt('zmienione_liczby.txt', liczby)
# Sprawdź - plik został zapisany na dysku

Obie funkcje posiadają liczne parametry, które pozwalają dostosować się do formatu oraz wybrać zakres danych. Do najważniejszych opcji genfromtxt należą:

  • delimiter – znak będący separatorem pól, często przecinek lub średnik
  • usecols – w formie listy liczb można podać numery tych kolumn, które mają być wczytane; pozostałe zostaną pominięte, dzięki czemu można na przykład ominąć kolumny tekstowe
  • skiprows – ilość wierszy, które mają być pominięte na początku, np. wiersz nagłówkowy
  • dtype – typ elementów
In [197]:
kwiatki = np.genfromtxt('pliki/iris.data', delimiter=',', usecols=[0,1,2,3])
In [198]:
kwiatki.shape
Out[198]:
(150, 4)
In [199]:
kwiatki
Out[199]:
array([[5.1, 3.5, 1.4, 0.2],
       [4.9, 3. , 1.4, 0.2],
       [4.7, 3.2, 1.3, 0.2],
       [4.6, 3.1, 1.5, 0.2],
       [5. , 3.6, 1.4, 0.2],
       [5.4, 3.9, 1.7, 0.4],
       [4.6, 3.4, 1.4, 0.3],
       [5. , 3.4, 1.5, 0.2],
       [4.4, 2.9, 1.4, 0.2],
       [4.9, 3.1, 1.5, 0.1],
       [5.4, 3.7, 1.5, 0.2],
       [4.8, 3.4, 1.6, 0.2],
       [4.8, 3. , 1.4, 0.1],
       [4.3, 3. , 1.1, 0.1],
       [5.8, 4. , 1.2, 0.2],
       [5.7, 4.4, 1.5, 0.4],
       [5.4, 3.9, 1.3, 0.4],
       [5.1, 3.5, 1.4, 0.3],
       [5.7, 3.8, 1.7, 0.3],
       [5.1, 3.8, 1.5, 0.3],
       [5.4, 3.4, 1.7, 0.2],
       [5.1, 3.7, 1.5, 0.4],
       [4.6, 3.6, 1. , 0.2],
       [5.1, 3.3, 1.7, 0.5],
       [4.8, 3.4, 1.9, 0.2],
       [5. , 3. , 1.6, 0.2],
       [5. , 3.4, 1.6, 0.4],
       [5.2, 3.5, 1.5, 0.2],
       [5.2, 3.4, 1.4, 0.2],
       [4.7, 3.2, 1.6, 0.2],
       [4.8, 3.1, 1.6, 0.2],
       [5.4, 3.4, 1.5, 0.4],
       [5.2, 4.1, 1.5, 0.1],
       [5.5, 4.2, 1.4, 0.2],
       [4.9, 3.1, 1.5, 0.1],
       [5. , 3.2, 1.2, 0.2],
       [5.5, 3.5, 1.3, 0.2],
       [4.9, 3.1, 1.5, 0.1],
       [4.4, 3. , 1.3, 0.2],
       [5.1, 3.4, 1.5, 0.2],
       [5. , 3.5, 1.3, 0.3],
       [4.5, 2.3, 1.3, 0.3],
       [4.4, 3.2, 1.3, 0.2],
       [5. , 3.5, 1.6, 0.6],
       [5.1, 3.8, 1.9, 0.4],
       [4.8, 3. , 1.4, 0.3],
       [5.1, 3.8, 1.6, 0.2],
       [4.6, 3.2, 1.4, 0.2],
       [5.3, 3.7, 1.5, 0.2],
       [5. , 3.3, 1.4, 0.2],
       [7. , 3.2, 4.7, 1.4],
       [6.4, 3.2, 4.5, 1.5],
       [6.9, 3.1, 4.9, 1.5],
       [5.5, 2.3, 4. , 1.3],
       [6.5, 2.8, 4.6, 1.5],
       [5.7, 2.8, 4.5, 1.3],
       [6.3, 3.3, 4.7, 1.6],
       [4.9, 2.4, 3.3, 1. ],
       [6.6, 2.9, 4.6, 1.3],
       [5.2, 2.7, 3.9, 1.4],
       [5. , 2. , 3.5, 1. ],
       [5.9, 3. , 4.2, 1.5],
       [6. , 2.2, 4. , 1. ],
       [6.1, 2.9, 4.7, 1.4],
       [5.6, 2.9, 3.6, 1.3],
       [6.7, 3.1, 4.4, 1.4],
       [5.6, 3. , 4.5, 1.5],
       [5.8, 2.7, 4.1, 1. ],
       [6.2, 2.2, 4.5, 1.5],
       [5.6, 2.5, 3.9, 1.1],
       [5.9, 3.2, 4.8, 1.8],
       [6.1, 2.8, 4. , 1.3],
       [6.3, 2.5, 4.9, 1.5],
       [6.1, 2.8, 4.7, 1.2],
       [6.4, 2.9, 4.3, 1.3],
       [6.6, 3. , 4.4, 1.4],
       [6.8, 2.8, 4.8, 1.4],
       [6.7, 3. , 5. , 1.7],
       [6. , 2.9, 4.5, 1.5],
       [5.7, 2.6, 3.5, 1. ],
       [5.5, 2.4, 3.8, 1.1],
       [5.5, 2.4, 3.7, 1. ],
       [5.8, 2.7, 3.9, 1.2],
       [6. , 2.7, 5.1, 1.6],
       [5.4, 3. , 4.5, 1.5],
       [6. , 3.4, 4.5, 1.6],
       [6.7, 3.1, 4.7, 1.5],
       [6.3, 2.3, 4.4, 1.3],
       [5.6, 3. , 4.1, 1.3],
       [5.5, 2.5, 4. , 1.3],
       [5.5, 2.6, 4.4, 1.2],
       [6.1, 3. , 4.6, 1.4],
       [5.8, 2.6, 4. , 1.2],
       [5. , 2.3, 3.3, 1. ],
       [5.6, 2.7, 4.2, 1.3],
       [5.7, 3. , 4.2, 1.2],
       [5.7, 2.9, 4.2, 1.3],
       [6.2, 2.9, 4.3, 1.3],
       [5.1, 2.5, 3. , 1.1],
       [5.7, 2.8, 4.1, 1.3],
       [6.3, 3.3, 6. , 2.5],
       [5.8, 2.7, 5.1, 1.9],
       [7.1, 3. , 5.9, 2.1],
       [6.3, 2.9, 5.6, 1.8],
       [6.5, 3. , 5.8, 2.2],
       [7.6, 3. , 6.6, 2.1],
       [4.9, 2.5, 4.5, 1.7],
       [7.3, 2.9, 6.3, 1.8],
       [6.7, 2.5, 5.8, 1.8],
       [7.2, 3.6, 6.1, 2.5],
       [6.5, 3.2, 5.1, 2. ],
       [6.4, 2.7, 5.3, 1.9],
       [6.8, 3. , 5.5, 2.1],
       [5.7, 2.5, 5. , 2. ],
       [5.8, 2.8, 5.1, 2.4],
       [6.4, 3.2, 5.3, 2.3],
       [6.5, 3. , 5.5, 1.8],
       [7.7, 3.8, 6.7, 2.2],
       [7.7, 2.6, 6.9, 2.3],
       [6. , 2.2, 5. , 1.5],
       [6.9, 3.2, 5.7, 2.3],
       [5.6, 2.8, 4.9, 2. ],
       [7.7, 2.8, 6.7, 2. ],
       [6.3, 2.7, 4.9, 1.8],
       [6.7, 3.3, 5.7, 2.1],
       [7.2, 3.2, 6. , 1.8],
       [6.2, 2.8, 4.8, 1.8],
       [6.1, 3. , 4.9, 1.8],
       [6.4, 2.8, 5.6, 2.1],
       [7.2, 3. , 5.8, 1.6],
       [7.4, 2.8, 6.1, 1.9],
       [7.9, 3.8, 6.4, 2. ],
       [6.4, 2.8, 5.6, 2.2],
       [6.3, 2.8, 5.1, 1.5],
       [6.1, 2.6, 5.6, 1.4],
       [7.7, 3. , 6.1, 2.3],
       [6.3, 3.4, 5.6, 2.4],
       [6.4, 3.1, 5.5, 1.8],
       [6. , 3. , 4.8, 1.8],
       [6.9, 3.1, 5.4, 2.1],
       [6.7, 3.1, 5.6, 2.4],
       [6.9, 3.1, 5.1, 2.3],
       [5.8, 2.7, 5.1, 1.9],
       [6.8, 3.2, 5.9, 2.3],
       [6.7, 3.3, 5.7, 2.5],
       [6.7, 3. , 5.2, 2.3],
       [6.3, 2.5, 5. , 1.9],
       [6.5, 3. , 5.2, 2. ],
       [6.2, 3.4, 5.4, 2.3],
       [5.9, 3. , 5.1, 1.8]])
In [200]:
kwiatki.mean(axis=0)
Out[200]:
array([5.84333333, 3.054     , 3.75866667, 1.19866667])
In [201]:
kwiatki.std(axis=0)
Out[201]:
array([0.82530129, 0.43214658, 1.75852918, 0.76061262])
In [202]:
np.median(kwiatki, axis=0)
Out[202]:
array([5.8 , 3.  , 4.35, 1.3 ])

Czytanie z URL-a¶

Funkcja genfromtxt potrafi czytać dane nie tylko z pliku lokalnego na dysku, ale także bezpośrednio z adresu URL, o ile zasób jest publicznie dostępny bez potrzeby logowania się itp.

In [203]:
adres = "http://students.alx.pl/~pczarnik/dane/iris.data"
kwiatki2 = np.genfromtxt(adres, delimiter=',', usecols=[0,1,2,3])
kwiatki2
Out[203]:
array([[5.1, 3.5, 1.4, 0.2],
       [4.9, 3. , 1.4, 0.2],
       [4.7, 3.2, 1.3, 0.2],
       [4.6, 3.1, 1.5, 0.2],
       [5. , 3.6, 1.4, 0.2],
       [5.4, 3.9, 1.7, 0.4],
       [4.6, 3.4, 1.4, 0.3],
       [5. , 3.4, 1.5, 0.2],
       [4.4, 2.9, 1.4, 0.2],
       [4.9, 3.1, 1.5, 0.1],
       [5.4, 3.7, 1.5, 0.2],
       [4.8, 3.4, 1.6, 0.2],
       [4.8, 3. , 1.4, 0.1],
       [4.3, 3. , 1.1, 0.1],
       [5.8, 4. , 1.2, 0.2],
       [5.7, 4.4, 1.5, 0.4],
       [5.4, 3.9, 1.3, 0.4],
       [5.1, 3.5, 1.4, 0.3],
       [5.7, 3.8, 1.7, 0.3],
       [5.1, 3.8, 1.5, 0.3],
       [5.4, 3.4, 1.7, 0.2],
       [5.1, 3.7, 1.5, 0.4],
       [4.6, 3.6, 1. , 0.2],
       [5.1, 3.3, 1.7, 0.5],
       [4.8, 3.4, 1.9, 0.2],
       [5. , 3. , 1.6, 0.2],
       [5. , 3.4, 1.6, 0.4],
       [5.2, 3.5, 1.5, 0.2],
       [5.2, 3.4, 1.4, 0.2],
       [4.7, 3.2, 1.6, 0.2],
       [4.8, 3.1, 1.6, 0.2],
       [5.4, 3.4, 1.5, 0.4],
       [5.2, 4.1, 1.5, 0.1],
       [5.5, 4.2, 1.4, 0.2],
       [4.9, 3.1, 1.5, 0.1],
       [5. , 3.2, 1.2, 0.2],
       [5.5, 3.5, 1.3, 0.2],
       [4.9, 3.1, 1.5, 0.1],
       [4.4, 3. , 1.3, 0.2],
       [5.1, 3.4, 1.5, 0.2],
       [5. , 3.5, 1.3, 0.3],
       [4.5, 2.3, 1.3, 0.3],
       [4.4, 3.2, 1.3, 0.2],
       [5. , 3.5, 1.6, 0.6],
       [5.1, 3.8, 1.9, 0.4],
       [4.8, 3. , 1.4, 0.3],
       [5.1, 3.8, 1.6, 0.2],
       [4.6, 3.2, 1.4, 0.2],
       [5.3, 3.7, 1.5, 0.2],
       [5. , 3.3, 1.4, 0.2],
       [7. , 3.2, 4.7, 1.4],
       [6.4, 3.2, 4.5, 1.5],
       [6.9, 3.1, 4.9, 1.5],
       [5.5, 2.3, 4. , 1.3],
       [6.5, 2.8, 4.6, 1.5],
       [5.7, 2.8, 4.5, 1.3],
       [6.3, 3.3, 4.7, 1.6],
       [4.9, 2.4, 3.3, 1. ],
       [6.6, 2.9, 4.6, 1.3],
       [5.2, 2.7, 3.9, 1.4],
       [5. , 2. , 3.5, 1. ],
       [5.9, 3. , 4.2, 1.5],
       [6. , 2.2, 4. , 1. ],
       [6.1, 2.9, 4.7, 1.4],
       [5.6, 2.9, 3.6, 1.3],
       [6.7, 3.1, 4.4, 1.4],
       [5.6, 3. , 4.5, 1.5],
       [5.8, 2.7, 4.1, 1. ],
       [6.2, 2.2, 4.5, 1.5],
       [5.6, 2.5, 3.9, 1.1],
       [5.9, 3.2, 4.8, 1.8],
       [6.1, 2.8, 4. , 1.3],
       [6.3, 2.5, 4.9, 1.5],
       [6.1, 2.8, 4.7, 1.2],
       [6.4, 2.9, 4.3, 1.3],
       [6.6, 3. , 4.4, 1.4],
       [6.8, 2.8, 4.8, 1.4],
       [6.7, 3. , 5. , 1.7],
       [6. , 2.9, 4.5, 1.5],
       [5.7, 2.6, 3.5, 1. ],
       [5.5, 2.4, 3.8, 1.1],
       [5.5, 2.4, 3.7, 1. ],
       [5.8, 2.7, 3.9, 1.2],
       [6. , 2.7, 5.1, 1.6],
       [5.4, 3. , 4.5, 1.5],
       [6. , 3.4, 4.5, 1.6],
       [6.7, 3.1, 4.7, 1.5],
       [6.3, 2.3, 4.4, 1.3],
       [5.6, 3. , 4.1, 1.3],
       [5.5, 2.5, 4. , 1.3],
       [5.5, 2.6, 4.4, 1.2],
       [6.1, 3. , 4.6, 1.4],
       [5.8, 2.6, 4. , 1.2],
       [5. , 2.3, 3.3, 1. ],
       [5.6, 2.7, 4.2, 1.3],
       [5.7, 3. , 4.2, 1.2],
       [5.7, 2.9, 4.2, 1.3],
       [6.2, 2.9, 4.3, 1.3],
       [5.1, 2.5, 3. , 1.1],
       [5.7, 2.8, 4.1, 1.3],
       [6.3, 3.3, 6. , 2.5],
       [5.8, 2.7, 5.1, 1.9],
       [7.1, 3. , 5.9, 2.1],
       [6.3, 2.9, 5.6, 1.8],
       [6.5, 3. , 5.8, 2.2],
       [7.6, 3. , 6.6, 2.1],
       [4.9, 2.5, 4.5, 1.7],
       [7.3, 2.9, 6.3, 1.8],
       [6.7, 2.5, 5.8, 1.8],
       [7.2, 3.6, 6.1, 2.5],
       [6.5, 3.2, 5.1, 2. ],
       [6.4, 2.7, 5.3, 1.9],
       [6.8, 3. , 5.5, 2.1],
       [5.7, 2.5, 5. , 2. ],
       [5.8, 2.8, 5.1, 2.4],
       [6.4, 3.2, 5.3, 2.3],
       [6.5, 3. , 5.5, 1.8],
       [7.7, 3.8, 6.7, 2.2],
       [7.7, 2.6, 6.9, 2.3],
       [6. , 2.2, 5. , 1.5],
       [6.9, 3.2, 5.7, 2.3],
       [5.6, 2.8, 4.9, 2. ],
       [7.7, 2.8, 6.7, 2. ],
       [6.3, 2.7, 4.9, 1.8],
       [6.7, 3.3, 5.7, 2.1],
       [7.2, 3.2, 6. , 1.8],
       [6.2, 2.8, 4.8, 1.8],
       [6.1, 3. , 4.9, 1.8],
       [6.4, 2.8, 5.6, 2.1],
       [7.2, 3. , 5.8, 1.6],
       [7.4, 2.8, 6.1, 1.9],
       [7.9, 3.8, 6.4, 2. ],
       [6.4, 2.8, 5.6, 2.2],
       [6.3, 2.8, 5.1, 1.5],
       [6.1, 2.6, 5.6, 1.4],
       [7.7, 3. , 6.1, 2.3],
       [6.3, 3.4, 5.6, 2.4],
       [6.4, 3.1, 5.5, 1.8],
       [6. , 3. , 4.8, 1.8],
       [6.9, 3.1, 5.4, 2.1],
       [6.7, 3.1, 5.6, 2.4],
       [6.9, 3.1, 5.1, 2.3],
       [5.8, 2.7, 5.1, 1.9],
       [6.8, 3.2, 5.9, 2.3],
       [6.7, 3.3, 5.7, 2.5],
       [6.7, 3. , 5.2, 2.3],
       [6.3, 2.5, 5. , 1.9],
       [6.5, 3. , 5.2, 2. ],
       [6.2, 3.4, 5.4, 2.3],
       [5.9, 3. , 5.1, 1.8]])