Jednym z podstawowych celów Netflixa jest dostarczenie 130-milionowej widowni spersonalizowanej treści. Wykorzystywane są do tego zaawansowane algorytmy uczenia maszynowego. Narzędziem, jakiego używają inżynierzy Netflixa do budowy swoich projektów, jest Jupyter Notebook.

Jupyter Notebook jest niesamowicie użytecznym narzędziem do interaktywnego rozwijania i prezentowania projektów Data Science. Ten post ma na celu przeprowadzić Cię przez proces od instalacji do jego praktycznego użycia w projekcie.

Zanim zaczniemy, warto sobie odpowiedzieć na pytanie, czym są notatniki Jupytera (pliki ipynb)? Notatnik integruje kod z wynikami w jednym miejscu. Wszystko wzbogacone wykresami, równaniami matematycznych i bogatym komentarzem. Przepływ informacji w projekcie sprawia, że jest narzędziem intuicyjnym i niezwykle szybkim w użyciu. Wszystko to sprawia, że rośnie jego popularność jako głównego narzędzia w projektach Data Science.

Ogromną zaletą jest to, iż jest całkowicie darmowy.

W tym poradniku skupię się na wykorzystaniu Jupytera z Pythonem, ale warto wiedzieć, że może być on użyty z innymi językami programowania, o czym napiszę nieco później.

Jeśli brakuje Ci wiedzy z zakresu Pythona i biblioteki Pandas, nie przejmuj się, wystarczy, że będziesz po kolei wykonywać moje instrukcje. Dodatkowo cały kod z danymi dostępny jest w darmowym kursie do pobrania na końcu postu.

Analiza z Jupyter Notebook

Na końcu przetestujemy zdobytą wiedzę, wczytamy dane zewnętrzne, zrobimy bardzo prostą analizę, aby odpowiedzieć na kilka pytań. Zaprezentuję, jak wygląda przepływ informacji w notatniku. Jest intuicyjny dla nas i będzie dla innych, kiedy już podzielimy się naszą pracą.

Uprzyjemnimy sobie sam projekt i zajmiemy się czymś z branży rozrywkowej i bliskiej Netflixowi. Mamy synka, który zaczął oglądać Pokemony.

Pojawiło się oczywiste pytanie. Który pokemon jest najfajniejszy? (czytaj najsilniejszy i nie do pokonania).

Ponieważ wpis jest o Jupyterze, to wykorzystamy go do odpowiedzi na to pytanie. Zanim jednak zobaczymy pokemony w akcji, zainstalujemy i uruchomimy Jupytera.

Instalacja

Sposobów na instalację i start pracy z Jupyterem jest kilka. Najprostszym sposobem jest instalacja Anacondy.

Jest to szeroko wykorzystywana dystrybucja Pythona. Dystrybucja, czyli gotowy do pracy pakiet, w którym mamy:

  • język programowania Python
  • ogromną liczbę bibliotek gotowych do użycia i rozszerzających możliwości Pythona (pełna lista bibliotek dla poszczególnych systemów tutaj), a w tym:
    • pakiety do pracy z danymi: NumPY, Pandas,
    • pakiety do wizualizacji: Matplotlib, Seaborn,
    • pakiety do uczenia maszynowego: Scikit-Learn,
    • darmowe środowiska programistyczne takie jak: Spyder, Jupyter Notebook czy RStudio.

Do zdobycia Anacondy, wystarczy:

  1. Pobrać najaktualniejszą wersję z Pythonem 3 dla własnego systemu operacyjnego z tej strony.
  2. Zainstalować Anacondę zgodnie z instrukcjami dla poszczególnych systemów dostępnymi na tej stronie.

Dla osób, które mają już pewne doświadczenie z Pythonem i chcą ręcznie sterować instalacją poszczególnych pakietów, najlepiej wykorzystać menadżera pakietów pip. W wierszu poleceń lub terminalu wpisujemy:

pip3 install jupyter

Pierwszy Notatnik

W tej sekcji uruchomimy i zapiszemy nasz pierwszy notatnik. Poznamy jego strukturę i interfejs. Zrozumiemy kluczowe koncepcje pracy z notatnikiem, tak abyśmy mogli to wszystko wykorzystać w naszym projekcie.

Uruchamiamy Jupyter Notebook

System Windows

W Windows, możemy otworzyć Jupytera na 3 sposoby:

  • Windows Start –> Anaconda3(64 bit) –> Jupyter Notebook
  • Windows Start –> Anaconda3(64 bit) –> Anaconda Prompt a następnie wpisujemy w konsoli jupyter notebook i zatwierdzamy Enterem
  • Windows Start –> Anaconda3(64-bit –> Anaconda Navigator, po pojawieniu się okna Navigatora odszukujemy kafelek z Jupyter Notebook i klikamy na przycisk Launch

System Linux i MacOs

W systemach Linux i MacOs uruchamiamy terminal, wpisujemy w konsoli jupyter notebook i zatwierdzamy Enterem.

Ekran Startowy

Po uruchomieniu Jupytera powinna uruchomić się domyślna przeglądarka systemowa, otworzy się nowa karta i pokaże okno startowe takie jak poniżej.

Ekran startowy Jupyter Notebook
Ekran startowy Jupyter Notebook

To nie jest jeszcze nasz notatnik. To ekran Jupytera, w którym będziemy zarządzać strukturą naszych projektów i w którym będziemy odnajdywać, tworzyć i edytować nasze notatniki.

Gdy zwrócimy uwagę na pasek adresu w przeglądarce, zobaczymy coś w stylu http://localhost:8888/tree. Oznacza to, że zawartość tego, co widzimy w oknie przeglądarki, znajduje się na naszym lokalnym komputerze.

Jupyter jest aplikacją webową, czyli potrzebuje przeglądarki, aby działać. Dzięki temu jest niezależny od konkretnego systemu operacyjnego, a naszą pracą łatwiej jest się dzielić.

Nowy Notatnik – Untitled.ipynb

OK. Utwórzmy nowy notatnik. Wybierzmy to, co na zdjęciu poniżej.

Tworzymy notatnik
Tworzymy nowy notatnik

Nowy notatnik pojawi się w nowej zakładce przeglądarki i otrzyma nazwę Untitled.

Notatnik Untitled
Nowy notatnik Untitled

Jupyter pozwala pracować na wielu notatnikach jednocześnie w wielu zakładkach przeglądarki. Jeśli przełączymy się na zakładkę Home (Ekran Startowy), zobaczymy nowy plik Untitled.ipynb.

Czym Jest Plik ipynb?

Każdy plik ipynb jest plikiem tekstowym zapisanym w formacie JSON. Każda komórka i jej zawartość, w tym zamieszczone obrazy zamieniana jest na łańcuchy znaków z własnymi metadanymi (czyli dane na temat danych). Edycja metadanych z poziomu notatnika odbywa się poprzez wybór z menu Edit –> Edit Notebook Metadata.

Interfejs

Rzut oka wystarczy, aby zorientować się, że menu nie jest rozbudowane. Przypomina bardziej edytory tekstu takie jak Wordpad. Po prawdzie Jupyter to bardziej zaawansowany edytor teksu. Wszystkie polecania znajdują się w menu górnym. Możemy jednak obejrzeć całą listę komend poprzez wybór małej ikony klawiatury (open the command pallete).

Okno command pallete - Jupyter Notebook
Lista wszystkich poleceń w Jupyter Notebook – command pallete

Przy okazji w menu pojawiają się dwa nowe pojęcia: Cell i Kernel. To one sprawiają, że Jupyter jest czymś więcej niż edytorem tekstu. Na szczęście nie są trudne do zrozumienia.

  • Kernel to „silnik” naszego notatnika, który wykonuje wszystkie obliczenia. W naszym przypadku będzie to Python w wersji 3.
  • Cell to pojedyncza komórka: nasz tekst lub kod, który chcemy wykonać.

Cell (Komórka)

Komórki tworzą zawartość naszego notatnika i z nich budujemy cały projekt. Zasadniczo są dwa główne typy komórek:

  • Code zawiera kod wykonywany przez kernel notatnika. Tuż pod kodem pojawią się wyniki tych obliczeń.
  • Markdown zawiera nasz tekst sformatowany przy użyciu składni Markdown. Wynik w postaci sformatowanego tekstu uzyskamy po wykonaniu komórki.

Pierwszy Kod

Standardowo pierwsza komórka nowo utworzonego notatnika jest komórką typu Code.

Stwórzmy nasz pierwszy kod ze standardowym powitaniem ‚Witaj świecie!’. Wpisujemy print('Witaj świecie!') w pierwszej komórce i naciskamy przycisk Run lub skrót klawiaturowy Ctrl + Enter. Wynik powinien być podobny do poniższego.

Pierwszy kod
Pierwszy kod

Run

Jest jeszcze jedno oznaczenie dla komórek. In [*] oznacza, że dana komórka jest właśnie wykonywana przez kernel. Będzie to miało miejsce w przypadku kodu, którego realizacja zajmuje trochę czasu.

Czasami po wykonaniu komórki brakuje wyniku. Co nie oznacza, że komórka się nie wykonała a jedynie, że brak jest wartości do wyświetlenia. Przykładem takiego kodu będzie import biblioteki lub wywołania funkcji bez zwracania wartości.

# import biblioteki
import time
# wywołanie funkcji bez zwracania wartości
time.sleep(10)

Dwa Tryby Pracy

Podczas pracy z notatnikiem zauważymy w pewnym momencie, że nasze komórki zmieniają kolor obramowania. Oznaczają one dwa tryby pracy:

  • Edycji (kolor zielony) gdzie do danej komórki wprowadzamy kod lub tekst. Aby wejść w tryb edycji, będąc na danej komórce, naciskamy na klawiaturze Enter.
  • Komend (kolor niebieski), w którym możemy wykonywać różne polecenia, wybierając je z menu lub za pomocą skrótów klawiaturowych. Aby wejść w tryb komend, będąc na danej komórce, naciskamy na klawiaturze Esc.

Skróty Klawiaturowe

Skróty klawiaturowe powstały, aby przyspieszyć pracę z notatnikami.

Poniżej prezentuje listę skrótów, które są najbardziej przydatne w codziennej pracy z notatnikami Jupyter.

  • Przełączanie w tryb edycji Enter i tryb komend Esc
  • Z poziomu trybu komend:
    • Poruszanie się po notatniku: Strzałka w górę – Up jedna komórka do góry, Strzałka w dół – Down jedna komórka w dół.
    • A (jak Above) wstawia nową komórkę powyżej aktualnie zaznaczonej.
    • B (jak Below) wstawia nową komórkę poniżej aktualnie zaznaczonej.
    • M zmienia aktualną komórkę na typ Markdown.
    • Y zmienia aktulaną komórkę na typ Code.
    • D + D (czyli dwa razy D) usuwa aktywną komórkę.
  • Ctrl + Shift + -, dzieli aktualną komórkę na dwie.

Zachęcam do przetestowania wszystkich tych skrótów. Przy okazji stwórz komórkę Markdown, bo w następnej sekcji opiszę jak formatować tekst w notatnikach.

Markdown

Czym jest Markdown? Językiem znaczników do formatowania tekstu. Główne zalety to jego prostota i łatwość użycia. Jego składnia jest powiązana ze znacznikami HTML, choć ich znajomość nie jest wymagana.

Poniżej kilka przykładów użycia Markdown w praktyce.

Składnia Markdown
Składnia Markdown

Po uruchomieniu komórki czyli przycisk Run albo Ctrl + Enter komórka sformatowana w Markdown wygląda tak:

Sformatowany tekst w Markdown
Sformatowany tekst w Markdown

Oczywiście możliwości samego Markdown jest znacznie więcej. Aby rozszerzyć wiedzę warto zajrzeć do linków poniżej:

Kernel – Czyli Silnik Notatnika

Pod spodem każdego notatnika działa kernel. Kiedy uruchamiamy komórkę za pomocą Run, kod jest wykonywany przez nasz kernel a wynik zwracany pod daną komórką.

Kernel działa na poziomie całego notatnika, a nie pojedynczej komórki np. jeśli stworzymy zmienną w jednej komórce, będzie dostępna w innych.

Praca nad notatnikiem zwykle przebiega od góry do dołu. Czasami jednak wykonuje się pewne zmiany, cofając się do wcześniejszych części notatnika. W takich sytuacjach informacja o kolejności wykonania znajdzie się po lewej stronie komórki jako np. In [10].

Jeśli chcemy zresetować ustawienia kernela, notatnik dysponuje wieloma użytecznymi funkcjami w menu Kernel:

  • Restart: restartujemy nasz kernel i usuwamy z pamięci wszystkie zmienne, które były zdefiniowane.
  • Restart & Clear Output: tak samo jak powyżej ale usuwamy także wyniki naszych obliczeń.
  • Restart & Run All: tak samo jak powyżej ale uruchamiamy dodatkowo wszystkie komórki po kolei od pierwszej do ostatniej.

Jeśli nasz kernel przyblokuje się z powodu ilości obliczeń, mamy możliwość zatrzymania go korzystając z opcji interrupt the kernel.

Wybór Kernela

W naszym przykładzie tworząc nasz pierwszy za pomocą notatnik New –> Python 3 wybraliśmy od razu nasz kernel.

Wybór kernela nie musi sprowadzać się tylko do wybrania odpowiedniej wersji Pythona. Możemy w naszej pracy wybierać kernele z tej listy, np.: Java, C, PHP itd.

Przykładowa Analiza

Pora skorzystać ze zdobytej wiedzy w praktyczny sposób. Wróćmy więc do naszego syna i jego prośby o sprawdzenie pokemonów. Naszym celem jest znalezienie pokemonów najsilniejszych, czyli tych, które mają największe szanse na wygraną w pojedynku.

Nazwa Notatnika

Zaczynając pracę, warto zadbać o jakąś znaczącą nazwę dla naszego projektu. Mamy już nasz pierwszy notatnik Untitled.ipynb. Możemy zmienić jego nazwę na kilka sposobów, ale najprościej jest wybrać z menu File –> Rename… i podać nową nazwę np. Pokemon Analiza i zatwierdzić nasz wybór.

Import

Standardowo w pierwszej komórce importuje się wszystkie biblioteki, z których będziemy korzystać oraz dokonuje początkowych ustawień.

import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sbn
sbn.set(style='darkgrid')
%matplotlib inline

W powyższym kodzie importujemy trzy biblioteki: pandas do pracy z danymi, matplotlib do tworzenia wykresów i seaborn, który sprawi, że wykresy będą ładniejsze. W linii sbn.set(style='darkgrid') ustawiam styl naszych wykresów. Linia %matplotlib inline sprawia, że nasze wykresy będą pojawiać się w notatniku.

Pora zadbać o dane. Skąd weźmiemy informację o naszych pokemonach?

Kaggle, o którym pisałem tutaj, posiada gotowy zbiór statystyk na ich temat w postaci pliku csv.

Pobierzmy dane do katalogu z naszym projektem i załadujmy je do naszego notatnika:

df = pd.read_csv('Pokemon.csv',encoding='latin2')

Używamy dodatkowego atrybutu encoding, aby właściwie odczytać znaki zapisane w pliku.

Wskazówka: warto dane wczytywać w pojedynczej komórce. W przypadku problemów łatwiej ponownie wczytać same dane.

Zapisywanie i Punkty Kontrolne

Najlepszą praktyką przy pracy nad projektem jest jego regularne zapisywanie. Wystarczy kombinacja Ctrl + S (polecenie Save and Checkpoint).

Czym są Punkty Kontrolne (Checkpoints)?

Za każdym razem, kiedy tworzymy nowy notatnik, razem z nim tworzony jest punkt kontrolny jako osobny plik w ukrytym podkatalogu o nazwie .ipynb_checkpoints. Plik punktu kontrolnego to także ipynb. Domyślnie Jupyter zapisuje twój notatnik do punktu kontrolnego co 120 sekund bez aktualizowania go z twoim głównym notatnikiem.

Kiedy wybieramy Save and Checkpoints, obydwa pliki są aktualizowane i wyglądają jednakowo.

Co daje punkt kontrolny? Pozwala na odzyskanie niezapisanej pracy w przypadku niespodziewanego problemu. Możemy powrócić do ostatniego punktu kontrolnego poprzez wybranie z menu File –> Revert to Checkpoint.

Skanowanie Danych

Zaczynamy pracę z danymi. Chcemy na samym początku odpowiedzieć sobie na kilka bazowych pytań dotyczących zbioru danych:

  • Ile obserwacji jest w zbiorze? (Ile wierszy?)
  • Ile atrybutów jest w zbiorze? (Ile kolumn?)
  • Jakiego typu są atrybuty? Czy są wartościami numerycznymi? Czy może są to kategorie?
  • Czy mam kolumnę, która może posłużyć mi za zmienną celową?

Przyjrzymy się danym. Wpisujemy w nasz notatnik kolejne polecenia:

# początek zbioru pokemonów
df.head()
Początek zbioru danych
Początek zbioru danych
# koniec zbioru pokemonów
df.tail()
Koniec zbioru danych
Koniec zbioru danych
# typy atrybutów
df.dtypes
Typy atrybutów
Typu atrybutów
# statystyki podsumowujące kolumny z wartościami numerycznymi.
df.describe()
Statystyki atrybutów numerycznych
Statystyki atrybutów numerycznych

Dane wyglądają dobrze. Mamy statystyki na temat Ataku, Obrony, Punktów Życia i parę innych. Na podstawie powyższych tabelek możemy odpowiedzieć na kilka zadanych wcześniej pytań:

  • Mamy 151 obserwacji (tyle wierszy) na temat różnych pokemonów (count w tabeli df.describe()).
  • Mamy 13 atrybutów (kolumn).
  • 9 atrybutów jest numerycznych (int64), 3 atrybuty jako łańcuchy znaków (object), 1 atrybut to typ logiczny (bool).
  • Istnieje kandydat na kolumnę celu: Total.
  • Brakuje nam danych w kolumnie: Type 2 (wartości NaN) ale nie jest to problem w naszym przypadku.

Wizualizacja z Seaborn

Mamy przyzwoity zbiór danych, warto byłoby zobaczyć go w bardziej przyjaznej dla oka formie. Zrobimy kilka wykresów i wykorzystamy w tym celu Seaborn.

Ponieważ bratu zależy tylko na najfajniejszych pokemonach zobaczmy więc 10% topowych pokemonów. Tych, które mają największy Total (suma wszystkich statystyk), Attack (siła ataku) i Defence (umiętność obrony).

W tym celu stworzymy podzbiór best_pokemons.

pareto = int(0.1*len(df))
best_pokemons = df.sort_values(['Total','Attack','Defense'],ascending=False).head(pareto)
best_pokemons
Top 15 najlepszych pokemonów
Top 15 najlepszych pokemonów

Mamy więc 15 najlepszych pokemonów czyli z największą sumą Total. W sumie tutaj moglibyśmy skończyć. Zobaczmy jednak kilka wizualizacji, które podsumują, to co widzimy w zbiorze.

Podział najlepszych pokemonów na typy
Podział najlepszych pokemonów na typy

Powyżej widzimy że wśród najlepszych pokemonów sporo jest tych wodnych i ogniowych. Może w nie warto inwestować?

Pokemony Legendarne
Pokemony Legendarne vs Zwyczajne

Powyżej podział na pokemony Legendarne i te Nie-Legendarne. Widać, że te pierwsze są bardzo wysoko. Ciekawe czy to, że są takie silne w porównaniu do innych, sprawiło, że są … legendarne.

Podsumowanie statystyk.
Podsumowanie statystyk

Podsumowujący wykres, który pokazuje rozrzut najlepszych pokemonów wg typu. Widać, że pokemony z lewej strony: Psychic i Dragon są na czele.

Być może nasz syn to wszystko już wie z serialu, ale fajnie jest znaleźć dane, wykorzystać je sprawdzić samemu wykorzystując nowe umiejętności.

Podsumowanie

W tym wpisie poznaliśmy schemat pracy Jupytera, jego podstawowe elementy i mechanizmy działania. Zrobiliśmy prostą zabawę z danymi. Cała analiza i dane, które wykorzystaliśmy w tym wpisie, możesz pobrać z darmowym kursem Pandas.

Do wykorzystania pokemonów w tym wpisie zainspirował mnie mój syn i wpis z tej strony (swoją drogą fajny tutorial Seaborn).

Jeśli miałbyś jakieś pytania zostaw swój komentarz lub napisz do mnie: pawel.kowalczyk@daneucza.pl.

Dodatkowe Materiały o Jupyter Notebook