Używamy cookies, żeby zwiększyć Twoje doświadczenia na stronie
CodeWorlds

EDA - zwiad przed polowaniem

Dostałeś nowy zbiór obserwacji z rezerwatu, @name. Zanim wytresujesz na nim jakikolwiek model, musisz zrobić to, co każdy dobry tropiciel przed polowaniem: zwiedzić teren. EDA (Exploratory Data Analysis) to ten zwiad - odpowiadasz kolejno na proste pytania o dane, aż poczujesz, z czym masz do czynienia. Przejdziemy je po kolei.

Pytanie 1: jak duży jest teren?

Pierwszy rzut oka to zawsze rozmiar i kształt danych - ile obserwacji, ile kolumn, jak wyglądają pierwsze wiersze.

1import pandas as pd
2import numpy as np
3import matplotlib.pyplot as plt
4import seaborn as sns
5
6df = pd.read_csv('safari_observations.csv')
7
8print("Shape:", df.shape)   # (liczba wierszy, liczba kolumn)
9print(df.head())            # kilka pierwszych obserwacji
10print(df.describe())        # statystyki kolumn liczbowych

Te trzy linie to Twój pierwszy meldunek z terenu:

shape
mówi, jak wielki jest zbiór,
head
pokazuje, co konkretnie zapisano w każdej kolumnie, a
describe
daje od razu średnie, minima i maksima. Zanim pójdziesz dalej - już wiesz, czy danych jest 100, czy milion.

Pytanie 2: co tu żyje? (typy kolumn)

Model inaczej traktuje liczby, a inaczej kategorie, więc musisz rozdzielić kolumny liczbowe (populacja, waga) od kategorycznych (gatunek, siedlisko).

1categorical = df.select_dtypes(include=['object', 'category']).columns
2numerical = df.select_dtypes(include=[np.number]).columns
3
4for col in categorical:
5    print(f"{col}: {df[col].nunique()} unikalnych")
6    print(df[col].value_counts().head())

select_dtypes
rozdziela kolumny po typie, a
value_counts
pokazuje, ile jest każdej kategorii. To ważny zwiad: jeśli kolumna "gatunek" ma 3 wartości, to kategoria; jeśli 10 000 - to raczej identyfikator, którego model nie powinien traktować jak cechy.

Pytanie 3: gdzie są dziury? (braki danych)

Prawdziwe dane z terenu są dziurawe - część obserwacji jest niekompletna. Musisz wiedzieć gdzie i ile, zanim braki zepsują model.

1missing = df.isnull().sum()
2missing_pct = (missing / len(df)) * 100
3missing_df = pd.DataFrame({'braki': missing, 'procent': missing_pct})
4print(missing_df[missing_df['braki'] > 0].sort_values('braki', ascending=False))
5
6sns.heatmap(df.isnull(), cbar=True, yticklabels=False)
7plt.title('Mapa brakujących wartości')
8plt.show()

Tabela mówi, które kolumny są dziurawe i jak bardzo (procent jest ważniejszy niż liczba). Mapa cieplna dokłada obraz: jeśli braki układają się w pasy, może brakować całych partii obserwacji - a to już trop, że coś poszło nie tak przy zbieraniu danych, nie przypadek.

Pytanie 4: co jest normalne, a co odstaje? (rozkłady)

Teraz przyglądasz się pojedynczym kolumnom liczbowym: jak rozłożone są wartości i czy są wartości odstające (outliery) - obserwacje, które nie pasują do reszty.

1df[numerical].hist(figsize=(15, 10), bins=30, edgecolor='black')
2plt.tight_layout()
3plt.show()
4
5def find_outliers_iqr(df, column):
6    Q1 = df[column].quantile(0.25)
7    Q3 = df[column].quantile(0.75)
8    IQR = Q3 - Q1
9    lower, upper = Q1 - 1.5 * IQR, Q3 + 1.5 * IQR
10    return df[(df[column] < lower) | (df[column] > upper)]
11
12outliers = find_outliers_iqr(df, 'population')
13print(f"Wartości odstające w populacji: {len(outliers)}")

Histogramy pokazują kształt rozkładu jednym spojrzeniem. Funkcja

find_outliers_iqr
używa reguły "1,5 × rozstęp kwartylny" - klasycznej metody na wskazanie obserwacji zbyt dużych lub zbyt małych. Uwaga: outlier nie zawsze znaczy błąd. Słoń w danych o wadze to naturalny "odstający" - Twoim zadaniem jest zdecydować, czy to pomyłka do usunięcia, czy prawdziwe, ważne zwierzę.

Pytanie 5: co chodzi razem? (korelacje)

Na koniec sprawdzasz, które cechy poruszają się razem - gdy jedna rośnie, druga też. To podpowiada, co pomoże w przewidywaniu, a które kolumny mówią właściwie to samo.

1correlation = df[numerical].corr()
2
3sns.heatmap(correlation, annot=True, cmap='coolwarm', center=0, fmt='.2f')
4plt.title('Macierz korelacji')
5plt.show()

Mapa korelacji koloruje pary cech: wartości blisko

1
(albo
-1
) to silny związek, blisko
0
- żaden. Po co to wiedzieć? Dwie cechy niemal identyczne (korelacja 0,98) niosą tę samą informację - zwykle wystarczy zostawić jedną. A cecha silnie związana z tym, co przewidujesz, to Twój najlepszy kandydat do modelu.

Pytanie 6: czym różnią się grupy?

Korelacja łączy liczby z liczbami. Ale często najciekawsze pytanie brzmi: czy kategoria wpływa na liczbę - np. czy populacja różni się między siedliskami? Do tego służy wykres pudełkowy z podziałem na kategorię.

1for cat in categorical:
2    for num in numerical:
3        sns.boxplot(data=df, x=cat, y=num)
4        plt.title(f'{num} według {cat}')
5        plt.xticks(rotation=45)
6        plt.show()

Każdy

boxplot
porównuje rozkład zmiennej liczbowej w podziale na grupy. Jeśli pudełka dla różnych siedlisk leżą na zupełnie innych wysokościach, to znak, że siedlisko naprawdę tłumaczy populację - i że warto dać tę kategorię modelowi. Jeśli pudełka się pokrywają, kategoria niewiele wnosi.

Zwiad spisany w jednej funkcji

Skoro te same pytania zadajesz każdemu nowemu zbiorowi, warto spakować pierwszy meldunek w jedną funkcję, którą odpalisz od razu po wczytaniu danych.

1def eda_summary(df):
2    print("=" * 50)
3    print("RAPORT EDA")
4    print("=" * 50)
5    print(f"Wierszy: {df.shape[0]}")
6    print(f"Kolumn: {df.shape[1]}")
7    print(f"Brakujące dane: {df.isnull().sum().sum()}")
8    print(f"Duplikaty: {df.duplicated().sum()}")
9    print(df.dtypes.value_counts())
10    print(df.describe())
11
12eda_summary(df)

Ta funkcja zbiera odpowiedzi na pytania 1-3 w jeden szybki raport - rozmiar, typy, braki, duplikaty - żebyś każdy nowy zbiór zaczynał od tego samego, sprawdzonego zwiadu.

Pamiętaj z tej lekcji kolejność, nie komendy: rozmiar → typy → braki → rozkłady → korelacje. Model trenujesz dopiero, gdy przejdziesz ten zwiad - inaczej polujesz na ślepo w nieznanym terenie.

Przejdź do CodeWorlds