Celem analizy/projektu było przygotowanie, oczyszczenie przesłanych danych, stworzenie modelu predykcyjnego dla zmiennej Price, wizualizacja danych oraz zawarcie wniosków/rekomendacji.
Przed rozpoczęciem pracy dotyczącej diamentów, zapoznałem się z podmiotem analizy. Wyszukałem w internecie informacje dotyczące klasycznych cen, wag, czy też wymiarów diamentów aby wiedzieć, jakie dane ewentualnie odrzucić z naszego modelu predykcji. Na wstępie włączyłem excel, zapoznałem się z danymi, przefiltrowałem wartości budzące wątpliwości oraz wykryłem braki w wierszach.
Następnie w Rstudio podjąłem kroki takie jak: ustalenie nierealistycznych wartości zmiennych, odpowiednie sformatowanie kolumn oraz usunięcie wierszy z brakami danych. Ponadto, przefiltrowałem dane zmiennych, tak aby pokazywały wyłącznie realne wartości: ustaliłem wartość dla masy karatowej (Carat) w przedziale 0.1-15, dla zmiennej (Depth) 40%-80%, dla zmiennej (Table) 40%-95%, ustaliłem dodatnią cenę bez ekstremów oraz dodatnie wymiary o realistycznych wartościach. Dzięki czynnościom powyżej otrzymałem wiarygodną podstawę do zbudowania modelu.
Celem analizy jest nie tylko predykcja ceny, ale również identyfikacja czynników wpływających na jej poziom oraz ocena siły tego wpływu. Z tego powodu jako model bazowy zastosowano regresję liniową, charakteryzującą się wysoką interpretacyjnością.
log_carat, czystość clarity i kolor
Color. Zmienne opisujące proporcje diamentu
(Depth, Table). okazały się statystycznie
nieistotne.unrealistic_carats=c(0.0001, 0.0111, 21, 85, 86, 91, 100, 321, 1022, 2121, 21212, 34241)
mutate(cut = trimws(cut)) %>% oraz mutate(across(c(carat, depth, table, price, x, y, z), as.numeric)) %>%
filter(!is.na(carat) & !is.na(depth) & !is.na(table) & !is.na(price) & !is.na(x) & !is.na(y) & !is.na(z)) %>%
Oraz przefiltrowałem dane w taki sposób, aby pozostawały realne informacje, takie jak:
Realistyczna masa karatowa (między 0.1 a 15)
Realistyczna wartość depth (40-80%)
Realistyczna wartość table (40-95%)
Realistyczna cena (dodatnia, bez ekstremow)
Realistyczne wymiary (dodatnie, z górnym sensownym limitem)
Po sprawdzeniu ilości wierszy bazy danych przed i po czyszczeniu (cleaningu), spora część oryginalnych danych została usunięta z powodu braków lub błędów:
Liczba wierszy przed czyszczeniem: 10050
Liczba wierszy po czyszczeniu: 7566
summary(diamenty_clean), możemy
wysnuć wstępną analizę mówiącą nam, że## carat cut color clarity
## Min. :0.2000 Length:7566 Length:7566 Length:7566
## 1st Qu.:0.4000 Class :character Class :character Class :character
## Median :0.7000 Mode :character Mode :character Mode :character
## Mean :0.8003
## 3rd Qu.:1.0400
## Max. :4.0100
## depth table price x
## Min. :43.00 Min. :51.00 Min. : 335 Min. : 3.840
## 1st Qu.:61.10 1st Qu.:56.00 1st Qu.: 942 1st Qu.: 4.700
## Median :61.90 Median :57.00 Median : 2402 Median : 5.690
## Mean :61.78 Mean :57.44 Mean : 3925 Mean : 5.733
## 3rd Qu.:62.60 3rd Qu.:59.00 3rd Qu.: 5282 3rd Qu.: 6.530
## Max. :79.00 Max. :73.00 Max. :18823 Max. :10.140
## y z
## Min. : 3.800 Min. :1.410
## 1st Qu.: 4.710 1st Qu.:2.910
## Median : 5.710 Median :3.520
## Mean : 5.735 Mean :3.542
## 3rd Qu.: 6.530 3rd Qu.:4.030
## Max. :10.100 Max. :6.310
Carat(Masa)
Większość diamentów w tym zbiorze jest stosunkowo mała (Mediana wynosi 0.7 karata, a średnia 0.8 karata)
Ponadto, wyższa średnia od mediany wskazuje nam, że rozkład wykazuje prawostronną skośność - jest więcej mniejszych diamentów, ale obecność kilku większych “ciągnie” średnią w górę
Depth(Głębokość%)
Wartości zmiennej ‘Depth’ koncentrują się wokół średniej 61.78% i mediany 61.90%
Oznacza to, że rozkład jest stosunkowo symetryczny
Table(Tabela)
Podobnie jak zmienna ‘Depth’, wartości skupiają się wokół średniej 57.44% i mediany 57.00%
Rozkład jest lekko prawostronnie skośny
Price(Cena)
Ceny są mocno prawostronnie skośne. Mediana wynosi 2402$, podczas gdy średnia to 3925$. Oznacza to, że większość diamentów jest tańsza, ale istnieje grupa znacznie droższych kamieni, które zawyżają średnią.
Najtańszy diament kosztuje 335$, a najdroższy 18823$. Połowa diamentów ma cenę w przedziale od 942$ do 5282$.
x,y,z(Wymiary)
Średnie i mediany dla ‘x’ i ‘y’ są bardzo zbliżone (ok. 5.73 mm), co jest oczekiwane dla większości szlifów. Wartość ‘z’ (głębokość w mm) jest mniejsza średnio o 3.54 mm
Minimalna wartość ‘z’ (1.41 mm) niska w porównaniu do minimalnych ‘x’ i ‘y’ (ok. 3.8 mm)
Wybierając zmienne objaśniające, pominąłem zmienne z wymiarami ‘(z, y, z)’, ponieważ założyłem, że są one bardzo silnie skorelowane ze zmienną ‘Carat’. Dzięki temu uniknąłem problemu współliniowości.
Wizualizacja rozkładu gęstości dla zmiennej ‘Price’
Analizując grafikę, rozkład zmiennej price jest mocno prawostronnie skośny. Gdy zmienna zależna (price) jest tak skośna, istnieje ryzyko, że model nie będzie spełniać potrzebnych założeń.
p1
diamenty_model_data = diamenty_clean %>% mutate(log_price = log(price), log_carat = log(carat))
split = sample.split(diamenty_model_data$log_price, SplitRatio = 0.75)
train_data = subset(diamenty_model_data, split == TRUE)
test_data = subset(diamenty_model_data, split == FALSE)
diamenty_model_data = diamenty_clean %>% mutate(log_price = log(price), log_carat = log(carat))
model = lm(log_price ~ log_carat + cut + color + clarity + depth + table, data = train_data)
summary(model)
##
## Call:
## lm(formula = log_price ~ log_carat + cut + color + clarity +
## depth + table, data = train_data)
##
## Residuals:
## Min 1Q Median 3Q Max
## -0.76242 -0.08641 0.00003 0.08455 1.33955
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 7.673738 0.132941 57.723 < 2e-16 ***
## log_carat 1.873444 0.003540 529.238 < 2e-16 ***
## cutGood 0.080299 0.011840 6.782 1.31e-11 ***
## cutIdeal 0.163597 0.011878 13.773 < 2e-16 ***
## cutPremium 0.145772 0.011366 12.826 < 2e-16 ***
## cutVery Good 0.121822 0.011387 10.698 < 2e-16 ***
## colorE -0.044543 0.006603 -6.746 1.67e-11 ***
## colorF -0.088037 0.006647 -13.244 < 2e-16 ***
## colorG -0.149305 0.006540 -22.830 < 2e-16 ***
## colorH -0.233454 0.006992 -33.390 < 2e-16 ***
## colorI -0.361166 0.007625 -47.365 < 2e-16 ***
## colorJ -0.499310 0.009498 -52.570 < 2e-16 ***
## clarityIF 1.101460 0.017719 62.163 < 2e-16 ***
## claritySI1 0.585067 0.014749 39.668 < 2e-16 ***
## claritySI2 0.421994 0.014814 28.485 < 2e-16 ***
## clarityVS1 0.811680 0.015105 53.737 < 2e-16 ***
## clarityVS2 0.738169 0.014843 49.731 < 2e-16 ***
## clarityVVS1 1.011508 0.016266 62.185 < 2e-16 ***
## clarityVVS2 0.938141 0.015656 59.922 < 2e-16 ***
## depth 0.001376 0.001451 0.948 0.343
## table 0.001496 0.001050 1.424 0.154
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.1345 on 5653 degrees of freedom
## Multiple R-squared: 0.9824, Adjusted R-squared: 0.9823
## F-statistic: 1.574e+04 on 20 and 5653 DF, p-value: < 2.2e-16
log_carat (Masa):
Logarytm masy ma najsilniejszy wpły na logarytm ceny (zależność log vs log). Oznacza to, że wzrost masy diamentu o 1% jest związany ze wzrostem jego ceny średnio o ok. 1.87% ceteris paribus
depth (Głębokość) i table:
Wartości P-value zmiennych depth i table wynoszą kolejno (0.290 i 0.199). Oznacza to, że na podstawie tych danych nie możemy stwierdzić, żeby miały one jakiekolwiek realny wpływ na cenę.
cut (Szlif):
Bazując na powyższej informacji, cena diamentu rośnie, im “lepszy” szlif:
W porównaniu do szlifu ‘Fair’, cena rośnie:
cutIdeal (Idealny szlif): o ok. 18.6% (ponieważ e 0.1707 ≈1.186).
cutPremium (Premium): o ok. 16.6% (e 0.1535 ≈1.166).
cutVery Good (Bardzo Dobry): o ok. 13.6% (e 0.1277 ≈1.136).
cutGood (Dobry): o ok. 9.0% (e 0.0866 ≈1.090).
color (Kolor):
Cena różni się w zależności od koloru (im gorszy kolor, tym niższa cena):
colorE: spadek o ok. 4.6% (e −0.0473 ≈0.954).
colorG: spadek o ok. 14.2% (e −0.1531 ≈0.858).
colorJ (najgorszy w zestawieniu): spadek o ok. 39.7% (e −0.5057 ≈0.603).
clarity (Czystość):
Cena rośnie w zależności od tego, jak ‘czysty’ jest diament:
claritySI2: wzrost o ok. 53.4% (e 0.4275 ≈1.534).
clarityVS2: wzrost o ok. 110.6% (e 0.7445 ≈2.106).
clarityIF (najlepsza): wzrost o ok. 202.5% (e 1.1072 ≈3.025). Diament o czystości IF jest średnio ponad 3 razy droższy niż diament I1 ceteris paribus.
R-kwadrat wynosi: 0.9824 a skorygowany R-kwadrat wynosi: 0.9823.
Skorygowany R-kwadrat jest bardzo bliski wartości 1, co potwierdza, że model nie jest “przeładowany” zbędnymi zmiennymi
Dodatkowo, ogólny test F-statistic ma p-value (< 2.2e-16), co oznacza, że model jako całość jest bardzo istotny statystycznie (przynajmniej jeden predyktor jest istotnie powiązany z ceną)
Oprócz tego, poziomy zmiennych ‘Cut’ (szlif), ‘Color’ (kolor) i ‘Clarity’ (czystość). Ich p-value jest ekstremalnie małe (< 2e-16), co oznacza, że ich związek z logarytmem ceny jest bardzo silny.
Jeśli chodzi o, p-value dla zmiennych depth (0.343) oraz table (0.154). Obie te wartości są znacznie wyższe niż standardowy próg istotności (np. α=0.05). Oznacza to, że w tym modelu, przy uwzględnieniu pozostałych zmiennych, depth (głębokość) i table (tafla) są statystycznie nieistotne.
Z tabeli można również wywnioskować, że zmienna ‘log_Carat’ to najważniejszy predyktor. Wartość 1.873444 w kolumnie Estimate oznacza, że wzrost logarytmu karatów o 1 jednostkę wiąże się ze wzrostem logarytmu ceny średnio o około 1.87 ceteris paribus. (Większe diamenty są droższe)
predictions_log = predict(model, newdata = test_data)
rmse_log=sqrt(mean((test_data$log_price - predictions_log)^2))
cat("RMSE (dla log_price):", rmse_log, "\n")
## RMSE (dla log_price): 0.1343203
Wartość RMSE (dla log_price): 0.1343203 oznacza, że standardowe odchylenie błędów prognozy modelu wynosi 0.1343. Jesto to miara tego, jak bardzo “przeciętnie” przewidywania modelu dotyczące logarytmu ceny różnią się od rzeczywistego logarytmu ceny. RMSE na poziomie 0.1343 dla log_price to bardzo dobry wynik przy R-kwadrat wynoszący 98%. Oznacza on, że model jest nie tylko dobrze dopasowany, ale jego błędy predykcji są relatywnie małe (13-14%).
Oraz obliczyłem R-kwadrat dla zbioru testowego
sse = sum((test_data$log_price - predictions_log)^2)
sst = sum((test_data$log_price - mean(test_data$log_price))^2)
r_squared_test = 1 - sse/sst
cat("R-kwadrat (na zbiorze testowym):", r_squared_test, "\n")
## R-kwadrat (na zbiorze testowym): 0.9828489
Wartość R-kwadrat (0.9828489) pokazuje, jak dobrze model radzi sobie z prognozowaniem na nowych, niewidzianych wcześniej danych. Dzięki niej, możemy sprawdzić, czy model nie uległ przeuczeniu. Co ma miejsce, gdy model zbyt dobrze “zapamiętał” dane treningowe, ale traci zdolność do prognozowania na nowych danych. Wartość 98,28% oznacza, że model jest w stanie wyjaśnić 98,28% zmienności ‘log_price’ w zbiorze testowym, na nowych danych.
Po czym zwizualizowałem rzeczywiste vs przewidywane wartości (w skali logarytmicznej)
Aby wizualnie ocenić jakość i wiarygodność modelu na danych, których nigdy nie „widział”, zrobiłem wykres na zbiorze testowym. Dzięki temu sprawdziłem, czy model jest “dobry”.
Warto zwrócić uwagę na fakt, że zlogarytmowanie zmiennej ‘Price’, sprawia, że model jest o wiele bardziej dopasowany do założeń statystyczych. Po samym porównaniu dwóch wykresów możemy dojść do wniosku, że rozkład jest znacznie bardziej symetryczny oraz jej wariancja jest bardziej stabilna niż w przypadku modelu z niezlogarytmowaną zmienną ‘Price’:
Log(Price) vs Log(Carat)
Log(Price) vs Cut
Log(Price) vs Color
Log(Price) vs Clarity
Po zbudowaniu modelu kluczowe jest sprawdzenie, czy jego założenia zostały spełnione. W tym celu przeanalizowano reszty (różnice między wartością rzeczywistą a przewidywaną) na zbiorze testowym.
Liniowość i Homoskedastyczność
Liniowość: Punkty są rozproszone losowo wokół czerwonej, przerywanej linii (bezkształtna, losowa chmura punktów), dzięki czemu możemy uznać, że założenie o liniowości jest spełnione.
Stała wariancja: (Homoskedastyczność): Grubość chmury punktów jest stała na całej osi X, co oznacza brak problemu heteroskedastyczności modelu
Braku systematycznego błędu: Chmura punktów jest równomiernie rozłożona powyżej i poniżej linii y=0. Oznacza to, że model nie ma tendencji do zawyżania ani zaniżania cen. Wykres ten potwierdził, że zbudowany model dostarczył mocnych dowodów na to, że jest dobrze dopasowany i poprawnie generalizuje nowe dane.
Aby sprawdzić, czy błędy modelu rozkładają się losowo (zgodnie z rozkładem normalnym), stworzono histogram reszt oraz wykres kwantylowy.
p_hist_res
Model jest poprawny metodologicznie, ponieważ jego błędy (reszty) przypominają rozkład normalny.
p_qq_res
Punkty leżą niemalże idealnie na prostej linii. Oznacza to, że błędy w modelu zachowują się tak, jak powinny (potwierdzenie o normalności błędów).
Na podstawie przeprowadzonej analizy danych i zbudowanego modelu można wysnuć następujące wnioski:
Bardzo wysoka skuteczność modelu: Zbudowany model regresji liniowej, jest wysoce skuteczny. Osiągnięty współczynnik \(R^2\) na poziomie 0.9824 oznacza, że model wyjaśnia aż 98,24% zmienności w logarytmie ceny diamentów.
Model nie jest przeuczony: Uzyskany \(R^2\) na danych testowych (0.9828) jest niemal identyczny z wynikiem na zbiorze treningowym. Dowodzi to, że model potrafi precyzyjnie przewidywać ceny nowych, nieznanych wcześniej danych.
Wysoka precyzja: Niski poziom błędu RMSE dla logarytmu ceny (0.1343) potwierdza, że prognozy modelu są bliskie wartościom rzeczywistym.
Transformacja danych: Zlogarytmowanie zmiennych price i carat była potrzebna dla sukcesu modelu. Jak pokazały wykresy (rozkładu ceny oraz p_carat), przekształcenie to ustabilizowało wariancję (rozwiązało problem heteroskedastyczności) i “wyprostowało” zależność, umożliwiając skuteczne zastosowanie regresji liniowej.
Spełnienie założeń statystycznych: Analiza reszt (wykres “Reszty vs Dopasowane Wartości”) potwierdziła, że błędy modelu są losowe, pozbawione wzorców i skupione wokół zera. Świadczy to o poprawności metodologicznej modelu i spełnieniu założeń regresji liniowej.
Zmienne o największym i najmniejszym wpływie na ceny diamentów
Zmienne o największym wpływie
log_Carat (Masa): Jest to najważniejszy predyktor ceny (p-value < 2.2e-16). Wykres Log(Cena) vs Log(Karat) pokazał bardzo silną, dodatnią zależność liniową w skali logarytmicznej.
Color (Kolor): Zmienna o bardzo silnym wpływie (p-value < 2.2e-16). Wykres pudełkowy wyraźnie zilustrował negatywną korelację – im “gorszy” kolor (bliżej ‘J’), tym cena jest systematycznie niższa ceteris paribus.
Clarity (Czystość): Kolejny istotny czynnik (p-value < 2.2e-16). Zaobserwowano silny trend pozytywny – cena rośnie wraz z poprawą klasy czystości (od ‘I1’ do ‘IF’).
Cut (Szlif): Szlif również jest zmienną o wysokiej istotności statystycznej (p-value < 2.2e-16). Jego wpływ jest jednak bardziej złożony, co pokazał wykres Log(Cena) vs Szlif, gdzie mediana ceny dla szlifu “Ideal” była niższa niż dla “Premium”, co sugeruje interakcje z innymi zmiennymi (np. masą).
Zmienne o najmniejszym (nieistotnym) wpływie
Depth: Wartość p-value dla tej zmiennej wyniosła 0.343. Jest to wartość znacznie powyżej progu 0.05, co oznacza, że depth jest statystycznie nieistotna w tym modelu.
Table: Podobnie jak depth, zmienna ta uzyskała wysokie p-value (0.154), co również czyni ją statystycznie nieistotną.