Wstęp
Z poprzedniego wpisu możecie dowiedzieć się jak załadować dane do DataFrame z pliku CSV oraz jak wykonać podstawowe operacje na tych danych. DataFrame można również utworzyć ręcznie na podstawie danych przechowywanych w różnych strukturach oferowanych przez Python, a następnie zapisać go w pliku. W ten sposób możemy utworzyć własny dataset.
Osobiście, jak mam doczynienia z danymi, które sam pozyskuję z różnych źródeł jak API, strony webowe poprzez WebScraping, itp. to najchętniej gromadzę je na liście elementów typu Dictionary. Dlaczego akurat tak? Głównie ponieważ jest to struktura samoopisująca się. Dzięki temu jak utworzę na bazie takiej struktury DataFrame to od razu będę miał nazwane kolumny w tym DataFrame.
Zacznijmy od początku, tj. od prostszego przykładu listy stringów.
Utworzenie DataFrame na podstawie listy
Na początek załaduję bibliotekę Pandas:
In [1]:
import pandas as pd
Następnie zdefiniuję listę elementów, na podstawie, której utworzę DataFrame:
In [2]:
langs = ['Python', 'Julia', 'R', 'Matlab', 'Fortran', 'Scala', 'Java', 'JavaScript']
W kolejnym kroku tworzę DataFrame na podstawie danych z listy langs i wyświetlam kilka jego początkowych wierszy:
In [3]:
df = pd.DataFrame(langs) df.head()
Out[3]:
| 0 | |
|---|---|
| 0 | Python |
| 1 | Julia |
| 2 | R |
| 3 | Matlab |
| 4 | Fortran |
Jak widzimy uzyskaliśmy DataFrame z jedną kolumną, której Pandas przydzielił nazwę '0′. Aby tego uniknąć możemy wprost wyspecyfikować nazwę kolumny do utworzenia w poleceniu tworzącym DataFrame:
In [4]:
df = pd.DataFrame(langs, columns=['Języki programowania']) df.head()
Out[4]:
| Języki programowania | |
|---|---|
| 0 | Python |
| 1 | Julia |
| 2 | R |
| 3 | Matlab |
| 4 | Fortran |
Utworzenie DataFrame na podstawie listy list
Przejdźmy teraz do drugiego, trochę bardziej złożonego przykładu. Będziemy teraz pracować z listą list. Lista w liście reprezentuje wiersz danych.
Wykonywałem ostatnio analizę danych dotyczących repozytoriów w Gitlab. Dane do tej analizy uzyskałem poprzez API Gitlaba. Tutaj pominę szczegóły jak korzystać z tego API i zdefiniuję analogiczną listę ręcznie. W poszczególnych wierszach zapiszę informacje o kolejnych aplikacjach:
- id – identyfikator projeku,
- name – nazwa projektu,
- folders – lista katalogów w repozytorium projektu.
Dla czytelności, każdą z zagnieżdżonych list, zapiszę w osobnym wierszu:
In [5]:
apps = [
[1, 'app1', ['WIN', 'UNIX']],
[2, 'app2', ['UNIX']],
[3, 'app3', []],
[4, 'app4', ['WIN']],
[5, 'app5', ['UNIX']],
[6, 'app6', ['WIN', 'UNIX']],
[7, 'app7', ['WIN', 'UNIX']],
[8, 'app8', []],
[8, 'app9', ['WIN', 'UNIX']],
[10,'app10',['UNIX']]
]
Wyświetlmy na próbę dane w pierwszym wierszu:
In [6]:
print(apps[0])
[1, 'app1', ['WIN', 'UNIX']]
Może się pojawić tutaj pytanie dlaczego pierwszy elementy listy to apps[0], a nie apps[1]? Wynika, to z tego, że w Python podobnie jak w większości języków programowania elementy są indeksowane od 0, a nie 1.
Następnie utworzę DataFrame na podsawie danych w liście apps i wyświetlę kilka jego początkowych wierszy:
In [7]:
df = pd.DataFrame(apps) df.head()
Out[7]:
| 0 | 1 | 2 | |
|---|---|---|---|
| 0 | 1 | app1 | [WIN, UNIX] |
| 1 | 2 | app2 | [UNIX] |
| 2 | 3 | app3 | [] |
| 3 | 4 | app4 | [WIN] |
| 4 | 5 | app5 | [UNIX] |
Podobnie jak we wcześniejszym przykładzie biblioteka Pandas automatycznie nadała nazwy kolumnom – są to kolejne liczby 0, 1 i 2. Możemy to skorygować, analogicznie jak wcześniej, poprzez podanie nazw kolumn, które będą oddawać ich zawartość:
In [8]:
df1 = pd.DataFrame(apps, columns = ['id', 'name', 'folders']) df1.head()
Out[8]:
| id | name | folders | |
|---|---|---|---|
| 0 | 1 | app1 | [WIN, UNIX] |
| 1 | 2 | app2 | [UNIX] |
| 2 | 3 | app3 | [] |
| 3 | 4 | app4 | [WIN] |
| 4 | 5 | app5 | [UNIX] |
Utworzenie DataFrame na podstawie listy elementów tuple
W trzecim przykładzie pokażę tworzenie DataFrame na podstawie listy, której elementy są typu tuple.
Na początek, krótkie wyjaśnienie czym jest typ tuple. Tuple jest swego rodzaju listą. Przy czym w odróżnieniu od listy do deklarowania zmiennej typu tuple używa się nawiasów okrągłych („()”) zamiast kwadratowych („[]”) i, co ważniejsze, tuple nie pozwala na modyfikowanie wartości, czyli jest typem niemutowalnym (ang. immutable). Chcąc zmienić wartości trzeba utworzyć nowy element typu tuple.
In [9]:
apps1 = [
(1, 'app1', ['WIN', 'UNIX']),
(2, 'app2', ['UNIX']),
(3, 'app3', []),
(4, 'app4', ['WIN']),
(5, 'app5', ['UNIX']),
(6, 'app6', ['WIN', 'UNIX']),
(7, 'app7', ['WIN', 'UNIX']),
(8, 'app8', []),
(8, 'app9', ['WIN', 'UNIX']),
(10,'app10',['UNIX'])
]
Podobnie jak we wcześniejszych przykładach elementy typu tuple nie zawierają nazw atrybutów. Z tego względu jeżeli chcemy aby nasz DataFrame miał nazwy kolumn adekwatne do ich zawartości musimy je znowu jawnie określić:
In [10]:
df2 = pd.DataFrame(apps1, columns = ['id', 'name', 'folders']) df2.head()
Out[10]:
| id | name | folders | |
|---|---|---|---|
| 0 | 1 | app1 | [WIN, UNIX] |
| 1 | 2 | app2 | [UNIX] |
| 2 | 3 | app3 | [] |
| 3 | 4 | app4 | [WIN] |
| 4 | 5 | app5 | [UNIX] |
Utworzenie DataFrame na podstawie listy elementów Dictionary
W ostatnim już przykładzie pokażę tworzenie DataFrame na bazie listy elementów typu Dictionary. Najpierw zdefiniuję taką listę zawierającą taki sam zakres informacji jak w dwóch poprzednich przykładach.
In [11]:
apps = [
{'id': 1, 'name': 'app1', 'folders': ['WIN', 'UNIX']},
{'id': 2, 'name': 'app2', 'folders': ['UNIX']},
{'id': 3, 'name': 'app3', 'folders': []},
{'id': 4, 'name': 'app4', 'folders': ['WIN']},
{'id': 5, 'name': 'app5', 'folders': ['UNIX']},
{'id': 6, 'name': 'app6', 'folders': ['WIN', 'UNIX']},
{'id': 7, 'name': 'app7', 'folders': ['WIN', 'UNIX']},
{'id': 8, 'name': 'app8', 'folders': []},
{'id': 8, 'name': 'app9', 'folders': ['WIN', 'UNIX']},
{'id': 10, 'name': 'app10', 'folders': ['UNIX']}
]
Kilka razy posłużyłem się nazwą typu Dictionary ale nie wyjaśniłem co się za nią kryje. Mamy już przykład, to teraz będzie łatwiej to zrobić.
Każdy z elementów listy w powyższym przykładzie jest typu Dictionary, np:
{’id’: 1, 'name’: 'app1′, 'folders’: [’WIN’, 'UNIX’]}
Jak widzimy element typu Dictionary jest to element, który zawiera oddzielone przecinkami pary klucz i wartość, a całość jest otoczona nawiasami klamrowymi.
Kluczami w naszym przykładzie są: 'id’, 'name’ i 'folders’. Po dwukropku wystepują wartości przechowywane dla danego klucza. Ogólniej możemy element typu Dictionary zapisać w postaci:
{’nazwa klucza 1′: wartosc1, 'nazwa klucza 2′: wartosc2, …, 'nazwa klucza N’: wartoscN}
Taki zapis jest wygodny gdy chcemy od razu zdefiniować w kodzie całą listę takich elementów.
Zazwyczaj jednak w praktyce taką listę budujemy dynamicznie. Na przykład, gdy dane pobieramy z zewnętrznego źródła jak API jakiegoś systemu. W takich sytuacjach wygodniej będzie posłużyć się trochę inną notacją:
In [12]:
application = {}
application['id'] = 1
application['name'] = 'app1'
application['folders'] = ['WIN', 'UNIX']
W pierszym kroku tworzymy pusty obiekt typu Dictionary i przypisujemy go do zmiennej application. W kolejnych krokach przypisujemy wartości dla poszczególnych kluczy stosując notację podobną jak dla list, przy czym w tym wypadku w nawiasach kwadratowych są klucze a nie indeksy listy.
Powyższy kod tworzy element analogiczny do pierwszego elementu listy z pkt. [11]
Wróćmy teraz do naszej listy przechowywanej w zmiennej apps. Poleceniem type() możemy sprawdzić jakiego typu jest określona zmienna:
In [13]:
type(apps)
Out[13]:
list
In [14]:
type(apps[0])
Out[14]:
dict
Jak widzimy lista reprezentowana przez zmienną apps jest typu list, a jej pierwszy element apps[0] typu dict (czyli Dictionary).
DataFrame tworzymy podobnie jak wcześniej poleceniem pd.DataFrame():
In [15]:
df = pd.DataFrame(apps)
Sprawdźmy rozmiar utworzonego DataFrame:
In [16]:
df.shape
Out[16]:
(10, 3)
Wyświetlmy jego początkowe dane:
In [17]:
df.head()
Out[17]:
| id | name | folders | |
|---|---|---|---|
| 0 | 1 | app1 | [WIN, UNIX] |
| 1 | 2 | app2 | [UNIX] |
| 2 | 3 | app3 | [] |
| 3 | 4 | app4 | [WIN] |
| 4 | 5 | app5 | [UNIX] |
Widzimy, że jako nazwy kolumn zostały użyte, przez Pandas, nazwy kluczy elementów typu Dictrionary przechowywanych w liście na podstawie której utworzyliśmy ten DataFrame.
Zapis danych z DataFrame do pliku
W przypadku pozyskiwania z różnych źródeł, jak np. z API, danych do analizy, zakładamy zazwyczaj, że zachowamy te dane w sposób trwały, tak by móc z nich korzystać w przyszłości.
Pokażę na przykładach jak zapisać zawartość DataFrame w najpopularniejszych formatach plików, czyli CSV, JSON i arkusz Excela.
Zapis do pliku CSV
Zapis danych z DataFrame do pliku CSV można wykonać za pomocą metody to_csv(). W poniższym przykładzie podaję w tej metodzie jedynie dwa parametry:
- nazwa pliku, w którym mają zostać zapisane dane,
- parametr index ustawiony na False, co oznacza, że w pliku nie powinien zostać zapisany indeks DataFrame.
Metoda to_csv() pozwala na dużo szersze sterowanie jej działaniem. Pełen wykaz i opis jej parametrów znajdziesz w dokumentacji.
In [18]:
df.to_csv('apps.csv', index=False)
Zapis w formacie JSON
Czas na zapis do pliku w formacie JSON. Służy do tego metoda to_json(). W przykładzie podaję parametry analogiczne jak wcześniej oraz dodatkowo parametr orient ustawiony na table. Dzięki takiemu ustawieniu w pliku zostanie zapisana lista wierszy DataFrame df wraz z nazwami kolumn. Pełen wykaz i opis parametrów metody to_json() znajdziesz w dokumentacji.
In [19]:
df.to_json('apps.json', orient='table', index=False)
Zapis do arkusza Excela
Dane z DataFrame do arkusza Excela możemy zapisać za pomocą metody to_excel(). W poniższym przykładzie podaję analogiczne parametry jak we wcześniejszych przykładach. Pełen wykaz i opis parametrów metody to_excel() znajdziesz w dokumentacji.
In [20]:
df.to_excel('apps.xlsx', index=False)
Podsumowanie
Pokazałem w tym artykule jak elastyczna jest w Pandas funkcja tworzenia DataFrame na podstawie danych przechowywanych w liście oraz zapisu danych do plików w różnych formatach.
Pandas oferuje dużo więcej możliwości w zakresie tworzenia DataFrame’ów na podstawie danych zapisanych w różnych struktruach Pythona. Szczegóły znajdziesz w dokumentacji. Zachęcam do własnych eksperymentów i pogłębienia znajomości biblioteki Pandas. Jako przykład trochę bardziej zaawansowany możesz sprawdzić czym będzie się różnić tworzenie DataFrame na podstawie listy elementów typu namedtuple od listy z tuple w przykładzie trzecim.
Notebook z zadaniami związanymi z tym postem do rozwiązania znajdziesz tutaj .