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).

CC BY-SA 4.0 Krzysztof Miernik. Last modified: December 07, 2023. Website built with Franklin.jl and the Julia programming language.