Materiál k cvičení DBM1 týden #3, sezona 2024/2025

Import dat a základní úpravy v Pandas

Při práci v Pandas budeme často využívat datový typ DataFrame, který je základním prvkem pro práci s tabulkovými daty. DataFrame je dvourozměrná datová struktura, která obsahuje data uspořádaná do řádků a sloupců.

Nainstalování knihoven

pip install pandas pip install openpyxl

Import dat

📄 Datové zdroje: customer_purchases.csv, customer_purchases.xlsx (umělá data o nákupech zákazníků)

import pandas as pd df_csv = pd.read_csv("data/customer_purchases.csv") df_xlsx = pd.read_excel("data/customer_purchases.xlsx") print(df_csv) print(df_xlsx)

Pokud není nainstalovná knihovna pandas, bude ve zdrojovém kódu podtrženo volání pd.read_csv a pd.read_excel.

Jelikož ke čtení Excel souborů je potřeba extra knihovna, s kterou na pozadí pracuje Pandas při načítání, není její absence detekována v kódu a na problém by se narazilo až při spuštění skriptu.

Pokud se tedy vypíše chyba ImportError: Missing optional dependency 'openpyxl'. Use pip or conda to install openpyxl., nainstalujte knihovnu openpyxl pomocí pip install openpyxl.

Výpisem by mělo být zobrazení (opsání) tabulky ze vstupního souboru. Všimněte si, že Pandas automaticky detekuje hlavičku tabulky a použije ji jako názvy sloupců a vlevo jsou čísla řádků od 0.

Problémy s oddělovačem a kódováním

Funkce read_csv má mnoho parametrů, které umožňují nastavit různé vlastnosti načítání souboru (viz bublina v IDE). My použili zatím volání bez parametrů, což znamená, že se použije výchozí nastavení.

📚 Dokumentace Pandas → read_csv

Pokud se některé znaky nezobrazují správně, může to být způsobeno špatným kódováním souboru. V češtině je často používáno kódování windows-1250 nebo cp1250, ideálně by ale mělo být použito kódování utf-8 (což je defaultní hodnota v používané funkci).

Při problému je možné explicitně při načítání souboru určit kódování pomocí parametru encoding. Např. pro načtení souboru s kódováním cp1250:

df_csv = pd.read_csv("data/customer_purchases.csv", encoding="cp1250")

Pokud jsou hodnoty v souboru oddělené jiným znakem než čárkou , (např. v českých souborech je často středník ;), je možné to nastavit pomocí parametru sep. Např. pro načtení souboru s oddělovačem středníkem:

df_csv = pd.read_csv("data/customer_purchases.csv", sep=";")

Samozřejmě lze kombinovat více parametrů, např.:

df_csv = pd.read_csv("data/customer_purchases.csv", sep=";", encoding="cp1250")

Náš CSV soubor má čárky jako oddělovače a je v kódování utf-8.

Základní explorace dat

print(df_csv.head()) print(df_xlsx.head())

Funkce head vypíše prvních 5 řádků tabulky. Podobně funguje funkce tail, která vypíše posledních 5 řádků tabulky.

print(df_csv.info()) print(df_xlsx.info())

Funkce info vypíše základní informace o DataFrame, jako jsou názvy sloupců, počet neprázdných hodnot, datové typy sloupců a celkovou velikost tabulky.

V našem případě máme následující výstup:

<class 'pandas.core.frame.DataFrame'> RangeIndex: 10 entries, 0 to 9 Data columns (total 5 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 CustomerID 10 non-null int64 1 Name 10 non-null object 2 PurchaseAmount 10 non-null float64 3 PurchaseDate 10 non-null object 4 DiscountApplied 5 non-null float64 dtypes: float64(2), int64(1), object(2) memory usage: 528.0+ bytes

Q1: K čemu slouží a co říkají Not-Null Count a Dtype?

Funkce describe vypíše základní statistické informace o číselných sloupcích (počet, průměr, směrodatná odchylka, min, max, kvartily).

print(df_csv.describe()) print(df_xlsx.describe())

Q2: Co je to kvartil a jakou informaci poskytuje?

Základní úpravy dat

Výběr a přístup k sloupcům, řádkům a datům

Výběr sloupce podle názvu:

print(df_csv["Name"])

Výběr řádku podle indexu:

print(df_csv.loc[0])

Výběr konkrétní hodnoty jako kombinace řádku a sloupce:

print(df_csv.loc[0, "Name"])

Pokud chceme vybírat sloupce přes indexy, nikoliv názvy, můžeme použít funkci iloc:

print(df_csv.iloc[:, 1]) # vše řádky, druhý sloupec print(df_csv.iloc[0, 1]) # první řádek, druhý sloupec print(df_csv.iloc[0:3, 1:3]) # první až třetí řádek, druhý až třetí sloupec print(df_csv.iloc[0:3, [0,2]]]) # první až třetí řádek, první a třetí sloupec

Přejmenování sloupců

df_csv = df_csv.rename(columns={"Name": "CustomerName"}) print(df_csv)

Všimněte si, že funkce rename vrací nový DataFrame, takže je potřeba uložit její výstup zpět do proměnné (byť se stejným názvem). Parametr columns je slovník, kde klíč je původní název sloupce a hodnota je nový název sloupce.

Přidání nového sloupce

Nový sloupec můžeme přidat jednoduše tak, že přiřadíme nový sloupec do DataFrame jako nový klíč v slovníku. Na pravé straně přiřazení může být i výpočet z jiných sloupců.

df_csv["X"] = "🐾" df_csv["DiscountPercentage"] = df_csv["DiscountApplied"] / (df_csv["PurchaseAmount"] + df_csv["DiscountApplied"]) print(df_csv.to_string())

Odstranění sloupce

df_csv = df_csv.drop(columns=["X"]) print(df_csv)

Změna hodnoty

Hodnota lze změnit přímo pomocí přístupu k dané buňce:

df_csv.loc[0, "DiscountApplied"] = 10 print(df_csv)

Změna typu sloupce

Dříve jsme pozorovaly, že sloupec PurchaseDate má datový typ object, což znamená, že Pandas neví, že se jedná o datum. Můžeme tento sloupec převést na typ datetime pomocí speciální funkce to_datetime.

Dále Name je typ object, což je obecný typ pro hodnoty.

df_csv["PurchaseDate"] = pd.to_datetime(df_csv["PurchaseDate"], format="%Y-%m-%d") df_csv["Name"] = df_csv["Name"].astype("string") print(df_csv.info())

📚 Datetime format "%Y-%m-%d" znamená, že datum je ve formátu YYYY-MM-DD. Detailnější popis formátovacích znaků je v dokumentaci Pythonu.

String na rozdíl od object je speciální datový typ v Pandas, který je optimalizovaný pro práci s textem.

Q3: Zkuste, jak se chová volání df_csv["Name"].str.upper() pokud je sloupec typu string a pokud je typu object. Zkuste změnit nějakou hodnotu na číslo a zjistěte, co se stane.

Filtrování dat

Filtrování dat můžeme provést pomocí podmíněného výběru přes zápis v hranatých závorkách. Např. získání řádků, kde je PurchaseAmount větší než 150:

df = df_csv.loc[df_csv["PurchaseAmount"] > 150] print(df)

Pro kombinaci podmínek můžeme použít logické operátory & (a) a | (nebo). Např. získání řádků, kde je PurchaseAmount větší než 150 a byla aplikována sleva:

df = df_csv.loc[(df_csv["PurchaseAmount"] > 150) & (df_csv["DiscountApplied"] > 0)] print(df)

Kromě toho lze vybrat klasicky rozsahem indexů, např.:

df = df_csv.loc[2:5] print(df)

Řazení dat

Řazení dat můžeme provést pomocí funkce sort_values. Např. seřazení podle sloupce PurchaseAmount sestupně:

df = df_csv.sort_values(by="PurchaseAmount", ascending=False) df = df_csv.sort_values(by=["PurchaseAmount", "PurchaseDate"], ascending=False) print(df)

Řešení chybějících hodnot

Dříve jsme mohli pozorovat, že ve sloupci DiscountApplied jsou některé hodnoty chybějící (NaN). Pandas má zabudované metody pro práci s chybějícími hodnotami.

Pro odstranění řádků s chybějícími hodnotami můžeme použít funkci dropna:

df = df_csv.dropna() print(df)

Pro nahrazení chybějících hodnot nějakou konstantou (např. 0) můžeme použít funkci fillna:

# df = df_csv.fillna(0) # všechny chybějící hodnoty df = df_csv.fillna({"DiscountApplied": 0}) # jen pro sloupec DiscountApplied print(df)

Uložení dat

Po provedení změn můžeme DataFrame uložit zpět do souboru. Pro uložení do CSV souboru:

df_csv.to_csv("data/customer_purchases_modified.csv", index=False)

index=False znamená, že se nebude ukládat číslování řádků do souboru.

Rychlá vizualizace

Načtený a upravený DataFrame můžeme rychle vizualizovat pomocí grafů. Pro jednoduchý scatter plot můžeme použít knihovnu Seaborn, která je postavena nad Matplotlib.

pip install matplotlib pip install seaborn
import matplotlib matplotlib.use("TkAgg") # Ensure an interactive backend is set import matplotlib.pyplot as plt import seaborn as sns # Create the scatter plot sns.set_theme(style="whitegrid") sns.scatterplot(data=df_csv, x="PurchaseDate", y="PurchaseAmount") # Save to file and display the plot plt.savefig("customer_purchases.png") plt.show()

Vizualizce budou detailněji probírány na pozdějších cvičeních.