74 KiB
74 KiB
In [ ]:
# jeśli notebook odpalamy w google colab:
#from google.colab import drive
#drive.mount('/content/drive')
#df = pd.read_csv('/content/drive/MyDrive/sciezka/do/pliku.csv')In [ ]:
# jeśli odpalamy poza google colab, ale plik z google drive jest dostępny publicznie (lub "każdy z linkiem")
#import pandas as pd
# Zamień w linku "view" lub uc?id= na format do pobrania
#file_id = 'XXXX'
#url = f'https://drive.google.com/uc?export=download&id={file_id}'
#df = pd.read_csv(url)In [ ]:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.datasets import load_iris
iris_raw = load_iris(as_frame=True)
iris = iris_raw.frame # DataFrame z cechami i kolumną target
iris['species'] = iris['target'].map(dict(enumerate(iris_raw.target_names)))In [ ]:
# Zawsze zaczynamy od: ile wierszy i kolumn? Jakie typy? Czy są braki?
print(iris.shape)
iris.info()
iris.head()In [ ]:
# Statystyki opisowe dla kolumn numerycznych
iris.describe()In [ ]:
# Histogramy wszystkich cech – podzielone według gatunku
features = ['sepal length (cm)', 'sepal width (cm)',
'petal length (cm)', 'petal width (cm)']
fig, axes = plt.subplots(2, 2, figsize=(12, 8))
for ax, col in zip(axes.ravel(), features):
sns.histplot(data=iris, x=col, hue='species', kde=True, ax=ax)
ax.set_title(col)
plt.tight_layout()
plt.show()In [ ]:
# Skosność rozkładów - miara asymetrii (0 = symetryczny rozkład)
for col in features:
print(f"{col:25s} skośność = {iris[col].skew():+.2f}")In [ ]:
# EKSPERYMENT: wykres pudełkowy cech z podziałem na gatunki
fig, axes = plt.subplots(1, 4, figsize=(14, 4))
for ax, col in zip(axes, features):
sns.boxplot(data=iris, x='species', y=col, ax=ax)
ax.tick_params(axis='x', rotation=30) # obrót etykiet osi X
plt.tight_layout()
plt.show()In [ ]:
# Macierz korelacji (dotyczy tylko kolumn numerycznych)
plt.figure(figsize=(6, 5))
sns.heatmap(iris[features].corr(), annot=True, cmap='coolwarm',
vmin=-1, vmax=1, fmt='.2f')
plt.title('Korelacja cech – Iris')
plt.show()In [ ]:
# Wykres rozrzutu (scatter plot) pary najbardziej skorelowanych cech
sns.scatterplot(data=iris, x='petal length (cm)', y='petal width (cm)',
hue='species')
plt.title('Długość vs szerokość płatka')
plt.show()In [ ]:
titanic = sns.load_dataset('titanic')
titanic.shapeIn [ ]:
titanic.head()In [ ]:
# Liczba i procent braków w każdej kolumnie - to zawsze pierwszy krok
braki = titanic.isnull().sum()
procent = 100 * braki / len(titanic)
pd.DataFrame({'braki': braki, 'procent': procent.round(1)})In [ ]:
# EKSPERYMENT: analiza zmiennej docelowej (survived) względem innych kolumn
# (observed=True -> grupujemy tylko po wartościach faktycznie obecnych w danych)
print(titanic.groupby('class', observed=True)['survived'].mean())
print()
print(titanic.groupby('sex', observed=True)['survived'].mean())
sns.barplot(data=titanic, x='class', y='survived', hue='sex')
plt.title('Szansa przeżycia wg klasy i płci')
plt.show()In [ ]:
from sklearn.cluster import KMeans
from sklearn.preprocessing import StandardScaler
X = iris[features]
X_scaled = StandardScaler().fit_transform(X) # skalowanie - dlaczego?, wyjaśnienie w części 2
kmeans = KMeans(n_clusters=3, random_state=42, n_init=10)
clusters = kmeans.fit_predict(X_scaled)
# Porównanie: prawdziwe gatunki vs grupy znalezione przez algorytm
tabela = pd.crosstab(iris['species'], clusters,
rownames=['gatunek'], colnames=['KMeans'])
print(tabela)In [ ]:
rng = np.random.default_rng(7)
# Startujemy od iris, ale "zabieramy" etykiety i psujemy dane
broken = iris[features].copy()
# 1) usuwamy losowo 8% wartości
mask = rng.random(broken.shape) < 0.08
broken[mask] = np.nan
# 2) dodajemy skrajne wartości (błędy pomiaru / wpisy ręczne)
for col in ['sepal width (cm)', 'petal length (cm)']:
losowe_indeksy = rng.choice(broken.index, 5, replace=False)
broken.loc[losowe_indeksy, col] = broken[col].max() * 10
# 3) duplikujemy kilka wierszy
broken = pd.concat([broken, broken.sample(15, random_state=1)])
broken = broken.reset_index(drop=True)
broken.info()In [ ]:
# Zadanie diagnostyczne: znajdź wszystkie problemy!
print("Braki w kolumnach:")
print(broken.isnull().sum())
print("\nLiczba duplikatów:", broken.duplicated().sum())
broken.describe()In [ ]:
# Sposób A: usunięcie wierszy z brakami
drop_rows = broken.dropna()
print("A) dropna:", drop_rows.shape, "-> tracimy",
len(broken) - len(drop_rows), "wierszy")In [ ]:
# Sposób B: uzupełnienie medianą (medianą, bo dane mogą być skośne lub mieć wartości odstające)
filled = broken.fillna(broken.median())
print("B) fillna(mediana):", filled.shape, "braki:", filled.isnull().sum().sum())
filled.describe()In [ ]:
# EKSPERYMENT: proszę porównać medianę cechy w danych oryginalnych i "uszkodzonych"
for col in features:
print(f"{col:25s} mediana oryginał: {iris[col].median():7.2f} | "
f"mediana uszkodzone: {broken[col].median():7.2f}")In [ ]:
# Metoda IQR: outlier = wartość poniżej Q1-1.5*IQR lub powyżej Q3+1.5*IQR
Q1 = filled.quantile(0.25)
Q3 = filled.quantile(0.75)
IQR = Q3 - Q1
jest_outlier = (filled < Q1 - 1.5 * IQR) | (filled > Q3 + 1.5 * IQR)
print("Liczba outlierów w każdej kolumnie:")
print(jest_outlier.sum())
# Oczyszczone dane: usuwamy TYLKO wiersze zawierające outliery
clean = filled[~jest_outlier.any(axis=1)]
print("\nPrzed czyszczeniem:", len(filled), "-> Po czyszczeniu:", len(clean))In [ ]:
# Wizualna kontrola: boxploty przed i po
fig, axes = plt.subplots(1, 2, figsize=(12, 4))
sns.boxplot(data=filled, ax=axes[0])
axes[0].set_title('Przed usunięciem outlierów')
sns.boxplot(data=clean, ax=axes[1])
axes[1].set_title('Po usunięciu outlierów')
for ax in axes:
ax.tick_params(axis='x', rotation=30)
plt.tight_layout()
plt.show()In [ ]:
from sklearn.preprocessing import StandardScaler, MinMaxScaler
demo = pd.DataFrame({
'wiek': [22, 25, 30, 35, 40, 55, 60],
'dochod_tys': [3.5, 4.2, 5.0, 6.1, 7.3, 9.8, 12.5],
'plan_PKB': [55.2, 61.7, 58.9, 72.1, 69.4, 80.3, 77.8]
})
standard = pd.DataFrame(StandardScaler().fit_transform(demo),
columns=demo.columns, index=demo.index) # średnia=0, odch=1
minmax = pd.DataFrame(MinMaxScaler().fit_transform(demo),
columns=demo.columns, index=demo.index) # zakres [0, 1]
fig, axes = plt.subplots(1, 3, figsize=(14, 4))
sns.boxplot(data=demo, ax=axes[0])
axes[0].set_title('Surowe dane')
sns.boxplot(data=standard, ax=axes[1])
axes[1].set_title('Standaryzacja (z-score)')
sns.boxplot(data=minmax, ax=axes[2])
axes[2].set_title('Normalizacja Min-Max')
for ax in axes:
ax.tick_params(axis='x', rotation=15)
plt.tight_layout()
plt.show()
print("Zakresy kolumn surowych:\n", demo.max() - demo.min())In [ ]:
# EKSPERYMENT: wpływ skalowania na odległość między dwoma obserwacjami
p1, p2 = demo.iloc[0], demo.iloc[6]
odleglosc_surowa = np.linalg.norm(p1 - p2)
odleglosc_skala = np.linalg.norm(standard.iloc[0] - standard.iloc[6])
print(f"Odległość surowa: {odleglosc_surowa:.2f}")
print(f"Odległość po standaryzacji: {odleglosc_skala:.2f}")In [ ]:
from sklearn.datasets import load_digits
digits = load_digits()
X_img = digits.images # 1797 obrazów 8x8
X_flat = digits.data # to samo, "spłaszczone" do 64 cech
y = digits.target
print(X_img.shape, X_flat.shape)
print("\nPierwsza cyfra (prawdziwa wartość:", y[0], ") jako macierz:")
print(X_img[0])In [ ]:
fig, axes = plt.subplots(1, 8, figsize=(12, 2))
for i, ax in enumerate(axes):
ax.imshow(X_img[i], cmap='gray_r')
ax.set_title(y[i])
ax.axis('off')
plt.suptitle('Pierwsze 8 cyfr ze zbioru digits')
plt.show()In [ ]:
jasnosc = X_flat.mean(axis=1) # średnia z 64 pikseli = 1 cecha
df = pd.DataFrame({'jasnosc': jasnosc, 'cyfra': y})
sns.boxplot(data=df, x='cyfra', y='jasnosc')
plt.title('Średnia jasność obrazu w zależności od cyfry')
plt.show()
print(df.groupby('cyfra')['jasnosc'].mean().round(2))In [ ]:
# EKSPERYMENT: proszę dodać drugą prostą cechę, np. liczbę "ciemnych" pikseli (< 5)
ciemne = (X_flat < 5).sum(axis=1)
plt.figure(figsize=(7, 5))
sns.scatterplot(x=jasnosc, y=ciemne, hue=y, palette='tab10', legend='full',
s=15, alpha=0.6)
plt.xlabel('średnia jasność')
plt.ylabel('liczba ciemnych pikseli')
plt.title('Cyfry w przestrzeni DWÓCH ręcznie zaprojektowanych cech')
plt.show()In [ ]:
# W Colab: from google.colab import files; files.upload()
from PIL import Image
img = Image.open('nazwa_pliku.jpg').convert('L') # L = skala szarości
arr = np.asarray(img, dtype=float)
print("Rozmiar obrazu:", arr.shape, "| min:", arr.min(), "| max:", arr.max())
fig, axes = plt.subplots(1, 3, figsize=(13, 4))
axes[0].imshow(arr, cmap='gray')
axes[0].set_title('Obraz')
axes[1].hist(arr.ravel(), bins=50)
axes[1].set_title('Histogram jasności')
axes[2].imshow(arr[:32, :32], cmap='gray_r')
axes[2].set_title('Powiększony fragment')
plt.show()In [ ]:
from scipy.ndimage import rotate, shift
obraz = X_img[0]
etykieta = y[0]
przyklady = {
'oryginał': obraz,
'obrót +20°': rotate(obraz, 20, reshape=False),
'obrót -20°': rotate(obraz, -20, reshape=False),
'przesunięcie': shift(obraz, (1, 2)),
}
fig, axes = plt.subplots(1, len(przyklady), figsize=(12, 3))
for ax, (nazwa, im) in zip(axes, przyklady.items()):
ax.imshow(im, cmap='gray_r')
ax.set_title(f"{nazwa}\netykieta: {etykieta}")
ax.axis('off')
plt.show()In [ ]:
# EKSPERYMENT: szum "sól i pieprz" oraz szum gaussowski
obrazek = X_flat[0].reshape(8, 8).astype(float)
# (a) szum gaussowski (rozmycie/pogorszenie jakości sensora)
z_gaussem = obrazek + np.random.default_rng(1).normal(0, 2.5, obrazek.shape)
# (b) sól i pieprz: losowe białe/czarne piksele (zakłócenia transmisji)
sol = obrazek.copy()
rng = np.random.default_rng(2)
maska = rng.random(sol.shape) < 0.10
sol[maska] = rng.choice([0, 16], size=maska.sum())
fig, axes = plt.subplots(1, 3, figsize=(10, 3.5))
for ax, (nazwa, im) in zip(axes, [('oryginał', obrazek),
('szum gaussowski', z_gaussem),
('sól i pieprz', sol)]):
ax.imshow(im, cmap='gray_r')
ax.set_title(nazwa)
ax.axis('off')
plt.show()
In [ ]:
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
rng = np.random.default_rng(0)
# (1) mały zbiór treningowy: tylko 80 przykładów
idx_treningowe = rng.choice(len(X_flat), 80, replace=False)
X_tr, y_tr = X_flat[idx_treningowe], y[idx_treningowe]
def ocena(X_train, y_train, X_test, y_test):
knn = KNeighborsClassifier(n_neighbors=5)
knn.fit(X_train, y_train)
return accuracy_score(y_test, knn.predict(X_test))
# stały zbiór testowy (bez augmentacji)
pozostale = np.setdiff1d(np.arange(len(X_flat)), idx_treningowe)
idx_testowe = rng.choice(pozostale, 300, replace=False)
X_te, y_te = X_flat[idx_testowe], y[idx_testowe]
acc_male = ocena(X_tr, y_tr, X_te, y_te)
# (2) augmentacja: dla KAŻDEGO przykładu treningowego dokładamy obrócone kopie
katy = [-15, -10, -5, 5, 10, 15]
X_aug, y_aug = [X_tr], [y_tr]
for kat in katy:
X_obrocone = np.array([rotate(im.reshape(8, 8), kat,
reshape=False).ravel() for im in X_tr])
X_aug.append(X_obrocone)
y_aug.append(y_tr)
X_aug = np.vstack(X_aug)
y_aug = np.concatenate(y_aug)
acc_aug = ocena(X_aug, y_aug, X_te, y_te)
print(f"Dokładność przy 80 przykładach: {acc_male:.2%}")
print(f"Dokładność przy 80 przykładach + augmentacja: {acc_aug:.2%} "
f"(przykładów treningowych: {len(X_aug)})")In [ ]:
# EKSPERYMENT: test odporności - ta sama ocena, ale na zaszumionym zbiorze testowym
X_te_szum = X_te + rng.normal(0, 3.0, X_te.shape) # dodajemy szum do danych testowych
acc_szum_bez = ocena(X_tr, y_tr, X_te_szum, y_te)
acc_szum_aug = ocena(X_aug, y_aug, X_te_szum, y_te)
print(f"Zaszumiony test, model bez augmentacji: {acc_szum_bez:.2%}")
print(f"Zaszumiony test, model z augmentacją: {acc_szum_aug:.2%}")In [ ]:
titanic['sex']In [ ]:
# Ręcznie: dwie kategorie -> 0 i 1
titanic['sex_label'] = titanic['sex'].map({'male': 0, 'female': 1})
titanic[['sex', 'sex_label']].head(10)Warning:
Output truncated. This notebook contains too many cells to display efficiently.