2024-programowanie-w-python.../zajecia2/data_analysis.ipynb
2024-12-07 11:54:47 +01:00

176 KiB
Raw Blame History

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 bazy Chinook.sqlite do arkusza kalkulacyjnego. Plik wynikowy nazwij customers.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()
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.