Rozszerzanie Pythona
Jedną z największych zalet Pythona jest niesamowite bogactwo bibliotek. W tej części zobaczymy na kilku przykładach, jak ładować i używać rozszerzeń.
Moduły
Moduł w pythonie to plik z rozszerzeniem *.py
zawierający funkcje, stałe i inne elementy, które możemy importować do innego programu. Jeżeli stworzymy zatem np. zestaw funkcji, które rozwiązują równania różniczkowe, to możemy zebrać je w jednym miejscu i w łatwy sposób korzystać z nich w innych programach. Następnym krokiem, który wykracza poza poruszane tematy jest tworzenie pakietu Pythona, który może zostać zainstalowany lokalnie lub opublikowany i udostępniony szerzej.
Oczywiście, jak można się spodziewać python ma mnóstwo gotowych bibliotek, częściowo zawartych w standardowej dystrybucji (czyli dostępnych zawsze, bez instalowania dodatkowych pakietów). Autorzy nazywają to batteries included. Najpierw zobaczymy zatem co mamy do wyboru, potem nauczymy się jak importować biblioteki, a na końcu tworzyć własne.
Biblioteka standardowa
Wybrane moduły biblioteki standardowej
argparse - interfejs linii poleceń (parametry)
datetime - operacje związane z czasem i kalendarzem
math - funkcje matematyczne
multiprocessing - przetwarzanie wielowątkowe
os - interakcje z systemem operacyjnym
random - generatory liczb losowych
re - wyrażenia regularne
timeit - pomiary wydajności programu
tkinter - Tk GUI (jedna z prostszych bibliotek GUI)
QtPy - biblioteka GUI Qt
unittest - testowanie programu
Jest to tylko mała część tego co jest dostępne i jest prezentowana tylko po to, aby pokazać gotowe od ręki możliwości pythona. Większości z nich raczej nie będziemy używać.
Inne przydatne biblioteki
Do analizy danych używane są biblioteki z poniżej listy, z których zapoznamy się bliżej z numpy
i matplotlib
.
numpy - numerical Python
matplotlib - wykresy
pandas - biblioteka do operacji na zbiorach danych, popularna w Big Data Science
sympy - obliczenia symboliczne
scipy - scientific Python = numpy + matplotlib + ipython + pandas + sympy
Importowanie modułów
Do importowania modułu służy słówko import
, które zwykle używamy na początku programu.
import math
Powyższy przykład daje nam dostęp do funkcji matematycznych. Korzystanie z nich wymaga używania nazwy biblioteki i nazwy funkcji oddzielonych kropką
vx = v * math.sin(alpha)
Taka składnia zapewnia, że jeżeli w innym module (np. numpy) także jest zdefiniowana funkcja sin
to będziemy dokładnie wiedzieli, z której w danym momencie korzystamy.
Czasem jednak jest wygodniej z danej biblioteki wyciągnąć tylko co to nam potrzeba, np. pojedynczą funkcję
from math import sin, pi
y = sin(45 * pi / 180)
Dla odmiany, szczególnie podczas interaktywnych sesji, czasem chcemy zaimportować wszystko i to tak, aby nie kłopotać się z nazwami biblioteki
from math import *
y = sin(1.0)
w = pi * acosh(2.0)
z = exp(0.5)
ale z tą formą trzeba uważać, aby nie narobić sobie bałaganu!
Wreszcie, wiele modułów ma strukturę drzewiastą (nazywane są wtedy pakietami), czyli wewnątrz modułu są kolejne moduły i dopiero w nich są interesujące nas funkcje (np. matplotlib.pyplot.plot
. Wypisywanie takich długich napisów za każdym razem byłoby żmudne. Dlatego istnieje specjalna składnia nadająca alias (czyli wymyśloną przez nas krótką nazwę) danej bibliotece. Bardzo często będziemy używać takiego czegoś
import matplotlib.pyplot as plt
import numpy as np
Po zastosowaniu tej składni użycie słówka plt
ma taki sam skutek jak matplotlib.pyplot
.
Własne moduły
W zasadzie każdy skrypt z rozszerzeniem *.py
można importować do innego programu. Wyobraźmy sobie, że napisaliśmy skrypt pitagoras.py
, który wygląda tak
def pitagoras(a, b):
c = a**2 + b**2
return c**(1/2)
print ("Test:", pitagoras(3, 4))
w którym mamy jedną dobrze już znaną funkcję i polecenie, którym testujemy, czy aby wzór jest dobry. Napiszmy teraz inny program np. trojki.py
, w którym użyjemy funkcji pitagoras
do szukania trójek pitagorejskich
import pitagoras
n = 10
for a in range (1, n):
for b in range (a, n):
c = pitagoras.pitagoras (a, b)
if c.is_integer():
print (a, b, c)
Zobaczymy napis
Test 5
3 4 5.0
6 8 10.0
Okazuje się, że nasza testowa linia wydrukowała się po zaimportowaniu modułu! Pierwsze rozwiązanie to jej usunięcie, ale nie zawsze chcemy ingerować w istniejący program. Dlatego zamiast tego można zastosować składnię, która powie pythonowi, że pewna część kodu ma być wywołana tylko, jeżeli uruchomimy dany skrypt bezpośrednio. W przypadku importowania ta część zostanie pomięta. Plik pitagoras.py
będzie teraz wyglądał tak
def pitagoras(a, b):
c = a**2 + b**2
return c**(1/2)
if __name__ == '__main__':
print ("Test:", pitagoras(3, 4))
Wykresy
W poprzednich rozdziałach wspomnieliśmy o bibliotece matplotlib
. Jest to bardzo użyteczna i prosta w obsłudze biblioteka do tworzenia bardzo bogatych, wysokiej jakości wykresów. Często, aby narysować coś bardziej skomplikowanego wystarczy zerknąć na listę gotowych przykładów.
Podstawowy wykres
Podstawową funkcją jaką będziemy używać będzie polecenie plot
, które tworzy wykres. Należy podać mu listę punktów x
, punktów y
i typ wykresu.
import matplotlib.pyplot as plt
plt.plot(x, y, 'o')
plt.show()
Typ wykresu to albo rodzaj punktów (kółka - o, kwadraty - s, trójkąty - v ^ > <, krzyżyki - + x, gwiazdki - *) więcej tutaj, mogą być to również linie ('-', '–', ':', '-.') albo kombinacje punktów i linii (np. 'o-'). Aby wykres się pojawił w okienku musimy podać polecenie 'show'.
W bardziej zaawansowanej postaci możemy modyfikować kolor, grubość, rozmiar znaczników itd.
plt.plot(x, y, color='green', marker='o', linestyle='dashed', linewidth=2, markersize=12)
Zakres osi
Matplotlib sam dobiera zakresy osi x i y, ale nie zawsze ten wybór musi nam odpowiadać. Do zmiany zakresu służą polecenia
plt.xlim(0, 10)
plt.ylim(-5, 5)
Linie pionowe i poziome
Dodatkowe pionowe i poziome linie, które mogą np. oznaczać jakieś przyjęte przez nas poziomy odniesienia, średnie wartości, itd. można dodawać przez:
plt.axvline(3.0, ls='--', color='red')
plt.axhline(0.0, ls=':', color='black')
Legendy i opisy osi
Legendy osi wstawiamy przez xlabel
i ylabel
, ale domyślny rozmiar czcionki jest często zbyt mały, więc można go zwiększyć
plt.xlabel('Czas (t)', fontsize=14)
plt.ylabel('Amplituda (V)', fontsize=14)
Czcionka na osiach teraz jest dużo mniejsza, więc warto ją też powiększyć
plt.tick_params(axis='both', labelsize=12)
Jeżeli nasze wykresy mają jakieś opisy, możemy dodać legendę wyjaśniającą jakie znaczniki i kolory przypisane są jakiej wielkości. Legendę dodajemy poleceniem
plt.legend()
Wreszcie poniższe polecenie spowoduje ładne rozmieszczenie wykresu w okienku
plt.tight_layout()
Łącząc wszystkie te elementy poniższy przykład prezentuje wykres z hipotetycznego pomiaru na pracowni. Zamiast prostego plot
używamy tutaj errorbar
, ponieważ zwykle pomiary mają swoją niepewność, którą chcemy pokazać. Tak przygotowany wykres jest w zasadzie gotowy do wklejenia do raportu!
import matplotlib.pyplot as plt
t = [0, 1, 2, 3, 4, 5, 6]
A = [1.0, 1.1, 0.9, 0.7, 0.3, 0.1, 0.02]
dA = [0.1, 0.1, 0.1, 0.1, 0.05, 0.05, 0.005]
plt.errorbar(t, A, yerr=dA, marker='o', ls='None', color='red', label='Seria 1')
plt.xlim(0, 7)
plt.ylim(0, 1.5)
plt.xlabel('Czas (t)', fontsize=14)
plt.ylabel('Amplituda (V)', fontsize=14)
plt.tick_params(axis='both', labelsize=12)
plt.legend()
plt.tight_layout()
plt.show()
Wiele wykresów
Jeżeli do przedstawienia mamy wiele krzywych, to możemy je umieścić na jednym wykresie (wtedy warto użyć legendy), albo podzielić je na kilka osobnych, mniejszych wykresów. Pierwsze rozwiązanie jest bardzo proste, wystarczy wydać polecenie plot
więcej niż jeden raz
plt.plot(t, A1, 'o', label='Seria 1')
plt.plot(t, A2, 's', label='Seria 2')
plt.plot(t, A3, 'v', label='Seria 3')
plt.legend()
plt.show()
W drugiej wersji stworzymy podwykresy za pomocą polecenia subplot
. Podajemy w nim liczbę wierszy, liczbę kolumn i numer wykresu liczony od 1.
plt.subplot(3, 1, 1)
plt.plot(t, A1, 'o', label='Seria 1')
plt.subplot(3, 1, 2)
plt.plot(t, A2, 's', label='Seria 2')
plt.subplot(3, 1, 3)
plt.plot(t, A3, 'v', label='Seria 3')
plt.legend()
plt.tight_layout()
plt.show()
W tym przykładzie legenda pojawi się tylko na ostatnim wykresie. Dzieje się tak, ponieważ polecenia (zmiana osi, legendy itd.) dotyczą tylko bieżącego, czyli ostatnio stworzonego podwykresu.
Numpy
Pakiet NumPy, czyli Numerical Python to biblioteka dodająca do pythona obsługę dużych, wielowymiarowych macierzy oraz mnóstwo narzędzi numerycznych (metod numerycznych) do ich obsługi oraz wykonywania najróżniejszych obliczeń. NumPy jest napisany w C i skompilowane biblioteki są wołane przez pythona, jest dzięki temu często równie szybki (o ile trzymamy się struktur numpy i nie używamy np. pętli czystego pythona) jak inne kompilowane języki, a dzięki optymalizacji zwykle o wiele szybszy niż to co można samemu napisać w krótkim czasie.
Tablice
Podstawową strukturą numpy jest tablica, która może być jedno lub więcej wymiarowa. Działania zwykle są wykonywane na tablicy w całości, zamiast na kolejnych wyrazach, dzięki czemu kod jest znacznie szybszy. Tablicę można utworzyć na kilka sposobów, najprostszy jest przez podanie listy
x = numpy.array([0, 1, 2, 3])
Oczywiście lista musi mieć sensowną postać, którą da się przełożyć na wektor lub macierz, a więc nie może zawierać wymieszanych napisów i liczb, być nieprostokątna itd. Tablicę o większej liczbie wymiarów tworzymy podając listę zawierającą listy, np. dwuwymiarowa
A = numpy.array([[0, 1, 2], [3, 4, 5], [6, 7, 8]])
Do elementów takiej tablicy możemy się odwoływać przez podwójny indeks
A[0, 0]
Ale także możemy dostać cały wiersz
A[0, :]
lub kolumnę
A[:, 0]
Ten sposób, podawania elementów wprost, jest wygodny jeżeli pracujemy z niewielkimi tablicami. A co jeśli chcemy stworzyć tablicę zawierającą 100, 1000 lub jeszcze więcej elementów? Najczęściej będziemy stosować dwie metody.
x = numpy.arange(100)
ta postać działa bardzo podobnie do polecenia range
, i tworzy tablicę liczb od 0 do 99. Jeżeli chcemy tablicę z krokiem innym niż 1, to musimy podać początek, koniec oraz krok
x = numpy.arange(0, 10, 0.1)
i dostaniemy tablicę 100 elementów od 0 do 9.9
Inny sposób wygląda następująco
x = numpy.linspace(0, 1)
linspace
tworzy równy podział podanego odcinka na zadaną liczbę odcinków (domyślnie - 50), włącznie z końcami przedziału. Dlatego jeżeli chcemy równo podzielić przedział 0, 1 na części co 0.01, musimy zrobić to następująco
x = numpy.linspace(0, 1, 101)
Rozmiar (a dokładniej kształt) tablicy można sprawdzić poleceniem shape
, które zwraca listę rozmiarów w poszczególnych wymiarach. Jeżeli tablica jest jednowymiarowa, to będzie to lista zawierająca jeden element (ale nadal lista, a nie pojedyncza liczba!). W przykładzie poniżej sprawdzamy zatem liczbę elementów w osi X dla jednowymiarowej tablicy x.
x_size = x.shape[0]
Funkcje
Kiedy mamy już tablicę można teraz łatwo wywołać na niej różne operacje, przykład pokazuje połączenie z matplotlib i stworzenie wykresów funkcji trygonometrycznych
x = numpy.linspace(-numpy.pi ,numpy.pi, 100)
plt.plot(x, numpy.sin(x), '-')
plt.plot(x, numpy.cos(x), '--')
plt.show()
NumPy jest niezwykle bogatą biblioteką i najlepiej szukać w niej narzędzi w bardzo dobrej dokumentacji projektu. Kilka przykładowych funkcji to
numpy.average(x)
numpy.std(x)
numpy.min(x)
numpy.max(x)
Te funkcje obliczają średnią, odchylenie standardowe, minimum i maksimum dla podanych danych. Bardzo przydatnym narzędziem jest możliwość ładowania plików z danymi w formacie tekstowym z wartościami oddzielonymi spacjami
data = numpy.loadtxt('plik.txt')
Automatycznie zwracana jest tablica o takim kształcie jaki miały dane. Linie zaczynające się od znaku # traktowane są jako komentarze i są ignorowane. Oczywiście te domyśle zachowania można modyfikować i podpowiedzieć funkcji inne ustawienia
data = numpy.loadtxt('plik.csv', delimiter=',', comments='%', skiprows=3)
co oznacza, że dane są oddzielone przecinkiem, komentarze oznaczone % i dodatkowo pierwsze trzy linie pliku należy pominąć (np. zawierają nagłówek).