Përdorimi i Tidymodels për të analizuar të dhënat dhe për të parashikuar se kush i mbijetoi tragjedisë së Titanikut

Të dhënat e Titanikut janë një zgjedhje popullore midis shkencëtarëve të të dhënave dhe praktikuesve të mësimit të makinerive, që përmban informacione rreth pasagjerëve në udhëtimin fatkeq, si demografia e tyre, klasa e biletave dhe statusi i mbijetesës. Duke përdorur algoritme të ndryshme klasifikimi dhe kornizën e modeleve të rregullta, përpiqem të parashikoj mbijetesën e pasagjerëve në Titanikun.

Prezantimi

Rreth të dhënave

Të dhënat e testit përmban informacionin e mëposhtëm

klasa: Një përfaqësues për statusin socio-ekonomik (SES)
- 1 = e sipërme
- 2 = e mesme
- 3 = e poshtme

mosha: Mosha është e pjesshme nëse është më pak se 1. Nëse mosha vlerësohet, a është në formën e xx.5

sibsp: grupi i të dhënave përcakton marrëdhëniet familjare në këtë mënyrë…
Vëllai/vëllai = vëllai, motra, njerku, njerka
Bashkëshorti = burri, gruaja (të dashurat dhe të fejuarit u injoruan)

parch: grupi i të dhënave përcakton marrëdhëniet familjare në këtë mënyrë…
- Prindi = nëna, babai
- Fëmija = vajza, djali, njerka, njerku
Disa fëmijë udhëtuan vetëm me dado, pra parch=0 per to.

Ngarkimi i paketave

Ngarkimi i paketave të nevojshme për analizën. Paketa tidymodels përdoret për të ndërtuar modelet e klasifikimit, dhe paketa discrim përdoret për analizën e diskriminimit linear. Paketa skimr përdoret për të ofruar një përmbledhje të grupit të të dhënave. Paketa knitr përdoret për të gjeneruar raportin, dhe për funksionin kable për të shfaqur grupin e të dhënave. Paketa tidyverse përdoret për grindje dhe vizualizim të të dhënave, dhe paketa hrbrthemes përdoret për temën e komploteve për t'i bërë ato më të këndshme estetikisht.

suppressPackageStartupMessages({
  library(tidyverse)
  library(skimr)
  library(knitr)
  library(tidymodels)
  library(discrim)
  })

Analiza e të dhënave eksploruese

Kaggle ofron një grup të dhënash trajnimi dhe një grup të dhënash testimi. Të dhënat e trajnimit përdoren për të ndërtuar modelet e klasifikimit dhe grupi i të dhënave testuese përdoret për të vlerësuar performancën e modeleve. Të dhënat e trajnimit përmban 891 vëzhgime dhe 12 atribute, ndërsa grupi i të dhënave testuese përmban 418 vëzhgime dhe 11 atribute. Të dhënat e testimit i mungon atributi i mbijetesës, i cili është variabli i synuar për analizën.

Ngarkimi i të dhënave

training_data <- read_csv("data/train.csv")
Rows: 891 Columns: 12
── Column specification ────────────────────────────────────────────────────────
Delimiter: ","
chr (5): Name, Sex, Ticket, Cabin, Embarked
dbl (7): PassengerId, Survived, Pclass, Age, SibSp, Parch, Fare
ℹ 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.
testing_data <- read_csv("data/test.csv")
Rows: 418 Columns: 11
── Column specification ────────────────────────────────────────────────────────
Delimiter: ","
chr (5): Name, Sex, Ticket, Cabin, Embarked
dbl (6): PassengerId, Pclass, Age, SibSp, Parch, Fare
ℹ 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.

Le të hedhim një vështrim në rreshtat e parë të grupit të të dhënave të trajnimit.

kable(training_data |> head(5))

Duke përdorur paketën skimr, mund të marrim një përmbledhje të grupit të të dhënave dhe atributeve.

skim(training_data)

Të dhënat përmbajnë atribute të ndryshme që kanë të bëjnë me pasagjerët në bordin e anijes në momentin e fundosjes. Objektivi i kësaj analize është të identifikojë se cilat atribute janë më të rëndësishme në përcaktimin e mbijetesës së pasagjerëve.

Pas inspektimit fillestar, disa atribute duket se janë më pak informuese në parashikimin e mbijetesës së pasagjerëve. Për shembull, atributi i ID-së së pasagjerit është arbitrar dhe nuk ofron ndonjë pasqyrë mbi rezultatin, dhe kështu mund të hiqet nga analiza. Në mënyrë të ngjashme, atributi i numrit të biletës nuk duket se ka ndonjë rëndësi në lidhje me mbijetesën e pasagjerëve, kështu që ai gjithashtu mund të hiqet. Megjithatë, atributi i emrit mund të sigurojë potencialisht informacion mbi demografinë si seksi, fisnikëria ose titulli, dhe kështu mund të jetë e nevojshme analiza e mëtejshme përpara se të vendoset për të hequr këtë atribut.

Për më tepër, grupi i të dhënave përmban disa atribute me vlera që mungojnë, si numri i kabinës dhe atributet e moshës. Këto atribute duhet të pastrohen dhe të përpunohen paraprakisht përpara përdorimit në ndërtimin e modelit. Meqenëse modelet e klasifikimit priren të performojnë më mirë me atributet e faktorit, atribute të caktuara si Pclass, SibSp, Fare, Embarked dhe Parch duhet të konvertohen në faktorë sipas nevojës.

Lufta dhe Konvertimi i të Dhënave

Kolona që përfaqëson moshën e pasagjerit në grupin e të dhënave përmban një sasi të konsiderueshme vlerash që mungojnë. Ndërsa përdorimi i metodave më të avancuara për imputimin e këtyre vlerave, si regresioni ose interpolimi, ka të ngjarë të japë rezultate më të sakta, për qëllimet e kësaj analize dhe paraqitjeje, vlera mesatare për moshën do të përdoret si zëvendësim për të dhënat që mungojnë.

Kombinimi i grupeve të të dhënave të trajnimit dhe testimit do të lejojë një analizë më të fortë të të dhënave, si dhe një imputim më të saktë të vlerave që mungojnë. Kjo do të lejojë gjithashtu heqjen e atributit "Emri", i cili do të bëhet në hapin tjetër. Atributet “PassengerId” dhe “Ticket” do të hiqen gjithashtu, pasi ato nuk japin ndonjë informacion të dobishëm për analizën. Atributi "Cabin" gjithashtu do të hiqet tani për tani, pasi përmban një sasi të konsiderueshme vlerash që mungojnë dhe do të kërkohet analizë e mëtejshme për të përcaktuar nëse ky atribut mund të përdoret në analizë.

training_data <- training_data |> 
  mutate(group = 'training')
testing_data <- testing_data |>
  mutate(group = 'testing')

combined_data <- bind_rows(training_data, testing_data)
combined_data <- combined_data |> select(-c(Name, PassengerId, Ticket,Cabin))
glimpse(combined_data)
Rows: 1,309
Columns: 9
$ Survived <dbl> 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0…
$ Pclass   <dbl> 3, 1, 3, 1, 3, 3, 1, 3, 3, 2, 3, 1, 3, 3, 3, 2, 3, 2, 3, 3, 2…
$ Sex      <chr> "male", "female", "female", "female", "male", "male", "male",…
$ Age      <dbl> 22, 38, 26, 35, 35, NA, 54, 2, 27, 14, 4, 58, 20, 39, 14, 55,…
$ SibSp    <dbl> 1, 1, 0, 1, 0, 0, 0, 3, 0, 1, 1, 0, 0, 1, 0, 0, 4, 0, 1, 0, 0…
$ Parch    <dbl> 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 1, 0, 0, 5, 0, 0, 1, 0, 0, 0, 0…
$ Fare     <dbl> 7.2500, 71.2833, 7.9250, 53.1000, 8.0500, 8.4583, 51.8625, 21…
$ Embarked <chr> "S", "C", "S", "S", "S", "Q", "S", "S", "S", "C", "S", "S", "…
$ group    <chr> "training", "training", "training", "training", "training", "…

Më pas do të imponojmë vlerat që mungojnë në atributin "Mosha". Kodi i mëposhtëm do të zëvendësojë vlerat që mungojnë në atributin "Mosha" me vlerën mesatare për atributin.

combined_data$Age[is.na(combined_data$Age)] <- mean(combined_data$Age,na.rm=T)
sum(is.na(combined_data$Age))
[1] 0

Hapi tjetër në analizë është konvertimi i variablave "mbijetuar", "seks", "SibSp" dhe "Embarked" nga llojet e tyre origjinale të të dhënave në faktorë. Kjo do të lehtësojë manipulimin dhe analizën e këtyre variablave kategorike.

combined_data <- combined_data |> 
  mutate(Survived = as.factor(Survived),
         Sex = as.factor(Sex),
         Embarked = as.factor(Embarked))

glimpse(combined_data)
glimpse(combined_data)
Rows: 1,309
Columns: 9
$ Survived <fct> 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0…
$ Pclass   <dbl> 3, 1, 3, 1, 3, 3, 1, 3, 3, 2, 3, 1, 3, 3, 3, 2, 3, 2, 3, 3, 2…
$ Sex      <fct> male, female, female, female, male, male, male, male, female,…
$ Age      <dbl> 22.00000, 38.00000, 26.00000, 35.00000, 35.00000, 29.88114, 5…
$ SibSp    <dbl> 1, 1, 0, 1, 0, 0, 0, 3, 0, 1, 1, 0, 0, 1, 0, 0, 4, 0, 1, 0, 0…
$ Parch    <dbl> 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 1, 0, 0, 5, 0, 0, 1, 0, 0, 0, 0…
$ Fare     <dbl> 7.2500, 71.2833, 7.9250, 53.1000, 8.0500, 8.4583, 51.8625, 21…
$ Embarked <fct> S, C, S, S, S, Q, S, S, S, C, S, S, S, S, S, S, Q, S, S, C, S…
$ group    <chr> "training", "training", "training", "training", "training", "…

E rëndësishme

Kolona e emrit mund të përmbajë potencialisht disa informacione të dobishme që mund të ndihmojnë në përcaktimin e shkallës së mbijetesës, por për këtë raund, unë preferoj të mos e përdor atë.

Deri më tani, kam mbaruar me hapat e para-përpunimit të të dhënave dhe 'inxhinierisë së veçorive' dhe jam gati të ndërtoj modelet që do të doja të provoja për këto të dhëna.

Ndërtesa Model

Duke përdorur paketën TidyModels për të ndërtuar modelet që do të doja të provoja për këto të dhëna. Meqenëse ne po shikojmë kryesisht nëse pasagjerët në Titanic mbijetuan, të dhënat janë kategorike, prandaj, modelet e klasifikimit janë të dobishme. Algoritmet kryesore që do të doja të provoja janë:

  • Regresioni logjistik,
  • Modelet e klasifikimit të rastësishëm të pyjeve,
  • K-Klasifikimi i fqinjëve më të afërt,
  • XGBoost,
  • Pemët e Vendimit, dhe
  • Analiza Diskriminuese Lineare.

Ka disa algoritme të tjera që mund të jenë të dobishëm, por për hir të thjeshtësisë, modelet e lartpërmendura do të kontrollohen kundrejt të dhënave.

Ndarja e të dhënave në Test dhe Train

Qëllimi i kësaj është të vlerësojë performancën e modelit që është trajnuar në grupin e trajnimit, duke e testuar atë në të dhënat e padukshme që është grupi i testimit. Kjo lejon një vlerësim më të saktë të aftësisë së modelit për të përgjithësuar në të dhëna të reja, të padukshme. Për më tepër, duke përdorur një grup të veçantë testimi, ne mund të parandalojmë mbipërshtatjen, e cila ndodh kur një model është trajnuar shumë mirë në grupin e trajnimit dhe performon dobët në të dhënat e reja. Meqenëse tashmë e kemi grupin e trajnimit, për hir të thjeshtësisë, ne mund ta përdorim atë si ndarje të trajnimit.

train <- 
  combined_data  |> 
  filter(group == "training") |> 
  mutate(
    Survived = as.factor(Survived)
  )  |> select(-group) 

set.seed(123)
split_data <- initial_split(train)
titanic_train <- training(split_data)  
titanic_test <- testing(split_data)

set.seed(234)
titanic_folds <- vfold_cv(titanic_train, strata = Survived)
titanic_folds
#  10-fold cross-validation using stratification 
# A tibble: 10 × 2
   splits           id    
   <list>           <chr> 
 1 <split [600/68]> Fold01
 2 <split [600/68]> Fold02
 3 <split [601/67]> Fold03
 4 <split [601/67]> Fold04
 5 <split [601/67]> Fold05
 6 <split [601/67]> Fold06
 7 <split [602/66]> Fold07
 8 <split [602/66]> Fold08
 9 <split [602/66]> Fold09
10 <split [602/66]> Fold10
titanic_formula <- Survived ~ .

Regresioni logjistik

glm_spec <-
  logistic_reg() |>
  set_engine("glm")
glm_wf    <- workflow(titanic_formula, glm_spec)
contrl_preds <- control_resamples(save_pred = TRUE)
glm_rs <- fit_resamples(
  glm_wf,
  resamples = titanic_folds,
  control = contrl_preds
)
collect_metrics(glm_rs)
# A tibble: 2 × 6
  .metric  .estimator  mean     n std_err .config             
  <chr>    <chr>      <dbl> <int>   <dbl> <chr>               
1 accuracy binary     0.815    10  0.0141 Preprocessor1_Model1
2 roc_auc  binary     0.855    10  0.0180 Preprocessor1_Model1

K Fqinjët më të afërt

knn_spec <-
  nearest_neighbor(mode = "classification", weight_func = "rectangular", neighbors = 5)  |> 
  set_engine("kknn")
knn_wf    <- workflow(titanic_formula, knn_spec)
knn_rs <- fit_resamples(
  knn_wf,
  resamples = titanic_folds,
  control = contrl_preds
)
collect_metrics(knn_rs)
# A tibble: 2 × 6
  .metric  .estimator  mean     n std_err .config             
  <chr>    <chr>      <dbl> <int>   <dbl> <chr>               
1 accuracy binary     0.810    10  0.0121 Preprocessor1_Model1
2 roc_auc  binary     0.850    10  0.0160 Preprocessor1_Model1

Pylli i rastësishëm

rf_spec <-
  rand_forest(mode = "classification", trees = 1000)  |> 
  set_engine("ranger")
rf_wf    <- workflow(titanic_formula, rf_spec)
rf_rs <- fit_resamples(
  rf_wf,
  resamples = titanic_folds,
  control = contrl_preds)
collect_metrics(rf_rs)
# A tibble: 2 × 6
  .metric  .estimator  mean     n std_err .config             
  <chr>    <chr>      <dbl> <int>   <dbl> <chr>               
1 accuracy binary     0.810    10  0.0150 Preprocessor1_Model1
2 roc_auc  binary     0.871    10  0.0190 Preprocessor1_Model1

Pemët e Vendimit

dt_spec <-
  decision_tree(mode = "classification")  |> 
  set_engine("rpart")
dt_wf    <- workflow(titanic_formula, dt_spec)
dt_rs <- fit_resamples(
  dt_wf,
  resamples = titanic_folds,
  control = contrl_preds
)
collect_metrics(dt_rs)
# A tibble: 2 × 6
  .metric  .estimator  mean     n std_err .config             
  <chr>    <chr>      <dbl> <int>   <dbl> <chr>               
1 accuracy binary     0.811    10  0.0129 Preprocessor1_Model1
2 roc_auc  binary     0.813    10  0.0192 Preprocessor1_Model1

XGBoost

xgb_spec <-
  boost_tree(mode = "classification")  |> 
  set_engine("xgboost")
xgb_wf    <- workflow(titanic_formula, xgb_spec)
xgb_rs <- fit_resamples(
  xgb_wf,
  resamples = titanic_folds,
  control = contrl_preds
)
collect_metrics(xgb_rs)
# A tibble: 2 × 6
  .metric  .estimator  mean     n std_err .config             
  <chr>    <chr>      <dbl> <int>   <dbl> <chr>               
1 accuracy binary     0.825    10  0.0163 Preprocessor1_Model1
2 roc_auc  binary     0.842    10  0.0253 Preprocessor1_Model1

Analiza Diskriminuese Lineare

lda_spec <-
  discrim_linear(
  mode = "classification",
  engine = "MASS"
)
lda_wf    <- workflow(titanic_formula, lda_spec)
lda_rs <- fit_resamples(
  lda_wf,
  resamples = titanic_folds,
  control = contrl_preds
)
collect_metrics(lda_rs)
# A tibble: 2 × 6
  .metric  .estimator  mean     n std_err .config             
  <chr>    <chr>      <dbl> <int>   <dbl> <chr>               
1 accuracy binary     0.804    10  0.0135 Preprocessor1_Model1
2 roc_auc  binary     0.853    10  0.0188 Preprocessor1_Model1

Asnjë nga modelet nuk u akordua, por rezultatet janë premtuese për shumë modele. Pemët e vendimit duket se nuk po ecin mirë, por modelet e tjera po shkojnë mirë për xhirimin e parë jo të sintonizuar. Saktësia e modeleve si dhe saktësia janë të gjitha mbi 80% që është një shenjë e mirë.

Krahasimi i modeleve

Krahasimi i modeleve duke përdorur lakoren ROC

bind_rows(
  collect_predictions(glm_rs) |>
    mutate(mod = "Logistical Regression"),
  collect_predictions(knn_rs) |>
    mutate(mod = "K-Nearest Neighbors"),
  collect_predictions(rf_rs)  |> 
  mutate(mod = "Random Forest"),
  collect_predictions(dt_rs)  |> 
  mutate(mod = "Decision Trees"),
  collect_predictions(xgb_rs)  |> 
  mutate(mod = "XGBoost"),
  collect_predictions(lda_rs)  |> 
  mutate(mod = "Linear Dis")) |>
  group_by(mod) |>
  roc_curve(Survived, .pred_0) |>
  autoplot() +
  hrbrthemes::theme_ipsum()

Duket sikur modeli i regresionit logjistik është një nga modelet më të mira i bazuar thjesht në kurbën ROC. Pra, për hapin tjetër, unë do të përdor modelin e regresionit logjistik për të parashikuar mbijetesën e pasagjerëve në grupin e të dhënave të testit.

Zgjedhja e modelit

final_fitted <- last_fit(glm_wf, split_data)
collect_metrics(final_fitted)
# A tibble: 2 × 4
  .metric  .estimator .estimate .config             
  <chr>    <chr>          <dbl> <chr>               
1 accuracy binary         0.760 Preprocessor1_Model1
2 roc_auc  binary         0.837 Preprocessor1_Model1

Parashikimi, Vendosja dhe Dorëzimi

Marrja e modelit përfundimtar të përshtatur dhe përdorimi i tij për të parashikuar mbijetesën e pasagjerëve në grupin e të dhënave të trajnimit si shembull

# predict on the test data
final_wf <- extract_workflow(final_fitted)
predict(final_wf, titanic_train[59,])
# A tibble: 1 × 1
  .pred_class
  <fct>      
1 0
titanic_train[59,]
# A tibble: 1 × 8
  Survived Pclass Sex     Age SibSp Parch  Fare Embarked
  <fct>     <dbl> <fct> <dbl> <dbl> <dbl> <dbl> <fct>   
1 1             1 male     28     0     0  26.6 S

Tani do të përdor modelin përfundimtar të montuar për të parashikuar mbijetesën e pasagjerëve në grupin e të dhënave të kombinuara që përfshin të dhënat e trajnimit dhe testimit.

final_predict <- predict(final_wf, combined_data)
final_predict
# A tibble: 1,309 × 1
   .pred_class
   <fct>      
 1 0          
 2 1          
 3 1          
 4 1          
 5 0          
 6 0          
 7 0          
 8 0          
 9 1          
10 1          
# … with 1,299 more rows

Ne marrim rezultatet, kështu që më pas do t'i kombinojmë rezultatet me të dhënat e testit dhe do t'i shkruajmë në një skedar csv.

konkluzioni

Modeli i regresionit logjistik performoi më së miri me një saktësi prej 0.82 dhe për këtë arsye unë do ta përdor këtë model për të parashikuar mbijetesën e pasagjerëve në grupin e të dhënave të testit.

# combine the final predictions with the test data
final_submission <- combined_data %>%
  mutate(Survived = final_predict$.pred_class) |> 
  filter(group == "testing")
submission_file <- final_submission  |> 
mutate(PassengerID = testing_data$PassengerId) 
#write a csv file
write_csv(submission_file, "submission.csv")
final_submission
# A tibble: 418 × 9
   Survived Pclass Sex      Age SibSp Parch  Fare Embarked group  
   <fct>     <dbl> <fct>  <dbl> <dbl> <dbl> <dbl> <fct>    <chr>  
 1 0             3 male    34.5     0     0  7.83 Q        testing
 2 0             3 female  47       1     0  7    S        testing
 3 0             2 male    62       0     0  9.69 Q        testing
 4 0             3 male    27       0     0  8.66 S        testing
 5 1             3 female  22       1     1 12.3  S        testing
 6 0             3 male    14       0     0  9.22 S        testing
 7 1             3 female  30       0     0  7.63 Q        testing
 8 0             2 male    26       1     1 29    S        testing
 9 1             3 female  18       0     0  7.23 C        testing
10 0             3 male    21       2     0 24.2  S        testing
# … with 408 more rows
submission_file
# A tibble: 418 × 10
   Survived Pclass Sex      Age SibSp Parch  Fare Embarked group   PassengerID
   <fct>     <dbl> <fct>  <dbl> <dbl> <dbl> <dbl> <fct>    <chr>         <dbl>
 1 0             3 male    34.5     0     0  7.83 Q        testing         892
 2 0             3 female  47       1     0  7    S        testing         893
 3 0             2 male    62       0     0  9.69 Q        testing         894
 4 0             3 male    27       0     0  8.66 S        testing         895
 5 1             3 female  22       1     1 12.3  S        testing         896
 6 0             3 male    14       0     0  9.22 S        testing         897
 7 1             3 female  30       0     0  7.63 Q        testing         898
 8 0             2 male    26       1     1 29    S        testing         899
 9 1             3 female  18       0     0  7.23 C        testing         900
10 0             3 male    21       2     0 24.2  S        testing         901
# … with 408 more rows

Prodhimi i dosjes së dorëzimit iu dorëzua Kaggle dhe rezultatet ishin:

Për modelin e regresionit logjistik:

Pozicioni i renditjes: 8700, Saktësia: 0,76794

Totali i dorëzimeve: 55,402

Pra, ne jemi në 16% më të lartë, që është një rezultat mjaft i mirë për një përpjekje të parë.

Ne mund ta përmirësojmë modelin duke bërë disa inxhinieri tipare, të cilat unë planifikoj ta bëj në të ardhmen. Mund të provojmë edhe modele të tjera si SVM, Naive Bayes, etj. dhe të shohim se si funksionojnë. dhe ne gjithashtu mund të përpiqemi të rregullojmë hiperparametrat e modeleve për të parë nëse mund të përmirësojmë saktësinë. Modelet që kam përdorur në këtë projekt nuk janë të akorduara, kështu që ka shumë vend për përmirësim. Plus, duke pasur parasysh faktin se grupi i të dhënave është i vogël, ne gjithashtu mund të përpiqemi të përdorim modele të të mësuarit të thellë si rrjetet nervore dhe të shohim se si funksionojnë ato.

Referencat

Disa nga burimet që kam përdorur për të mësuar rreth modeleve të rregullta dhe grupit të të dhënave të Titanic dhe si t'i përdor ato së bashku. Për më tepër, disa nga burimet që përdora për të mësuar rreth grupit të të dhënave të Titanikut, ku autorët përdorën modele të rregullta për të parashikuar mbijetesën e pasagjerëve.

  • Tidymodels - "Link"
  • Parashikimi i mbijetesës së pasagjerëve të Titanikut duke përdorur modele të rregullta | Niels van der Velden - "Lidhja"
  • Tidymodels dhe Titaniku | ditari i të dhënave - "Lidhja"
  • Eksperimentimi me mësimin e makinerive në R me modele të rregullta dhe grupin e të dhënave titanik Kaggle | Olivier Gimenez - "Lidhje"
  • Sintonizoni XGBoost me modele të rregullta dhe volejboll plazhi #TidyTuesday — Link
  • Titaniku - Mësimi i Makinerisë nga Fatkeqësia - "Lidhja"

Më gjeni në Twitter: @karat_sidhu