176 KiB
Analiza Danych w Pythonie: pandas
pandas
Biblioteka pandas
jest podstawowym narzędziem w ekosystemie Pythona do analizy danych:
- dostarcza dwa podstawowe typy danych:
Series
(szereg, 1D)DataFrame
(ramka danych, 2D)
- operacje na tych obiektach: obsługa brakujących wartości, łączenie danych;
- obsługuje dane różnego typu, np. szeregi czasowe;
- biblioteka bazuje na
numpy
-- bibliotece do obliczeń numerycznych; - pozwala też na prostą wizualizację danych;
- ETL: extract, transform, load.
Żeby zaimportowąc bibliotekę pandas
wystarczy:
import pandas as pd
pd
Zadanie 0: sprawdź, czy masz zainstalowaną bibliotekę pandas
.
Szeregi (pd.Series
)
Szereg reprezentuje jednorodne dane jednowymiarowe - jest odpowiednikiem wektora w R.
Szeregi możemy tworzyć na różne sposoby (więcej za chwilę), np. z obiektów tj. listy i słowniki.
Dane muszą być jednorodne. W przeciwnym przypadku nastąpi automatyczna konwersja.
Podczas tworzenia szeregu musimy podać jeden obowiązkowy argument
data
- dane.Ponadto możemy podać też indeks (
index
), typ danych (dtype
) lub nazwę (name
).class pandas.Series(data=None, index=None, dtype=None, name=None)
Podczas tworzenie szeregu mozemy podać dane w formacie listy lub słownika.
Poniżej jest przykład przedstawiający tworzenie szeregu z danych, które są zawarte w liście:
data = [211819, 682758, 737011, 779511, 673790, 673790, 444177, 136791]
s = pd.Series(data)
s
0 211819 1 682758 2 737011 3 779511 4 673790 5 673790 6 444177 7 136791 dtype: int64
W przypadku, gdy dane pochodzą z listy i nie podaliśmy indeksu, pandas doda automatyczny indeks liczbowy zaczynający się od 0.
W przypadku przekazania słownika jako danych do szeregu, pandas wykorzysta klucze do stworzenia indeksu:
members = {'April': 211819,'May': 682758, 'June': 737011, 'July': 779511}
s = pd.Series(members)
s
April 211819 May 682758 June 737011 July 779511 dtype: int64
Podczas tworzenia szeregu możemy zdefiniować indeks, jak i nazwę szeregu:
months = ['April', 'May', 'June', 'July']
data = [211819, 682758, 737011, 779511]
s = pd.Series(data=data, index=months, dtype=float, name='Rides')
s
April 211819.0 May 682758.0 June 737011.0 July 779511.0 Name: Rides, dtype: float64
Odwołanie się do poszczególnego elementu odbywa się przy pomocy klucza z indeksu.
members = {'April': 211819,'May': 682758, 'June': 737011, 'July': 779511}
s = pd.Series(members)
print(s['April'])
s['August'] = 673790
s
211819
April 211819 May 682758 June 737011 July 779511 August 673790 dtype: int64
Dodanie elementu do szeregu odbywa się poprzez definiowanie nowego klucza:
members = {'April': 211819,'May': 682758, 'June': 737011, 'July': 779511}
s = pd.Series(members)
s['August'] = 673790
s
April 211819 May 682758 June 737011 July 779511 August 673790 dtype: int64
Więcej nt. indeksowania w szeregach w dalszej części kursu.
Podstawowa cechą szeregu jest wykonywanie operacji w sposób wektorowy. Działa to w następujący sposób:
- gdy w obu szeregach jest zawarty ten sam klucz, to są sumowane ich wartości;
- w przeciwnym przypadku wartość klucza w wynikowym szeregu to
pd.NaN
. - Równoważnie możemy wykorzystać metodę
pandas.Series.add
. W tym przypadku możemy podać domyślną wartość w przypadku braku klucza.
members = pd.Series({'May': 682758, 'June': 737011, 'August': 673790, 'July': 779511,
'September': 673790, 'October': 444177})
occasionals = pd.Series({'May': 147898, 'June': 171494, 'July': 194316, 'August': 206809,
'September': 140492})
all_data = members + occasionals
# Równoważnie
all_data = members.add(occasionals)
all_data
August 880599.0 July 973827.0 June 908505.0 May 830656.0 October NaN September 814282.0 dtype: float64
Możemy wykonać operacje arytmetyczne na szeregu:
members = pd.Series({'May': 682758, 'June': 737011, 'July': 779511, 'August': 673790,
'September': 673790, 'October': 444177})
members += 1000
members
May 683758 June 738011 July 780511 August 674790 September 674790 October 445177 dtype: int64
members
May 683758 June 738011 July 780511 August 674790 September 674790 October 445177 dtype: int64
members + 10000000000000000
May 10000000000683758 June 10000000000738011 July 10000000000780511 August 10000000000674790 September 10000000000674790 October 10000000000445177 dtype: int64
Podsumowanie
- Szeregi działają podobnie do słowników, z tą różnicą, że wartości muszą być jednorodne (tego samego typu).
- Odwołanie do poszczególnych elementów odbywa się poprzez nawiasy
[]
i podanie klucza. - W przeciwieństwie do słowników, możemy w prosty sposób wykonywać operacje arytmetyczne.
Zadanie 1
- Stwórz szereg
n
, który będzie zawierać liczby od 0 do 10 (włącznie). - Stwórz szereg
n2
, który będzie zawierać kwadraty liczb od 0 do 10 (włącznie). - Następnie stwórz szereg
trojkatne
, który będzie sumą powyższych szeregów podzieloną przez 2.
n= list(range(10+1))
n
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
n = pd.Series(n)
n
0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 10 dtype: int64
n2 = n**2
n2
0 0 1 1 2 4 3 9 4 16 5 25 6 36 7 49 8 64 9 81 10 100 dtype: int64
trojkatne = ( n + n2 ) / 2
trojkatne
0 0.0 1 1.0 2 3.0 3 6.0 4 10.0 5 15.0 6 21.0 7 28.0 8 36.0 9 45.0 10 55.0 dtype: float64
Ramka danych (pd.DataFrame
)
Ramka danych jest podstawową strukturą danych w bibliotece pandas
, która pozwala na trzymanie i reprezentowanie danych tabelarycznych (dwuwymiarowych).
- Posiada kolumny (cechy) i wiersze (obserwacje, przykłady).
- Możemy też patrzeć na nią jak na słownik, którego wartościami są szeregi.
class pandas.DataFrame(data=None, index=None, columns=None, dtype=None)
Ramkę danych możemy stworzyć na różne sposoby.
Pierwszy z nich ("kolumnowy") polega na zdefiniowaniu ramki poprzez podanie szeregów jako kolumn:
members
May 682758 June 737011 July 779511 dtype: int64
occasionals
May 147898 June 171494 July 194316 dtype: int64
members = pd.Series({'Maydfdsgfdg': 682758, 'June': 737011, 'July': 779511})
occasionals = pd.Series({'May': 147898, 'June': 171494, 'July': 194316})
df = pd.DataFrame({'members': members, 'occasionals': occasionals})
df
members | occasionals | |
---|---|---|
July | 779511.0 | 194316.0 |
June | 737011.0 | 171494.0 |
May | NaN | 147898.0 |
Maydfdsgfdg | 682758.0 | NaN |
Drugim popularnym sposobem jest przekazanie listy słowników. Wtedy pandas
zinterpretuje to jako listę przykładów:
data = [
{'members': 682758, 'occasionals': 147898},
{'occasionals': 171494,'members': 737011},
{'members': 779511, 'occasionals': 194316},
]
df = pd.DataFrame(data)
df
members | occasionals | |
---|---|---|
0 | 682758 | 147898 |
1 | 737011 | 171494 |
2 | 779511 | 194316 |
Możemy też wykorzystać metodę from_dict
(doc), która pozwala zdefiniować czy podane dane są w podane w postaci kolumnowej lub wierszowej:
data = {
'May': {'members': 682758, 'occasionals': 147898},
'June': {'members': 737011, 'occasionals': 171494},
'July': {'members': 779511, 'occasionals': 194316}
}
df = pd.DataFrame.from_dict(data, orient='index')
print('index\n', df)
print()
df = pd.DataFrame.from_dict(data, orient='columns')
print('columns\n', df)
index members occasionals May 682758 147898 June 737011 171494 July 779511 194316 columns May June July members 682758 737011 779511 occasionals 147898 171494 194316
Wczytywanie danych
Biblioteka pandas
pozwala na wczytanie i zapis danych z różnych formatów:
- formaty tekstowe, np.
csv
,json
- pliki arkuszy kalkulacyjnych: Excel (xls, xlsx)
- bazy danych
- inne:
sas
spss
Efektem wczytania danych jest odpowiednio stworzona ramka danych (DataFrame
).
Jednym z najprostszych formatów danych jest format csv
, gdzie kolejne wartości są rozdzielone przecinkiem.
Żeby wczytać dane w takim formacie należy użyć funkcji pandas.read_csv
.
Pandas pozwala na ustawienie wielu parametrów (np. separator, cudzysłowy). Więcej na ten temat w dokumentacji.
df = pd.read_csv('gapminder.csv')
df
Country | female_BMI | male_BMI | gdp | population | under5mortality | life_expectancy | fertility | |
---|---|---|---|---|---|---|---|---|
0 | Afghanistan | 21.07402 | 20.62058 | 1311.0 | 26528741.0 | 110.4 | 52.8 | 6.20 |
1 | Albania | 25.65726 | 26.44657 | 8644.0 | 2968026.0 | 17.9 | 76.8 | 1.76 |
2 | Algeria | 26.36841 | 24.59620 | 12314.0 | 34811059.0 | 29.5 | 75.5 | 2.73 |
3 | Angola | 23.48431 | 22.25083 | 7103.0 | 19842251.0 | 192.0 | 56.7 | 6.43 |
4 | Antigua and Barbuda | 27.50545 | 25.76602 | 25736.0 | 85350.0 | 10.9 | 75.5 | 2.16 |
... | ... | ... | ... | ... | ... | ... | ... | ... |
170 | Venezuela | 28.13408 | 27.44500 | 17911.0 | 28116716.0 | 17.1 | 74.2 | 2.53 |
171 | Vietnam | 21.06500 | 20.91630 | 4085.0 | 86589342.0 | 26.2 | 74.1 | 1.86 |
172 | Palestine | 29.02643 | 26.57750 | 3564.0 | 3854667.0 | 24.7 | 74.1 | 4.38 |
173 | Zambia | 23.05436 | 20.68321 | 3039.0 | 13114579.0 | 94.9 | 51.1 | 5.88 |
174 | Zimbabwe | 24.64522 | 22.02660 | 1286.0 | 13495462.0 | 98.3 | 47.3 | 3.85 |
175 rows × 8 columns
df = pd.read_csv('./titanic_train.tsv', delimiter='\t', index_col=0, nrows=5)
df
Survived | Pclass | Name | Sex | Age | SibSp | Parch | Ticket | Fare | Cabin | Embarked | |
---|---|---|---|---|---|---|---|---|---|---|---|
PassengerId | |||||||||||
1 | 0 | 3 | Braund\t Mr. Owen Harris | male | 22 | 1 | 0 | A/5 21171 | 7.2500 | NaN | S |
2 | 1 | 1 | Cumings\t Mrs. John Bradley (Florence Briggs T... | female | 38 | 1 | 0 | PC 17599 | 71.2833 | C85 | C |
3 | 1 | 3 | Heikkinen\t Miss. Laina | female | 26 | 0 | 0 | STON/O2. 3101282 | 7.9250 | NaN | S |
4 | 1 | 1 | Futrelle\t Mrs. Jacques Heath (Lily May Peel) | female | 35 | 1 | 0 | 113803 | 53.1000 | C123 | S |
5 | 0 | 3 | Allen\t Mr. William Henry | male | 35 | 0 | 0 | 373450 | 8.0500 | NaN | S |
Do wczytania danych z arkusza kalkulacyjnego służy funkcja pandas.read_excel
. Do otworzenia pliku xlsx
może być koniecnze ustawienie parametru: engine='openpyxl
. Więcej opcji w dokumentacji.
df = pd.read_excel('./bikes.xlsx', engine='openpyxl', nrows=5)
df
Innym ważnym źródłem informacji są bazy danych. Pandas potrafi komunikować się z bazą danych za pomocą biblioteki SQLAlchemy i dostarcza odpowiedną funkcję:
pandas.read_sql
- wczytanie całej tabeli lub zapytania do bazy danych
df = pd.read_sql('Album', con='sqlite:///Chinook.sqlite', index_col='AlbumId')
df
Title | ArtistId | |
---|---|---|
AlbumId | ||
1 | For Those About To Rock We Salute You | 1 |
2 | Balls to the Wall | 2 |
3 | Restless and Wild | 2 |
4 | Let There Be Rock | 1 |
5 | Big Ones | 3 |
... | ... | ... |
343 | Respighi:Pines of Rome | 226 |
344 | Schubert: The Late String Quartets & String Qu... | 272 |
345 | Monteverdi: L'Orfeo | 273 |
346 | Mozart: Chamber Music | 274 |
347 | Koyaanisqatsi (Soundtrack from the Motion Pict... | 275 |
347 rows × 2 columns
import sqlalchemy
engine = sqlalchemy.create_engine('sqlite:///Chinook.sqlite', echo=True)
connection = engine.raw_connection()
df = pd.read_sql('SELECT * FROM Album', con='sqlite:///Chinook.sqlite', index_col='AlbumId')
df
Title | ArtistId | |
---|---|---|
AlbumId | ||
1 | For Those About To Rock We Salute You | 1 |
2 | Balls to the Wall | 2 |
3 | Restless and Wild | 2 |
4 | Let There Be Rock | 1 |
5 | Big Ones | 3 |
... | ... | ... |
343 | Respighi:Pines of Rome | 226 |
344 | Schubert: The Late String Quartets & String Qu... | 272 |
345 | Monteverdi: L'Orfeo | 273 |
346 | Mozart: Chamber Music | 274 |
347 | Koyaanisqatsi (Soundtrack from the Motion Pict... | 275 |
347 rows × 2 columns
Podsumowanie
- Biblioteka
pandas
wspiera pobieranie danych z różnych formatów i źródeł. - Każda funkcja ma listę argumentów, które pozwalają na ustawić poszczególne parametry (np. read_csv).
Zapis i eksport danych
Pandas pozwala w prosty sposób na zapisywanie ramki danych do pliku.
members = pd.Series({'May': 682758, 'June': 737011, 'July': 779511})
occasionals = pd.Series({'May': 147898, 'June': 171494, 'July': 194316})
df = pd.DataFrame({'members': members, 'occasionals': occasionals})
# zapis do formatu CSV
df.to_csv('tmp.csv')
# zapis do arkusza kalkulacyjnego
df.to_excel('tmp.xlsx')
Ponadto możemy przekonwertować ramkę danych do JSONa lub Pythonowego słownika:
print(df.to_json())
{"members":{"May":682758,"June":737011,"July":779511},"occasionals":{"May":147898,"June":171494,"July":194316}}
print(df.to_dict())
{'members': {'May': 682758, 'June': 737011, 'July': 779511}, 'occasionals': {'May': 147898, 'June': 171494, 'July': 194316}}
Lub przekopiować dane do schowka:
df.to_clipboard()
Zadanie
- Przekonwertuj tabele
Customer
z bazyChinook.sqlite
do arkusza kalkulacyjnego. Plik wynikowy nazwijcustomers.xlsx
. - Tabela
Employee
zawiera informacje o pracownikach firmy Chinook. Wyswietl dane na ekranie i podaj miasta, w których mieszkają pracownicy. - Tabela
Invoice
zawiera informacje o fakturach. Przekonwertuj kolumnęBillingCountry
do pythonowego słownika, a następnie podaj najcześciej występującą wartość. Ile razy pojawiła się?
Ramka danych - podstawy
Kolumny
Na ramkę danych możemy patrzeć jak na swego rodzaju słownik, którego wartościami są szeregi. Pozwoli to na uzyskanie lepszej intuicji.
df = pd.read_csv('./gapminder.csv', index_col='Country', nrows=8, usecols=['Country', 'gdp', 'population','life_expectancy'])
df
gdp | population | life_expectancy | |
---|---|---|---|
Country | |||
Afghanistan | 1311.0 | 26528741.0 | 52.8 |
Albania | 8644.0 | 2968026.0 | 76.8 |
Algeria | 12314.0 | 34811059.0 | 75.5 |
Angola | 7103.0 | 19842251.0 | 56.7 |
Antigua and Barbuda | 25736.0 | 85350.0 | 75.5 |
Argentina | 14646.0 | 40381860.0 | 75.4 |
Armenia | 7383.0 | 2975029.0 | 72.3 |
Australia | 41312.0 | 21370348.0 | 81.6 |
Dostęp do poszczególnej kolumny możemy uzystać na dwa sposoby:
# notacja z kropką
df.population
Country Afghanistan 26528741.0 Albania 2968026.0 Algeria 34811059.0 Angola 19842251.0 Antigua and Barbuda 85350.0 Argentina 40381860.0 Armenia 2975029.0 Australia 21370348.0 Name: population, dtype: float64
# Operator []
df['population']
Country Afghanistan 26528741.0 Albania 2968026.0 Algeria 34811059.0 Angola 19842251.0 Antigua and Barbuda 85350.0 Argentina 40381860.0 Armenia 2975029.0 Australia 21370348.0 Name: population, dtype: float64
Do operatora []
możemy też podać listę nazw kolumn:
df[['gdp','population']]
gdp | population | |
---|---|---|
Country | ||
Afghanistan | 1311.0 | 26528741.0 |
Albania | 8644.0 | 2968026.0 |
Algeria | 12314.0 | 34811059.0 |
Angola | 7103.0 | 19842251.0 |
Antigua and Barbuda | 25736.0 | 85350.0 |
Argentina | 14646.0 | 40381860.0 |
Armenia | 7383.0 | 2975029.0 |
Australia | 41312.0 | 21370348.0 |
Listę kolumn możemy pobrać za pomocą:
df.columns
Index(['gdp', 'population', 'life_expectancy'], dtype='object')
df
gdp | population | life_expectancy | |
---|---|---|---|
Country | |||
Afghanistan | 1311.0 | 26528741.0 | 52.8 |
Albania | 8644.0 | 2968026.0 | 76.8 |
Algeria | 12314.0 | 34811059.0 | 75.5 |
Angola | 7103.0 | 19842251.0 | 56.7 |
Antigua and Barbuda | 25736.0 | 85350.0 | 75.5 |
Argentina | 14646.0 | 40381860.0 | 75.4 |
Armenia | 7383.0 | 2975029.0 | 72.3 |
Australia | 41312.0 | 21370348.0 | 81.6 |
df.columns = ['PKB', 'Populacja', 'ODŻ']
df
PKB | Populacja | ODŻ | |
---|---|---|---|
Country | |||
Afghanistan | 1311.0 | 26528741.0 | 52.8 |
Albania | 8644.0 | 2968026.0 | 76.8 |
Algeria | 12314.0 | 34811059.0 | 75.5 |
Angola | 7103.0 | 19842251.0 | 56.7 |
Antigua and Barbuda | 25736.0 | 85350.0 | 75.5 |
Argentina | 14646.0 | 40381860.0 | 75.4 |
Armenia | 7383.0 | 2975029.0 | 72.3 |
Australia | 41312.0 | 21370348.0 | 81.6 |
Żeby odwołać się do poszczególnych wierszy należy wykorzystać metodę loc
:
df.loc['Argentina']
PKB 14646.0 Populacja 40381860.0 ODŻ 75.4 Name: Argentina, dtype: float64
Metoda loc
również może przyjąć listę wierszy:
df.loc[['Albania', 'Angola']]
PKB | Populacja | ODŻ | |
---|---|---|---|
Country | |||
Albania | 8644.0 | 2968026.0 | 76.8 |
Angola | 7103.0 | 19842251.0 | 56.7 |
Możemy również podać drugi parametr: nazwy kolumn:
df2 = df.loc[['Albania', 'Angola'], ['PKB', 'Populacja']]
df2
PKB | Populacja | |
---|---|---|
Country | ||
Albania | 8644.0 | 2968026.0 |
Angola | 7103.0 | 19842251.0 |
Albo wykorzystać tzw. _slicing, cyzli operator :
:
df.loc['Albania': 'Angola', 'PKB': 'ODŻ']
PKB | Populacja | ODŻ | |
---|---|---|---|
Country | |||
Albania | 8644.0 | 2968026.0 | 76.8 |
Algeria | 12314.0 | 34811059.0 | 75.5 |
Angola | 7103.0 | 19842251.0 | 56.7 |
Żeby odwołać się do pojedyńczej wartości możemy użyć metody at
:
df.at['Angola', 'PKB']
Dostęp do indeksu:
df.index
Index(['Afghanistan', 'Albania', 'Algeria', 'Angola', 'Antigua and Barbuda', 'Argentina', 'Armenia', 'Australia'], dtype='object', name='Country')
Podstawowe metody pd.Series
i pd.DataFrame
members = pd.Series({'May': 682758, 'June': 737011, 'July': 779511, 'August': 673790,
'September': 673790, 'October': 444177})
occasionals = pd.Series({'May': 147898, 'June': 171494, 'July': 194316, 'August': 206809,
'September': 140492, 'October': 53596})
df = pd.DataFrame({'members': members, 'occasionals': occasionals})
df
members | occasionals | |
---|---|---|
May | 682758 | 147898 |
June | 737011 | 171494 |
July | 779511 | 194316 |
August | 673790 | 206809 |
September | 673790 | 140492 |
October | 444177 | 53596 |
Metoda head
pozwala tworzy nową ramkę danych z pierwszymi 5 przykładami:
df.head(2)
members | occasionals | |
---|---|---|
May | 682758 | 147898 |
June | 737011 | 171494 |
Metoda tail
robi to samo, ale z 5 ostatnymi przykładami:
df.tail()
members | occasionals | |
---|---|---|
June | 737011 | 171494 |
July | 779511 | 194316 |
August | 673790 | 206809 |
September | 673790 | 140492 |
October | 444177 | 53596 |
Metoda sample
pozwala na stworzenie nowej ramki danych z wylosowanymi n
przykładami:
df.sample(3)
members | occasionals | |
---|---|---|
September | 673790 | 140492 |
May | 682758 | 147898 |
June | 737011 | 171494 |
Metoda describe
zwraca podstawowe statystyki m.in.: liczebność, średnią, wartości skrajne:
df
members | occasionals | |
---|---|---|
May | 682758 | 147898 |
June | 737011 | 171494 |
July | 779511 | 194316 |
August | 673790 | 206809 |
September | 673790 | 140492 |
October | 444177 | 53596 |
df.describe()
members | occasionals | |
---|---|---|
count | 6.000000 | 6.000000 |
mean | 665172.833333 | 152434.166667 |
std | 116216.045456 | 54783.506738 |
min | 444177.000000 | 53596.000000 |
25% | 673790.000000 | 142343.500000 |
50% | 678274.000000 | 159696.000000 |
75% | 723447.750000 | 188610.500000 |
max | 779511.000000 | 206809.000000 |
Metoda info
zwraca informacje techniczne o kolumnach: np. typ danych:
df.info()
<class 'pandas.core.frame.DataFrame'> Index: 6 entries, May to October Data columns (total 2 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 members 6 non-null int64 1 occasionals 6 non-null int64 dtypes: int64(2) memory usage: 144.0+ bytes
Podstawową informacją o ramce danych to liczba przykładów w ramce danych. Możemy wykorzystać to tego funkcję len
:
len(df)
6
Natomiast atrybut shape
zwraca nam krotkę z liczbą przykładów i liczbą kolumn:
df.shape
(6, 2)
Operacja arytmetyczne
max
,idxmax
min
,idxmin
mean
count
df
members | occasionals | |
---|---|---|
May | 682758 | 147898 |
June | 737011 | 171494 |
July | 779511 | 194316 |
August | 673790 | 206809 |
September | 673790 | 140492 |
October | 444177 | 53596 |
Zbiór wartości i zliczanie wartości:
dane = pd.Series([1, 3, 2, 3, 1, 1, 2, 3, 2, 3])
print(dane.unique())
dane = pd.Series([1, 3, 2, 3, 1, 1, 2, 3, 2, 3])
print(dane.value_counts())
[1 3 2] 3 4 1 3 2 3 Name: count, dtype: int64
dane
0 1 1 3 2 2 3 3 4 1 5 1 6 2 7 3 8 2 9 3 dtype: int64
print(dane.value_counts())
3 4 1 3 2 3 Name: count, dtype: int64
Sprawdzanie czy brakuje danych:
df = pd.read_csv('./titanic_train.tsv', sep='\t', index_col='PassengerId')
df.Age.isnull()
PassengerId 1 False 2 False 3 False 4 False 5 False ... 887 False 888 False 889 True 890 False 891 False Name: Age, Length: 891, dtype: bool
Dodawanie i modyfikowanie danych
df = pd.read_csv('./gapminder.csv', index_col='Country', nrows=5)
df
conts = pd.Series({
'Afghanistan': 'Asia', 'Albania': 'Europe', 'Algeria':' Africa', 'Angola': 'Africa', 'Antigua and Barbuda': 'Americas'})
df['continent'] = conts
df['tmp'] = 1
df
df.loc['Argentina'] = {
'female_BMI': 27.46523,
'male_BMI': 27.5017,
'gdp': 14646.0,
'population': 40381860.0,
'under5mortality': 15.4,
'life_expectancy': 75.4,
'fertility': 2.24
}
df
df.drop('gdp', axis='columns')
Filtrowanie danych
Biblioteka pandas posiada 2 sposoby na filtrowanie danych zawartych w ramce danych:
- operator
[]
-- najbardziej rozpowszechniony; - metoda
query()
. Oba sposoby mają różną składnię.
df = pd.read_csv('./titanic_train.tsv', sep='\t', index_col='PassengerId')
df.head()
Survived | Pclass | Name | Sex | Age | SibSp | Parch | Ticket | Fare | Cabin | Embarked | |
---|---|---|---|---|---|---|---|---|---|---|---|
PassengerId | |||||||||||
1 | 0 | 3 | Braund\t Mr. Owen Harris | male | 22.0 | 1 | 0 | A/5 21171 | 7.2500 | NaN | S |
2 | 1 | 1 | Cumings\t Mrs. John Bradley (Florence Briggs T... | female | 38.0 | 1 | 0 | PC 17599 | 71.2833 | C85 | C |
3 | 1 | 3 | Heikkinen\t Miss. Laina | female | 26.0 | 0 | 0 | STON/O2. 3101282 | 7.9250 | NaN | S |
4 | 1 | 1 | Futrelle\t Mrs. Jacques Heath (Lily May Peel) | female | 35.0 | 1 | 0 | 113803 | 53.1000 | C123 | S |
5 | 0 | 3 | Allen\t Mr. William Henry | male | 35.0 | 0 | 0 | 373450 | 8.0500 | NaN | S |
df['Survived']
PassengerId 1 0 2 1 3 1 4 1 5 0 .. 887 0 888 1 889 0 890 1 891 0 Name: Survived, Length: 891, dtype: int64
df['Survived']
df['Survived'] == 1
PassengerId 1 False 2 True 3 True 4 True 5 False ... 887 False 888 True 889 False 890 True 891 False Name: Survived, Length: 891, dtype: bool
df_survived = df[df['Pclass'] == 1]
len(df)
891
len(df_survived)
216
df_survived
Survived | Pclass | Name | Sex | Age | SibSp | Parch | Ticket | Fare | Cabin | Embarked | |
---|---|---|---|---|---|---|---|---|---|---|---|
PassengerId | |||||||||||
2 | 1 | 1 | Cumings\t Mrs. John Bradley (Florence Briggs T... | female | 38.0 | 1 | 0 | PC 17599 | 71.2833 | C85 | C |
4 | 1 | 1 | Futrelle\t Mrs. Jacques Heath (Lily May Peel) | female | 35.0 | 1 | 0 | 113803 | 53.1000 | C123 | S |
7 | 0 | 1 | McCarthy\t Mr. Timothy J | male | 54.0 | 0 | 0 | 17463 | 51.8625 | E46 | S |
12 | 1 | 1 | Bonnell\t Miss. Elizabeth | female | 58.0 | 0 | 0 | 113783 | 26.5500 | C103 | S |
24 | 1 | 1 | Sloper\t Mr. William Thompson | male | 28.0 | 0 | 0 | 113788 | 35.5000 | A6 | S |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
872 | 1 | 1 | Beckwith\t Mrs. Richard Leonard (Sallie Monypeny) | female | 47.0 | 1 | 1 | 11751 | 52.5542 | D35 | S |
873 | 0 | 1 | Carlsson\t Mr. Frans Olof | male | 33.0 | 0 | 0 | 695 | 5.0000 | B51 B53 B55 | S |
880 | 1 | 1 | Potter\t Mrs. Thomas Jr (Lily Alexenia Wilson) | female | 56.0 | 0 | 1 | 11767 | 83.1583 | C50 | C |
888 | 1 | 1 | Graham\t Miss. Margaret Edith | female | 19.0 | 0 | 0 | 112053 | 30.0000 | B42 | S |
890 | 1 | 1 | Behr\t Mr. Karl Howell | male | 26.0 | 0 | 0 | 111369 | 30.0000 | C148 | C |
216 rows × 11 columns
Operatory
&
- koniukcja (i)|
- alternatywa (lub)~
- negacja (nie)()
- jeżeli mamy kilka warunków to warto je uporządkować w nawiasy
pierwsza_klasa = df['Pclass'] == 1
kobiety = df['Sex'] == 'female'
df[pierwsza_klasa & kobiety]
Survived | Pclass | Name | Sex | Age | SibSp | Parch | Ticket | Fare | Cabin | Embarked | |
---|---|---|---|---|---|---|---|---|---|---|---|
PassengerId | |||||||||||
2 | 1 | 1 | Cumings\t Mrs. John Bradley (Florence Briggs T... | female | 38.0 | 1 | 0 | PC 17599 | 71.2833 | C85 | C |
4 | 1 | 1 | Futrelle\t Mrs. Jacques Heath (Lily May Peel) | female | 35.0 | 1 | 0 | 113803 | 53.1000 | C123 | S |
12 | 1 | 1 | Bonnell\t Miss. Elizabeth | female | 58.0 | 0 | 0 | 113783 | 26.5500 | C103 | S |
32 | 1 | 1 | Spencer\t Mrs. William Augustus (Marie Eugenie) | female | NaN | 1 | 0 | PC 17569 | 146.5208 | B78 | C |
53 | 1 | 1 | Harper\t Mrs. Henry Sleeper (Myna Haxtun) | female | 49.0 | 1 | 0 | PC 17572 | 76.7292 | D33 | C |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
857 | 1 | 1 | Wick\t Mrs. George Dennick (Mary Hitchcock) | female | 45.0 | 1 | 1 | 36928 | 164.8667 | NaN | S |
863 | 1 | 1 | Swift\t Mrs. Frederick Joel (Margaret Welles B... | female | 48.0 | 0 | 0 | 17466 | 25.9292 | D17 | S |
872 | 1 | 1 | Beckwith\t Mrs. Richard Leonard (Sallie Monypeny) | female | 47.0 | 1 | 1 | 11751 | 52.5542 | D35 | S |
880 | 1 | 1 | Potter\t Mrs. Thomas Jr (Lily Alexenia Wilson) | female | 56.0 | 0 | 1 | 11767 | 83.1583 | C50 | C |
888 | 1 | 1 | Graham\t Miss. Margaret Edith | female | 19.0 | 0 | 0 | 112053 | 30.0000 | B42 | S |
94 rows × 11 columns
df[df['SibSp'] > df['Parch']]
Survived | Pclass | Name | Sex | Age | SibSp | Parch | Ticket | Fare | Cabin | Embarked | |
---|---|---|---|---|---|---|---|---|---|---|---|
PassengerId | |||||||||||
1 | 0 | 3 | Braund\t Mr. Owen Harris | male | 22.0 | 1 | 0 | A/5 21171 | 7.2500 | NaN | S |
2 | 1 | 1 | Cumings\t Mrs. John Bradley (Florence Briggs T... | female | 38.0 | 1 | 0 | PC 17599 | 71.2833 | C85 | C |
4 | 1 | 1 | Futrelle\t Mrs. Jacques Heath (Lily May Peel) | female | 35.0 | 1 | 0 | 113803 | 53.1000 | C123 | S |
8 | 0 | 3 | Palsson\t Master. Gosta Leonard | male | 2.0 | 3 | 1 | 349909 | 21.0750 | NaN | S |
10 | 1 | 2 | Nasser\t Mrs. Nicholas (Adele Achem) | female | 14.0 | 1 | 0 | 237736 | 30.0708 | NaN | C |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
861 | 0 | 3 | Hansen\t Mr. Claus Peter | male | 41.0 | 2 | 0 | 350026 | 14.1083 | NaN | S |
862 | 0 | 2 | Giles\t Mr. Frederick Edward | male | 21.0 | 1 | 0 | 28134 | 11.5000 | NaN | S |
864 | 0 | 3 | Sage\t Miss. Dorothy Edith "Dolly" | female | NaN | 8 | 2 | CA. 2343 | 69.5500 | NaN | S |
867 | 1 | 2 | Duran y More\t Miss. Asuncion | female | 27.0 | 1 | 0 | SC/PARIS 2149 | 13.8583 | NaN | C |
875 | 1 | 2 | Abelson\t Mrs. Samuel (Hannah Wizosky) | female | 28.0 | 1 | 0 | P/PP 3381 | 24.0000 | NaN | C |
192 rows × 11 columns
pd.DataFrame.query
Innym sposobem na filtrowanie danych jest metoda query
, która jako argument przyjmuje wyrażenie:
df.query('Pclass == 1').head()
Survived | Pclass | Name | Sex | Age | SibSp | Parch | Ticket | Fare | Cabin | Embarked | |
---|---|---|---|---|---|---|---|---|---|---|---|
PassengerId | |||||||||||
2 | 1 | 1 | Cumings\t Mrs. John Bradley (Florence Briggs T... | female | 38.0 | 1 | 0 | PC 17599 | 71.2833 | C85 | C |
4 | 1 | 1 | Futrelle\t Mrs. Jacques Heath (Lily May Peel) | female | 35.0 | 1 | 0 | 113803 | 53.1000 | C123 | S |
7 | 0 | 1 | McCarthy\t Mr. Timothy J | male | 54.0 | 0 | 0 | 17463 | 51.8625 | E46 | S |
12 | 1 | 1 | Bonnell\t Miss. Elizabeth | female | 58.0 | 0 | 0 | 113783 | 26.5500 | C103 | S |
24 | 1 | 1 | Sloper\t Mr. William Thompson | male | 28.0 | 0 | 0 | 113788 | 35.5000 | A6 | S |
df.query('(Pclass == 1) and (Sex == "female")').head()
Survived | Pclass | Name | Sex | Age | SibSp | Parch | Ticket | Fare | Cabin | Embarked | |
---|---|---|---|---|---|---|---|---|---|---|---|
PassengerId | |||||||||||
2 | 1 | 1 | Cumings\t Mrs. John Bradley (Florence Briggs T... | female | 38.0 | 1 | 0 | PC 17599 | 71.2833 | C85 | C |
4 | 1 | 1 | Futrelle\t Mrs. Jacques Heath (Lily May Peel) | female | 35.0 | 1 | 0 | 113803 | 53.1000 | C123 | S |
12 | 1 | 1 | Bonnell\t Miss. Elizabeth | female | 58.0 | 0 | 0 | 113783 | 26.5500 | C103 | S |
32 | 1 | 1 | Spencer\t Mrs. William Augustus (Marie Eugenie) | female | NaN | 1 | 0 | PC 17569 | 146.5208 | B78 | C |
53 | 1 | 1 | Harper\t Mrs. Henry Sleeper (Myna Haxtun) | female | 49.0 | 1 | 0 | PC 17572 | 76.7292 | D33 | C |
df.query('SibSp > Parch')
Survived | Pclass | Name | Sex | Age | SibSp | Parch | Ticket | Fare | Cabin | Embarked | |
---|---|---|---|---|---|---|---|---|---|---|---|
PassengerId | |||||||||||
1 | 0 | 3 | Braund\t Mr. Owen Harris | male | 22.0 | 1 | 0 | A/5 21171 | 7.2500 | NaN | S |
2 | 1 | 1 | Cumings\t Mrs. John Bradley (Florence Briggs T... | female | 38.0 | 1 | 0 | PC 17599 | 71.2833 | C85 | C |
4 | 1 | 1 | Futrelle\t Mrs. Jacques Heath (Lily May Peel) | female | 35.0 | 1 | 0 | 113803 | 53.1000 | C123 | S |
8 | 0 | 3 | Palsson\t Master. Gosta Leonard | male | 2.0 | 3 | 1 | 349909 | 21.0750 | NaN | S |
10 | 1 | 2 | Nasser\t Mrs. Nicholas (Adele Achem) | female | 14.0 | 1 | 0 | 237736 | 30.0708 | NaN | C |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
861 | 0 | 3 | Hansen\t Mr. Claus Peter | male | 41.0 | 2 | 0 | 350026 | 14.1083 | NaN | S |
862 | 0 | 2 | Giles\t Mr. Frederick Edward | male | 21.0 | 1 | 0 | 28134 | 11.5000 | NaN | S |
864 | 0 | 3 | Sage\t Miss. Dorothy Edith "Dolly" | female | NaN | 8 | 2 | CA. 2343 | 69.5500 | NaN | S |
867 | 1 | 2 | Duran y More\t Miss. Asuncion | female | 27.0 | 1 | 0 | SC/PARIS 2149 | 13.8583 | NaN | C |
875 | 1 | 2 | Abelson\t Mrs. Samuel (Hannah Wizosky) | female | 28.0 | 1 | 0 | P/PP 3381 | 24.0000 | NaN | C |
192 rows × 11 columns
young = 18
df.query('Age < @young').shape
(113, 11)
Zadanie
Operacje na wierszach i kolumnach
df = pd.read_csv('./gapminder.csv', index_col='Country', nrows=5)
df
Iterowanie po ramce danych oznacza oznacza przejście po nazwach kolumn:
for column_name in df:
print(column_name)
for col_name, series in df.items():
print(col_name, series)
break
for idx, row in df.iterrows():
print(idx, '\n', row)
break
def bmi_level(bmi):
if bmi <= 18.5:
level = 'underweight'
elif bmi < 25:
level = 'normal'
elif bmi < 30:
level = 'overweight'
else:
level = 'obese'
return level
s = df['male_BMI'].map(bmi_level)
s
def bmi_level(row_data):
bmi = row_data['male_BMI']
if bmi <= 18.5:
return 'underweight'
elif bmi < 25:
return 'normal'
elif bmi < 30:
return 'overweight'
return 'obese'
df.apply(bmi_level, axis=1)
df.transpose()
Zadanie
Grupowanie (groupby
)
Często zdarza się, gdy potrzebujemy podzielić dane ze względu na wartości w zadanej kolumnie, a następnie obliczenie zebranie danych w każdej z grup. Do tego służy metody groupby
.
import pandas as pd
df = pd.read_csv('./nba.csv')
#df.sample(5)
df
Name | Team | Number | Position | Age | Height | Weight | College | Salary | |
---|---|---|---|---|---|---|---|---|---|
0 | Avery Bradley | Boston Celtics | 0.0 | PG | 25.0 | 6-2 | 180.0 | Texas | 7730337.0 |
1 | Jae Crowder | Boston Celtics | 99.0 | SF | 25.0 | 6-6 | 235.0 | Marquette | 6796117.0 |
2 | John Holland | Boston Celtics | 30.0 | SG | 27.0 | 6-5 | 205.0 | Boston University | NaN |
3 | R.J. Hunter | Boston Celtics | 28.0 | SG | 22.0 | 6-5 | 185.0 | Georgia State | 1148640.0 |
4 | Jonas Jerebko | Boston Celtics | 8.0 | PF | 29.0 | 6-10 | 231.0 | NaN | 5000000.0 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
453 | Shelvin Mack | Utah Jazz | 8.0 | PG | 26.0 | 6-3 | 203.0 | Butler | 2433333.0 |
454 | Raul Neto | Utah Jazz | 25.0 | PG | 24.0 | 6-1 | 179.0 | NaN | 900000.0 |
455 | Tibor Pleiss | Utah Jazz | 21.0 | C | 26.0 | 7-3 | 256.0 | NaN | 2900000.0 |
456 | Jeff Withey | Utah Jazz | 24.0 | C | 26.0 | 7-0 | 231.0 | Kansas | 947276.0 |
457 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
458 rows × 9 columns
_Przykład: chcemy obliczyć średnią wypłatę dla każdej z drużyn.
df[['Team', 'Salary']]
Team | Salary | |
---|---|---|
0 | Boston Celtics | 7730337.0 |
1 | Boston Celtics | 6796117.0 |
2 | Boston Celtics | NaN |
3 | Boston Celtics | 1148640.0 |
4 | Boston Celtics | 5000000.0 |
... | ... | ... |
453 | Utah Jazz | 2433333.0 |
454 | Utah Jazz | 900000.0 |
455 | Utah Jazz | 2900000.0 |
456 | Utah Jazz | 947276.0 |
457 | NaN | NaN |
458 rows × 2 columns
df[['Team', 'Salary']].groupby('Team').median().h
Salary | |
---|---|
Team | |
Atlanta Hawks | 2854940.0 |
Boston Celtics | 3021242.5 |
Brooklyn Nets | 1335480.0 |
Charlotte Hornets | 4204200.0 |
Chicago Bulls | 2380440.0 |
Możemy też podać listę nazw kolumn. Wtedy wartości zostaną obliczone dla każdej z wytworzonych grup:
df.groupby(['Team', 'Position'])['Salary'].mean()
sum()
min()
max()
mean()
size()
describe()
first()
last()
count()
std()
var()
sem()
df[['Position', 'Salary']].groupby('Position').agg(['mean', 'std', 'count'])
def group_range(x):
return x.max() - x.min()
df[['Position', 'Salary']].groupby('Position').apply(group_range)
gb = df.groupby(['Position'])
print('Liczba grup:', gb.ngroups)
print(gb.groups.keys())
print(gb.get_group('C').head())
df.Height.str.split('-').str[0].astype('Int64') * 2.56
Pivot
Metoda pivot
pozwala na stworzenie nowej ramki danych, gdzie indeks i nazwy kolumn są wartościami początkowej ranki danych.
_Przykład: zobaczmy na poniższą ramkę danych, która zawiera informacje o jakości tłumaczenia dla pary językowej hausa-angielski. Kolumna system
zawiera nazwę systemu, kolumna metric
- nazwę metryki, zaś kolumna score
- wartość metryki. Chcemy przedstawić te dane w następujący sposób: jako klucz chcemy mieć nazwę systemu, zaś jako kolumny - metryki. Możemy wykorzystać do tego metodę pivot
, gdzie musimy podać 3 argumenty:
index
: nazwę kolumny, na podstawie której zostanie stworzony indeks;columns
: nazwa kolumny, które zawiera nazwy kolumn dla nowej ramki danych;values
: nazwa kolumny, która zawiera interesujące nas dane.
df = pd.read_csv('https://raw.githubusercontent.com/wmt-conference/wmt21-news-systems/main/scores/automatic-scores.tsv', sep='\t')
df = df[df.pair == 'ha-en']
df
df.pivot(index='system', columns='metric', values='score')
Zadanie
Dane tekstowe
pandas
posiada udogodnienia do pracy z wartościami tekstowymi:
- dostęp następuje przez atrybut
str
; - funkcje:
- formatujące:
lower()
,upper()
; - wyrażenia regularne:
contains()
,match()
; - inne:
split()
- formatujące:
df = pd.read_csv('./titanic_train.tsv', sep='\t', index_col='PassengerId')
df.head()
df.Name.str.upper()
print(df.Name.head())
df.Name.str.contains('Miss|Mrs').head()
df.Name.str.split('\t', expand=True)
df.Name.str.split('\t')
df.Name.str.split('\t').str[1]
df.Name.str.split('\t').str[1].str.strip().str.split(' ').str[0]
Zadanie
Zestaw nba.csv
zawiera informaję o wysokości zawodników. Oblicz wzrost każdego z zawodników w systemie metrycznym przyjmując, że stop to 30.48
cm., a cal to 2.54
cm.