Giriş

Ad: Ramil

Soyad: Musayev

Ödev kapsamında Kaggle’da bulunan Spaceship Titanic verisi kullanılacaktır. Spaceship Titanic, uzayda geçen bir hikayeye sahip olan bir Kaggle yarışmasıdır. Goreviniz, Spaceship Titanic’te yaşanan bir olay sonucu başka boyuta taşınan yolcuları tahmin etmek ve kurtarma ekiplerine yardımcı olmaktır.

Öncelikle veri önişleme işlemi yapılıp daha sonra gemide hangi yolcuların taşındığını tahmin eden veri modelleri geliştirilecektir. Son olarak bu modellerin doğruluk değerleri hesaplanarak overfit ve outfit durumları değerlendirilecektir.

Öncelikle gerekli kütüphaneler yüklenir ve veri sistemden çekilir.

# Kütüphanelerin yüklenmesi
library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.1.4     ✔ readr     2.1.4
## ✔ forcats   1.0.0     ✔ stringr   1.5.1
## ✔ ggplot2   3.4.4     ✔ tibble    3.2.1
## ✔ lubridate 1.9.3     ✔ tidyr     1.3.0
## ✔ purrr     1.0.2     
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(caret)
## Loading required package: lattice
## 
## Attaching package: 'caret'
## 
## The following object is masked from 'package:purrr':
## 
##     lift
library(mice)
## 
## Attaching package: 'mice'
## 
## The following object is masked from 'package:stats':
## 
##     filter
## 
## The following objects are masked from 'package:base':
## 
##     cbind, rbind
library(gridExtra)
## 
## Attaching package: 'gridExtra'
## 
## The following object is masked from 'package:dplyr':
## 
##     combine
# Verinin okunması
df = read_csv("C:\\Project\\train.csv")
## Rows: 8693 Columns: 14
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (5): PassengerId, HomePlanet, Cabin, Destination, Name
## dbl (6): Age, RoomService, FoodCourt, ShoppingMall, Spa, VRDeck
## lgl (3): CryoSleep, VIP, Transported
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
# grafik oluşturmak için kullanılacak olan plot_graph fonksiyonu
plot_graph = function(column){
    ggplot(data = df, mapping = aes(x = {{column}}, fill = Transported)) +
      geom_bar(position = 'dodge') +
      scale_fill_manual('legend', values = c("#FF0000", "#00FF00"))
}

Verinin İncelenmesi

head(df)
## # A tibble: 6 × 14
##   PassengerId HomePlanet CryoSleep Cabin Destination     Age VIP   RoomService
##   <chr>       <chr>      <lgl>     <chr> <chr>         <dbl> <lgl>       <dbl>
## 1 0001_01     Europa     FALSE     B/0/P TRAPPIST-1e      39 FALSE           0
## 2 0002_01     Earth      FALSE     F/0/S TRAPPIST-1e      24 FALSE         109
## 3 0003_01     Europa     FALSE     A/0/S TRAPPIST-1e      58 TRUE           43
## 4 0003_02     Europa     FALSE     A/0/S TRAPPIST-1e      33 FALSE           0
## 5 0004_01     Earth      FALSE     F/1/S TRAPPIST-1e      16 FALSE         303
## 6 0005_01     Earth      FALSE     F/0/P PSO J318.5-22    44 FALSE           0
## # ℹ 6 more variables: FoodCourt <dbl>, ShoppingMall <dbl>, Spa <dbl>,
## #   VRDeck <dbl>, Name <chr>, Transported <lgl>

PassengerId - Her yolcu için benzersiz bir kimlik. Her kimlik, gggg_pp biçiminde olup, gggg yolcu ile seyahat eden grubu gösterir ve pp grubun içindeki sıra numarasını belirtir. Bir grup içindekiler genellikle aile üyeleridir, ancak her zaman değil.

HomePlanet - Yolcunun ayrıldığı gezegen, genellikle kalıcı ikamet gezegenidir.

CryoSleep - Yolcunun seyahat süresince askıya alınmak üzere seçilip seçilmediğini belirtir. Cryosleep’te olan yolcular kabinlerine kapatılır.

Cabin - Yolcunun kaldığı kabin numarası. Şekli deck/num/side’dir, side P (Port) veya S (Starboard) olabilir.

Destination - Yolcunun iniş yapacağı gezegen.

Age - Yolcunun yaşı.

VIP - Yolcunun seyahat sırasında özel VIP hizmeti için ödeme yapılıp yapılmadığını belirtir.

RoomService, FoodCourt, ShoppingMall, Spa, VRDeck - Yolcunun Spaceship Titanic’in birçok lüks olanaklarından her birinde ne kadar harcama yaptığını gösterir.

Name - Yolcunun adı ve soyadı.

Transported - Yolcunun başka bir boyuta taşınıp taşınmadığını belirtir. Bu, tahmin etmeye çalıştığınız hedef sütundur.

HomePlanet:

# home planet - transported korelasyon grafiği
plot_graph(HomePlanet) + 
    ggtitle("HomePlanet sütunu ile Transported ilişkisi")

Grafik incelendiğinde HomePlanet kesinlikle Transported ile ilişkili görünüyor.

Cryosleep:

# Cyrosleep Transported ilişkisi
plot_graph(CryoSleep) + 
    ggtitle("Cryosleep sütunu ile Transported ilişkisi")

CryoSleep sütununun Transported ile güçlü bir şekilde ilişkili olduğu görülüyor.

Destination:

# Varış yeri Trasnsported ilişkisi
plot_graph(Destination) + 
    ggtitle("Destination sütunu ile Transported ilişkisi")

Taşınan yolcuların oranı, Varış Noktasına bağlı olarak önemli ölçüde farklılık gösteriyor.

VIP:

# VIP grafiği
plot_graph(VIP) + 
    ggtitle("VIP sütunu ile Transported ilişkisi")

Direkt etkilemediği için çok kullanışlı bir sütun gibi gözükmüyor. Oluşturulacak modellerden çıkartılabilir.

Veri Ön İşleme

Veri Mühendisliği

Veri incelendiğinde PassengerId ve Cabin sütunlarından çıkarımlar yapılabileceği görüldü. PassengerId verisinin 2.kısmı o kişinin grubunda kaç kişi olduğunu belirtmektedir. Bu özniteliği çıkarmak için Size adında yeni bir sütun oluşturulur ve 4 e kadar olan sizelar ayrı ayrı ele alınarak 4 ve üstü kişi sayısına sahip gruplar 4 olarak işaretlenir.

# Cabin ve PassangerId kolonlarının ayrıştırılması
df2 = df %>% 
  mutate(deck = str_sub(Cabin, 1, 1),
         num = as.numeric(str_sub(Cabin, 3, -3)),
         side = str_sub(Cabin, -1, -1),
         id1 = substring(PassengerId, 1, 4),
         id2 = substring(PassengerId, 6, 7)
)

# Grup büyüklüklerinin hesaplanması
group_size = df2 %>% 
  select(id1, id2) %>% 
  group_by(id1) %>% 
  summarize(size = max(as.numeric(id2))) %>% # grubun en büyük numarası, grup numarası olarak belirlenir.
  select(id1, size)

# size özelliğinin eklenmesi
df2 = merge(x = df2, y = group_size, by = "id1") %>% 
  mutate(size = ifelse(size >= 4, 4, size)) # 4 ten büyük değerler 4 olarak ele alınır.
rm(group_size) # group_size kolondan çıkarılır

Yeni eklenen size özniteliğinin, yani kişinin grubunda kaç kişi olduğunun gemiden kurtulma ile ilişkisine bakacak olursak:

# Group size grafiği
ggplot(data = df2, mapping = aes(x = size, fill = Transported)) +
  geom_bar(position = 'dodge') +
  ggtitle("Group Size sütununun Transported ile ilişkisi") +
  scale_fill_manual('legend', values = c("#FF0000", "#00FF00"))

Grafik incelendiğinde tek yolcuların gemide kalmaya daha eğilimli oldukları görülmektedir. Bu nedenle size özniteliği güzel bir belirteç olabilir.

Cabin özniteliğini 2 ye ayrılarak güverte (deck) ve yan taraf (side) olarak 2 öznitelik çıkarıldığında bu özniteliklerin taşınan yolculara etkisi yukarıdaki grafiklerde görülür.

Gereksiz olacağı düşünülen Name ve Cabin özellikleri kaldırılır. Name sütunundan herhangi bir şey çıkmayacaktır. Benzer şekilde, Cabin’ın ikinci bileşeni olan num özniteliği de değişken olduğu için Cabin özniteliğinin modellerde tutulmasına gerek yoktur.

# istenmeyen öznitelikler çıkarılır
df3 = df2 %>% 
  select(-c(Name, Cabin))

Eksik Veriler ve Çözüm Yöntemleri

Veri setindeki eksik verileri nasıl işleyeceğimize karar vermemiz gerekmektedir. İlk olarak, kaç verinin eksik olduğu görülür.

# eksik veriler
na_prop = function(vec){
  sum(is.na(vec)) / length(vec)
}
lapply(df2[,1:ncol(df3)], na_prop)
## $id1
## [1] 0
## 
## $PassengerId
## [1] 0
## 
## $HomePlanet
## [1] 0.02312205
## 
## $CryoSleep
## [1] 0.02496261
## 
## $Cabin
## [1] 0.02289198
## 
## $Destination
## [1] 0.02093639
## 
## $Age
## [1] 0.02059128
## 
## $VIP
## [1] 0.02335212
## 
## $RoomService
## [1] 0.02082135
## 
## $FoodCourt
## [1] 0.02105142
## 
## $ShoppingMall
## [1] 0.0239273
## 
## $Spa
## [1] 0.02105142
## 
## $VRDeck
## [1] 0.0216266
## 
## $Name
## [1] 0.02300702
## 
## $Transported
## [1] 0
## 
## $deck
## [1] 0.02289198
## 
## $num
## [1] 0.02289198
## 
## $side
## [1] 0.02289198

Çoğu sütunun verisinin yaklaşık %2’sinin eksik olduğu görülür. Daha sonra herhangi bir sütundaki verisi eksik olan satırların oranı kontrol edilir.

Herhangi bir verisi eksik olan satırlar

1 - (nrow(drop_na(df2)) / nrow(df2))

Toplam veri göz önüne alındığında kayıtların %24’ünde eksik veri olduğu görülür. Bu nedenle sadece eksik veri içeren satırları silmek uygun bir seçenek olmayacaktır. Boşluk doldurma işlemleri yapılması gerekecektir.

Crypto = TRUE durumu

İlk olarak, CryoSleep’te olan yolcuların hiçbir hizmette para harcayamayacaklarını biliyoruz. Buradaki eksik değere sahip olan satırları silebiliriz.

# Crypto = True durumu
df3 = df3 %>%
  mutate(RoomService = ifelse(is.na(RoomService) & CryoSleep == TRUE, 0, RoomService),
         FoodCourt = ifelse(is.na(FoodCourt) & CryoSleep == TRUE, 0, FoodCourt),
         ShoppingMall = ifelse(is.na(ShoppingMall) & CryoSleep == TRUE, 0, ShoppingMall),
         Spa = ifelse(is.na(Spa) & CryoSleep == TRUE, 0, Spa),
         VRDeck = ifelse(is.na(VRDeck) & CryoSleep == TRUE, 0, VRDeck))

lapply(df3 %>% select(RoomService, FoodCourt, ShoppingMall, Spa, VRDeck), na_prop)
## $RoomService
## [1] 0.01299896
## 
## $FoodCourt
## [1] 0.01299896
## 
## $ShoppingMall
## [1] 0.01288393
## 
## $Spa
## [1] 0.01357414
## 
## $VRDeck
## [1] 0.01449442

Harcamalar için eksik verilerin bir kısmını bu şekilde doldurmuş olduk.

Group sütunu kullanarak boşluk doldurma

Kişinin dahil olduğu grup bazında, bazı değerleri doldurmak için PassengerId verilerini kullanabiliriz. Eğer iki kişi birlikte seyahat ediyorsa, muhtemelen aynı HomePlanet’ten gelmişlerdir ve aynı Destination’a gidiyorlardır ve büyük ihtimalle de aynı güvertede konaklıyorlardır.

# Homeplanet
df3_missing_hp = df3 %>% 
  select(HomePlanet, id1) %>% 
  filter(id1 %in% df3$id1[is.na(df3$HomePlanet)] & !is.na(HomePlanet)) %>% 
  unique()

df3 = left_join(df3, df3_missing_hp, by = "id1") %>%
  mutate(HomePlanet = ifelse(!is.na(HomePlanet.x), HomePlanet.x, ifelse(is.na(HomePlanet.y), NA, HomePlanet.y))) %>%     
  select(-c(HomePlanet.x, HomePlanet.y))

# Varış Yeri
df3_missing_dest = df3 %>% 
  select(Destination, id1) %>% 
  filter(id1 %in% df3$id1[is.na(df3$Destination)] & !is.na(Destination))  %>% 
  group_by(id1) %>%
  mutate(Destination = min(Destination)) %>% 
  unique()

df3 = left_join(df3, df3_missing_dest, by = "id1") %>%
    mutate(Destination = ifelse(!is.na(Destination.x), Destination.x, 
                                ifelse(is.na(Destination.y), NA, Destination.y))) %>%
  select(-c(Destination.x, Destination.y))

# Deck
df3_missing_deck = df3 %>% 
  select(deck, id1) %>% 
  filter(id1 %in% df3$id1[is.na(df3$deck)] & !is.na(deck)) %>%  
  group_by(id1) %>%
  mutate(deck = min(deck)) %>% 
  unique()

df3 = left_join(df3, df3_missing_deck, by = "id1") %>%
    mutate(deck = ifelse(!is.na(deck.x), deck.x, ifelse(is.na(deck.y), NA, deck.y))) %>%
    select(-c(deck.x, deck.y))

lapply(df3 %>% select(HomePlanet, Destination, deck), na_prop)
## $HomePlanet
## [1] 0.01276889
## 
## $Destination
## [1] 0.01184861
## 
## $deck
## [1] 0.01138847

Bu işlem ile bu kolonlara ait eksik değerlerin yaklaşık yarısını daha doldurmuş olduk.

Boşlukları Sabit Değerlerle Doldurma İşlemi

Bazı öznitelikler sadeleştirmek için sabit değerlerle doldurulur. Örneğin:

HomePlanet: Önceki grafikler göz önüne alınarak, eksik değerler “Mars” ile doldurulur.

Deck, Side, CryoSleep, Destination, VIP: Eksik değerler her sütun için mod ile doldurulur. (en sık görülen değer).

Num (Cabin’ın ikinci kısmı): Eksik değerler her sütun için medyan ile doldurulur.

# mod almak için kullanılacak fonksiyon
find_mode <- function(x) {
  u <- unique(x)
  tab <- tabulate(match(x, u))
  u[tab == max(tab)]
}

# lazy fill
df3 = df3 %>% 
  mutate(HomePlanet = ifelse(is.na(HomePlanet), "Mars", HomePlanet), 
         deck = ifelse(is.na(deck), find_mode(df2$deck), deck),
         side = ifelse(is.na(side), find_mode(df2$side), side),
         num = ifelse(is.na(num), median(df2$num, na.rm = TRUE), num),
         CryoSleep = ifelse(is.na(CryoSleep), find_mode(df2$CryoSleep), CryoSleep),
         Destination = ifelse(is.na(Destination), find_mode(df2$Destination), Destination),
         VIP = ifelse(is.na(VIP), find_mode(df2$VIP), VIP),
         Transported = as.factor(as.numeric(Transported))
)

# gereksiz verilerin çıkarılması
rm(df3_missing_deck, df3_missing_dest, df3_missing_hp)

Lineer bir model kullanarak yaş tahmini

Lineer bir model kullanılarak yolcuların yaşları tahmin edilerek, ilgili sütundaki eksik değerler doldurulur.

df3 = df3 %>% 
  mutate(id1 = as.numeric(id1),
         id2 = as.numeric(id2),
         side = as.factor(side),
         HomePlanet = as.factor(HomePlanet),
         Destination = as.factor(Destination),
         deck = as.factor(deck))

rmse = function(model){
  preds = predict(model, test_age)
  sqrt(mean((preds - test_age$Age) ^ 2, na.rm = TRUE))
}

age_df = df3 %>% filter(!is.na(Age))
na_age_df = df3 %>% filter(is.na(Age))

# test // train
set.seed(1453)
trn_idx_age = sample(1:nrow(age_df), 0.8 * nrow(age_df))
train_age = age_df[trn_idx_age,] %>% select(-PassengerId)
test_age = age_df[-trn_idx_age,] %>% select(-PassengerId)

# lineer model
age_lm = lm(Age ~ ., train_age)
# tüm data ile train edilmesi
age_lm = lm(Age ~ VIP + size + HomePlanet + Destination + deck, data = age_df)

# yaş değerlerinin oluşturulan lineer modele göre tahmini
df3$Age[is.na(df3$Age)] = predict(age_lm, na_age_df)

Harcama yapılan sütunların tek bir sütunda toplam olarak kaydedilmesi (ServiceCost)

df3 = df3 %>%
mutate(RoomService = ifelse(is.na(RoomService), 0, RoomService),
       FoodCourt = ifelse(is.na(FoodCourt), 0, FoodCourt),
       ShoppingMall = ifelse(is.na(ShoppingMall), 0, ShoppingMall),
       Spa = ifelse(is.na(Spa), 0, Spa),
       VRDeck = ifelse(is.na(VRDeck), 0, VRDeck)  
)

df3  = df3  %>%
mutate(
  ServiceCost =  RoomService + FoodCourt + ShoppingMall + Spa + VRDeck
)

Modeller

Şimdi modelleme zamanı geldi. Modelleri eğitmek için R’deki caret paketi kullanılacaktır. Projedeki Transported sütununun tahmini için 3 adet model kullanılacaktır: Logistic Regression, kNN ve Gradient Boosting Machine. İlk olarak, bunun için veriseti %80 eğitim %20 test seti olarak ikiye ayrılır. Cross-validation 5 olarak belirlenir.

# test // train
trn_idx = sample(1:nrow(df3), 0.8 * nrow(df3))
train = df3[trn_idx,] %>% select(-PassengerId) 
test = df3[-trn_idx,] %>% select(-PassengerId)

# Test doğruluğunu hesaplamak için kullanılacak olan fonksiyon
test_acc = function(model, type = "raw"){
  preds = as.numeric(predict(model, test, type = type))-1
  actual = test$Transported
  mean(preds == actual)
}

# 5 fold cross-validation
cv_5 = trainControl(method = "cv", number = 5)

Logistic Regression

VIP sütunu tablodan çıkarılarak diğer öznitelikler logistic regression modeli kullanılarak eğitilmiştir. Eğitimde Cross-validation 5 olarak kullanılmıştır. Daha sonra test fonksiyonu kullanılarak modelin doğruluğu test veriseti için hesaplanmıştır.

# logistic regression
lrm = train(form = Transported ~ . - VIP,
                 data = train,
                 method = "glm",
                 family = 'binomial',
                 trControl = cv_5
)
## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred

## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred
## Warning in predict.lm(object, newdata, se.fit, scale = 1, type = if (type == :
## prediction from rank-deficient fit; attr(*, "non-estim") has doubtful cases
## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred
## Warning in predict.lm(object, newdata, se.fit, scale = 1, type = if (type == :
## prediction from rank-deficient fit; attr(*, "non-estim") has doubtful cases
## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred

## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred
## Warning in predict.lm(object, newdata, se.fit, scale = 1, type = if (type == :
## prediction from rank-deficient fit; attr(*, "non-estim") has doubtful cases
## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred
test_acc(lrm)
## Warning in predict.lm(object, newdata, se.fit, scale = 1, type = if (type == :
## prediction from rank-deficient fit; attr(*, "non-estim") has doubtful cases
## [1] 0.7883841

KNN

VIP sütunu tablodan çıkarılarak diğer öznitelikler logistic k-nearest neighbour modeli kullanılarak eğitilmiştir. Eğitimde Cross-validation 5 olarak kullanılmıştır. Daha sonra test fonksiyonu kullanılarak modelin doğruluğu test veriseti için hesaplanmıştır. Fonksiyon içindeki parametreler yakınsama uzaksama yöntemi ile tune edilerek en doğru parametreler bulunmuştur.

# k nearest neighbor
knn_tune = expand.grid(
  k = seq(2, 30, 2)
)

# model
knn = train(Transported ~ . - VIP,
                 data = train,
                 method = "knn", 
                 trControl = cv_5,
                 tuneGrid = knn_tune
)

# modelin doğruluk oranı
test_acc(knn)
## [1] 0.7820587

Gradient Boosting Machine (GBM)

Eğitimde Cross-validation 5 olarak kullanılmıştır. Daha sonra test fonksiyonu kullanılarak modelin doğruluğu test veriseti için hesaplanmıştır. Fonksiyon içindeki parametreler yakınsama uzaksama yöntemi ile tune edilerek en doğru parametreler bulunmuştur.

gbm_tune = expand.grid(
  n.trees = 600,
  interaction.depth = 5,
  shrinkage = 0.2,
  n.minobsinnode = 10
)

# train edilmesi
gbm = train(form = Transported ~ .,
                 data = train,
                 method = "gbm", 
                 trControl = cv_5,
                 tuneGrid = gbm_tune,
                 verbose = FALSE
)

# doğruluk oranı
test_acc(gbm)
## [1] 0.8010351

TEST VERİSİ İLE MODEL TAHMİNİ VE YARIŞMAYA KATILIM SAĞLANMASI

Tüm işlemler full Test verisi için tekrar yapılır:

test = read_csv("C:\\Project\\test.csv")
## Rows: 4277 Columns: 13
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (5): PassengerId, HomePlanet, Cabin, Destination, Name
## dbl (6): Age, RoomService, FoodCourt, ShoppingMall, Spa, VRDeck
## lgl (2): CryoSleep, VIP
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
test = test %>% mutate(Transported = NA)

test = test %>% 
  mutate(deck = str_sub(Cabin, 1, 1),
         num = as.numeric(str_sub(Cabin, 3, -3)),
         side = str_sub(Cabin, -1, -1),
         id1 = substring(PassengerId, 1, 4),
         id2 = substring(PassengerId, 6, 7)
  )

group_size = test %>% 
  select(id1, id2) %>% 
  group_by(id1) %>% 
  summarize(size = max(as.numeric(id2))) %>% 
  select(id1, size)

test = merge(x = test, y = group_size, by = "id1") %>% 
  mutate(size = ifelse(size >= 4, 4, size))
rm(group_size)

test = test %>% 
  select(-c(Cabin, Name))

find_mode <- function(x) {
  u <- unique(x)
  tab <- tabulate(match(x, u))
  modes = u[tab == max(tab)]
  modes[1]
}

test_missing_hp = test %>% 
  select(HomePlanet, id1) %>% 
  filter(id1 %in% test$id1[is.na(test$HomePlanet)] & !is.na(HomePlanet)) %>% 
  group_by(id1) %>% 
  mutate(HomePlanet = find_mode(HomePlanet)) %>% 
  unique()

test = left_join(test, test_missing_hp, by = "id1") %>%
  mutate(HomePlanet = ifelse(!is.na(HomePlanet.x), HomePlanet.x, ifelse(is.na(HomePlanet.y), NA, HomePlanet.y))) %>%     
  select(-c(HomePlanet.x, HomePlanet.y)) %>% 
  unique()

test_missing_dest = test %>% 
  select(Destination, id1) %>% 
  filter(id1 %in% test$id1[is.na(test$Destination)] & !is.na(Destination)) %>% 
  group_by(id1) %>% 
  mutate(Destination = find_mode(Destination)) %>% 
  unique()

test = left_join(test, test_missing_dest, by = "id1") %>%
  mutate(Destination = ifelse(!is.na(Destination.x), Destination.x, 
                              ifelse(is.na(Destination.y), NA, Destination.y))) %>%
  select(-c(Destination.x, Destination.y)) %>% 
  unique() 

test_missing_deck = test %>% 
  select(deck, id1) %>% 
  filter(id1 %in% test$id1[is.na(test$deck)] & !is.na(deck)) %>% 
  group_by(id1) %>% 
  mutate(deck = find_mode(deck)) %>% 
  unique()

test = left_join(test, test_missing_deck, by = "id1") %>%
  mutate(deck = ifelse(!is.na(deck.x), deck.x, ifelse(is.na(deck.y), NA, deck.y))) %>%
  select(-c(deck.x, deck.y))
test = test %>% 
  mutate(id1 = as.numeric(id1),
         id2 = as.numeric(id2),
         HomePlanet = ifelse(is.na(HomePlanet), "Mars", HomePlanet),
         deck = ifelse(is.na(deck), 'F', deck),
         side = ifelse(is.na(side), 'S', side),
         num = ifelse(is.na(num), median(df3$num, na.rm = TRUE), num),
         CryoSleep = ifelse(is.na(CryoSleep), find_mode(df3$CryoSleep), CryoSleep),
         Destination = ifelse(is.na(Destination), 'TRAPPIST-1e', Destination),
         VIP = ifelse(is.na(VIP), find_mode(df3$VIP), VIP),
         Transported = as.factor(as.numeric(Transported))) %>%
   mutate(HomePlanet = as.factor(HomePlanet),
          Destination = as.factor(Destination),
          deck = as.factor(deck),
          side = as.factor(side))
na_age_df_test = test %>% filter(is.na(Age))
test$Age[is.na(test$Age)] = predict(age_lm, na_age_df_test)


test = test %>%
mutate(RoomService = ifelse(is.na(RoomService), 0, RoomService),
       FoodCourt = ifelse(is.na(FoodCourt), 0, FoodCourt),
       ShoppingMall = ifelse(is.na(ShoppingMall), 0, ShoppingMall),
       Spa = ifelse(is.na(Spa), 0, Spa),
       VRDeck = ifelse(is.na(VRDeck), 0, VRDeck)  
)

test  = test  %>%
mutate(
  ServiceCost =  RoomService + FoodCourt + ShoppingMall + Spa + VRDeck
)
gbm_full = train(form = Transported ~ . - VIP,
                 data = df3[,-2],
                 method = "gbm",
                 trControl = cv_5,
                 tuneGrid = gbm_tune,
                 verbose = FALSE)


predictions = data.frame(PassengerId = test$PassengerId, Transported = str_to_title(as.logical(as.numeric(predict(gbm_full, test))-1)))

Kaggle’da bulunan yarışmaya katılmak için submission.csv dosyasına tüm test datası için tahmin edilen değerler yazılır.

write.csv(predictions, "submission.csv", row.names = FALSE)

Submission dosyası oluşturulduktan sonra ekranın sağ tarafında bulunan Submit butonu tıklanır ve oluşturulan “submission.csv” dosyası yarışma için gönderilmiş olur: