Praktyczne przykłady NumPy: przydatne techniki
Biblioteka Numpy to biblioteka Python używana do obliczeń naukowych. Zapewnia wielowymiarowy obiekt tablicy do przechowywania i analizy danych na wiele różnych sposobów. W tym samouczku zobaczysz przykłady niektórych funkcji, które zapewnia Numpy, które nie zawsze są podkreślane w innych samouczkach. Będziesz także miał okazję ćwiczyć swoje nowe umiejętności z różnymi ćwiczeniami.
W tym samouczku nauczysz się:
- Utwórz tablice wielowymiarowe z danych przechowywanych w plikach
- Zidentyfikuj i usuń zduplikowane dane z tablicy Numpy
- Użyj ustrukturyzowanych tablic NumPy, aby pogodzić różnice między zbiorami danych
- Analizuj i sporządzaj wykresy określonych części hierarchicznych danych
- Utwórz wektoryzowane wersje własnych funkcji
Jeśli dopiero zaczynasz korzystać z NumPy, dobrym pomysłem jest zapoznanie się z podstawami analityki danych w Pythonie, zanim zaczniesz. W tym samouczku będziesz także używać Matplotlib do tworzenia wykresów. Chociaż nie jest to konieczne, wcześniejsze zapoznanie się z Matplotlibem może być korzystne.
Konfigurowanie środowiska pracy
Zanim zaczniesz ten samouczek, musisz wykonać wstępną konfigurację. Oprócz Numpy musisz zainstalować bibliotekę Matplotlib, której użyjesz do wykresu danych. Będziesz także używać biblioteki Python Pathlib
, aby uzyskać dostęp do systemu plików komputera, ale nie ma potrzeby instalowania PathLib
, ponieważ jest to część standardowej biblioteki Pythona.
Możesz rozważyć użycie środowiska wirtualnego, aby upewnić się, że konfiguracja tutoriala nie koliduje z niczym w istniejącym środowisku Pythona.
Kolejna przydatna opcja, użycie notebooka Jupyter w JUPYTERLAB do uruchomienia kodu zamiast reprezentacji Python. Pozwala eksperymentować i dokumentować swoje ustalenia, a także szybko przeglądać i edytować pliki. Wersja do pobrania kodu i rozwiązań ćwiczeń przedstawiono w formacie notebooka Jupyter.
Polecenia do konfigurowania rzeczy na wspólnych platformach pokazano poniżej:
Pierwszy fragment kodu powinien być używany w systemie Windows, a drugi fragment kodu jest dla Linux + MacOS:
Uprzeprzecztuj terminal (admin)
Wymaga, w zależności od używanej wersji systemu Windows. Teraz wpisz następujące polecenia:
PS> python -m venv venv\
PS> venv\Scripts\activate
(venv) PS> python -m pip install numpy matplotlib jupyterlab
(venv) PS> jupyter lab
Tutaj tworzysz środowisko wirtualne o nazwie venv\
, które następnie aktywujesz. Jeśli aktywacja zakończy się pomyślnie, nazwa środowiska wirtualnego będzie poprzedzać monit PowerShell. Następnie instalujesz numpy
i matplotlib
w tym środowisku wirtualnym, a następnie opcjonalny jupyterlab
. Na koniec uruchamiasz JupyterLab.
Uwaga: po aktywowaniu środowiska wirtualnego może pojawić się błąd informujący, że system nie może uruchomić skryptu. Nowoczesne wersje systemu Windows nie pozwalają na uruchamianie skryptów pobranych z Internetu ze względów bezpieczeństwa.
Aby to naprawić, musisz wpisać polecenie Set-ExecutionPolicy RemoteSigned
, a następnie odpowiedzieć Y
na pytanie. Na Twoim komputerze będą teraz uruchamiane skrypty zweryfikowane przez firmę Microsoft. Gdy już to zrobisz, polecenie venv\Scripts\activate
powinno działać.
Uprzepustuj terminal i wpisz następujące polecenia:
$ python -m venv venv/
$ source venv/bin/activate
(venv) $ python -m pip install numpy matplotlib jupyterlab
(venv) $ jupyter lab
Tutaj tworzysz środowisko wirtualne o nazwie venv/
, które następnie aktywujesz. Jeśli aktywacja się powiedzie, nazwa środowiska wirtualnego pojawi się przed wierszem poleceń. Następnie instalujesz numpy
i matplotlib
w tym środowisku wirtualnym, a następnie opcjonalny jupyterlab
. Na koniec uruchamiasz JupyterLab.
Zauważysz, że monit jest poprzedzony
Teraz, gdy już wszystko skonfigurowałeś, czas rozpocząć główną część swojej podróży edukacyjnej.
NumPy Przykład 1: Tworzenie tablic wielowymiarowych z plików
Podczas tworzenia tablicy Numpy tworzysz wysoce zoptymalizowaną strukturę danych. Jednym z powodów jest to, że tablica Numpy przechowuje wszystkie swoje elementy w ciągłym obszarze pamięci. Ta technika zarządzania pamięcią oznacza, że dane są przechowywane w tym samym regionie pamięci, co sprawia, że czasy dostępu są szybkie. Jest to oczywiście wysoce pożądane, ale problem występuje, gdy trzeba rozszerzyć tablicę.
Załóżmy, że musisz zaimportować wiele plików do wielowymiarowej tablicy. Możesz je odczytać w osobnych tablicach, a następnie połączyć za pomocą np.concatenate()
. Utworzy to jednak kopię oryginalnej tablicy przed rozszerzeniem kopii o dodatkowe dane. Kopiowanie jest niezbędne, aby upewnić się, że zaktualizowana tablica będzie nadal istniała przy użyciu pamięci, ponieważ oryginalna tablica mogła mieć przylegające do niej niezwiązane treści.
Ciągłe kopiowanie tablic za każdym razem, gdy dodajesz nowe dane z pliku, może spowolnić przetwarzanie i powodować marnotrawstwo pamięci systemu. Problem staje się gorszy, im więcej danych dodasz do tablicy. Chociaż ten proces kopiowania jest wbudowany w NumPy, możesz zminimalizować jego skutki, wykonując te dwa kroki:
Podczas konfigurowania początkowej tablicy ustal, jak duża musi być przed jego zaludnieniem. Możesz nawet zastanowić się nad jego rozmiarem w celu obsługi przyszłych dodatków danych. Gdy znasz te rozmiary, możesz utworzyć swoją tablicę z góry.
Drugim krokiem jest wypełnienie go danymi źródłowymi. Dane te zostaną umieszczone w istniejącej tablicy bez konieczności jej rozszerzania.
Następnie dowiesz się, jak wypełnić trójwymiarową tablicę NumPy.
Wypełnianie tablic danymi pliku
W tym pierwszym przykładzie użyjesz danych z trzech plików, aby wypełnić trójwymiarową tablicę. Zawartość każdego pliku jest pokazana poniżej, a także znajdziesz te pliki w materiałach do pobrania:
Pierwszy plik ma dwa wiersze i trzy kolumny o następującej treści:
1.1, 1.2, 1.3
1.4, 1.5, 1.6
Ten drugi plik, który również ma te same wymiary, zawiera to:
2.1, 2.2, 2.3
2.4, 2.5, 2.6
Trzeci plik, ponownie z tymi samymi wymiarami, przechowuje te liczby:
3.1, 3.2, 3.3
3.4, 3.5, 3.6
Zanim będziesz kontynuować, dodaj te trzy pliki do folderu programu. Materiały do pobrania zawierają także dwa pliki o nazwach file10.csv
i file11.csv
, z którymi będziesz pracować później.
Poniższy diagram przedstawia wynikową tablicę NumPy, którą utworzysz z trzech plików:
Jak widać, file1.csv
tworzy front tablicy, file2.csv
środkowa sekcja i file3.csv
znajduje się na z powrotem.
Kod używany do utworzenia tej tablicy pokazano poniżej. Przed uruchomieniem tego kodu upewnij się, że utworzyłeś trzy pliki pokazane na schemacie lub użyj wersji podanych w materiałach do pobrania. Tak czy inaczej, umieść je w tym samym katalogu, w którym uruchamiasz kod, a następnie uruchom go:
>>> from pathlib import Path
>>> import numpy as np
>>> array = np.zeros((3, 2, 3))
>>> print(id(array))
2250027286128
>>> for file_count, csv_file in enumerate(Path.cwd().glob("file?.csv")):
... array[file_count] = np.loadtxt(csv_file.name, delimiter=",")
...
>>> print(id(array))
2250027286128
>>> print(array.shape)
(3, 2, 3)
>>> array
array([[[1.1, 1.2, 1.3],
[1.4, 1.5, 1.6]],
[[2.1, 2.2, 2.3],
[2.4, 2.5, 2.6]],
[[3.1, 3.2, 3.3],
[3.4, 3.5, 3.6]]])
Na początek możesz przejrzeć każdy z plików i użyć informacji, aby określić ostateczny kształt oczekiwanej tablicy. W tym przykładzie wszystkie trzy pliki mają taką samą liczbę elementów ułożonych w dwóch wierszy i trzech kolumnach. Powstała tablica będzie miała właściwość kształtu (3, 2, 3)
. Spójrz na schemat, a będziesz mógł to zobaczyć.
Linie 1 i 2 pokazują bibliotekę Numpy importowaną ze standardowym aliasem np
, a także klasą ścieżki importowanej z biblioteki ścieżki
. Ta biblioteka pozwala Pythonowi uzyskać dostęp do systemu plików komputera za pomocą podejścia zorientowanego na obiekt. Obiekty klasy ścieżki
pozwalają na określenie ścieżki pliku, a także zawierać metody, dzięki czemu można wykonywać wywołania systemu do systemu operacyjnego. Ta funkcja jest używana później w kodzie.
Nauczyłeś się wcześniej, jak dobrym pomysłem jest utworzenie z góry tablicy, zanim zaczniesz zapełniać ją danymi w celu zmniejszenia śladu pamięci. W wierszu 4 tworzysz tablicę zawierającą zerę o kształcie (3, 2, 3)
, jak określono wcześniej.
Następnie wypełniasz tablicę danymi z plików. Tworzysz pętlę for
za pomocą wbudowanej w Pythona funkcji enumerate
pokazanej w liniach 8 i 9. Jej parametr pozwala na zapętlenie zestawu plików i zwrócenie referencji do każdego jeden. Rejestruje również liczbę napotkanych plików. Każde odwołanie do pliku jest przechowywane w zmiennej csv_file
, natomiast licznik rosnący jest przechowywany w zmiennej file_count
.
Aby uzyskać dostęp do każdego z trzech plików .csv
, użyj Path
. Wywołując Path.cwd()
, mówisz Pythonowi, aby poszukał plików w bieżącym katalogu roboczym. Innymi słowy, katalog, z którego uruchamiasz program. Zwraca to obiekt Path
reprezentujący bieżący katalog, z którego wywołujesz metodę .glob()
w celu określenia nazw plików, do których chcesz uzyskać dostęp.
W takim przypadku, ponieważ chcesz uzyskać dostęp do plików o nazwie file1.csv, file2.csv
i file3 ? .CSV
. To informuje .Glob()
, aby wybrać tylko pliki, których nazwy muszą dopasować te dokładne znaki, ale których piąty znak może być dowolnym znakiem, jak określono znakiem dzikiego karty (
Niestety .Glob()
może nie zwracać plików w oczekiwanej kolejności. W tym przykładzie wszystko działa zgodnie z oczekiwaniami, ponieważ nazwa każdego pliku zawiera pojedynczą cyfrę jako piątą postać. Gdyby istniał plik o nazwie file11.csv
, zostałby odczytany w niewłaściwej kolejności. Dowiesz się więcej o tym, dlaczego tak jest i jak to rozwiązać później.
Uwaga: Podczas korzystania z systemu Windows, .Glob()
wyznaczy pliki posortowane leksykograficznie. Niekoniecznie tak jest w przypadku innych systemów operacyjnych. Jeśli polegasz na sortowanym plikach, powinieneś również wyraźnie wywołać sorted()
:
>>> for csv_file in sorted(Path.cwd().glob("file?.csv")):
... print(csv_file.name)
...
file1.csv
file2.csv
file3.csv
Jak zobaczysz, ten rodzaj sortowania nie zawsze jest wystarczający podczas pracy z ponumerowanymi plikami.
Za każdym razem, gdy pętla się zapętli, wywołujesz funkcję np.loadtxt()
i przekazujesz jej nazwę pliku, która jest określona przy użyciu jej właściwości name
. Każ mu także użyć przecinka (,
) jako separatora pól, aby umożliwić oddzielenie poszczególnych liczb w pliku. Zawartość każdego pliku jest następnie przypisana do utworzonej wcześniej tablicy
.
Aby mieć pewność, że zawartość każdego pliku jest wstawiona we właściwej pozycji wzdłuż osi 0
, użyj array[file_count]
. Przy pierwszym wykonaniu pętli zawartość file1.csv
zostanie przypisana do array[0]
, czyli pozycji 0 wzdłuż osi-0. Następna iteracja w pętli przypisze file2.csv
do array[1]
wzdłuż tej osi, zanim file3.csv
zostanie przypisany do tablica[2]
. Spójrz jeszcze raz na diagram, a zobaczysz dokładnie, co się stało.
W liniach 5 i 11 wydrukowałeś wynik id(array)
. Funkcja id()
zwraca tożsamość obiektu. Każdy obiekt ma unikalną wartość identyfikacyjną, ponieważ każdy obiekt zajmuje unikalne miejsce w pamięci komputera. Kiedy uruchomisz kod na swoim komputerze, liczby również będą identyczne, ale prawdopodobnie będą się różnić od pokazanych.
W liniach 6 i 12 wyświetlone wartości tożsamości dowodzą, że obiekt array
, który na początku zawierał tylko zera, jest tym samym obiektem tablicowym, który później zawierał zawartość każdego pliku. To pokazuje, że przez cały czas używany był tylko jeden obiekt i że pamięć była wykorzystywana efektywnie.
Tworząc w ten sposób tablice, warto zadbać o to, aby każdy z plików wejściowych miał taką samą liczbę wierszy i kolumn elementów. Następnie dowiesz się, jak postępować w sytuacjach, gdy pliki danych nie są aż tak jednolite.
Radzenie sobie z różnymi rozmiarami danych
Na początek dodasz kilka niewymiarowych danych. Znajdziesz go w pliku o nazwie short_file.csv
, który ma tylko jeden wiersz:
4.1, 4.2, 4.3
Chcesz go dodać z tyłu tablicy, jak pokazano poniżej:
Przed uruchomieniem kodu używanego do utworzenia tej drugiej tablicy, upewnij się, że pobierasz lub dodaj plik o nazwie short_file.csv
do tego samego katalogu, który uruchamiasz kod:
>>> array = np.zeros((4, 2, 3))
>>> for file_count, csv_file in enumerate(Path.cwd().glob("file?.csv")):
... array[file_count] = np.loadtxt(csv_file.name, delimiter=",")
...
>>> array[3, 0] = np.loadtxt("short_file.csv", delimiter=",")
>>> array
array([[[1.1, 1.2, 1.3],
[1.4, 1.5, 1.6]],
[[2.1, 2.2, 2.3],
[2.4, 2.5, 2.6]],
[[3.1, 3.2, 3.3],
[3.4, 3.5, 3.6]],
[[4.1, 4.2, 4.3],
[0. , 0. , 0. ]]])
Tym razem czytasz w czterech osobnych plikach, więc tablica, którą początkowo utworzysz, będzie miała kształt (4, 2, 3)
. Potrzebujesz dodatkowego wymiaru wzdłuż Axis-0, aby pomieścić czwarty plik, więc utworzysz to w wierszu 1.
Pętla dla For służy do odczytu pierwszych trzech plików jak poprzednio. Aby przeczytać w krótkim pliku, musisz powiedzieć Python dokładnie, gdzie chcesz umieścić go w swojej tablicy. Wcześniej zrobiłeś to, wskazując pozycję, taką jak tablica [2]
, aby wstawić dane do pozycji 2 wzdłuż osi-0. Takie podejście działało, ponieważ wprowadzone dane wypełniły istniejącą tablicę już na tej pozycji. Jednak tym razem sprawy są inaczej.
Aby powiedzieć Pythonowi, że chcesz wstawić krótki plik zaczynający się od góry pozycji indeksu 3 w osi 2, użyj array[3, 0]
. 3
reprezentuje pozycję osi 2 jak poprzednio, ale musisz także podać 0
, aby wskazać, że dane powinny zostać wstawione zaczynając od wiersza 0. Możesz rzucić okiem w liniach 18 i 19 wyjścia programu, a następnie na diagramie, aby zobaczyć, gdzie zostały umieszczone dane.
Tak jak poprzednio, obiekt tablicowy utworzony na początku kodu jest jedynym używanym w całym kodzie. Chociaż Twój kod dodał do niego dane kilka razy, nie było potrzebne nieefektywne kopiowanie, ponieważ tablicę utworzyłeś od razu.
Załóżmy, że czwarty plik zamiast być za krótki, był za długi. Być może zastanawiasz się, jak sobie poradzić z takimi plikami, co może być problematyczne. Tym razem użyjesz pliku o nazwie long_file.csv
, który zawiera dodatkowy wiersz:
4.1, 4.2, 4.3
4.4, 4.5, 4.6
4.7, 4.8, 4.9
Teraz chcesz włączyć go do swojej tablicy w pozycji pokazanej poniżej. Jak widać na schemacie, reszta tablicy będzie musiała zostać przedłużona o dodatkowy rząd, aby go pomieścić:
Kod używany do utworzenia tej trzeciej tablicy pokazano poniżej. Przed uruchomieniem upewnij się, że pobieraj lub utworzył plik o nazwie
>>> array = np.zeros((4, 2, 3))
>>> print(id(array))
2250027278352
>>> for file_count, csv_file in enumerate(Path.cwd().glob("file?.csv")):
... array[file_count] = np.loadtxt(csv_file.name, delimiter=",")
...
>>> array = np.insert(arr=array, obj=2, values=0, axis=1)
>>> array[3] = np.loadtxt("long_file.csv", delimiter=",")
>>> print(id(array))
2250027286224
>>> array
array([[[1.1, 1.2, 1.3],
[1.4, 1.5, 1.6],
[0. , 0. , 0. ]],
[[2.1, 2.2, 2.3],
[2.4, 2.5, 2.6],
[0. , 0. , 0. ]],
[[3.1, 3.2, 3.3],
[3.4, 3.5, 3.6],
[0. , 0. , 0. ]],
[[4.1, 4.2, 4.3],
[4.4, 4.5, 4.6],
[4.7, 4.8, 4.9]]])
Tym razem, ponieważ oryginalna tablica jest za krótka, aby pomieścić zawartość long_file.csv
, musisz ją wydłużyć o dodatkowy wiersz wzdłuż osi-1. Następnie możesz dodać zawartość pliku long_file.csv
. Robisz to w linii 9, używając funkcji np.insert()
, która umożliwia wstawianie wartości wzdłuż osi.
Do np.insert()
przekazujesz cztery parametry. Parametr arr
określa tablicę, do której chcesz wstawić wartości, ustawiając obj
na 2
, axis
na 1
, a parametr values
na 0
, umożliwia wstawienie wartości 0
na pozycję indeksu 2
wzdłuż osi 1
. Innymi słowy, wzdłuż dolnego rzędu tablicy, jak pokazano na schemacie. Na koniec, aby dodać zawartość long_file.csv
do swojej tablicy, ponownie użyj funkcji loadtxt()
, jak pokazano w linii 11.
Poświęć chwilę, aby spojrzeć na diagram i wynikową tablicę utworzoną przez Twój kod, a zobaczysz, że wszystko działa zgodnie z oczekiwaniami.
Zwróć uwagę, że wiersze 4 i 14 wskazują, że obiekty tablicy przed i po włożeniu nowych danych są różne. Dzieje się tak, ponieważ funkcja insert()
zwraca kopię oryginalnej tablicy. Aby uniknąć tego marnotrawstwa pamięci, naprawdę dobrym pomysłem jest upewnienie się, że prawidłowe rozmiar początkowej tablicy przed zaczniesz jej wypełnianie.
Upewnienie się, że zamówienie pliku jest prawidłowe
Po uruchomieniu kodu Path.cwd().glob("file?.csv")
zwracany jest iterator generatora, którego można użyć do przeglądania kolekcji WindowsPath
lub PosixPath
. Każdy z nich reprezentuje ścieżkę i nazwę pliku systemu operacyjnego zgodną ze wzorcem file?.csv
. Jednak kolejność, w jakiej te pliki są zwracane przez generator, nie jest taka, jakiej można się spodziewać.
Aby zobaczyć przykład tego, dodaj dwa pliki o nazwie plik 10.csv i file11.csv
z materiałów do pobrania do istniejącego folderu:
10.1,10.2,10.3
10.4,10.5,10.6
Prawdopodobnie już odgadłeś, co było w
11.1,11.2,11.3
11.4,11.5,11.6
Teraz uruchom następujący kod:
>>> for csv_file in Path.cwd().glob("file*.csv"):
... print(csv_file.name)
...
file1.csv
file10.csv
file11.csv
file2.csv
file3.csv
Aby mieć pewność, że generator zobaczy każdy z tych dodatkowych plików, musisz dostosować kryteria dopasowania do file*.csv
. Znak wieloznaczny (*
) reprezentuje dowolną liczbę nieznanych znaków. Jeśli pozostawiono znak wieloznaczny (?
), wówczas uwzględnione zostałyby tylko pliki zawierające jeden znak po ciągu file
. Nawet po tym dodatku coś nadal wygląda nie tak.
Uwaga: w systemach Linux i macOS może być konieczne dodanie wywołania funkcji sorted()
, aby wyświetlić pliki uporządkowane jak powyżej.
Zobaczysz, że nowe pliki są umieszczane pomiędzy file1.csv
i file2.csv
, a nie, jak prawdopodobnie oczekiwałeś, na końcu. Powodem tego jest to, że nazwy plików są sortowane alfanumerycznie. Oznacza to, że ten rodzaj odczytuje nazwy plików od lewej do prawej i uważa wszystko równe, aż znajdzie różnicę. Gdy to zrobi, sort oparty jest na tej różnicy.
Na przykład, gdy analizowane są znaki w nazwie każdego pliku, wszystko jest uważane za równe w ciągu pierwszych czterech znaków nazwy każdego pliku — w tym przypadku plik
. Następnie Python musi zdecydować, który z piątych znaków pojawi się jako pierwszy. Robi to, biorąc pod uwagę numery kodów znaków Unicode każdego z nich.
Kod znaku Unicode dla znaku 1
to 49, natomiast kody dla 2
i 3
to odpowiednio 50 i 51. W rezultacie każda nazwa pliku zawierająca 1
jako piąty znak zostanie posortowana wcześniej niż nazwa pliku zawierająca 2
lub 3
na tej samej pozycji.
W przypadku file1.csv
, file10.csv
i file11.csv
piąty znak każdej nazwy pliku jest taki sam. Dlatego o kolejności sortowania zadecyduje ich szósty znak. Biorąc pod uwagę ich wartości znaków Unicode, kropka (.) ma wartość 46, która występuje przed znakami 0
i 1
, których wartości wynoszą odpowiednio 48 i 49 . W rezultacie kolejność sortowania będzie następująca: file1.csv
, następnie file10.csv
, a następnie file11.csv
.
Możesz mieć ochotę na funkcję wbudowanej
>>> for csv_file in sorted(Path.cwd().glob("file*.csv")):
... print(csv_file.name)
...
file1.csv
file10.csv
file11.csv
file2.csv
file3.csv
Jednakże funkcja sorted()
dała ten sam niepożądany wynik, co poprzednio.
Aby czytać pliki w bardziej naturalnej kolejności, możesz skorzystać z biblioteki natsort
. Najpierw zainstaluj go, uruchamiając polecenie python -m pip install natsort
. Po instalacji możesz zaimportować funkcję natsorted()
i używać jej zamiast wbudowanej funkcji sorted()
, aby uzyskać bardziej naturalne sortowanie plików. Poniżej znajduje się kod ilustrujący to:
>>> from natsort import natsorted
>>> for csv_file in natsorted(Path.cwd().glob("file*.csv")):
... print(csv_file.name)
...
file1.csv
file2.csv
file3.csv
file10.csv
file11.csv
W końcu udało Ci się rozwiązać problem z sortowaniem nazw plików. Możesz teraz pójść o krok dalej ze swoim wcześniejszym kodem i dodać zawartość pliku w odpowiednich miejscach tablicy:
>>> array = np.zeros((5, 2, 3))
>>> for file_count, csv_file in enumerate(
... natsorted(Path.cwd().glob("file*.csv"))
... ):
... array[file_count] = np.loadtxt(csv_file.name, delimiter=",")
...
>>> array
array([[[ 1.1, 1.2, 1.3],
[ 1.4, 1.5, 1.6]],
[[ 2.1, 2.2, 2.3],
[ 2.4, 2.5, 2.6]],
[[ 3.1, 3.2, 3.3],
[ 3.4, 3.5, 3.6]],
[[10.1, 10.2, 10.3],
[10.4, 10.5, 10.6]],
[[11.1, 11.2, 11.3],
[11.4, 11.5, 11.6]]])
Tym razem przekazujesz różne ścieżki plików do funkcji natsorted()
, która posortuje je w sposób, jaki prawdopodobnie zamierzyłeś. Dane wyjściowe pokazują, że zawartość zarówno file10.csv
, jak i file11.csv
znajduje się teraz w odpowiednich miejscach w tablicach. Ponownie zwróć uwagę, jak użyto tutaj operatora wieloznacznego (*
). Ponadto wymiary tej wersji array
zostały zwiększone do (5, 2, 3)
, aby zapewnić miejsce na nowe dane.
Uwaga: Wspólna sztuczka, aby upewnić się, że ponumerowane pliki są obsługiwane w kolejności, jest wybicie liczb za pomocą wiodących zer. Jeśli pliki zostały nazwane file01.csv
, file02.csv
, file03.csv
, file10.csv
, oraz file11.csv
, wówczas sorted()
byłby w stanie sortować nazwy plików.
Jak widać, całkowicie możliwe jest konstruowanie tablic Numpy z danych w plikach. Musisz jednak upewnić się, że wzięto pod uwagę różne rozmiary.
Przed przejściem zakończysz ćwiczenie, aby przetestować swoje zrozumienie. Jest to pierwsze z kilku ćwiczeń zawartych w tym samouczku, które pomogą Ci skonsolidować naukę.
Testowanie swoich umiejętności: wypełnianie tablic danymi plików
Nadszedł czas, abyś sprawdził swoją wiedzę na temat tworzenia tablic z danych plikowych. Sprawdź, czy dasz radę rozwiązać swoje pierwsze wyzwanie pokazane poniżej:
Znajdź plik ex1a.csv
, ex1b.csv
i ex1c.csv
w materiałach do pobrania. Teraz zastosuj techniki, które nauczyłeś się tworzyć poprawnie trójwymiarową tablicę, która pozwoli na dodanie każdego pliku. Treść każdego pliku powinien zostać włożona tak, aby dotykał lewego górnego rogu oddzielnego indeksu wzdłuż osi 0. Powinieneś wstawić zawartość pliku ex1b.csv
jako wiersz, a zawartość ex1c.csv
jako kolumna.
Jednym z możliwych rozwiązań jest:
>>> import numpy as np
>>> from pathlib import Path
>>> array = np.zeros((3, 4, 4), dtype="i8")
>>> array[0] = np.loadtxt("ex1a.csv", delimiter=",")
>>> narrow_array = np.loadtxt("ex1b.csv", delimiter=",")
>>> narrow_array = np.insert(arr=narrow_array, values=0, obj=3, axis=0)
>>> array[1, 0] = narrow_array
>>> short_array = np.loadtxt("ex1c.csv", delimiter=",")
>>> short_array = np.insert(arr=short_array, values=0, obj=3, axis=0)
>>> array[2, :, 0] = short_array
>>> array
array([[[ 5, 10, 15, 20],
[25, 30, 35, 40],
[45, 50, 55, 60],
[65, 70, 75, 80]],
[[ 1, 3, 5, 0],
[ 0, 0, 0, 0],
[ 0, 0, 0, 0],
[ 0, 0, 0, 0]],
[[ 2, 0, 0, 0],
[ 4, 0, 0, 0],
[ 6, 0, 0, 0],
[ 0, 0, 0, 0]]])
Najpierw przeglądasz pliki i widzisz, że tablica o rozmiarze (3, 4, 4)
jest niezbędna, aby zapewnić dopasowanie danych. Następnie tworzysz tablicę zer o tym rozmiarze i określasz, że będą w niej przechowywane liczby całkowite. Następnie przypisujesz ją do zmiennej array
przed wypełnieniem array[0]
, czyli osi-0, zawartością pliku ex1a.csv
, który dobrze dopasowuje się do kształtu.
Plik ex1b.csv
ma tylko trzy wartości, co oznacza, że jest zbyt krótki, aby można go było bezpośrednio wstawić do tablicy [1]
. Aby to naprawić, najpierw odczytasz go w np.insert()
, aby dodać dodatkowy 0
do pozycji indeksu 3, Zgodnie z definicją jego wartości i obj
parametry. Na koniec wstawiasz wąb_array
do górnego wiersza tablica
w indeksie 1
wzdłuż jego 0-osi za pomocą tablicy [1, 0]
.
Plik ex1c.csv
również ma tylko trzy wartości. Oznacza to, że musisz dopasować go do kolumny, więc dodasz dodatkowe zero na jej końcu. Tym razem, aby dodać short_array
, użyjesz array[2, :, 0]
. Zostanie wstawiony w pozycji indeksu 2
wzdłuż osi 0, ale :, 0
oznacza, że zostanie wstawiony w dół w pierwszej kolejności kolumnie, a nie w rzędzie.
Zamiast rozszerzyć przeczytane tablice, możesz być bardziej szczegółowy, gdzie umieszczasz wartości. Na przykład możesz wstawić Short_array
do tablicy [2, 0: 3, 0]
Bezpośrednio przed włożeniem dodatkowego
W następnej sekcji dowiesz się trochę o tablicach strukturalnych i o tym, jak można ich używać do uzgadniania różnic między różnymi tablicami.
Numpy Przykład 2: uzgadnianie danych przy użyciu tablic ustrukturyzowanych
Dzięki tablicom Numpy utworzonym w poprzedniej sekcji nie było możliwości poznania znaczenia danych każdej kolumny. Czy nie byłoby miło, gdybyś mógł odwołać się do określonych kolumn według znaczących nazw zamiast numerów indeksu? Na przykład zamiast używać student_grades=wyniki [:, 1]
, możesz zamiast tego użyć student_grades=wyniki [„egzamin_grade”]
. Dobra wiadomość! Możesz to zrobić, tworząc strukturę.
Tworzenie tablicy strukturalnej
A struktura to tablica Numpy z typem danych złożonym z zestawu krotek, z których każdy zawiera nazwę pola i zwykłego typu danych. Po ich zdefiniowaniu możesz uzyskać dostęp i zmodyfikować każde poszczególne pole, używając jego nazwy pola.
Poniższy kod zawiera przykład tworzenia i odwołania się do ustrukturyzowanej tablicy Numpy:
>>> import numpy as np
>>> race_results = np.array(
... [
... ("At The Back", 1.2, 3),
... ("Fast Eddie", 1.3, 1),
... ("Almost There", 1.1, 2),
... ],
... dtype=[
... ("horse_name", "U12"),
... ("price", "f4"),
... ("position", "i4"),
... ],
... )
>>> race_results["horse_name"]
array(['At The Back', 'Fast Eddie', 'Almost There'], dtype='<U12')
>>> np.sort(race_results, order="position")[
... ["horse_name", "price"]
... ]
array([('Fast Eddie', 1.3), ('Almost There', 1.1), ('At The Back', 1.2)],
dtype={'names': ['horse_name', 'price'],
⮑ 'formats': ['<U12', '<f4'],
⮑ 'offsets': [0, 48], 'itemsize': 56})
>>> race_results[race_results["position"] == 1]["horse_name"]
array(['Fast Eddie'], dtype='<U12')
Tablica strukturalna jest zdefiniowana w liniach od 3 do 14, ale zacznij od linii od 4 do 8, które definiują coś, co wygląda jak normalna tablica NumPy. Składa się z trzech wierszy i trzech kolumn danych dotyczących wyścigu konnego. Możesz łatwo wybrać imiona koni z danych, ale możesz mieć trudności ze zrozumieniem, co oznaczają pozostałe dwie liczby.
Ta tablica jest w rzeczywistości tablicą strukturalną ze względu na definicję jej typów danych w wierszu 9. Każdy typ danych składa się z nazwy pola i powiązanego typu danych. Trzy pola to nazwa_napie
, pozycja
. Powiązane typy danych są definiowane przy użyciu kodów protokołów interfejsu tablicy. f4
i i4
Określ 4
-Byte Floating-Point i formaty liczb całkowitych odpowiednio.
Po skonfigurowaniu tablicy strukturalnej możesz używać tych nazw pól do odwoływania się do kolumn. W linii 16 użyłeś "horse_name"
do wyświetlenia tablicy imion koni wyścigowych. Aby znaleźć kolejność kończenia, przekazałeś pole "position"
do funkcji np.sort()
w linii 19. To posortowało biegaczy według kolejności ich kończenia. Następnie przefiltrowałeś dane wyjściowe, aby wyświetlić tylko nazwa_konia
i cenę
. Na koniec w linii 27 wybrałeś imię zwycięskiego konia.
Pogodzenie różnych tablic
Włączenie nazw terenów do tablic Numpy ma wiele przydatnych celów. Załóżmy, że chciałeś dopasować rekordy, dopasowując nazwy pola w osobnych tablicach Numpy. Aby to zrobić, dołączasz do swoich tablic, aby wyświetlano tylko dopasowanie rekordów z każdej tablicy. Ten pomysł będzie ci znany, jeśli kiedykolwiek wykonałeś wewnętrzny łącznik SQL między dwoma relacyjnymi tabelami bazy danych. Termin wewnętrzny służy do zdefiniowania połączenia używanego tutaj.
W tej sekcji będziesz pracować z dwoma nowymi plikami: clashed_checks.csv .
Plik emised_checks.csv
zawiera cztery pola: check_id
, payee
, kwota
i data_ise_issued . Ten plik symuluje zestaw czeków wydanych przez Twoją firmę do wierzycieli:
Check_ID,Payee,Amount,Date_Issued
1341,K Starmer,150.00,2024-03-29
1342,R Sunak,175.00,2024-03-29
1343,L Truss,30.00,2024-03-29
1344,B Johnson,45.00,2024-03-22
1345,T May,65.00,2024-03-22
1346,D Cameron,430.00,2024-03-22
1347,G Brown,100.00,2024-03-15
1348,T Blair,250.00,2024-03-15
1349,J Major,500.00,2024-03-15
1350,M Thatcher,220.00,2024-03-15
Plik cashed_checks.csv
zawiera tylko trzy pola: check_ID
, Amount
i Date_Cashed
. Ten plik symuluje zestaw czeków zrealizowanych przez wierzycieli Twojej firmy:
Check_ID,Amount,Date_Cashed
1341,150.00,2024-04-12
1342,175.00,2024-04-16
1343,30.00,2024-04-12
1345,65.00,2024-04-12
1346,430.00,2024-04-08
1349,500.00,2024-04-08
1350,220.00,2024-04-15
Załóżmy, że chcesz zobaczyć odbiorcę płatności
, date_issued
i date_cashed
dla czeków, które zostały zrealizowane. Jeśli przyjrzysz się uważnie, zobaczysz, że szczegóły Payee
i Date_Issued
nie są zawarte w pliku cashed_checks.csv
, podczas gdy pozostałe dwa pola są. Aby zobaczyć wszystkie potrzebne dane, musisz połączyć oba pliki.
Dodaj pliki issued_checks.csv
i cashed_checks.csv
do folderu swojego programu, a następnie uruchom ten kod:
>>> import numpy.lib.recfunctions as rfn
>>> from pathlib import Path
>>> issued_dtypes = [
... ("id", "i8"),
... ("payee", "U10"),
... ("amount", "f8"),
... ("date_issued", "U10"),
... ]
>>> cashed_dtypes = [
... ("id", "i8"),
... ("amount", "f8"),
... ("date_cashed", "U10"),
... ]
>>> issued_checks = np.loadtxt(
... Path("issued_checks.csv"),
... delimiter=",",
... dtype=issued_dtypes,
... skiprows=1,
... )
>>> cashed_checks = np.loadtxt(
... Path("cashed_checks.csv"),
... delimiter=",",
... dtype=cashed_dtypes,
... skiprows=1,
... )
>>> cashed_check_details = rfn.rec_join(
... "id",
... issued_checks,
... cashed_checks,
... jointype="inner",
... )
>>> cashed_check_details[
... ["payee", "date_issued", "date_cashed"]
... ]
array([('K Starmer', '2024-03-29', '2024-04-12'),
('R Sunak', '2024-03-29', '2024-04-16'),
('L Truss', '2024-03-29', '2024-04-12'),
('T May', '2024-03-22', '2024-04-12'),
('D Cameron', '2024-03-22', '2024-04-08'),
('J Major', '2024-03-15', '2024-04-08'),
('M Thatcher', '2024-03-15', '2024-04-15')],
dtype={'names': ['payee', 'date_issued', 'date_cashed'],
⮑'formats': ['<U10', '<U10', '<U10'],
⮑'offsets': [8, 64, 104], 'itemsize': 144})
Aby połączyć dwie tablice NumPy, użyj jednej z kilku funkcji pomocniczych recarray, które umożliwiają pracę z tablicami strukturalnymi. Aby uzyskać do nich dostęp, musisz najpierw zaimportować moduł biblioteczny numpy.lib.recfunctions
. Aby uzyskać dostęp do plików, ponownie użyjesz biblioteki pathlib
. Są importowane w liniach 1 i 2.
W wierszach od 4 do 15 tworzysz dwie listy Python krotek, które definiują typy danych, które mają być używane zarówno dla pliku cashed_checks.csv . Są one przekazywane do parametru
, aby umożliwić prawidłowe odczyt plików w tablicach strukturalnych Numpy. dType
np.loadtext()
Rzeczywiste pliki są odczytywane przez Twój kod w liniach od 17 do 29. Używasz funkcji np.loadtext()
tak jak to robiłeś wcześniej, ale tym razem ustawiasz jej parametr skiprows
na < kod>1. Dzięki temu pierwszy wiersz każdego pliku zostanie zignorowany, ponieważ każdy z nich zawiera informacje nagłówkowe, a nie dane.
Dwie tablice NumPy wczytane z plików są następnie łączone w liniach od 31 do 36 za pomocą funkcji pomocniczej .rec_join()
. Ta funkcja wykorzystuje cztery parametry. Pierwszy parametr określa pole, za pomocą którego zostaną połączone obie tablice. W tym przypadku chcesz połączyć tablice w oparciu o ich pola id
, które zawierają unikalne numery identyfikujące każde sprawdzenie w obu plikach.
Następnie przekazujesz nazwiska tablic, które należy połączyć jako drugie i trzecie parametry.
Ostateczny parametr jest najciekawszy. Określając wspólne="inner"
, wykonujesz wewnętrzne połączenie. Oznacza to, że wynikowa tablica będzie zawierać tylko dopasowane rekordy z obu plików, podając pełne dane wszystkich czeków. Wszelkie rekord, którego identyfikator pojawia się w jednym pliku, ale nie drugi nie pojawi się w wyjściu.
Aby potwierdzić, że połączenie zadziałało, spójrz na dane wyjściowe wyprodukowane przez wiersze 38 do 40. DATE_CASHED
. Pierwsze dwa z tych pól pochodziło z pliku emised_checks.csv , podczas gdy ten ostatni pochodzi z cashed_checks.csv . Jak widać, rekordy zostały poprawnie dopasowane.
Postępowanie w przypadku zduplikowanych nazw pól
Załóżmy teraz, że chcesz również wyświetlić kwotę czeku. Możesz mieć ochotę spróbować tego:
>>> cashed_check_details[
... [
... "payee",
... "date_issued",
... "date_cashed",
... "amount",
... ]
... ]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'amount'
Wystąpił KeyError
, ponieważ pole amount
już nie istnieje w połączonej tablicy. Stało się tak, ponieważ oba oryginalne pliki danych zawierały pole amount
. Nie jest możliwe utworzenie tablicy strukturalnej zawierającej dwa pola z tą samą etykietą. Operacja łączenia zmieniła ich unikalną nazwę.
Aby dowiedzieć się, jak się teraz nazywają, najpierw przejrzyj typy danych połączonej tablicy. Dostęp do nich uzyskujesz poprzez właściwość .dtype
:
>>> cashed_check_details.dtype
dtype([('id', '<i8'), ('payee', '<U10'), ('amount1', '<f8'), ('amount2', '<f8'),
⮑ ('date_issued', '<U10'), ('date_cashed', '<U10')])
Jeśli przyjrzysz się uważnie wynikom, zauważysz, że nazwy pierwotnych pól amount
zostały zmienione na amount1
i amount2
. Aby zobaczyć ich zawartość, musisz użyć tych nazw. W tym scenariuszu oba pola zawierają te same dane, więc nie ma znaczenia, które z nich wybierzesz:
>>> cashed_check_details[
... [
... "payee",
... "date_issued",
... "date_cashed",
... "amount1",
... ]
... ]
array([('K Starmer', '2024-03-29', '2024-04-12', 150.),
('R Sunak', '2024-03-29', '2024-04-16', 175.),
('L Truss', '2024-03-29', '2024-04-12', 30.),
('T May', '2024-03-22', '2024-04-12', 65.),
('D Cameron', '2024-03-22', '2024-04-08', 430.),
('J Major', '2024-03-15', '2024-04-08', 500.),
('M Thatcher', '2024-03-15', '2024-04-15', 220.)],
dtype={'names': ['payee', 'date_issued', 'date_cashed', 'amount1'],
⮑ 'formats': ['<U10', '<U10', '<U10', '<f8'],
⮑'offsets': [8, 64, 104, 48], 'itemsize': 144})
Tym razem kwoty czeków zostaną uwzględnione w wynikach.
Teraz, gdy wiesz, jakie czeki wydałeś i co zostało wypłacone, możesz chcieć zobaczyć listę czeków, które są obecnie nieistotne:
>>> outstanding_checks = [
... check_id
... for check_id in issued_checks["id"]
... if check_id not in cashed_checks["id"]
... ]
>>> outstanding_checks
[np.int64(1344), np.int64(1347), np.int64(1348)]
>>> [int(check_id) for check_id in outstanding_checks]
[1344, 1347, 1348]
W wierszu 1 tworzysz listę o nazwie clashed_checks
. Te są nadal wyjątkowe.
Ponownie, używając nazwy pola, możesz szybko określić pola do użycia do porównania i zachować wysokie kod.
Dla wygody wyodrębniasz liczby z wynikowej tablicy, używając drugiego rozumienia listowego w wierszu 10.
W celu uzyskania kompletności możesz dowiedzieć się, czy było jakieś czeki, które zostały oszukane w odniesieniu do twojego konta, którego nie wydałeś:
>>> [
... check_id
... for check_id in cashed_checks["id"]
... if check_id not in issued_checks["id"]
... ]
[]
Pusta lista pokazuje, że na szczęście nie ma. Uff!
Radzenie sobie ze zduplikowanymi wartościami kluczy
Przed dołączeniem do tablic Numpy ważne jest, aby upewnić się, że nie ma żadnych duplikatów kluczowych wartości. Mogą to powodować niepożądane wyniki, ponieważ tworzą relacje jeden do wielu. W przypadkach, w których zduplikowane klawisze są prawidłowe, na przykład, w których musisz wykonać połączenie jeden do wielu, lepszą alternatywą jest użycie funkcji Merge()
w pandy.
Poniższy kod wykorzystuje plik check_list_duplicates.csv
. Ten plik ma taką samą strukturę jak plik issued_checks.csv
, którego użyłeś wcześniej, ale zawiera zduplikowany rekord:
>>> from pathlib import Path
>>> import numpy.lib.recfunctions as rfn
>>> issued_dtypes = [
... ("id", "i8"),
... ("payee", "U10"),
... ("amount", "f8"),
... ("date_issued", "U10"),
... ]
>>> issued_checks = np.loadtxt(
... Path("check_list_duplicates.csv"),
... delimiter=",",
... dtype=issued_dtypes,
... skiprows=1,
... )
>>> rfn.find_duplicates(np.ma.asarray(issued_checks))
masked_array(data=[(1344, 'B Johnson', 45.0, '2024-03-22'),
(1344, 'B Johnson', 45.0, '2024-03-22')],
mask=[(False, False, False, False),
(False, False, False, False)],
fill_value=(999999, 'N/A', 1e+20, 'N/A'),
dtype=[('id', '<i8'), ('payee', '<U10'),
⮑ ('amount', '<f8'), ('date_issued', '<U10')])
Aby znaleźć zduplikowane wiersze w tablicy strukturalnej, możesz użyć funkcji pomocniczej .find_duplicates()
. Ta funkcja wymaga przekazania tablicy maskowanej, czyli tablicy, która może zawierać brakujące lub nieprawidłowe wpisy. Chociaż pod tym względem Twoja tablica jest w porządku, przed przekazaniem jej do funkcji musisz ją przekonwertować na tablicę maskowaną. W wierszu 19 wyniku widać zduplikowany wiersz — rekord 1344
występuje dwukrotnie.
Aby się go pozbyć, możesz użyć funkcji np.unique()
:
>>> issued_checks = np.unique(issued_checks, axis=0)
>>> issued_checks
array([(1341, 'K Starmer', 150., '2024-03-29'),
(1342, 'R Sunak', 175., '2024-03-29'),
(1343, 'L Truss', 30., '2024-03-29'),
(1344, 'B Johnson', 45., '2024-03-22'),
(1345, 'T May', 65., '2024-03-22'),
(1346, 'D Cameron', 430., '2024-03-22'),
(1347, 'G Brown', 100., '2024-03-15'),
(1348, 'T Blair', 250., '2024-03-15'),
(1349, 'J Major', 500., '2024-03-15'),
(1350, 'M Thatcher', 220., '2024-03-15')],
dtype=[('id', '<i8'), ('payee', '<U10'),
⮑ ('amount', '<f8'), ('date_issued', '<U10')])
Aby usunąć zduplikowane wiersze, przekazujesz tablicę issued_checks
wraz z axis=0
do funkcji np.unique()
, aby poinformować ją o konieczności usunięcia wierszy. Spowoduje to utworzenie nowej tablicy, a wszelkie zduplikowane wiersze zostaną usunięte, pozostawiając tylko jedną instancję. Jeśli spojrzysz na wynik, zobaczysz, że wiersz 1344
pojawia się teraz tylko raz.
Testowanie swoich umiejętności: razem dołączanie do tablic
Czas na kolejne wyzwanie. Sprawdź, czy potrafisz rozwiązać to ćwiczenie:
Linia lotnicza przechowuje dane na swoich pasażerach w dwóch osobnych plikach: Passengers.csv
i paszports.csv
plik. Spójrz na oba pliki, które znajdziesz w materiałach do pobrania, aby zapoznać się z ich treścią. Teraz użyj swoich umiejętności, aby rozwiązać następujące zadania:
Zadanie 1: Wyprodukuj ustrukturyzowaną tablicę zawierającą imię, nazwisko i narodowość każdego pasażera.
Zadanie 2: Ustal, czy na liście jest jakieś pasażerowie, którzy nie mają paszportów.
Zadanie 3: Sprawdź, czy na liście znajdują się paszporty, które nie należą do żadnego pasażera.
Jednym z możliwych rozwiązań Zadania 1 jest:
>>> import numpy as np
>>> import numpy.lib.recfunctions as rfn
>>> passenger_dtype = [
... ("passenger_no", "i8"),
... ("first_name", "U20"),
... ("last_name", "U20"),
... ]
>>> passport_dtype = [
... ("passport_no", "i8"),
... ("passenger_no", "i8"),
... ("nationality", "U20"),
... ]
>>> passengers = np.unique(
... np.loadtxt(
... "passengers.csv",
... delimiter=",",
... dtype=passenger_dtype,
... skiprows=1,
... ),
... axis=0,
... )
>>> passports = np.unique(
... np.loadtxt(
... "passports.csv",
... delimiter=",",
... dtype=passport_dtype,
... skiprows=1,
... ),
... axis=0,
... )
>>> flight_details = rfn.rec_join(
... "passenger_no",
... passengers,
... passports,
... jointype="inner",
... )
>>> flight_details[
... ["first_name", "last_name", "nationality"]
... ]
rec.array([('Olivia', 'Smith', 'British'), ('Amelia', 'Jones', 'British'),
('Isla', 'Williams', 'American'),
('Ava', 'Taylor', 'American'), ('Ivy', 'Brown', 'Austrian'),
('Freya', 'Davies', 'Norwegian'), ('Lily', 'Evans', 'French'),
('Florence', 'Wilson', 'German'), ('Mia', 'Thomas', 'Danish'),
('Willow', 'Johnson', 'Dutch'), ('Noah', 'Roberts', 'Dutch'),
('Oliver', 'Robinson', 'French'),
('George', 'Thompson', 'Danish'),
('Muhammad', 'Walker', 'Dutch'), ('Leo', 'White', 'British'),
('Harry', 'Edwards', 'American'),
('Oscar', 'Hughes', 'Spanish'),
('Archie', 'Green', 'Norwegian'), ('Henry', 'Hall', 'British')],
dtype={'names': ['first_name', 'last_name', 'nationality'],
⮑ 'formats': ['<U20', '<U20', '<U20'],
⮑ 'offsets': [8, 88, 176], 'itemsize': 256})
Zaczynasz od utworzenia dwóch list, paszport_dtype
, jak pokazano w wierszach 4 i 10. Te krotki sklepowe, które będą używane do zdefiniowania rodzajów danych każdej kolumny w układach strukturalnych, które będą przechowywać dane plików. Każdy krotek składa się z nazwy pola i typu danych. Możesz przypomnieć sobie, że u20
definiuje ciąg dwudziestu znaków.
W wierszach 16 i 26 odczytasz dane z każdego pliku do tablic paszportów i paszportów. Używasz również
np.unique()
, aby upewnić się, że zduplikowane rekordy są usuwane. Jeśli zapomnisz to zrobić, zobaczysz niektóre wartości pojawiają się w twoich wynikach. Zauważ, że pomijasz górną linię każdego pliku, ponieważ zawiera on nagłówki.
W wierszu 36 dołączasz zarówno do swoich pensjonatów , jak i Passports
tablice za pomocą pól Passenger_No jako klucza łączenia. Jest to wewnętrzne połączenie, co oznacza, że tylko pasażerowie z paszportami są zawarte w wyjściu.
W linii 43 wyświetlasz wymagane szczegóły lotu. Dane wyjściowe pokazują nazwiska pasażerów z pliku passengers.csv
i ich narodowości z pliku passports.csv
. Podejście to wykorzystuje wygodę i przejrzystość nazw pól do wyodrębniania danych.
Jednym z możliwych rozwiązania zadania 2 jest:
>>> passengers_without_passports = [
... passenger
... for passenger in passengers["passenger_no"]
... if passenger not in passports["passenger_no"]
... ]
>>> passengers_without_passports
[np.int64(14)]
Aby odpowiedzieć na to pytanie, używasz rozumienia listy, które zapętla się za pośrednictwem każdego pasager_no
w tablicy Passengers i zwraca tylko te, które nie pojawiają się w Twoim tablica paszportów
. W takim przypadku pasażer 14
nie podał szczegółów paszportu.
Jednym z możliwych rozwiązań zadania 3 jest:
>>> passports_without_passengers = [
... passenger
... for passenger in passports["passenger_no"]
... if passenger not in passengers["passenger_no"]
... ]
>>> passports_without_passengers
[np.int64(21)]
Aby odpowiedzieć na to pytanie, ponownie użyj funkcji listowej, ale tym razem przechodzi ona przez każdy passenger_no
w tablicy passports
i zwraca tylko te, które nie pojawią się w tablicy pasażerowie
. W tym przypadku ujawniany jest paszport nieistniejącego pasażera 21
.
W następnej sekcji dowiesz się, jak analizować dane hierarchiczne.
Numpy Przykład 3: Analiza i wykresy danych hierarchicznych
Dane hierarchiczne to dane składające się z różnych poziomów, przy czym każdy poziom jest powiązany z poziomami znajdującymi się bezpośrednio powyżej i poniżej. Często rysuje się go za pomocą diagramu drzewa, a różne poziomy są często opisywane jako posiadające relację rodzic-dziecko.
Na przykład możesz mieć organizację z kilkoma działami, a każdy dział zawierał kilku pracowników. Istnieje hierarchiczne relacje między organizacją, jej danymi departamentowymi i danymi pracowników pracujących w każdym dziale.
Tablice strukturalne są odpowiednie do łączenia danych hierarchicznych, ponieważ umożliwiają referencje danych według etykiet. Aby użyć Numpy z danymi hierarchicznymi, możesz skonsolidować je w jednej tablicy.
Uwaga: łącząc tablice NumPy za pomocą funkcji rec_join()
, ograniczasz się do tworzenia złączeń wewnętrznych. Jeśli potrzebujesz bardziej szczegółowych możliwości łączenia, powinieneś zamiast tego rozważyć użycie funkcji pandy merge()
.
W tej sekcji przyjrzysz się analizie portfela akcji. Portfel akcji to nazwa nadana zbiorowi akcji posiadanych w różnych spółkach. Tworzenie portfela to mądra strategia dla inwestorów, ponieważ pomaga rozłożyć ryzyko inwestycyjne. Pomysł jest taki, że straty poniesione na niektórych akcjach zostaną zrekompensowane zyskami na innych.
Twój portfel akcji zawiera dane hierarchiczne, ponieważ składa się z wielu inwestycji, z których każda ma własną kolekcję codziennych ruchów cen. Analizując te dane, możesz zobaczyć, jak dobrze radzi sobie Twój portfel.
Tworzenie pustej tablicy
Dane wykorzystane w tej sekcji symulują niektóre dane hierarchiczne. Załóżmy, że utrzymujesz plik zawierający listę różnych firm, w których przechowujesz akcje. W tym przykładzie znajdziesz te informacje w pliku portfolio.csv zawarty w plikach do pobrania. Tutaj pokazano poniżej:
Company,Sector
Company_A,technology
Company_B,finance
Company_C,healthcare
Company_D,technology
Company_E,finance
Company_F,healthcare
Kolumna Firma
zawiera nazwy firm, natomiast kolumna Sektor
zawiera sektory, do których należy dana firma.
Każdego dnia, w ciągu tygodnia, pobierasz ceny akcji dla każdej firmy, w których jesteś zainteresowany, i dodajesz je do ustrukturyzowanej tablicy Numpy o nazwie Portfolio . Każdego dnia znajdziesz dane cenowe w szeregu osobnych plików o nazwie share_prices-n.csv
, gdzie n
to liczba od jednego do pięciu. Na przykład share_prices-1.csv zawiera ceny za poniedziałek, share_prices-2.csv ceny we wtorek i tak dalej.
Przykładowy plik cen akcji pokazano poniżej:
Company,mon
Company_A,100.5
Company_B,200.1
Company_C,50.3
Company_D,110.5
Company_E,200.1
Company_F,55.3
W tym pliku share_prices-1.csv
istnieją dwie kolumny. Kolumna spółki wyświetla nazwy firm, podczas gdy kolumna mon pokazuje ceny każdej akcji spółki za poniedziałek. Reszta plików podąża za podobnym wzorem, oprócz kolumnowych kolumn, które są różne.
Aby przeanalizować te dane, tablica portfolio będzie potrzebować siedem pól . Oprócz nazwy firmy i sektora, do którego należy, potrzebujesz również pięciu pól, aby utrzymać codzienne ceny dla każdej firmy. Pierwsze dwa pola będą to struny, a reszta będą pływakami.
Kod pokazany poniżej tworzy początkową tablicę portfolio
:
>>> import numpy as np
>>> from pathlib import Path
>>> days = ["mon", "tue", "wed", "thu", "fri"]
>>> days_dtype = [(day, "f8") for day in days]
>>> company_dtype = [("company", "U20"), ("sector", "U20")]
>>> portfolio_dtype = np.dtype(company_dtype + days_dtype)
>>> portfolio = np.zeros((6,), dtype=portfolio_dtype)
>>> portfolio
array([('', '', 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.), ('', '', 0., 0., 0., 0., 0.)],
dtype=[('company', '<U20'), ('sector', '<U20'), ('mon', '<f8'),
⮑ ('tue', '<f8'), ('wed', '<f8'), ('thu', '<f8'), ('fri', '<f8')])
Tak jak poprzednio, będziesz używać bibliotek numpy
i pathlib
, więc importujesz je w liniach 1 i 2.
Aby zdefiniować nazwy pól i typy danych każdego pola w tablicy portfolio
, zacznij od utworzenia trzech list pokazanych w wierszach 4, 5 i 6. Lista days_dtype
zawiera serię krotek, po jednej dla każdej wartości tablicy days
utworzonej w linii 4, z typem danych f8
, który reprezentuje liczbę zmiennoprzecinkową. Lista company_dtype
zawiera definicje danych firmowych w pliku portfolio.csv
.
Aby utworzyć rzeczywisty obiekt typu danych, który zdefiniuje typy danych tablicy portfolio
, należy połączyć listy company_dtype
i days_dtype
. Następnie rzutujesz wynik na obiekt dtype
za pomocą funkcji np.dtype()
, jak pokazano w linii 8.
Obiekt dtype
jest następnie wprowadzany do funkcji np.zeros()
jako parametr dtype
. Konfigurujesz także tablicę w kształcie (6,)
, aby zapewnić oddzielny wiersz dla danych każdego udziału. Tworzy to tablicę zawierającą puste ciągi w pierwszych dwóch polach i zera w pozostałych, jak pokazano na wyjściu.
Wypełnianie tablicy
Teraz, gdy utworzyłeś tablicę, która jest wystarczająco duża, aby przechowywać wszystkie potrzebne dane, następnym krokiem jest zacząć je zaludnie. Na początek dodasz szczegółowe informacje o firmach przechowywanych w
>>> companies = np.loadtxt(
... Path("portfolio.csv"),
... delimiter=",",
... dtype=company_dtype,
... skiprows=1,
... ).reshape((6,))
>>> portfolio[["company", "sector"]] = companies
>>> portfolio
array([('Company_A', 'technology', 0., 0., 0., 0., 0.),
('Company_B', 'finance', 0., 0., 0., 0., 0.),
('Company_C', 'healthcare', 0., 0., 0., 0., 0.),
('Company_D', 'technology', 0., 0., 0., 0., 0.),
('Company_E', 'finance', 0., 0., 0., 0., 0.),
('Company_F', 'healthcare', 0., 0., 0., 0., 0.)],
dtype=[('company', '<U20'), ('sector', '<U20'), ('mon', '<f8'),
⮑ ('tue', '<f8'), ('wed', '<f8'), ('thu', '<f8'), ('fri', '<f8')])
Ponownie używasz funkcji portfolio.csv
do tablicy strukturalnej o nazwie firmy . Należy pamiętać, że .Reshape ((6,))
został użyty w wierszu 6, aby nadać tablice tego samego kształtu, co tablica portfela utworzona wcześniej. Jest to konieczne, aby wstawić firmy
do portfolio
.
Linia 8 to miejsce wstawiania. Dwa pola tablicowe companies
są wstawiane jako pola company
i sector
w portfolio
, jak widać z wyjście.
Wszystko, co pozostaje do zrobienia, to dodanie różnych dziennych cen akcji. Kod umożliwiający wykonanie tej czynności pokazano poniżej:
>>> share_prices_dtype = [("company", "U20"),("day", "f8"),]
>>> for day, csv_file in zip(
... days, sorted(Path.cwd().glob("share_prices-?.csv"))
... ):
... portfolio[day] = np.loadtxt(
... csv_file.name,
... delimiter=",",
... dtype=share_prices_dtype,
... skiprows=1,
... )["day"]
>>> portfolio
array([('Company_A', 'technology', 100.5, 101.2, 102. , 101.8, 112.5),
('Company_B', 'finance', 200.1, 199.8, 200.5, 201. , 200.8),
('Company_C', 'healthcare', 50.3, 50.5, 51. , 50.8, 51.2),
('Company_D', 'technology', 110.5, 101.2, 102. , 111.8, 97.5),
('Company_E', 'finance', 200.1, 200.8, 200.5, 211. , 200.8),
('Company_F', 'healthcare', 55.3, 50.5, 53. , 50.8, 52.2)],
dtype=[('company', '<U20'), ('sector', '<U20'), ('mon', '<f8'),
⮑ ('tue', '<f8'), ('wed', '<f8'), ('thu', '<f8'), ('fri', '<f8')])
Na początek tworzysz listę definiującą dwa pola w każdym codziennym pliku share_prices-
. Aby dodać te dane do głównej tablicy portfolio
, jeszcze raz utwórz pętlę, która iteruje po każdym z nich, ale tym razem robisz to za pomocą wbudowanej w Pythonie funkcji zip()
funkcja. Pliki będą przetwarzane w kolejności, o której dowiedziałeś się wcześniej.
W tym przypadku przekazujesz zip()
zdefiniowaną wcześniej listę dni
oraz każdy z przetwarzanych plików. Tworzy to serię krotek, po jednej na każdy znaleziony dzień i parę plików. Każdy dzień w krotce jest przypisany do zmiennej day
pętli, natomiast każdy plik jest przypisany do zmiennej csv_file
.
W ramach pętli każdy plik jest ponownie odczytany za pomocą np.loadtxt()
, ale tym razem, zamiast przechowywania całego pliku, przechowywane są tylko dane w polu . Po raz pierwszy wokół pętli dane te są wstawiane do pola Portfolio Array mon , po raz drugi zostaje wstawiany do pola
w ”. i tak dalej. Zrobiłeś to, przypisując odczytane dane do portfolio
[dzień]
dla każdego innego dnia.
Ostateczna wersja tablicy zawiera nazwę firmy, a także sektor, do którego należy. Ostatnie pięć liczb w każdym rekordzie to ceny akcji od poniedziałku do piątku w centrach.
Teraz, gdy połączyłeś swoje dane hierarchiczne w ustrukturyzowanej tablicy, możesz je przeanalizować przy użyciu nazw pola dla uproszczenia. Załóżmy, że chcesz wyodrębnić jedną firmę, aby przyjrzeć się bliżej:
>>> portfolio[portfolio["company"] == "Company_C"]
array([('Company_C', 'healthcare', 50.3, 50.5, 51., 50.8, 51.2)],
dtype=[('company', '<U20'), ('sector', '<U20'),
⮑ ('mon', '<f8'), ('tue', '<f8'), ('wed', '<f8'),
⮑ ('thu', '<f8'), ('fri', '<f8')])
Tutaj wyodrębniasz pojedynczą firmę, umieszczając ją w kolumnie company
. Aby to zrobić, użyj portfolio["company"] == "Firma_C"
, która wybiera te wiersze, których wartości w kolumnie company
odpowiadają "Company_C"
. Ta metoda jest znacznie bardziej intuicyjna niż wybieranie wierszy za pomocą indeksowania.
Podobnie, jeśli chcesz zobaczyć, w jaki sposób technologia
Firmy w twoim portfelu działają w piątek, możesz również wybrać te liczby:
>>> portfolio[portfolio["sector"] == "technology"]["fri"]
array([112.5, 97.5])
Aby wyświetlić rekordy technologii, używasz portfolio [„Sector”] == „Technology”
. Następnie, aby zobaczyć tylko piątkowe rekordy, filtrujesz je za pomocą [„pt”]
.
Załóżmy, że posiadasz 250 akcji w każdej z twoich firm technologicznych. Możesz zobaczyć, ile pieniędzy są warte pod koniec tygodnia:
>>> portfolio[portfolio["sector"] == "technology"]["fri"] * 250 * 0.01
array([281.25, 243.75])
Aby to zrobić, użyj poznanych już technik, aby wybrać piątkowe dane dla każdej części technologii
swojego portfolio. Następnie mnożysz te liczby przez 250
, czyli liczbę posiadanych udziałów. Na koniec wynik mnoży się przez 0,01
, aby zamienić kwoty na dolary, pamiętając, że ceny akcji podawane są w centach. Jeśli chcesz mieć wartość netto, wystarczy, że użyjesz sum()
:
>>> sum(portfolio[portfolio["sector"] == "technology"]["fri"] * 250 * 0.01)
np.float64(525.0)
Część Twojego portfolio zawierająca technologię
jest warta 525,00 USD.
Jak widać, użycie tablic strukturalnych umożliwia dostęp do danych w wysoce intuicyjny sposób. Aby pójść dalej, możesz również zastosować to podejście, używając tablicy ustrukturyzowanej jako podstawy wykresu MATPlotlib. To właśnie zrobisz dalej.
Wykres danych
Załóżmy, że chcesz wyświetlić analizę technologii
Część swojego portfela na wykresie. Ponownie, ponieważ pracujesz z ustrukturyzowaną tablicą, kod staje się intuicyjny:
>>> import matplotlib.pyplot as plt
>>> tech_mask = portfolio["sector"] == "technology"
>>> tech_sector = portfolio[tech_mask]["company"]
>>> tech_valuation = portfolio[tech_mask]["fri"] * 250 * 0.01
>>> (
... plt.bar(x=tech_sector, height=tech_valuation, data=tech_valuation)[0]
... .set_color("g")
... )
>>> plt.xlabel("Tech Companies")
>>> plt.ylabel("Friday Price ($)")
>>> plt.title("Tech Share Valuation ($)")
>>> plt.show()
Najpierw tworzysz tablicę pomocniczą tech_mask
. Następnie utwórz dwie tablice, które zostaną użyte na wykresie. Tablica tech_sector
zdefiniowana w linii 4 zawiera nazwy firm dla każdej firmy tech_sector
. Tablica tech_valuation
zdefiniowana w linii 5 zawiera piątkowe wyceny dla każdej firmy tech_sector
.
Linie od 7 do 10 Utwórz wykres słupkowy. Oś X zawiera Tech_sector
Nazwy firm używane do tworzenia pasków, podczas gdy parametr
Jeśli uruchomisz powyższy kod w notatniku Jupyter, nie musisz używać plt.show()
. Jeśli uruchomisz go w standardowym Pythonie REPL, odniesienia do obiektów zostaną pokazane po liniach 11, 12 i 13. Możesz je po prostu zignorować i dla przejrzystości zostały usunięte z danych wyjściowych.
Wynikowy wykres pokazano poniżej:
Jak widać, Company_a wydaje się lepiej, choć tylko nieznacznie.
Testowanie swoich umiejętności: analizowanie i sporządzanie wykresów danych hierarchicznych
Zanim przejdziesz dalej, oto trzecie wyzwanie:
Poproszono Cię o zestawienie średnich dziennych miesięcznych temperatur w każdym miesiącu roku w celu analizy. Dane są przechowywane w plikach london_temperatures.csv
, new_york_temperatures.csv
i rome_temperatures.csv
.
Korzystając z umiejętności, których się nauczyłeś, utwórz ustrukturyzowaną Weather_Data
tablica z rozsądnymi nazwami pola i typami danych, które zawierają cztery wartości danych dla każdego miesiąca. Pierwszy z nich powinien zawierać miesiąc, podczas gdy pozostałe trzy powinny zawierać temperaturę miesiąca dla każdego miasta.
Użyj tablicy strukturalnej, aby wykreślić każdą miesięczną temperaturę w trzech miastach na wykresie liniowym.
Jednym z możliwych rozwiązań tworzenia tablicy jest:
>>> import numpy as np
>>> from pathlib import Path
>>> cities = ["london", "new_york", "rome"]
>>> cities_dtype = [(city, "i8") for city in cities]
>>> city_files_dtype = [("month", "U20"), ("temp", "i8")]
>>> weather_data_dtype = np.dtype([("month", "U20")] + cities_dtype)
>>> weather_data = np.zeros((12,), dtype=weather_data_dtype)
>>> weather_data
array([('', 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, 0, 0), ('', 0, 0, 0), ('', 0, 0, 0), ('', 0, 0, 0)],
dtype=[('month', '<U20'), ('london', '<i8'),
⮑ ('new_york', '<i8'), ('rome', '<i8')])
>>> for city in cities:
... temps = np.loadtxt(
... Path(f"{city}_temperatures.csv"),
... delimiter=",",
... dtype=city_files_dtype,
... )
... weather_data[["month", city]] = temps
...
>>> weather_data
array([('Jan', 5, 2, 8), ('Feb', 7, 2, 9), ('Mar', 9, 4, 12),
('Apr', 11, 11, 14), ('May', 14, 16, 21), ('Jun', 16, 22, 23),
('Jul', 19, 25, 26), ('Aug', 19, 24, 24), ('Sep', 17, 20, 22),
('Oct', 13, 14, 18), ('Nov', 10, 12, 13), ('Dec', 7, 9, 10)],
dtype=[('month', '<U20'), ('london', '<i8'),
⮑ ('new_york', '<i8'), ('rome', '<i8')])
Zaczynasz od utworzenia tablicy o odpowiednim rozmiarze, aby zaakceptować dane. Tak jak poprzednio, używasz list i wyrażeń listowych do definiowania każdego pola i jego typu danych przed utworzeniem tablicy weather_data
w wierszach 9 i 10. Wynik możesz zobaczyć w wierszach od 11 do 14. Zauważ, że każde < polemiesiąc
jest inicjowane pustym ciągiem znaków, natomiast każde pole liczb całkowitych ma wartość 0
.
W linii 17 zaczynasz zapętlić się nad poszczególnymi miastami. W przypadku Londynu odczytasz plik temps
, a w wierszu 23 przypisujesz jego dane do miesiąc
i Weather_Data
tablica. Dane w Nowym Jorku i Rzymie są odczytywane i dodawane w ten sam sposób. Etykiety miesięczne są zastępowane dla każdego miasta. Ale to jest w porządku, o ile są takie same we wszystkich zestawach danych.
Linie od 27 do 32 pokazują pełną tablicę. Jedną z możliwości wykresu liniowego może być następująca:
>>> import matplotlib.pyplot as plt
>>> plt.plot(weather_data["month"], weather_data["london"])
>>> plt.plot(weather_data["month"], weather_data["new_york"])
>>> plt.plot(weather_data["month"], weather_data["rome"])
>>> plt.ylabel("Temperature (C)")
>>> plt.xlabel("Month")
>>> plt.title("Average Monthly Temperatures")
>>> plt.legend(["London", "New York", "Rome"])
>>> plt.show()
Aby skonstruować działkę, wykreślasz miesiące wzdłuż osi X i tworzysz osobną linię dla każdego z trzech temperatur w oślecie wzdłuż osi Y. Następnie dodajesz etykiety do obu toporów wykresu i dodajesz tytuł i legendę.
Ostateczny wynik powinien wyglądać mniej więcej tak:
Jak widać, z trzech miast Rzym ma niezmiennie wyższe temperatury.
W końcowej sekcji dowiesz się o jednej z głównych efektywności Numpy oraz o tym, jak pisać funkcje, które z nich korzystają.
NumPy Przykład 4: Pisanie własnych funkcji wektorowych
Jedną z efektywności Numpy jest jego zdolność do wykonywania obliczeń na całej tablicy bez konieczności pisania powolnych pętli, które ręcznie zapętli się przez każdy rząd lub element. Zamiast tego Numpy używa podstawowego języka C do wykonywania obliczeń w całej tablicy. Jest to znane jako wektoryzacja .
W tej ostatniej sekcji będziesz pracować z plikiem full_portfolio.csv
pokazanym poniżej:
Company,Sector,Mon,Tue,Wed,Thu,Fri
Company_A,technology,100.5,101.2,102,101.8,112.5
Company_B,finance,200.1,199.8,200.5,201.0,200.8
Company_C,healthcare,50.3,50.5,51.0,50.8,51.2
Company_D,technology,110.5,101.2,102,111.8,97.5
Company_E,finance,200.1,200.8,200.5,211.0,200.8
Company_F,healthcare,55.3,50.5,53.0,50.8,52.2
Te dane będą wyglądać znajomo, ponieważ jest to połączenie plików używanych w poprzedniej sekcji. Nagłówek każdej kolumny ma takie samo znaczenie jak wcześniej.
Poniższy kod pokazuje wektoryzację w akcji:
>>> import numpy as np
>>> from pathlib import Path
>>> share_dtypes = [
... ("company", "U20"),
... ("sector", "U20"),
... ("mon", "f8"),
... ("tue", "f8"),
... ("wed", "f8"),
... ("thu", "f8"),
... ("fri", "f8"),
... ]
>>> portfolio = np.loadtxt(
... Path("full_portfolio.csv"),
... delimiter=",",
... dtype=share_dtypes,
... skiprows=1,
... )
>>> portfolio["fri"] - portfolio["mon"]
array([ 12. , 0.7, 0.9, -13. , 0.7, -3.1])
Po skonstruowaniu tablicy strukturalnej portfolio
postanawiasz sprawdzić, jak radziły sobie Twoje akcje w ciągu tygodnia. Aby to zrobić, wybierasz dwie tablice - jedną zawierającą ceny akcji z poniedziałku i drugą zawierającą ceny akcji z piątku. Aby zobaczyć tygodniową zmianę, odejmij ceny z poniedziałku od cen z piątku.
Zauważ w wierszu 21, że chociaż odejmujesz jedną tablicę od drugiej, Numpy odejmuje każdy indywidualny element tablic bez konieczności pisania kodu, który zajął się nimi indywidualnie. To jest wektoryzacja.
Załóżmy teraz, że otrzymasz dodatkowe 10% premii od akcji, które wzrosły o ponad 1% wartości w ciągu tygodnia, w którym analizujesz. Aby znaleźć swój zysk, w tym bonus, musisz rozważyć dwa przypadki - te akcje otrzymują bonus i te, które tego nie robią. Aby to zrobić, możesz spróbować następujących czynności:
>>> def profit_with_bonus(first_day, last_day):
... if last_day >= first_day * 1.01:
... return (last_day - first_day) * 1.1
... else:
... return last_day - first_day
...
>>> profit_with_bonus(portfolio["mon"], portfolio["fri"])
Traceback (most recent call last):
...
ValueError: The truth value of an array with more than one element is ambiguous.
⮑ Use a.any() or a.all()
W wierszach od 1 do 5 zdefiniowałeś funkcję o nazwie
Kiedy wywołujesz swoją funkcję w wierszu 7, podnosi wyjątek ValueRorror
, ponieważ nie może interpretować przekazanych jej tablic. Będzie to działać tylko wtedy, gdy zdasz to dwie liczby. Aby ta funkcja działała z tablicami, czytaj dalej.
Dodanie funkcjonalności wektoryzacji za pomocą np.vectorize()
Aby Twój Profit_with_bonus()
Funkcja z tablicami, musisz przekształcić ją w funkcję wektoryzowaną . Jednym ze sposobów na to jest użycie funkcji np.vectorize()
. Ta funkcja zwróci wersję twojej oryginalnej funkcji, ale taka, która przyjmuje tablice jako dane wejściowe zamiast skalarów.
Kod pokazany poniżej przekształca twoją funkcję w funkcję wektoryzowaną:
>>> def profit_with_bonus(first_day, last_day):
... if last_day >= first_day * 1.01:
... return (last_day - first_day) * 1.1
... else:
... return last_day - first_day
...
>>> vectorized_profit_with_bonus = np.vectorize(profit_with_bonus)
>>> vectorized_profit_with_bonus(portfolio["mon"], portfolio["fri"])
array([ 13.2 , 0.7 , 0.99, -13. , 0.7 , -3.1 ])
Aby zamienić funkcję profit_with_bonus()
w wersję wektorową, przekazujesz ją do funkcji np.vectorize()
w linii 7. Wersja wektoryzowana jest następnie przypisana do vectorized_profit_with_bonus
nazwa.
W linii 8 wywołujesz nową funkcję i przekazujesz jej tablice, które chcesz analizować. W tym przykładzie wynik pokazuje Twój zysk, łącznie z dodaną premią. Jeśli porównasz liczby z prostą kalkulacją zysku, którą wykonałeś wcześniej, zauważysz, że otrzymałeś premię za swoje udziały w pierwszej i trzeciej spółce. Zauważ, że nie musisz zmieniać oryginalnej funkcji w liniach od 1 do 5.
Ostatnią kwestią związaną z używaniem np.vectorize()
jest to, że oryginalna funkcja jest nadal dostępna, jeśli jej potrzebujesz:
>>> in_profit(3, 5)
2.2
Tym razem zwracana jest pojedyncza wartość. Ponieważ 5 - 3
to 2
, a 2
to więcej niż 1% z 3
, dodajesz 10% do pobierz 2.2
.
Dodanie funkcjonalności wektoryzacji za pomocą @np.vectorize
Jak właśnie zauważyłeś, funkcja np.vectorize()
tworzy drugą funkcję, co oznacza, że oryginalna wersja jest nadal dostępna, jeśli zajdzie potrzeba jej użycia. Alternatywnie możesz zamiast tego użyć np.vectorize
jako dekoratora:
>>> @np.vectorize
... def profit_with_bonus(first_day, last_day):
... if last_day >= first_day * 1.01:
... return (last_day - first_day) * 1.1
... else:
... return last_day - first_day
...
>>> profit_with_bonus(portfolio["mon"], portfolio["fri"])
array([ 13.2 , 0.7 , 0.99, -13. , 0.7 , -3.1 ])
Stosując @np.vectorize
do swojej funkcji w linii 1, funkcja profit_with_bonus()
zostaje przekształcona w wersję wektorową. Ta nowa wersja działa w taki sam sposób, jak funkcja vectorized_profit_with_bonus()
, którą widziałeś wcześniej. Aby z niego skorzystać, wywołaj metodę profit_with_bonus()
tak jak zwykle, ale pamiętaj o przekazaniu do niej tablic. Spowoduje to zwrócenie tej samej tablicy zysków, co poprzednio.
W przeciwieństwie do wersji utworzonej przez funkcję np.vectorize()
, oryginalna skalarna wersja funkcji profit_with_bonus()
już nie istnieje. Jeśli jednak przekażesz mu dwa skalary, nadal będzie działać, ale zwróci wynik jako tablicę:
>>> in_profit(3, 5)
array(2.2)
Jak widać, tym razem wynikiem jest tablica.
Używanie istniejącej funkcjonalności wektoryzacji z np.where()
Wiele dostępnych funkcji NumPy obsługuje już wektoryzację. Dobrym pomysłem jest zapoznanie się z dokumentacją, aby sprawdzić, czy dostępna jest już funkcja odpowiadająca Twoim potrzebom. Aby na przykład obliczyć zysk z akcji, zamiast pisać własną funkcję, możesz użyć funkcji np.where()
do uwzględnienia zarówno przypadku bonusowego, jak i zwykłego zysku:
>>> np.where(
... portfolio["fri"] > portfolio["mon"] * 1.01,
... (portfolio["fri"] - portfolio["mon"]) * 1.1,
... portfolio["fri"] - portfolio["mon"],
... )
array([ 13.2 , 0.7 , 0.99, -13. , 0.7 , -3.1 ])
Tym razem przekazujesz swój warunek do np.where()
. W przeciwieństwie do oryginalnego . Where()
obsługuje wektoryzację natywnie. W takim przypadku używasz
Teraz np.where()
oceni warunek dla każdego elementu i wybierze odpowiedni element z jednej z dwóch tablic. Jeśli warunek to true
, wówczas używa elementu z pierwszej tablicy, a jeśli jest to false
, wybiera element z drugiego.
Sprawdzanie swoich umiejętności: pisanie funkcji wektoryzowanej
Czas zanurzyć się w ostatnim wyzwaniu ćwiczeń. Jesteś prawie skończony.
Zostałeś poproszony o utworzenie dwóch tablic zawierających minimalne i maksymalne temperatury z tablicy Weather_Data
utworzona w ostatnim wyzwaniu. Jeśli jeszcze tego nie zrobiłeś, skonstruuj tablicę
Następnie napisz funkcję o nazwie find_min_max()
, która akceptuje trzy wartości skalarne, na przykład liczby całkowite, i zwraca wartości maksymalne i minimalne. Teraz użyj @np.vectorize
lub np.vectorize()
, aby ulepszyć swoją funkcję do pracy z tablicami NumPy. Upewnij się, że możesz wywołać wektoryzowaną wersję swojej funkcji, używając jej oryginalnej nazwy.
Na początek funkcja początkowa może wyglądać mniej więcej tak:
>>> def find_min_max(first, second, third):
... min_temp = min(first, second, third)
... max_temp = max(first, second, third)
... return min_temp, max_temp
>>> find_min_max(2, 1, 3)
(1, 3)
Twoja początkowa funkcja Weather_Data
tablica, nie zadziała:
>>> london_temps = weather_data["london"]
>>> new_york_temps = weather_data["new_york"]
>>> rome_temps = weather_data["rome"]
>>> find_min_max(london_temps, new_york_temps, rome_temps)
>>> # ...
ValueError: The truth value of an array with more than one element is ambiguous.
Jak widzisz, zgłosiłeś wyjątek ValueError
. Aby móc pracować z tablicami, musisz go zaktualizować.
Możliwym rozwiązaniem przy użyciu @np.vectorize
może być:
>>> @np.vectorize
... def find_min_max(first, second, third):
... min_temp = min(first, second, third)
... max_temp = max(first, second, third)
... return min_temp, max_temp
>>> london_temps = weather_data["london"]
>>> new_york_temps = weather_data["new_york"]
>>> rome_temps = weather_data["rome"]
>>> find_min_max(london_temps, new_york_temps, rome_temps)
(array([ 2, 2, 4, 11, 14, 16, 19, 19, 17, 13, 10, 7]),
⮑ array([ 8, 9, 12, 14, 21, 23, 26, 24, 22, 18, 13, 10]))
Aby użyć wektoryzowanej wersji funkcji find_min_max()
, dekorujesz ją tagiem @np.vectorize
. Aby wywołać funkcję wektoryzowaną, przekazujesz bezpośrednio trzy tablice danych pogodowych. Dekorator pozwala mu rozumieć tablice jako dane wejściowe i tworzyć je jako dane wyjściowe. Funkcja wykorzystuje wbudowane funkcje min()
i max()
do analizy każdego z trzech elementów dla każdego miesiąca i zwraca wynik w dwóch tablicach.
Alternatywnym rozwiązaniem może być powrót do oryginalnej funkcji i użycie np.vectorize()
:
>>> def find_min_max(first, second, third):
... min_temp = min(first, second, third)
... max_temp = max(first, second, third)
... return min_temp, max_temp
>>> london_temps = weather_data["london"]
>>> new_york_temps = weather_data["new_york"]
>>> rome_temps = weather_data["rome"]
>>> find_min_max = np.vectorize(find_min_max)
>>> find_min_max(london_temps, new_york_temps, rome_temps)
(array([ 2, 2, 4, 11, 14, 16, 19, 19, 17, 13, 10, 7]),
⮑ array([ 8, 9, 12, 14, 21, 23, 26, 24, 22, 18, 13, 10]))
Aby użyć wektoryzowanej wersji funkcji find_min_max()
, przekazujesz ją do np.vectorize()
i przypisując dane wyjściowe z powrotem do zmiennej find_min_max
, możesz następnie wywołać wersję wektorową, używając jej oryginalnej nazwy. Aby wywołać tę wersję wektorową, przekazujesz bezpośrednio trzy tablice danych pogodowych.