Understanding Titanic Dataset with H2O’s AutoML, DALEX, and lares library

If you have been studying or working with Machine Learning for at least a week, I am sure you have already played with the Titanic dataset! Today I bring some fun DALEX (Descriptive mAchine Learning EXplanations) functions to study the whole set’s response to the Survival feature and some individual explanation examples.

Before we start, I invite you all to install my personal library lares so you can follow step by step the following examples and use it to speed up your daily ML and Analytics tasks:

install.packages("lares")

A quick good model with h2o_automl

Let’s run the lares::h2o_automl function to generate a quick good model on the Titanic dataset. The lares library has this dataset already loaded, so with data(dft) you will load everything you need to reproduce these examples.

NOTE: when using the lares::h2o_automl function with our data frame as it is, with no ‘train_test’ parameter, it will automatically split 70/30 for our training and testing sets (use ‘split’ in the function if you want to change this relation).

library(lares)
library(dplyr)
data(dft) # Titanic dataset

results <- h2o_automl(dft, y = "Survived", 
                      ignore = c("Ticket","PassengerId","Cabind"), 
                      project = "Titanic Dataset",
                      max_models = 6, 
                      seed = 123)
2019-08-26 15:22:34 | Started process...
NOTE: The following variables contain missing observations: 'Age (19.87%)'. Consider setting the impute parameter.
NOTE: There are 6 non-numerical features. Consider using ohse() for One Hot Smart Encoding before automl if you want to custom your inputs.
Model type: Classifier
    tag   n     p   pcum
1 FALSE 549 61.62  61.62
2  TRUE 342 38.38 100.00
>>> Splitting datasets
train_size  test_size 
       623        268 
Algorithms excluded: 'StackedEnsemble', 'DeepLearning'
>>> Iterating until 6 models or 600 seconds...
Succesfully trained 6 models:
                                   model_id       auc   logloss
1              GBM_1_AutoML_20190826_102236 0.8850000 0.3862788
2              DRF_1_AutoML_20190826_102236 0.8808631 0.4039227
3          XGBoost_3_AutoML_20190826_102236 0.8720238 0.4233976
4          XGBoost_1_AutoML_20190826_102236 0.8610119 0.4318725
5          XGBoost_2_AutoML_20190826_102236 0.8588988 0.4396421
6 GLM_grid_1_AutoML_20190826_102236_model_1 0.8522619 0.4447343

[6 rows x 3 columns] 
Check results in H2O Flow's nice interface: http://localhost:54321/flow/index.html
Model selected: GBM_1_AutoML_20190826_102236
>>> Running predictions for Survived...
Target value: FALSE
>>> Generating plots...
Process duration: 15.59s
    AUC     ACC     PRC  TPR     TNR
1 0.885 0.85821 0.82979 0.78 0.90476
Warning messages:
1: In doTryCatch(return(expr), name, parentenv, handler) :
  Test/Validation dataset column 'Cabin' has levels not trained on: [A10, A19, A23, A5, B28, B3, B35, B73, B78, B79, B82 B84, B86, C103, C111, C87, C91, C92, D36, D45, D56, D6, E17, E31, E46, E68, E77, F2]
2: In doTryCatch(return(expr), name, parentenv, handler) :
  Test/Validation dataset column 'Embarked' has levels not trained on: []

Let’s quickly check our model’s performance with some plots:

plot(results$plots$dashboard)

Gives this plot:

Basically, we got a very nice performing model, with an AUC of 88.5% which splits quite well the Titanic’s survivals. (Get much better models by increasing max_models parameter). Notice that from the top 25% scored-people, only 3% did not survive (the Captain? Sorry…) and 61% are true survivors.

Once we have this model, we can study which features did it chose as the most relevant and why. To understand this, there are probably more than a million ways, but today we are going to check the DALEX results.

The most important variables

Every dataset has relevant and irrelevant features. Sometimes it is our work as data scientist or analysts to detect which ones are these, how do they affect our independent variable and, most importantly, why. I happen to have the following function to help us see these results quickly:

results$plots$importance

Which will plot:

You can also plot something similar with the DALEX library but personally, I do like mine better! :$

Partial Dependency Plots (PDP)

Now that we know that Sex, Age, Fare, and Pclass are the most relevant features, we should check how the model detects the relationship between the target (Survival) and these features. Besides, these plots not only are incredibly powerful for communicating our insights to non-technical users but will also help us implement with more confidence our models (fewer black-boxes) into production.

To start plotting with DALEX (must be installed), we have to create an explainer. If you are using h2o or my functions above, this is all you need to do:

explainer <- dalex_explainer(
  df = filter(results$datasets$global, train_test == "train"), 
  model = results$model, 
  y = "Survived")
Preparation of a new explainer is initiated
  -> model label       :  GBM_1_AutoML_20190826_102236 
  -> data              :  623  rows  14  cols 
  -> target variable   :  623  values 
  -> predict function  :  h2o 
  -> predicted values  :  numerical, min =  0.01811329 , mean =  0.3886338 , max =  0.988011  
  -> residual function :  difference between y and yhat (default)
  -> residuals         :  numerical, min =  -0.8562805 , mean =  -0.0001908275 , max =  0.9191866  
A new explainer has been created!

So, let’s check our PDPs on our main features. Note that we will have different outputs regarding our variables’ class: if it’s a numerical value, then well have a Partial Dependency Plot (line-plot); if we have a categorical or factor variable, then we will get a Merging Path Plot (dendogram-plot).

sex <- dalex_variable(explainer, y = "Sex")
sex$plot

We get this plot:

How chivalrous! Basically, if you were a man and survived, you were quite lucky (spoiler: and rich!).

age <- dalex_variable(explainer, "Age")
age$plot

We get this plot:

If you were a child, you shouldn’t even have to be scared when it all went ‘down’. Independently of your class, sex, and age, children were the ones who had a higher score, thus the highest probability of surviving. We get some picks around 33 years old and to hell with the elders.

fare <- dalex_variable(explainer, "Fare")
fare$plot

Another plot:

Basically, if you paid less than 50 bucks, you didn’t pay for the boats nor the lifesavers. So rude!

class <- dalex_variable(explainer, "Pclass", force_class = "factor")
class$plot

The plot:

To emphasize once more on the economical situation vs survivals, we see how the 1st and 2nd classes were “luckier” than the 3rd class passengers.

With these plots (and with a little help from the movie to put us in perspective) we now can understand better the macro situation of Titanic’s survival. Now, let’s check some particular cases, as individuals, to go further into our analysis.

Individual Interpretation

The DALEX’s local interpretation functions are awesome! If you have used LIME before, in my taste, these are quite similar but better. Note than we can see that several predictors have zero contribution, while others have positive, and others negative contributions.

Before you start, let me tell you that each example we run lasts almost a minute to the plot… some patients when using this function!

Subject #1 (Randomly chose #23):

local23 <- dalex_local(explainer, observation = explainer$data[23,], plot = TRUE)

Gives this plot:

Here we can see a specific woman who scored pretty OK: 0.699 (can’t be seen in the image). The predicted value for this individual observation was positively and strongly influenced by the Sex = Female and Age = 15. Alternatively, the Pclass = 3 variable reduced this person’s probability of surviving.

Subject #2 (Worst score):

results$datasets$test[results$scores$score == min(results$scores$score),]
   tag Pclass  Sex Age SibSp Parch Fare Embarked
60   0      3 male  11     5     2 46.9        S

We have 1 person (out of 268) on our test set which scored 0.01. (Can’t help to mention that he really didn’t survive). Let’s study now this guy guy with our DALEX function:

local60 <- lares::dalex_local(explainer, observation = explainer$data[60,], plot = TRUE)

Gives this plot:

This poor boy sailed with a 3rd class ticket, having 11 years, traveling with 5 siblings and bot parents. The most important features were his Sex = male, and his low Fare/Pclass. Even though we noticed that children are more probable to have survived, this little man might be one of the exceptions because of (maybe) his amount of familiars onboard. If you think about it, the story behind it might be that he was in the ship with all his brothers and parents, which were 3rd class as well, and sinked with them instead of being save alone. Sad (but true) story.

Subject #3 (Best score):

If we repeat the prior example but with the highest score, we get 4 women, all 1st class, embarked through C Gates, and all 100% survivors. Let’s take a look at one:

local196 <- lares::dalex_local(explainer, row = 196)

The plot:

This handsome old lady, 58, traveled in first class with no family members, and payed a 147$ fare. Our model detects that this person had a very high probability of surviving, mainly because she payed a lot and is a woman!

Conclussions

  • One way to understand a dataset is running a model and analyzing the Machine Learning’s intelligence behind.
  • Studying the important variables on a macro view and some particular cases on a micro view will give us confidence and a global understanding.
  • Partial dependence plots are a great way to extract insights from complex models. They can be very useful when showing our insights and results with other people.
  • With DALEX we can stop showing our Machine Learning models to the world as plain black boxes.
  • With the lares library we can automate and fasten our daily tasks for Analytics and Machine Learning jobs.

Hope this article was fun to read and something could be learnt! Don’t hesitate to comment beloew if you have any further question, comment or insight and I’ll be delighted to answer back as soon as possible. Please, keep in touch and feel free to contact me via Linkedin or email.

27 Comments

  1. EJ
    Ebi John-Iyayi August 20, 2019

    dalex_explainer question is presently unaddressed. I think you explainers are the best I have seen in the market but they are currently not working. Aki posted the same question 5 months ago.

    Reply
    1. BL
      Bernardo Lares August 26, 2019

      Hey Ebi! Sorry for the late reply.. you might want to update the library (refresh your session) and retry. Let me know 😉
      Update: devtools::install_github(“laresbernardo/lares”)

      Reply
  2. EJ
    Ebi John-Iyayi August 20, 2019

    dalex_explainer question is presently unaddressed. I think you explainers are the best I have seen in the market but they are currently not working. Aki posted the same question 5 months ago.

    Reply
  3. PB
    Przemyslaw Biecek May 12, 2019

    Great post. Try newest version of DALEX. It has much better themes. https://pbiecek.github.io/PM_VEE/ceterisParibus.html

    Reply
    1. BL
      Bernardo Lares May 13, 2019

      Awesome, thanks! Noticed there’s an example with the Titanic dataset as well 🙂

      Reply
  4. A
    Aki March 20, 2019

    This package is great!

    I ran into an error when running the explainer <- lares::dalex_explainer(df = results$datasets$test, model=results$model) command. The error message is: Error in UseMethod("explain") : no applicable method for 'explain' applied to an object of class "c('H2OBinomialModel', 'H2OModel')". Can you please kindly help? Thank you!

    Reply
    1. EJ
      Ebi John-Iyayi August 20, 2019

      Hey Bernardo, this bug does not seem fixed yet. It would be great to get the explainer to work properly. It will be a game changer for some people.

      Reply
    2. EJ
      Ebi John-Iyayi August 20, 2019

      Hi Aki,
      did you manage to get this to work? If so, please how?

      Reply
    3. BL
      Bernardo Lares August 26, 2019

      Should be fixed by now 😉
      Updated the post as well

      Reply
  5. EW
    Eugene Wang August 18, 2018

    I have copied and pasted the syntax but run into an error when running “results <- lares::h2o_automl(df = dfm, seed = seed, max_time = 60)". This is the error: "Error in filter(., !is.na(tag)) : object 'tag' not found". Can someone help?

    Reply
    1. BL
      Bernardo Lares August 19, 2018

      Do you have any reproducible example I can run to check to problem? It is suggesting that dfm doesn’t have a tag column

      Reply
      1. EW
        Eugene Wang August 21, 2018

        Thank you Bernardo–I was running the exact code listed in the example above. However, I have been able to get the functions to run on my own data, so I’m not sure what the problem was

        Reply
  6. SL
    Stewart Lee August 1, 2018

    I have an error when installing your package “lares”. Please kindly help. Thank you.
    ** building package indices
    ** testing if installed package can be loaded
    *** arch – i386
    Fatal error: cannot open file ‘C:UsersStewart’: No such file or directory

    *** arch – x64
    Fatal error: cannot open file ‘C:UsersStewart’: No such file or directory

    ERROR: loading failed for ‘i386’, ‘x64’
    * removing ‘C:/Users/Stewart Li/Documents/R/win-library/3.4/lares’
    In R CMD INSTALL
    Installation failed: Command failed (1)

    Reply
    1. BL
      Bernardo Lares August 2, 2018

      Hello Stewart. Please retry by re-running devtools::install_github(“laresbernardo/lares”) and let me know what you get

      Reply
  7. BS
    Bean Sìth August 1, 2018

    I get the following error when I run your “dfm <- df" command: Error in UseMethod("select_") : no applicable method for 'select_' applied to an object of class "function" I am not experienced enough with dplyr to quickly debug this. If there is something obvious to you that may be wrong, please reply.

    Reply
    1. B
      BarcaDad August 1, 2018

      try dfm = train instead. there is no df data frame in his code

      Reply
      1. DS
        Duleep Samuel August 2, 2018

        will you please give the full code, finding it difficult

        Reply
        1. BS
          Bean Sìth August 2, 2018

          replace df with train.

          dfm <- train %>%
          select(-Name, -Ticket, -PassengerId, -Cabin) %>%
          rename(“tag” = “Survived”) %>%
          mutate(tag = as.factor(tag),
          Pclass = as.factor(Pclass))

          Reply
      2. BS
        Bean Sìth August 2, 2018

        Thanks BarcaDad. (I had tried to place train inside the select statement. dumb move … but I do not use the “tiddy data” piping techniques … my R-skills are old and rusty. Again, thanks.

        Reply
      3. BL
        Bernardo Lares August 2, 2018

        You are absolutely right. Fixed the code in the post! Changed train for df. Nice catch BarcaDad!

        Reply
      4. BL
        Bernardo Lares August 2, 2018

        You are absolutely right. Fixed the code in the post! Changed train for df. Nice catch BarcaDad!

        Reply
    2. DS
      Duleep Samuel August 2, 2018

      I also have the same problem

      Reply
      1. DS
        Duleep Samuel August 2, 2018

        Sir, thank you so much, it solved the problem, a new problem has come
        > lares::mplot_full(tag = results$score$tag,
        + score = results$scores$score,
        + subtitle = “Titanic dataset”)
        Error: ‘mplot_full’ is not an exported object from ‘namespace:lares’
        can you please help, I find that the code and plots are so nice thanks

        Reply
        1. BL
          Bernardo Lares August 2, 2018

          Thanks Duleep. Glad you like the posts! Please retry by running devtools::install_github(“laresbernardo/lares”) or lares::updateLares() and let me know if it gets fixed

          Reply
        2. BL
          Bernardo Lares August 2, 2018

          Thanks Duleep. Glad you like the posts! Please retry by running devtools::install_github(“laresbernardo/lares”) or lares::updateLares() and let me know if it gets fixed

          Reply
          1. DS
            Duleep Samuel August 3, 2018

            Sir now there are no problems, all codes run fine
            ‘yaImpute’ https://cran.r-project.org/web/packages/yaImpute/ package is required
            A few warnings / information are given, but it is not so important
            Variable Sex is of the class factor. Type of explainer changed to ‘factor’.
            Scale for ‘x’ is already present. Adding another scale for ‘x’, which will
            replace the existing scale. Even on a fast PC it takes time to compute
            Thank you so much for nice code and explanations. I will definitely use this in my plant virus research. Please blog more on ML

      2. BL
        Bernardo Lares August 2, 2018

        Fixed the df / train issue in the post 😉

        Reply

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.