Twój koszyk jest obecnie pusty!
Podstawy analizy danych z biblioteką Pandas
W wcześniejszym wpisie: Wstęp do przetwarzania i analizy danych z biblioteką Pandas omówiliśmy podstawy biblioteki Pandas. W tym wpisie skupimy się na nieco bardziej zaawansowanych funkcjach.
W tej części poznamy metody podsumowywać serie danych w Pandas i sprawdzimy możliwości edycji danych poprzez mapowanie. Omówimy jak wykonać grupowanie danych oraz jak pracować z pojedynczymi i mnogimi indeksami ramek. Na koniec sprawdzimy funkcje sortowania danych w DataFrame.
Podsumowywanie danych w Pandas
W świecie analizy danych, skuteczne podsumowywanie informacji zawartych w zbiorze danych to kluczowy krok w zrozumieniu ich istoty. Biblioteka Pandas, będąca nieodłącznym narzędziem dla specjalistów od machine learning, oferuje szereg funkcji podsumowujących, które ułatwiają szybkie i efektywne analizy. Aby zaprezentować różne funkcje podsumowujące wykorzystamy bazę danych statystyk policyjnych dostępną na kaggle.com pod adresem: https://www.kaggle.com/datasets/melihkanbay/police. Plik CSV zawiera informacje tj. data i godzina zatrzymania, wiek zatrzymanego, rodzaj przekroczenia przepisów czy rasę kierowcy.
Funkcja describe()
Jedną z najczęściej używanych funkcji podsumowujących jest describe()
. Ta prosta, a jednocześnie potężna metoda dostarcza podstawowe statystyki opisowe, takie jak średnia, odchylenie standardowe, minimum, maksimum, oraz kwartyle dla każdej kolumny w DataFrame.
import pandas as pd
df = pd.read_csv('police.csv')
print(df.driver_age.describe())
# Wynik:
# count 86120.000000
# mean 34.011333
# std 12.738564
# min 15.000000
# 25% 23.000000
# 50% 31.000000
# 75% 43.000000
# max 99.000000
# Name: driver_age, dtype: float64
W użytym przykładzie widzimy, że średnia wieku zatrzymanej osoby to ~34 lata (mean). Odchylenie standardowe wynosi ~12,74. Maksymalny wiek osoby zatrzymanej to 99 lat :), a minimalny to 15 lat.
Funkcja unique()
Inną ważną funkcją jest unique()
, która umożliwia identyfikację unikalnych wartości w kolumnie. Dla specjalisty od machine learning, który stara się zrozumieć różnorodność danych, ta funkcja jest niezastąpiona, umożliwiając szybkie określenie zakresu i dystrybucji poszczególnych cech.
Jedna z kolumn w naszej testowej bazie danych zawiera informacje o rodzaju zatrzymania. Sprawdźmy, jakie wartości są wpisane w tej kolumnie, funkcja unique()
idealnie się do tego sprawdzi.
import pandas as pd
df = pd.read_csv('police.csv')
print(df.violation.unique())
# Wynik:
# ['Speeding' 'Other' 'Equipment' 'Moving violation' nan 'Registration/plates' 'Seat belt']
W wyniku widzimy kilka rodzajów powodów zatrzymania, które znajdują się w bazie danych.
Funkcja mean()
Średnia, czyli mean()
, to kolejne kluczowe narzędzie, pozwalające na ocenę centralnej tendencji danych. W połączeniu z describe()
i unique()
, specjalista może szybko uzyskać szerszy obraz struktury danych, zidentyfikować ewentualne skoki czy nietypowe wartości.
Funkcja value_counts()
Funkcja value_counts()
stanowi istotne narzędzie do analizy rozkładu kategorii w kolumnie. Dla specjalisty od machine learning, który pracuje z danymi kategorycznymi, ta funkcja pozwala na szybkie zrozumienie częstości występowania poszczególnych wartości, co może być kluczowe przy podejmowaniu decyzji dotyczących przetwarzania i przygotowywania danych.
We wcześniejszym przykładzie sprawdziliśmy jakie kategorie znajdują się w kolumnie: violation – czyli rodzaj zatrzymania. Dzięki funkcji value_counts()
możemy sprawdzić, jak często występują poszczególne kategorie.
import pandas as pd
df = pd.read_csv('police.csv')
print(df.violation.value_counts())
# Wynik:
# violation
# Speeding 48463
# Moving violation 16224
# Equipment 11020
# Other 4317
# Registration/plates 3432
# Seat belt 2952
# Name: count, dtype: int64
Jak widać, najczęstsza przyczyna zatrzymania do kontroli to przekroczenie prędkości.
W sumie, korzystanie z tych funkcji podsumowujących w bibliotece Pandas stanowi solidny punkt wyjścia dla specjalisty od machine learning, umożliwiając skuteczne przygotowanie danych do bardziej zaawansowanych analiz i modelowania. To narzędzia, które pozwalają efektywnie zgłębiać strukturę danych, identyfikować istotne cechy i przyspieszyć procesy decyzyjne w projektach machine learning.
Mapowanie wartości w DataFrame w Pandas
W świecie analizy danych, umiejętność mapowania danych to kluczowy aspekt, który umożliwia przetwarzanie danych i wyciąganie głębszych wniosków z informacji zawartych w DataFrame’ach biblioteki Pandas. Mapowanie danych w Pandas może odnosić się do dwóch głównych kontekstów: mapowania wartości w kolumnie na inne wartości oraz mapowania funkcji na całe kolumny.
Mapowanie wartości w kolumnie na inne wartości
W przypadku pierwszego scenariusza, kiedy chcemy przekształcić lub zakodować kategorie w kolumnie, wykorzystujemy funkcję map()
. Dla przykładu, w naszej bazie danych mamy kolumnę driver_gender określającą płeć kierowcy: M lub F. Możemy użyć map()
do przypisania im wartości liczbowych, co ułatwia ich analizę przez modele machine learning.
# Przykład mapowania kategorii na wartości liczbowe
df['driver_gender'] = df['driver_gender'].map({'F': 1, 'M': 2})
Metodę map()
możemy także wykorzystać w inny sposób, przekazując jej funkcję przekształcającą. Zobaczmy na przykład:
import pandas as pd
import datetime
df = pd.read_csv('police.csv')
# Mapujemy kolumnę z wykorzystaniem funkcji mapującej lambda
df['stop_year'] = df.stop_date.map(lambda sd: pd.to_datetime(sd).year)
print(df.head())
W przykładzie powyżej tworzymy nową kolumnę stop_year. W kolumnie zapisujemy efekt mapowania kolumny stop_date, z której wyciągamy rok zatrzymania. Taka operacja, wraz z wywołaniem funkcji values_counts()
na nowoutworzonej kolumnie pokaże nam informacje, niczym z roczników statystycznych policji:
# Podsumowanie nowoutworzonej kolumny stop_year
print(df.stop_year.value_counts())
# Wynik:
# stop_year
# 2012 10970
# 2006 10639
# 2007 9476
# 2014 9228
# 2008 8752
# 2015 8599
# 2011 8126
# 2013 7924
# 2009 7908
# 2010 7561
# 2005 2558
Wynika z tego, że najwięcej zatrzymań miało miejsce w 2012 roku.
Mapowanie na całe kolumny
W tym przypadku wykorzystamy funkcję apply()
. Funkcja apply()
pozwala na zastosowanie dowolnej funkcji do każdej wartości w kolumnie, co jest szczególnie przydatne, gdy chcemy przekształcić dane na podstawie bardziej zaawansowanych operacji.
import pandas as pd
df = pd.read_csv('police.csv')
def transform_row(row):
row.stop_year = pd.to_datetime(row.stop_date).year
row.driver_gender = 1 if row.driver_gender == 'F' else 2
return row
df['stop_year'] = None
df = df.apply(transform_row, axis='columns')
print( df.head() )
W powyższym przykładzie nadajemy funkcję przekształcającą na każdy wiersz danych. Funkcja za jednym zamachem uzupełnia wartość stop_year z rokiem zatrzymania oraz driver_gender wartością 1 jeśli wcześniejsza wartość pola driver_gender wynosiła 'F’, lub 2 w każdym innym przypadku.
Jeśli użylibyśmy funkcji apply()
z parametrem axis='index'
, to zamiast przekazywać funkcję do transformacji każdego wiersza, musielibyśmy dostarczyć funkcję do przekształcenia każdej kolumny.
Wbudowane operatory mapowania
Pewne operacje mapowania możemy wykonać w Pandas za pomocą wbudowanych operatorów.
Przykładowo, we wcześniejszym przykładzie dołożyliśmy kolumnę danych stop_year zawierającą wyłącznie dokładny rok zatrzymania. Jeśli chcielibyśmy zmodyfikować kolumnę stop_year, aby zawierała informację o tym, jak dawno temu odbyło się zatrzymanie, możemy wykonać dodatkowy kod do powyższego przykładu:
import datetime
today = datetime.date.today()
df.stop_year = today.year - df.stop_year
print(df.head())
Wbudowane operatory mapowania mają taką przewagę, że są szybsze niż użycie funkcji apply()
lub map()
. Innym przykładem użycia wbudowanych operatorów mapowania na naszej bazie danych może być połączenie daty i czasu w jedno pole:
df['stop_datetime'] = df.stop_date + " " + df.stop_time
print(df.head())
Grupowanie danych
Grupowanie danych umożliwia przeprowadzanie zwięzłych analiz i identyfikowanie ukrytych wzorców w zestawach danych. W poprzednich sekcjach omówiliśmy już przykład grupowania przy użyciu funkcji value_counts()
. Ta funkcja grupuje etykiety i zlicza liczbę wystąpień każdej z nich. Podobną operację można zrealizować przy użyciu funkcji grupowania w bibliotece Pandas, jak pokazuję poniżej:
print(df.groupby('violation').violation.count())
# Wynik:
# violation
# Equipment 11020
# Moving violation 16224
# Other 4317
# Registration/plates 3432
# Seat belt 2952
# Speeding 48463
# Name: violation, dtype: int64
Nasz testowy dataset zawiera informacje o policyjnych zatrzymaniach. Jedna z serii zawiera informację o płci, a inna wiek. Sprawdźmy, czy kobiety i mężczyźni przekraczając granice prawa w podobnym wieku:
print( df.groupby('driver_gender').driver_age.describe() )
Sam wyciągnij wnioski z otrzymanych rezultatów :).
Grupowanie wielu kolumn
We wcześniejszych przykładach grupowaliśmy etykiety dla jednej kolumny. Ramka danych (DataFrame) z Pandas daje także możliwości grupowania danych po więcej niż jednej kolumnie.
print( df.groupby(['driver_race', 'driver_gender']).driver_race.count() )
Powyższy przykład zgrupuje najpierw dane według rasy kierowcy, a następnie jego płci i policzy jak często osoby o tych parametrach były zatrzymywane.
Jeśli chcielibyśmy przeprowadzić konkurs na najstarszego zatrzymanego w różnych kategoriach rasy i płci, moglibyśmy wykonać operację:
print( df.groupby(['driver_race', 'driver_gender']).apply(lambda df: df.loc[df.driver_age.idxmax()]) )
Funkcja idxmax()
w bibliotece Pandas służy do zwracania indeksu pierwszego wystąpienia maksymalnej wartości w danej serii danych lub kolumnie w ramce danych. Na przykład, po zastosowaniu idxmax()
do kolumny, otrzymujemy indeks wiersza, gdzie wartość jest maksymalna. Jest to przydatne narzędzie do identyfikowania lokalizacji maksymalnych wartości w ramce danych.
Każdą z generowanych grup możemy postrzegać jako fragment naszej ramki danych (DataFrame), zawierający jedynie dane z wartościami pasującymi do określonych kryteriów. Ten fragment ramki danych jest także dostępny dla nas bezpośrednio poprzez zastosowanie metody apply()
, co umożliwia swobodną manipulację danymi. Dzięki temu dla powyższego grupowania mogliśmy dopisać dane najstarszego zatrzymanego w danym grupowaniu.
Funkcje agregujące
Funkcja agg()
w bibliotece Pandas (skrócona forma od „aggregate”), pełni istotną rolę w przetwarzaniu i analizie danych. Pozwala ona na jednoczesne zastosowanie wielu funkcji agregujących do określonych kolumn lub grup w ramce danych. Przy użyciu agg()
, możemy szybko uzyskać różnorodne statystyki dla naszych danych, takie jak suma, średnia, minimum, maksimum czy niestandardowe funkcje. Jest to szczególnie użyteczne przy grupowaniu danych, gdzie możemy zastosować różne funkcje agregujące dla różnych kolumn lub grup, zwiększając tym samym elastyczność analizy.
print( df.groupby(['driver_race', 'driver_gender']).driver_age.agg([len, min, max]))
# Wynik:
# len min max
# driver_race driver_gender
# Asian F 513 17.0 75.0
# M 1746 17.0 82.0
# Black F 2580 17.0 78.0
# M 9664 15.0 85.0
# Hispanic F 1871 16.0 71.0
# M 7636 15.0 76.0
# Other F 26 20.0 60.0
# M 214 18.0 74.0
# White F 18521 15.0 99.0
# M 43635 15.0 94.0
Multi-indeksy
W bibliotece Pandas, multi-indeks (ang. multi-index lub hierarchical index) to sposób hierarchicznego indeksowania w ramkach danych. Pozwala on na utworzenie indeksu, który składa się z wielu poziomów. Dzięki temu można reprezentować dane wielowymiarowe w ramce danych, co jest szczególnie przydatne w przypadku pracy z danymi hierarchicznymi lub wielowymiarowymi.
Multi-indeksy w Pandas można utworzyć na różne sposoby. Jednym z nich jest przekazanie listy indeksów do parametru index
podczas tworzenia obiektu DataFrame.
import pandas as pd
# Tworzenie multiindeksu
arrays = [['A', 'A', 'B', 'B'], [1, 2, 1, 2]]
multi_index = pd.MultiIndex.from_arrays(arrays, names=('letters', 'numbers'))
# Tworzenie ramki danych z multiindeksem
df = pd.DataFrame({'value': [1, 2, 3, 4]}, index=multi_index)
print(df)
Indeksowanie hierarchiczne wielu kolumn łatwo wykonać operacją grupowania danych. Można powiedzieć, że multi-indeksy są konsekwencją operacji grupowania danych. Wróćmy do wcześniejszego przykładu grupowania danych według wielu kolumn:
import pandas as pd
df = pd.read_csv('police.csv')
grouped = df.groupby(['driver_race', 'driver_gender']).driver_age.agg([len, "min", "max"])
print( grouped )
# Wynik:
# len min max
# driver_race driver_gender
# Asian F 513 17.0 75.0
# M 1746 17.0 82.0
# Black F 2580 17.0 78.0
# M 9664 15.0 85.0
# Hispanic F 1871 16.0 71.0
# M 7636 15.0 76.0
# Other F 26 20.0 60.0
# M 214 18.0 74.0
# White F 18521 15.0 99.0
# M 43635 15.0 94.0
Jak nawet widać wizualnie w powyższym przykładzie, dane zostały zaindeksowane najpierw według kolumny driver_race
, a następnie driver_gender
. Aby sprawdzić, jak są indeksowane dane w ramce możemy sprawdzić właściwość index
:
print( grouped.index )
Problemy z multi-index
Mimo że multiindeksy w Pandas są potężnym narzędziem do pracy z danymi hierarchicznymi, mogą też wprowadzać pewne problemy i wyzwania. Oto niektóre z problemów, które mogą wystąpić przy korzystaniu z multiindeksów:
- Skomplikowany kod: Praca z multiindeksami może sprawić, że kod staje się bardziej złożony i trudny do zrozumienia, zwłaszcza dla osób, które nie są zaznajomione z tym mechanizmem.
- Trudność w indeksowaniu i selekcji danych: Operacje indeksowania i selekcji danych mogą być bardziej złożone przy użyciu multiindeksów, zwłaszcza gdy chcemy uzyskać dostęp do konkretnych poziomów indeksu lub kombinacji wartości.
- Problemy z sortowaniem: Sortowanie ramki danych z multiindeksem może być wyzwaniem, zwłaszcza jeśli dane nie są już posortowane według indeksu. Wprowadzenie niepoprawnego porządku może prowadzić do błędnych wyników.
- Wprowadzenie duplikatów w indeksie: W przypadku multiindeksów istnieje możliwość wprowadzenia duplikatów, co może prowadzić do niejednoznaczności w operacjach na danych.
- Zużycie pamięci: Multiindeksy mogą zużywać więcej pamięci niż jednowymiarowy indeks, co może być problemem w przypadku dużych zbiorów danych.
- Ograniczenia w niektórych operacjach: Niektóre operacje mogą być bardziej ograniczone lub skomplikowane przy użyciu multiindeksów, co może wpływać na wydajność w niektórych przypadkach.
Konwersja multi-indeksu do indeksu pojedynczego
W związku z omówionymi powyżej problemami z danymi z wieloma indeksami często jest potrzeba konwersji do indeksy pojedynczego. Służy do tego funkcja reset_index()
:
import pandas as pd
df = pd.read_csv('police.csv')
grouped = df.groupby(['driver_race', 'driver_gender']).driver_age.agg([len, "min", "max"])
grouped_no_index = grouped.reset_index()
print( grouped_no_index )
# Wynik:
# driver_race driver_gender len min max
# 0 Asian F 513 17.0 75.0
# 1 Asian M 1746 17.0 82.0
# 2 Black F 2580 17.0 78.0
# 3 Black M 9664 15.0 85.0
# 4 Hispanic F 1871 16.0 71.0
# 5 Hispanic M 7636 15.0 76.0
# 6 Other F 26 20.0 60.0
# 7 Other M 214 18.0 74.0
# 8 White F 18521 15.0 99.0
# 9 White M 43635 15.0 94.0
Jak widać nawet wizualnie, dane zostały pozbawione wielu indeksów (driver_race i driver_gender), a dane są indeksowane numerycznie, od 0 do 9. Porównajmy jeszcze, jak wygląda selekcja pojedynczego wiersza danych przed i po konwersji indeksów:
import pandas as pd
df = pd.read_csv('police.csv')
grouped = df.groupby(['driver_race', 'driver_gender']).driver_age.agg([len, "min", "max"])
print( grouped.loc["Hispanic", "F"] )
grouped_no_index = grouped.reset_index()
print( grouped_no_index.loc[4] )
Sortowanie danych
W bibliotece Pandas istnieje kilka funkcji do sortowania danych w ramkach danych. Najważniejsza z nich to sort_values
, ale możemy wykorzystać sort_index
, aby sortować dane według indeksu. Poniżej znajduje się kilka przykładów sortowania:
import pandas as pd
df = pd.read_csv('police.csv')
grouped = df.groupby(['driver_race', 'driver_gender']).driver_age.agg([len, "min", "max"])
grouped_no_index = grouped.reset_index()
# Sortujemy rosnąco według kolumny `len`
print( grouped_no_index.sort_values(by='len') )
# Sortujemy malejąco według kolumny `min`
print( grouped_no_index.sort_values(by='min', ascending=False) )
# Sortujemy rosnąco według dwóch kolumn `min` i `max`
print( grouped_no_index.sort_values(by=['min', 'max']) )
# Sortujemy rosnąco według wartości indeksu
print( grouped_no_index.sort_index() )