Skip to content
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
147 changes: 147 additions & 0 deletions src/use_case_classification.Rmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
# Use Cases

## Classification

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would remove "#Use Cases" and think of a more specific title like "# Classification: Predicting Survival on the Titanic"


One popular example regarding a classification task is the "Titanic" showcase. We have different passenger information - like name, age or fare - available with the aim to predict which kind of people would have survived the titanic sinking.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Titanic
(also below)


Therefore we load the titanic dataset and other libraries that are needed for this use case.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

libraries -> packages


```{r, results='hide', message=FALSE, warning=FALSE}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are all those options necessary?

library(titanic)
library(mlr)
library(BBmisc)
```


```{r}
data = titanic_train
head(data)
```

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is wrong with titanic_train?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm using this data set for training and testing and thought that "data" is not so confusing than titanic_train.


Our aim - as mentioned before - is to predict which kind of people would have survided.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

survived


Therefore we will work off the following steps:

* preprocessing, [here](http://mlr-org.github.io/mlr-tutorial/devel/html/preproc/index.html) and [here](http://mlr-org.github.io/mlr-tutorial/devel/html/impute/index.html)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I overlooked this during my first review. If you want to link to other tutorial pages please use e.g. [here](preproc.md).

* create a task,[here](http://mlr-org.github.io/mlr-tutorial/devel/html/task/index.html)
* provide a learner, [here](http://mlr-org.github.io/mlr-tutorial/devel/html/learner/index.html)
* train the model, [here](http://mlr-org.github.io/mlr-tutorial/devel/html/train/index.html)
* predict the survival chance, [here](http://mlr-org.github.io/mlr-tutorial/devel/html/predict/index.html)
* validate the model,[here](http://mlr-org.github.io/mlr-tutorial/devel/html/performance/index.html) and [here](http://mlr-org.github.io/mlr-tutorial/devel/html/resample/index.html)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general I like this list.
But in my opinion we could make it a little less technical. Things like "create a task" or "provide a learner" won't necessarily mean sth. to someone new to mlr. Therefore, I would change these lines to sth. like "Define the learning task/problem" and "select a learning method"

#### Preprocessing

The data set is corrected regarding their data types.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would show beforehand that they are bad types and say, "Hey we need to fix this"


```{r}
data[, c("Survived", "Pclass", "Sex", "SibSp", "Embarked")] = lapply(data[, c("Survived", "Pclass", "Sex", "SibSp", "Embarked")], as.factor)
```

Next, unuseful columns will be dropped.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unuseful -> useless


```{r}
data = dropNamed(data, c("Cabin","PassengerId", "Ticket", "Name"))
```

And missing values will be imputed, in this case Age and Fare.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please also add a half sentence to say which imputation methods you use.


```{r}
data$Embarked[data$Embarked == ""] = NA
data$Embarked = droplevels(data$Embarked)
data = impute(data, cols = list(Age = imputeMedian(), Fare = imputeMedian(), Embarked = imputeMode()))
data = data$data
```

### Create a task

In the "task" the data set and the target column is specified. People who survived are labelled with "1".

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the first sentence in l. 57 you kind of assume that the reader has some knowledge about what the mlr Tasks are about.

Please say what you are actually doing here, sth. like: "Let's first define our learning problem. To this end, we need to provide the data and the name of the target column we are going to predict..."


```{r}
task = makeClassifTask(data = data, target = "Survived", positive = "1")
```

### Define a learner

A classification learner is selected. You can find an overview of all learners [here](http://mlr-org.github.io/mlr-tutorial/devel/html/integrated_learners/index.html)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • missing . at the end of the 2nd sentence
  • Regarding the first sentence in l. 65: Please give more specific information about what you are doing. To illustrate what I mean (feel free to tweak):
    "As this method often works well off the shelf, we use a random forest and set it up to predict class probabilities additional to class labels"


```{r}
lrn = makeLearner("classif.randomForest", predict.type = "prob")
```

### Fit the model

To fit the model - and afterwards predict - the data set is split into a training and a test data set.

```{r}
n = getTaskSize(task)
trainSet = seq(1, n, by = 2)
testSet = seq(2, n, by = 2)

@schiffner schiffner Mar 8, 2017

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use random train and test sets here.
(I know that we use this kind of train/test sets elsewhere in the tutorial, but this is in general bad and I'm going to change that.)

```

```{r}
mod = train(learner = lrn, task = task, subset = trainSet)
```

### Predict

Predicting the target values for new observations is implemented the same way as most of the other predict methods in R. In general, all you need to do is call predict on the object returned by train and pass the data you want predictions for.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would leave out all technical information, i.e. remove the first sentence in l. 87 completely and make the second sentence more specific (in the same spirit as my suggestions above).


```{r}
pred = predict(mod, task, subset = testSet)
```

The quality of the predictions of a model in mlr can be assessed with respect to a number of different performance measures. In order to calculate the performance measures, call performance on the object returned by predict and specify the desired performance measures.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • In the first sentence in l. 93 please link to the performance measure table in the tutorial: [number of different performance measures](measures.md).
  • Again I think the 2nd sentence is too technical. For the use cases I wouldn't explain function APIs (that's what the tutorial and manual pages are for). Just say concisely what you are doing (in this instance, calculate accuracy as well as false and true positive rates) and the reader will make the connection to the performance call.
  • You are doing a lot more things than the performance call in the code chunk below. Please give more info about that.


```{r}
calculateConfusionMatrix(pred)
performance(pred, measures = list(acc, fpr, tpr))
df = generateThreshVsPerfData(pred, list(fpr, tpr, acc))
plotThreshVsPerf(df)
plotROCCurves(df)
```

### Extension of the original use case

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This header is too meta. Please make this more specific. You are making predictions on an additional test set, right?


As you might have seen the titanic library also provides a second dataset.

```{r}
test = titanic_test

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would keep the name titanic_test

head(test)
```

This one does not contain any survival information, but we now can use our fitted model and predict the survival probability for this data set.

The same preprocessing steps - as for the "data" data set - have to be applied

```{r}
test[, c("Pclass", "Sex", "SibSp", "Embarked")] = lapply(test[, c("Pclass", "Sex", "SibSp", "Embarked")], as.factor)

test = dropNamed(test, c("Cabin","PassengerId", "Ticket", "Name"))

test = impute(test, cols = list(Age = imputeMedian(), Fare = imputeMedian()))

@schiffner schiffner Mar 9, 2017

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not totally happy about how you are doing the imputation.

  1. For titanic_train you are imputing on the whole data set before splitting it into train and test samples.
  2. You are doing the imputation separately for titanic_train and titanic_test.

I think the cleanest way would be imputation on the train samples and reimputation (using the estimated means and medians from the train samples) on the test samples, respectively. This is easy to do with an imputation wrapper, but the least beginner-friendly variant.

Therefore, I would stick to approach 2., but use it consistently.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi Julia,
I've got your point, but I thought that I already used approach 2 because I've imputaed sperately for test and training?
What should I change?
Thank you!

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe what Julia is saying is both

  1. Demeaning the whole data set before splitting into test and train gives information about the testing data to your training data.
  2. Say you had one data point in test and demeaned it, you would have a value of zero.

I believe you can use makeImputeWrapper() which should handle this for you

@schiffner schiffner Mar 13, 2017

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi,

sorry for the late reply.

I've got your point, but I thought that I already used approach 2 because I've imputaed sperately for test and training?

To better explain what I mean:
Your use case contains two instances where you train and evaluate a learner.

  1. You train and evaluate your learner on train and test samples drawn from data.
  2. You train on data and evaluate your learner on test.

The first thing I was trying to say is that your example is not consistent w.r.t. the order of
imputation and splitting:
Situation 1. Here, you first impute the whole data (l. 49) and then split it into train and test sets (l. 77-78).
Situation 2. You impute data and test separately. So in contrast to 1. the whole data set, consisting of data and test combined, is split first and then the two parts are
imputed.

What Steve is saying is that the order in Situation 1 is usually discouraged because you are using information from the test sample (by calculating mean and median) for training, which might lead to overoptimistic performance values.

What should I change?

I thought about it a little more and now think it would be easiest to use the imputation wrapper.

Let me know if you need help.
Thanks!!

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EDIT: A more beginne-friendly variant would be (if it's possible)

  • to first remove NAs and show train, predict, performance and
  • then argue that throwing away information is bad and proceed with the imputation wrapper.

test = test$data

summarizeColumns(test)
```

You can use the task and learner that you have already created.

```{r}
task
lrn
```

The training step will be different now. We don't use a subset to fit the model, but use all data.

```{r}
mod = train(learner = lrn, task = task)
```

For the prediction part, we will use the new test data set.

```{r}
pred = predict(mod, newdata = test)
pred
```