Wprowadzenie do pandas (Python)

06 Jan 2020 / admin

W tym wpisie spróbuję napisać krótkie wprowadznie do modułu (biblioteki) pandas. Obiekt pandas DataFrame służy nam do prostych operacji na danych, początkowe wrażenie jest takie jak byśmy operowali na dużym arkuszu kalkulacyjnym. Róznica jest taka, że pandas potrafi dużo więcej (zobacz wpis o operacjach groupby itp.) i a operacje wykonywane są znacznie szybciej. Przed prezentacją podstaw "pandy" dodajmy, że podobne konstrukcje występują w wielu programach do obliczeń statystycznych (np. data frame albo data table w R). Przed dalszymi krokami zaimportujemy moduł komendą import pandas as pd (będziemy często odwoływać się do pandas, więc wygodnie będzie nam używać "aliasu" pd).
Utwórzmy zatem przykładowy DataFrame, złożony z 2 kolumn i 5 wierszy (co można później sprawdzić wykonując komendę df1.shape).

df1 = pd.DataFrame({"col_a": [1, 2, 3, 5, 10], "col_b": [10, 10, 20, 25, 20]})

Aby poznać podstawy, spróbujmy przeprowadzić kilka prostych operacji:

  • odwołanie do pojedynczej kolumny: df1.col_a lub df1['col_a']
  • odwołanie do kilku kolumn: df1[lista_kolumn] - np. df1[['col_a', 'col_b']]
  • odwołanie do wybranych wierszy kolumny: df1.col_a[2:4] lub df1['col_a'][2:4] (domyślnie indeksowanie tak jak w listach, czyli od zera, ale jeśli chcemy możemy zmienić indeksy)

Oczywiście na wynikach tych operacji (czyli pojedynczej kolumnie lub wycinku ramki danych) chcemy przeprowadzić obliczenia. Spójrzmy na te przykłady:

  • suma z danej kolumny: df1.col_a.sum() lub df1['col_a'].sum()(oczywiście analogicznie możemy obliczyć sumę "wycinka" - wybranych wierszy pojedynczej kolumny)
  • średnia z danej kolumny: df1.col_a.mean() lub df1['col_a'].mean()
  • podobnie możemy skorzystać z metod np.: .std() (odchylenie standardowe), .min(), .max(), .cumsum() (skumulowana suma)
  • radzenie sobie z obserwcjami - brakami danych (not a number: NaN lub np.nan): df1.dropna() () lub df1.fillna(-99) (zastąpienie NaN na podaną wartość, np. -99)
  • suma kolumn DataFrame: df1.sum(axis=0)
  • suma wierszy DataFrame: df1.sum(axis=1)

df1.head() - ogląd danych (wyświetla pierwszych kilka wierszy) df1.columns - lista kolumn c.d.n.

Operacja na zbiorach danych (w Python i R) - cz. 1

18 Aug 2018 / admin

Już pisałem, że same obliczenia zwykle zajmują 10-15% czasu. Z pozostałych 85-90% większość trzeba przezanczyć na sprawdzenie i przekształcanie danych. Dziś przestawię jak efektywnie przeszktałcać dane w Python (będzie też trochę o R). Wyobrażmy sobie, że mamy tabelę (ramkę danych) z codziennymi wydatkami, np.:

KATEGORIA CENA ILOSC WART
Żywność 1,5 2 3
Rozrywka 24 1 24
Żywność 4 5 20
Ubrania 99 1 99
Ubrania 25 2 50
Żywność 3 1 3
Żywność 0,5 40 20
Rozrywka 7,5 3 22,5
Żywność 14,99 3 44,97
Rozrywka 50 1 50


Naturalnym problemem analitycznym jest policzenie sumy wydaktów dla każdej kategorii. Wydawać się może, że osiągniemy to przy pomocy pętli, coś w rodzaju:

  • Weź zmienną SUMA = 0
  • Kolejno dla wierszy i = 1,...,N (gdzie N to liczba wierszy) wykonaj polecenia:
    • Sprawdź czy kolumna KATEGORIA == "Żywność"
    • Jeśli powyższy warunek jest prawdziwy to dodaj do zmiennej SUMA wartość kolumny WART


Następnie to samo to dla kategorii "Ubrania" i "Rozrywka". Takie rozwiązanie zadziała i otrzymany prawidłowy wyniki (90,97), ale można lepiej (bardziej zwięzły zapis i znacznie krótszy czas niezbędny do obliczenia). Takie operacje na danych to standardowa operacja agregująca ("GROUP BY" w SQL), standardowo zaimplementowana w bazach danych np. SQL, ale także w Python (Pandas) i R (data.table albo dplyr). W takiej operacji podajemy zmienną grupującą (czyli zmienną, która rozróżnia podgrupy, tu: KATEGORIA) i operacje jakie chcemy wykonać w każdej z podgrup (tu: suma zmiennej WART). Oczywiście potencjalny wachlarz zastosować to nie tylko suma, równie często korzystamy ze średniej i liczby wystąpień ("COUNT" w SQL) albo liczby unikalnych wartości danej zmiennej ("COUNT DISTINCT").

Przykład z życia wzięty.

W bazie danych z niewiadonych przyczyn "pojawiły się" braki obseracji. Skonstruowałem indykator (zmienną zerojedynkową) dla braku danych i okazało się, że udział braków danych sięga ok. 40%. Przyjrzyjmy się temu bliżej - policzyłem udział braków danych wg miejscowości, wyniki są takie:

Zgierz 0
Łódź 1
Ostrołęka 1
Warszawa 0
Radomsko 0
Pabianice 0
Piotrków Tryb. 1
Tomaszów Maz. 1
Kalisz 0


Widzicie to co ja? Braki obserwacji są tylko dla tych miejscowości, które mają "polskie litery". To pokazało, gdzie tkwi problem - różne kodowanie polskich znaków w różnych plikach (baza powstawała poprzez łączenie różnych zbiorów danych). Teraz rozwiązanie problemu zajęło mniej niż 5 minut.

Przykładowe kody (implementacja w Pandas + Python i Data.table + R)

Okazuje się, że implementacja przedstawionych wyżej operacji jest bardzo prosta i przyjemna, zarówno w Python jak i R. Przy okazji tych przykładów podaję odpowiedniki komend dla zapytań SQL -- przyda się tym, którzy znają choćby podstawy SQL albo gdy chcecie wyjaśnić sens jakieś operacji "czystemu" programiście.
Poniżej kody, przy założeniu że mamy w pamięci ramkę danych o nazwie DF, zawierającą zmienne Kategoria i Wartosc.

Operacje group by w Python - Pandas import pandas as pd
# wprowadz ramke danych, np. DF = pd.DataFrame( {"Wart": [0,1,3,3,4,5,6,6,8,8], "Kategoria": [1,1,1,1,2,2,2,2,2,2]} )
DF.groupby(by = "Kategoria").sum()
DF.groupby(by = "Kategoria").count()
DF.groupby(by = "Kategoria").unique()
DF.groupby(by = "Kategoria").nunique()
# Trochę bardziej elegancko
DF_GroupBy = DF.groupby(by = "Kategoria")
DF_GroupBy.sum()
DF_GroupBy.count()    # itd.


Operacje group by w R - data.table library(data.table)
# wprowadz ramke danych, np. DF = data.frame( Wart = c(0,1,3,3,4,5,6,6,8,8), Kategoria = c(1,1,1,1,2,2,2,2,2,2) )
DF = data.table(DF)
DF[ , .(Suma = sum(Wart)), by = Kategoria]
DF[ , .N, by = Kategoria]
DF[ , .(Unikalne = unique(Wart)), by = Kategoria]
DF[ , .(L_Unikalnych = length(unique(Wart))), b = Kategoria]


1