wtorek, 7 maja 2024

Exploratory Data Analysis: balansowanie zbioru i normalizacja danych

Ten wpis, podobnie jak cała zawartość bloga, odzwierciedla moje zainteresowania rozwojem w dziedzinie informatyki. Główne cele tego przedsięwzięcia to dzielenie się wiedzą, demonstracja moich umiejętności oraz chęć poznawania nowych zagadnień, co może zainteresować potencjalnych współpracowników zarówno w sferze zawodowej, jak i poza nią. Blog ten jest również okazją do samodzielnego przetwarzania zdobytej wiedzy i tworzenia osobistych notatek. Jako że sam jestem w trakcie nauki, zachęcam do niezależnego myślenia i, jeśli tematyka wpisów wpisuje się w zakres Twoich zainteresowań, do dalszej eksploracji i weryfikacji podanych przeze mnie informacji.

Dzisiaj zajmiemy się wybranymi elementami statystyki opisowej, które są kluczowe w kontekście uczenia maszynowego i analizy eksploracyjnej danych (EDA). Omówimy, jak średnia arytmetyczna i odchylenie standardowe pomagają w analizie i przygotowaniu danych.

Średnia arytmetyczna opisuje centralną tendencję zbioru danych, co umożliwia ocenę, czy dane są odpowiednio zrównoważone między klasami. Przywołując ponownie przykład z mojego bloga, gdzie użytkowników podzielono na "zainteresowanych" i "niezainteresowanych" treściami. Załóżmy, że mamy 900 próbek od użytkowników zainteresowanych i 100 próbek od użytkowników niezainteresowanych. Chociaż wiemy, że 900 użytkowników jest zainteresowanych, a 100 nie, naszym celem jest dogłębne zrozumienie cech charakteryzujących obie grupy.

Problem pojawia się, gdy zbiory danych są niezbalansowane. Dominacja danych z grupy zainteresowanych może nieumyślnie wpływać na analizę cech grupy niezainteresowanych, prowadząc do potencjalnych błędów w interpretacji wyników. Może to skutkować sytuacją, w której model oparty na tych danych błędnie klasyfikuje nowe przypadki niezainteresowanych jako zainteresowanych. Innymi słowy, dane dominującej grupy zainteresowanych mogą nieświadomie zaciemniać obraz danych grupy mniejszościowej.

Innymi słowy, pewne cechy zachowań użytkowników niezainteresowanych mogą być mylnie przypisywane do zachowań użytkowników zainteresowanych, co skutkuje błędnym przyporządkowaniem osób niezainteresowanych do grupy zainteresowanych, szczególnie w sytuacji, gdy brakuje odpowiedniej liczby danych dla grupy mniejszościowej (niezainteresowanych). Ta nierównowaga jest od razu widoczna, gdy porównamy liczbę 900 zainteresowanych do 100 niezainteresowanych. Aby to matematycznie uzasadnić, obliczmy współczynnik średniej.

Przyjmijmy, że klasa użytkowników zainteresowanych jest oznaczona wartością 1, a klasa użytkowników niezainteresowanych wartością 0. Mamy 900 użytkowników zainteresowanych i 100 niezainteresowanych, co daje razem 1000 użytkowników.

średnia = (1 * 900 + 0 * 100) / 1000 = 0,9

Współczynnik średniej równy 0,9 wskazuje na niezbalansowane zbiory. Oczekujemy, że wartość średniej dla idealnie zbalansowanego zbioru będzie bliższa 0,5. Należy jednak pamiętać, że to stwierdzenie jest prawdziwe tylko wtedy, gdy klasy są wartościowane jako 0 i 1. W takim przypadku idealnie zbalansowany zbiór miałby wartość średniej równą 0,5. Trzeba mieć na uwadze, że z pewnością istnieją inne przypadki, w których pożądana równowaga między klasami nie będzie przybierać postaci "pół na pół" jak w tym przypadku.

Kod przeprowadzający powyższą operację wyliczenia średniej oraz informację o tym czy zbiór jest zbalansowany czy też nie może wyglądać jak poniżej. Liczba 900 została przekształcona do tablicy dziewięciuset wartości równych 1, a 100 do tablicy wartości równych 0. Następnie te tablice połączono w jedną i użyto na niej gotowej metody mean() z biblioteki numpy, która oblicza średnią:

import numpy as np

# Input data
interested = np.ones(900)  # Creates an array of 900 values, all set to 1
not_interested = np.zeros(100)  # Creates an array of 100 values, all set to 0

# Combine the data into one array
data = np.concatenate([interested, not_interested])

# Calculate the mean
mean = np.mean(data)

print(f"The mean is: {mean}")

# Check if the dataset is balanced
print("The dataset is " + ("balanced" if mean == 0.5 else "unbalanced"))

Kolejnym zastosowaniem średniej wraz z odchyleniem standardowym jest normalizacja danych, co jest kluczowe w przetwarzaniu danych liczbowych przez algorytmy uczenia maszynowego. Odchylenie standardowe to miara rozproszenia wartości wokół średniej w zestawie danych. Przykładowo, w modelu regresji cen mieszkań możemy rozważać takie cechy, jak rok budowy obiektu, na przykład 1999, 2011 czy 2017, oraz ilość pokoi, takich jak 1, 2 lub 3.

Surowe dane jak rok 1999 czy liczba pokoi 1, mogą być mylące dla modelu, jeśli nie zostaną odpowiednio znormalizowane. Nie zagłębiając się w szczegóły matematyczne, istotne jest, aby zrozumieć, że tak różne wartości z obcych sobie klas, przetwarzane równocześnie, mogą generować chaos. Różnica między wartościami jak rok budowy i ilość pokoi jest znacząca i może znacznie wpływać na wyniki modelowania.

Gdybyśmy napotkali dane, w których rok budowy wynosiłby 1999, a inny rekord zawierałby rok równy 1, byłoby to zjawisko nietypowe. Teoretycznie moglibyśmy przypuszczać, że mamy do czynienia z budynkiem zabytkowym. W takiej sytuacji moglibyśmy zdecydować, czy utrzymać te dane w zbiorze treningowym modelu, czy też odrzucić takiego outliera, czyli wartość znacząco odbiegającą od średniej.

Jednakże, gdy dane, w których rok budowy 1999r. jest zestawiony z liczbą pokoi równą 1, znajdują się razem w jednym wektorze danych uczących, może to znacznie obniżyć wartość modelu w kontekście dokładnych predykcji. W związku z tym, poniżej przedstawię metodę normalizacji danych, która pozwoli uniknąć tego typu problemów:

import pandas as pd
from sklearn.preprocessing import StandardScaler

# Sample data
data = {
    'year_built': [1999, 2011, 2017],
    'number_of_rooms': [1, 2, 3]
}

# Creating a DataFrame
df = pd.DataFrame(data)

# Display the original data
print("Original Data:")
print(df)

# Initialize the StandardScaler
scaler = StandardScaler()

# Fit and transform the data
normalized_data = scaler.fit_transform(df)

# Creating a DataFrame for the normalized data
normalized_df = pd.DataFrame(normalized_data, columns=['year_built', 
                                                       'number_of_rooms'])

# Display the normalized data
print("\nNormalized Data:")
print(normalized_df)

Nie będę tworzył pełnego opisu kodu, natomiast pozwolę sobie wyróżnić kilka rzeczy.

StandardScaler to klasa służąca do normalizacji danych.

data to słownik z listami zawierającymi wartości liczbowe.

fit_transform() oblicza średnią i odchylenie standardowe, aby w kolejnym kroku skalować każdą wartość w kolumnie tak, aby średnia wynosiła 0, a odchylenie standardowe 1.

Nie wszystko jest tak idealne, jak mogłoby się wydawać. Muszę zauważyć, że normalizacja danych ilościowych, takich jak liczba pokoi, niekoniecznie pasuje do danych porządkowych, takich jak lata. Poniżej przedstawiam kod, który normalizuje liczbę pokoi w zakresie od 0 do 1, pozostawiając dane o latach bez zmian. Zamiast używać StandardScaler, zastosowałem MinMaxScaler. Kod został opracowany z pomocą ChatGPT:

import pandas as pd
from sklearn.preprocessing import MinMaxScaler

# Sample data
data = {
    'year_built': [1999, 2011, 2017],
    'number_of_rooms': [1, 2, 3]
}

# Creating a DataFrame
df = pd.DataFrame(data)

# Display the original data
print("Original Data:")
print(df)

# Initialize the MinMaxScaler
scaler = MinMaxScaler()

# Fit and transform the data
normalized_data = scaler.fit_transform(df[['number_of_rooms']])

# Creating a DataFrame for the normalized data
# We only apply scaling to 'number_of_rooms' to avoid distorting 
# the ordinal nature of 'year_built'
normalized_df = pd.DataFrame(data={'year_built': df['year_built'], 
                                   'number_of_rooms': normalized_data.flatten()})

# Display the normalized data
print("\nNormalized Data:")
print(normalized_df)

Cóż, czy to wyczerpuje temat? Myślę, że nie. Ja również ciągle się uczę... Ważne jest, aby nieustannie się rozwijać, podchodzić krytycznie do swojej pracy i nieustannie poszukiwać nowych, lepszych rozwiązań.

Brak komentarzy:

Prześlij komentarz

Analiza sentymentów - wpisy na mediach społecznościowych (podział danych)

Ten wpis zaczniemy od stworzenia DataFrame z danymi treningowymi train_df = pd.read_csv('train.csv', encoding='ISO-8859-1')....