Zapisz jako PDF
Transkrypt
Zapisz jako PDF
Krótkie wprowadzenie do biblioteki NumPy pojawiło się już w zeszłym roku. Spis treści 1 Wycinki 1.1 Indeksy 1.2 Przykład — indeksy dla napisu "string" 1.2.1 Indeksy dodatnie 1.2.2 Indeksy ujemne 1.3 Wycinki 2 Numpy 2.1 Tablice 2.2 Trochę wiadomości o tablicach w numpy 3 Problemy 3.1 Proste operacje 3.2 Plansza kółko-krzyżyk 3.3 Dopasowanie metodą najmniejsztch kwadratów 3.3.1 Zadanie 3.3.2 Wskazówka 1 3.3.3 Wskazówka 2 3.4 Zadanie_z_chi2/rozwiązanie macierzowe 3.5 Działania macierzowe 3.5.1 Zadanie 1 3.5.2 Zadanie 2 3.6 Deklarowanie własnych typów danych 3.6.1 Zadanie 1 3.6.2 Zadanie 2 Wycinki Fragment z http://en.wikibooks.org/wiki/NonProgrammer%27s_Tutorial_for_Python_2.0/Revenge_of_the_Strings, autorstwa wikibooksen:User:Jrincayc, wikibooksen:User:Whiteknight, wikibooksen:User:Siebengang, wikibooksen:User:33rogers. Z poprawkami! Indeksy Indeksując napis odwołujemy się do wybranego znaku. Znaki mogą być liczone od początku napisu (pierwszy ma numer 0) lub od jego końca (ostatni ma numer –1). 1 ↓ T 0 ↓ tekst = " S ↑ [ 2 ↓ R ... ↓ I –2 ↓ N –1 ↓ G " ↑ ] Na powyższym rysunku czerwone indeksy są liczone od początku napisu, a niebieskie od jego końca. Teraz przykłady: tekst[0] → "S" tekst[1] → "T" tekst[2] → "R" tekst[−2] → "N" tekst[−0] → "S" tekst[20] → red|IndexError Nie ma indeksu −0, bo w Pythonie −0 == 0, więc −0 również wskazuje na początek napisu. Numery wskazujące na miejsca poza końcem napisu lub przed jego początkiem są błędne, Python zwróci informację o błędzie, tzw. IndexError. Przykład — indeksy dla napisu "string" Indeksy dodatnie >>> 's' >>> 't' >>> 'r' >>> 'i' >>> 'n' >>> 'g' tekst[] tekst[1] tekst[2] tekst[3] tekst[4] tekst[5] Indeksy ujemne >>> tekst = "string" >>> 'g' >>> 'n' >>> 'i' >>> 't' >>> 'r' >>> 't' >>> 's' tekst[-1] tekst[-2] tekst[-3] tekst[-5] tekst[-4] tekst[-5] tekst[-6] Wycinki Pobranie wycinka sekwencji polega na wybraniu jej fragmentu — podsekwencji. Miejsca początku i końca zakresu z którego pobieramy wycinek liczone są podobnie jak przypadku indeksowania. Istotna różnica jest taka, że wskazujemy podsekwencję, a nie pojedynczy element — i to w sposób szczególny. Przedział który wskazujemy jako [a:b], jest zamknięty od strony a, a otwarty od strony b. Lewy i prawy kraniec wycinka podajemy oddzielając indeksy dwukropkiem :. Każdą z tych wartości można pominąć, w skrajnym przypadku zostawiając tylko dwukropek. Jeśli dany indeks pominiemy, zostaną użyte wartości domyślne: domyślne położenie początku wycinka to początek sekwencji domyślne położenie końca wycinka to koniec całej sekwencji Taka konwencja ma pewne zalety, w szczególności dla dowolnego indeksu a prawdą jest, że sekwencja[:a] + sekwencja[a:] == sekwencja Konstruując wycinek możemy pobierać co n-ty element napisu. Robi się to podając wartość kroku n po kolejnym dwukropku. Domyślnie n ma wartość 1. Teraz przykłady: tekst[1:4] → "TRI" tekst[:5] → "STRIN" tekst[:–1] → "STRIN" tekst[–4:] → "RING" tekst[:] → "STRING" (tzw. pełny wycinek, czyli kopia sekwencji) tekst[::–1] → "GNIRTS" tekst[−10:−2] → "STRI" Numery wskazujące na miejsca poza rozmiarem napisu nie są błędne, Python wybierze tylko istniejące znaki. Numpy Tablice W zeszłym roku była mowa o tablicach (arrays) w numpy. Ograniczaliśmy się praktycznie do przypadku dwuwymiarowego, czyli macierzy. Warto sobie uświadomić, że obiekt array może być wielowymiarową tablicą. Przytoczymy teraz kilka przydatnych funkcji dotyczących tablic w numpy. import numpy as np a = np.ones((2,3,4)) # tworzy tablicę jedynek o wymiarze 2x3x4 a = np.empty((2,3)) # tworzy pustą tablicę o wymiarze 2x3 a = np.zeros((2,3,4,5)) # twrzy tablicę zer o wymiarze 2x3x4x5 # tak, w numpy można tworzyć więcej niż 3-wymiarowe tablice! # jeśli potrzebowalibyśmy bardzo dużo wymiarów, a nie chce nam się ich po kolei wpisywać możemy zrobić tak: a = np.empty(tuple([10 for i in range(20)])) # 20-sto wymiarowa tablica, w której każdy wymiar ma długość 10 #np.empty przyjmuje jako argument obiekt tuple, dlatego utworzyliśmy w wygodny sposób listę wymiarów, a następnie przerabiamy ją na tuple, żeby podać jako argument # łatwiejszym do wyobrażenia, 3-wymiarowym odpowiednikiem tego jest: a = np.empty(tuple([10 for i in range(3)])) # 3-wymiarowa tablica, w której każdy z wymiarów ma długość 10 A = np.random.randint(10,size = (3,3)) # wytwarza tablicę losowych elementów całkowitych o wymiarach 3x3 (takie tablicę częst się przydają jako dane testowe do naszych funkcji/programów) A = np.random.randint(10,size = (3,3,5)) # wytwarza tablicę analogiczną do powyższej, ale o wymiarze 3x3x5 wiersze,kolumny = numpy.shape(A) # co się stanie, gdy A ma wymiar mniejszy/większy niż 2? Jaki będzie wynik następujących instrukcji: a = np.arange(1,10) a = np.arange(10) a = np.arange(1., 10) a = np.arange(1, 10, 2.5) a = np.arange(1,2,10) Utwórz 3-wymiarową tablicę A, o wymiarach 10x5x3 taką, że A[i,j,k] = "i,j,k" (wartością każdego elementu będzie string zawierający wszystkie indeksy). Aby zainicjalizować poprawnie tablicę pamiętaj o wybraniu odpowiedniego typu zmiennych (dtype). Wypisz ją, przyjrzyj się w jakiej kolejności są wypisywane elementy, gdy napiszesz po prostu. print A Utwórz 100-wymiarową tablicę zer, o każdym wymiarze = 10. Ile maksymalnie wymiarów może mieć tablica w numpy? Z listy [[1, 2, 3], [4, 5, 6],[7, 8, 9]] utwórz macierz. Zastanów się, jak zmienić swoją funkcję obliczającą splot, tak, żeby radziła sobie też z wektorami różnej długości. Trochę wiadomości o tablicach w numpy W numpy jak i w Pythonie obowiązuje numeracja od zera — stąd pierwszy element wektora a o długości N to a[0], a ostatni a[N-1]. Elementy tablicy indeksuje się: , gdzie tablicy dwuwymiarowej: A[wiersz, kolumna]. to indeks w wymiarze j. Tzn dla Łączenie tablic wykonuje się za pomocą funkcji numpy.concatenate((a1, a2, ...), axis=0). a1, a2, ... oznacza tablice, a axis oś, w której tablice zostaną połączone. Parametr ten ma domyślnie wartość 0. Tablice muszą mieć taki sam rozmiar w osi, w której będą połączone. Funkcja zwraca tablicę wynikową. Usuwanie elementu z tablicy (wielowymiarowej): dane2 = dane[numpy.r_[:i,i+1:dane_1.shape[]]] Problemy Proste operacje from numpy import * l1 = [1,1,1] 12 = ones(3) Jaki będzie wynik następujących operacji: l1 * 3 l2 * 3 Jakie operacje należy wykonać na liście l1, aby otrzymać wynik analogiczny do l2*3? l1 / 3 l2 / 3 Plansza kółko-krzyżyk Zaprojektuj tablicę złużącą do gry w kółko i krzyżyk w trzech wymiarach. Kółka to zera, krzyżyki to jedynki. To znaczy zdefiniuj kilka funkcji, które będą umożliwiały grę w kółko i krzyżyk w trzech wymiarach: funkcję tworzącą pustą planszę funkcję stawiąjącą krzyżyk w wybranym miejscu(określonym współrzędnymi x,y,z) funkcję stawiąjącą kółko w wybranym miejscu funkcję wypisującą aktualny stan planszy Rozszerz poprzednie zadanie o wyrysowywanie trójwymiarowe stanu planszy. Utwórz ze zdefiniowanych funkcji moduł Dopasowanie metodą najmniejsztch kwadratów W pracy doświadczalnej wielokrotnie badamy zależność z jaką wielkość Zwykle ta zależność fizyczna jest postaci: zależy od wielkości . gdzie — zależność fizyczna, — parametry. Żeby zobaczyć, czy funkcja dobrze oddaje realną zależność fizyczną między a obliczamy suma kwadratów residuów ( ) — sumę kwadratów różnic wyników pomiaru i ich wartości oczekiwanych, wyliczonych z (Equation 1): gdzie para oznacza wynik piątego pomiaru wielkości i . Szukając parametrów (Equation 1), wybieramy te, dla których jest najmniejsze. Jest wiele różnych algorytmów, którymi można się w tym posłużyć. Można też po prostu przeskanować przestrzeń parametrów, w szczególności, kiedy wiemy, jakie są fizyczne zakresy wartości tychże; i wybrać takie, które minimalizują . Błąd dopasowania wybranego już parametru przedstawia się następującym wzorem: [Error parsing LaTeX formula. Error 7: error copying result: 57f5c3e0bc7a5952bb91e1fe8c5ce613.png --> /var/www/html/edu/images/math/math-9b4bf153d311b30aa1ee6e384a93ae69.png]. Zadanie Napisz moduł zawierający funkcję do obliczenia sumy kwadratów residuów (Equation 2) i błędu dopasowania parameterów (Equation 3). 1. Załóż, że funcja opisująca relację między danymi ma postać (Equation 1) func(dane,parametry) . 2. Dane znajdują się w macierzy o wymiarach n×2 (dane). 3. Funkcja obliczająca (Equation 2) powinna mieć przekazywane dane w wektorze o wymiarach (dane), nazwę funkcji charakteryzującej zależność fizyczną i służącej do obliczania wartości oczekiwanych (func) i listę parametrów funkcji (params): def chi2(dane, func, params): Pamietaj o rozpakowaniu listy z parametrami funkcji func. 4. Funkcja obliczająca (Equation 3) powinna zwracać niepewności wszystkich podanych w liście parametrów. Parametrem opcjonalnym powinno być , służące do obliczenia drugiej pochodnej (patrz wskazówka 1). Funkcja powinna korzystać z funkcji chi2. Nagłówek funkcji: def delta_p(dane, func, params,delta=0.000001): 5. Metodą brute force, skanując przestrzeń parametrów, sprawdź, czy do danych pasuje zależność (Equation 4) (opis danych i parametrów w tytule rysunku). 6. Zobacz na wykresie, jak dobre jest Twoje dopasowanie. 7. Zmodyfikuj funkcję, tak żeby mogła uwzględnić niepewności pomiarowe w obliczaniu sumy kwadratów residuów: , gdzie i-tą niepewność pomiarową . Załóż, że jeżeli tablica z danymi ma trzy kolumny, w trzeciej znajdują się niepewności pomiarowe . Jeżeli tablica z danymi ma cztery kolumny, w czwartej znajdują się niepewności pomiarowe y-ka — , a w trzeciej niepewności pomiarowe x-a — . Dane znajdują się w Plik:Dane.txt. Pierwsza kolumna przedstawia interwały . Druga obliczone wartości wzmocnienia synaptycznego FR, które może być opisywane następującą zależnością: [Error parsing LaTeX formula. Error 7: error copying result: b0edabae8ab5714bd34fd3dc69006acd.png --> /var/www/html/edu/images/math/math-4ef22631c6484e1a77eced51600d9442.png] W jakim zakresie Delta wynik niepewności jest stabilny? Wskazówka 1 Numeryczne obliczanie pierwszej pochodnej: Numeryczne obliczanie drugiej pochodnej: Wskazówka 2 Skorzystaj z modułu Numpy do wczytania danych (loadtxt) oraz obliczenia wartości funkcji wykładniczej (exp). Zadanie_z_chi2/rozwiązanie macierzowe Najpierw definiujemy funkcję służącą do obliczania FR na podstawie modelu teoretycznego: from __future__ import division import numpy def FRobl(tau, Use, T): return 1 + (1 + (1 - Use)*numpy.exp(-T/tau))**4 Proszę zwrócić uwagę, że tak zdefiniowana funkcja działa równie dobrze dla zwykłych liczb, jak i dla tablic numpy.ndarray, o ile podane argumenty obsługują działania wykorzystywane w funkcji, czyli /, *, **, -, + i numpy.exp. Z kolei __future__.division jest importowane bo chce się upewnić, że dzielenie zawsze jest zwykłym dzieleniem, nawet jeśli funkcja zostanie wywołana jako FRobl(2, 0.03, 1). Następnie definiujemy funkcje do obliczania pierwszej i drugiej pochodnej: def pochodna(f, i, *params, **opts): eps = opts.get('eps', 1e-6) params_eps = list(params) params_eps[i] += eps return (f(*params_eps) - f(*params))/eps def pochodna2(f, i, *params, **opts): eps = opts.get('eps', 1e-6) params_eps = list(params) params_eps[i] += eps return (pochodna(f, i, *params_eps) pochodna(f, i, *params)) / eps Jeśli się pamięta wzór na pochodną, to tutaj nie ma wielkich niespodzianek. Jak działa *params, **opts w liście parametrów? Mamy argument params, który zawierać będzie wszystkie nadmiarowe argumenty pozycyjne, oraz opts który zawierać będzie wszystkie nazwane argumenty. Dzięki temu możemy funkcję wywołać z dowolną liczbą parametrów params, które zostaną przekazane do f. Do opts trafiają argumenty nazwane. Jeśli napiszemy eps=..., to nasz argument zostanie wydobyty ze słownika i użyty jako eps. Jeśli nie podamy argumentu eps=..., to jako eps zostanie użyty drugi argument do get, czyli 1e-6. Jeśli podamy argumenty nazwane inne niż eps=... to zostaną one zignorowane -- niestety nasza funkcja nie jest doskonała. Aby obliczyć niepewność parametru, musimy mieć funkcję obliczającą rms (rmsfunc) i miejsce w którym liczymy (params) i numer parametru który nas interesuje (i). def niepewnosc(rmsfunc, i, *params): return (pochodna2(rmsfunc, i, *params)/2)**-0.5 W naszym wypadku funkcja obliczająca rms może wyglądać tak: def frms(T, FR, tau, Use): FR2 = FRobl(tau, Use, T) return ((FR2 - FR)**2).mean()**0.5 Sens jest taki, że obliczamy co daje model teoretyczny FRobl jak dla danego zestawu ( , )= (tau, Use) i pewnego T i porównujemy z wynikami doświadczalnymi FR. Otrzymujemy zestaw odchyleń, a rms jest oczywiście pierwiastkiem ze średniej kwadratów tych odchyleń. Przechodąc do meritum, możemy albo się posłużyć danymi doświadczalnymi, albo np. wygenerować dane dla znanych ($\tau$, $U_{SE}$). To drugie podejście pozwala nam sprawdzić naszą metodę, bo wiemy dokładnie jaki wynik powinniśmy otrzymać. Zestaw danych wygenerowanych wraz z parametrami: def dane_symulowane(tau, Use): T = numpy.arange(5, 80, 5) return T, FRobl(tau, Use, T), tau, Use T_opt, FR_opt, tau_opt, Use_opt = dane_symulowane(30, 0.02) Dopasowanie wykonujemy metodą brute force, czyli po prostu przeszukujemy całą przestrzeń parametrów. Implementację rozbijmy na dwie części — najpierw obliczanie rms, potem znajdywanie minimum: def rms_brute_force(T, FR): tau = numpy.arange(5, 101, 1) Use = numpy.arange(0.001, 0.031, 0.001) # tau3 = tau # Use3 = Use[:, numpy.newaxis] # T3 = T[:, numpy.newaxis, numpy.newaxis] tau3 = tau[numpy.newaxis, numpy.newaxis, :] Use3 = Use[numpy.newaxis, :, numpy.newaxis] T3 = T[:, numpy.newaxis, numpy.newaxis] print('kształty tau, Use, T: ', tau3.shape, Use3.shape, T3.shape) wynik = FRobl(tau3, Use3, T3) FR3 = FR[:, numpy.newaxis, numpy.newaxis] odchylenie = wynik - FR3 rms = (odchylenie**2).mean(axis=) return tau, Use, rms Do szybkiego przeprowadzenia obliczeń na całej siatce wykorzystujemy broadcasting. Tworzymy trzy wektory, czyli tablice o długości większej niż 1 w jednym tylko kierunku — (1, 1, M2), (1, M1, 1), (N, 1, 1). Każdy z wektorów jest skierowany w inną stronę, więc po wykonaniu działania element po elemencie (tak jak to się dzieje w FRobl), otrzymujemy macierz o wymiarze (N, M1, M2). def dopasowanie_brute_force(T, FR): tau, Use, rms = rms_brute_force(T, FR) ind_min = numpy.unravel_index(rms.argmin(), rms.shape) Use_min = Use[ind_min[]] tau_min = tau[ind_min[1]] print('minimum dopasowania to τ={} Use={}'.format(tau_min, Use_min)) assert frms(T, FR, tau_min, Use_min) == rms.min() D_tau_min = niepewnosc(frms, 2, *(T, FR, tau_min, Use_min)) D_Use_min = niepewnosc(frms, 3, *(T, FR, tau_min, Use_min)) print('niepewności wynoszą Δτ={} ΔUse={}'.format(D_Use_min, D_tau_min)) return tau, Use, rms, tau_min, D_tau_min, Use_min, D_Use_min Aby znaleźć optymalne wartości parametrów, czyli takie dla których rms jest najmniejszy, wykorzystujemy numpy.argmin. Ta funkcja nam zwraca indeks minimalnego elementu, ale po "spłaszczeniu". Numer wiersza i kolumny uzyskujmy przez numpy.unravel_index. Do obliczenia niepewności mamy zdefiniowaną wcześniej funkcję niepewnosc Wynik: tau, Use, rms, tau_min, D_tau_min, Use_min, D_use_min = dopasowanie_brute_force(T_opt, FR_opt) </tt> Wyniki możemy narysować przy pomocy następujących funkcji, które zostawie prawie bez komentarza: <source lang=python> from __future__ import unicode_literals from matplotlib import pyplot, patches __name__ == '__main__' and pyplot.ion() # unicode_literals powoduje, że matplotlib wie, że wszystkie stringi to Unicode # pyplot.ion() podowuje coś def plot_map(tau, Use, rms): f = pyplot.figure() ax = f.add_subplot(111) X, Y = numpy.meshgrid(tau, Use) map = ax.pcolor(X, Y, rms) ax.set_xlabel(r'tau') ax.set_ylabel(r'Use') f.colorbar(map) f.show() return f def plot_map2(tau, Use, rms, **kwargs): f = pyplot.figure() ax = f.add_subplot(111) X, Y = numpy.meshgrid(tau, Use) cont = ax.contour(X, Y, rms) ax.set_xlabel(r'tau') ax.set_ylabel(r'Use') f.colorbar(cont) ax.clabel(cont, inline=1, **kwargs) f.show() return f def plot_dane(T, FR, label1, T2, FR2, label2): f = pyplot.figure() ax = f.add_subplot(111) ax.set_xlabel(r'T') ax.set_ylabel(r'FR') ax.plot(T, FR, 'ro', label=label1) ax.plot(T2, FR2, '-g', label=label2) ax.legend(loc='best') return f def plot_dopasowanie(T, FR, tau, Use): T2 = numpy.linspace(T.min() - 1, T.max() + 10, 300) FR2 = FRobl(tau, Use, T2) return plot_dane(T, FR, 'dane', T2, FR2, 'dopasowanie') I na koniec rysunek z zeznaczonym rms: def plot_dopasowanie_map(tau, Use, rms, tau_min, D_tau_min, Use_min, D_Use_min): f = plot_map(tau, Use, rms) ax = f.gca() ax.plot(tau_min, Use_min, 'xy', markersize=5) ellip = patches.Ellipse(xy=(tau_min, Use_min), width=D_tau_min, height=D_Use_min) ellip.set_facecolor('none') ellip.set_linestyle('dashed') ellip.set_edgecolor('yellow') ax.add_artist(ellip) return f Działania macierzowe Broadcasting (dopełnianie) jest słowem, które określa, jak numpy traktuje wektory o różnym kształcie. Przy spełnieniu pewnych warunków, mniejszy wektor jest "rozszerzony" (powiększony) tak, żeby mieć kształt korespondujący z kształtem większego wektora — odpowiednie wymiary zostają dopełnione. Dopełnienie umożliwia przeprowadzenie operacji macierzowych tak, że pętle są wykonywane w C, a nie w Pythonie, co zwykle prowadzi do bardziej efektywnych implementacji algorytmów. Dodanie liczby 3 do wektora. Najprostszym przypadkiem broadcastingu jest dodawanie liczby (skalara) do macierzy. W tej sytuacji, każdy element macierzy zostanie zwiększony o tęże liczbę. Przyjrzyj się następującemu przykładowi i obrazkowi Figure 1: >>> x = np.arange(4) >>> x = array([, 1, 2, 3]) >>> x + 3 array([3, 4, 5, 6]) Dopełnienie jednowymiarowego wektora tak, żeby można było go dodać do wektora dwuwymiarowego. W przypadku więcejwymiarowym, oś, która powinna zostać dodana — w którą stronę wektor powinien zostać dopełniony, zostaje zaznaczona explicite słowem np.newaxis (albo None). Tak, jak na poniższym przykładzie i Rys. Figure 2: >>> a = np.arange(12).reshape((3,4)) >>> b = np.array([1,2,3])[:,np.newaxis] >>> a + b array([[ 1, 2, 3, 4], [ 6, 7, 8, 9], [11, 12, 13, 14]]) Dopełnienie dwuwymiarowego wektora tak, żeby po dodaniu wektora jednowymiarowego, utworzyć trójwymiarowy wektor. Przypadek trójwymiarowy (Rys. Figure 3): >>> >>> >>> (3, x = np.zeros((3, 5)) y = np.zeros(8) (x[..., None] + y).shape 5, 8) Można tez skorzystać z funkcji np.broadcast_arrays. Zadanie 1 Przed przystąpieniem do zadania polecamy zapoznać się z przykładami stąd Zadanie 2 Przepisz operacje zachodzące w pętlach i funkcjach w zadaniu z dopasowaniem metodą najmniejszych kwadratów tak, żeby skorzystać z operacji macierzowych. Żeby znaleźć najmniejszą wartość w macierzy z kwadratami residuów skorzystaj z metody min() strunktury ndarray. Żeby uzyskać spłaszczony indeks tego elementu skorzystaj z metody argmin() strunktury ndarray i następnie z funkcji numpy.unravel_index(indices, dims, order='C'), żeby znaleźć wartości parametrów, które minimalizują sumę kwadratów residuów. Deklarowanie własnych typów danych Deklarowanie własnych typów danych odbywa się za pomocą funkcji dtype. Przykładowo (przykłady z dokumentacji): >>> np.dtype([('f1', np.int16)]) dtype([('f1', '<i2')]) Daje nam rekord o polu 'f1' o typie danych int16. np.dtype([('f1', np.uint), ('f2', np.int32)]) dtype([('f1', '<u4'), ('f2', '<i4')]) Daje nam dwa pola rekordu jedno typu unsigned int, drugie typu int32. Można też zadeklarować rekord zawierający liczbę typu double i string dziesięciobajtowy: np.dtype([('a','f8'),('b','S10')]) dtype([('a', '<f8'), ('b', '|S10')]) Przykład użycia: >>> typ = np.dtype([("a", np.int16), ("b", "|S10")]) >>> typ dtype([('a', '<i2'), ('b', '|S10')]) >>> a = np.array([(5, "napis")], dtype = typ) >>> a array([(5, 'napis')], dtype=[('a', '<i2'), ('b', '|S10')]) >>> a = np.array([("n", "napis")], dtype = typ) Traceback (most recent call last): File "<pyshell#171>", line 1, in <module> a = np.array([("n", "napis")], dtype = typ) TypeError: expected a readable buffer object >>> a = np.array([(5, 5)], dtype = typ) >>> a array([(5, '5')], dtype=[('a', '<i2'), ('b', '|S10')]) >>> a = np.array([(5,)], dtype = typ) Traceback (most recent call last): File "<pyshell#174>", line 1, in <module> a = np.array([(5,)], dtype = typ) TypeError: expected a readable buffer object >>> typ2 dtype([('c', '<i2')]) >>> typ2 = np.dtype([("c", (np.int16, 3))]) >>> a = np.array([((3.5,1),)], dtype = typ2) Traceback (most recent call last): File "<pyshell#218>", line 1, in <module> a = np.array([((3.5,1),)], dtype = typ2) TypeError: expected a readable buffer object >>> a = np.array([((3,3,3),)], dtype = typ2) >>> a array([([3, 3, 3],)], dtype=[('c', '<i2', 3)]) >>> typ3 = np.dtype([("c", (np.int16, 5))]) >>> a = np.array([((3,3,4,5),)], dtype = typ3) Traceback (most recent call last): File "<pyshell#225>", line 1, in <module> a = np.array([((3,3,4,5),)], dtype = typ3) TypeError: expected a readable buffer object >>> a = np.array([((3,3,4,5,6),)], dtype = typ3) >>> a array([([3, 3, 4, 5, 6],)], dtype=[('c', '<i2', 5)]) Zadanie 1 Pod linkiem znajduje się plik tekstowy zawierający dane o triggerach z jednego z eksperymentów. Obejrzyj dane. Zaprojektuj za pomocą funkcji dtype z modułu numpy typ danych, który opisuję dane, znajdujące się w rzędach. Następnie wczytaj je za pomocą funkcji loadtxt, pamietając o opuszczeniu pierwszej linii i zadaniu przecinków, jako separatorów między kolumnami i wypisz wektor z danymi. Program nie powinien mieć więcej niż 5 linii. Zadanie 2 Wczytaj dane z poprzedniego zadania i spod łącza, a następnie wypisz średnie czasy odpowiedzi dla bodźców typu pozytywnego, neutralnego i negatywnego