Sometimes we have some problems with missing values in real-life datasets. Sometimes missing values is a real problem for research. How to recover missing values in our datasets? In this post we will consider one very interesting method of recovering missing values. It is method a bagging of regression trees. It provides the recovery of missing values for several variables at once, based on regression dependencies.

For bagging of regression trees we will use caret package for R programming language. As learning dataset we use file with results air pollution monitoring on one of monitoring station in Kryvyi Rih city (Ukraine).

For writing this post I used R version 3.3.3 and caret package version 6.0-76.

So! Let’s go!

First at all we need to install and download caret package to the work environment:

library(caret)
Loading required package: lattice
Loading required package: ggplot2

Secondly we must download learning dataset. Its name is PSZ72014.csv. After this we download dataset into the work environment and use function summary for observation:

PSZ72014 <- read.csv("PSZ72014.csv", header = T, sep = ",", dec = ".")
summary(PSZ72014)
            time.posCT.Kiev       ws              temp          dew_point      
 2014-01-02 08:00:00:   1   Min.   : 0.000   Min.   :-22.00   Min.   :-27.000  
 2014-01-02 13:00:00:   1   1st Qu.: 3.000   1st Qu.:  2.00   1st Qu.: -1.000  
 2014-01-02 20:00:00:   1   Median : 4.000   Median : 10.00   Median :  5.000  
 2014-01-03 01:00:00:   1   Mean   : 4.058   Mean   : 10.69   Mean   :  4.519  
 2014-01-03 08:00:00:   1   3rd Qu.: 5.000   3rd Qu.: 19.00   3rd Qu.: 11.000  
 2014-01-03 13:00:00:   1   Max.   :13.000   Max.   : 36.00   Max.   : 21.000  
 (Other)            :1070   NA's   :12       NA's   :12       NA's   :12       
       rh           atmos_pres        SO2                CO             NO2         
 Min.   : 18.40   Min.   : 995   Min.   :0.00000   Min.   :0.000   Min.   :0.00000  
 1st Qu.: 53.52   1st Qu.:1011   1st Qu.:0.01400   1st Qu.:1.000   1st Qu.:0.03000  
 Median : 73.70   Median :1016   Median :0.02100   Median :2.000   Median :0.05000  
 Mean   : 70.19   Mean   :1016   Mean   :0.02372   Mean   :2.137   Mean   :0.05892  
 3rd Qu.: 87.40   3rd Qu.:1021   3rd Qu.:0.03000   3rd Qu.:3.000   3rd Qu.:0.08000  
 Max.   :100.00   Max.   :1040   Max.   :0.08600   Max.   :7.000   Max.   :0.27000  
 NA's   :12       NA's   :12     NA's   :280       NA's   :513     NA's   :280      
      H2S               C6H6O               NH3               CH2O       
 Min.   :0.000000   Min.   :0.000000   Min.   :0.00000   Min.   :0.0000  
 1st Qu.:0.001000   1st Qu.:0.001000   1st Qu.:0.02000   1st Qu.:0.0040  
 Median :0.001000   Median :0.001000   Median :0.03000   Median :0.0070  
 Mean   :0.001544   Mean   :0.001674   Mean   :0.03588   Mean   :0.0091  
 3rd Qu.:0.002000   3rd Qu.:0.002000   3rd Qu.:0.04000   3rd Qu.:0.0120  
 Max.   :0.013000   Max.   :0.014000   Max.   :0.21000   Max.   :0.0440  
                                                         NA's   :513     

And now we look our data. Our dataset contain 13 columns and 1076 rows. What do we see? We see that some variables have only several missing values. But two variables such as CH2O (formaldehyde) and CO (carbon monooxyde) have 513 missing values both of them. Also two variables such as SO2 (sulphur dioxide) and NO2 (nitric dioxide) have 280 missing values. We will try to recover all missing values in our dataset.

Firstly we have to do data preparation for regression trees bagging modeling:

PSZ72014.pre <- preProcess(PSZ72014[, c(2:13)], method = "bagImpute")

After data preparation we make a secondary dataset. In this dataset we will write predicted values.

PSZ72014.bagImpute <- PSZ72014
PSZ72014.bagImpute[, c(2:13)]  <- predict(PSZ72014.pre, PSZ72014.bagImpute[, c(2:13)])

After predicting all non-missing values are stored!

But we have a problem! How can we validate our new predicted values? One of the simpliest way is comparison a central moments for variables of both datasets. We will make two matrixes with values of central moments and compare these values

tmp <- do.call(cbind, lapply(PSZ72014[, c(2:13)], summary))
number of rows of result is not a multiple of vector length (arg 9)
tmp2 <- do.call(cbind, lapply(PSZ72014.bagImpute[, c(2:13)], summary))
tmp[1:6,] - tmp2
           ws temp dew_point    rh atmos_pres    SO2     CO      NO2 H2S C6H6O NH3      CH2O
Min.    0.000 0.00     0.000  0.00          0  0e+00  0.000  0.00000   0     0   0  0.000000
1st Qu. 0.000 0.00     0.000 -0.22          0 -2e-03 -0.361 -0.01000   0     0   0 -0.000561
Median  0.000 0.00     0.000 -0.40          0 -1e-03  0.000  0.00000   0     0   0 -0.000493
Mean    0.008 0.02    -0.018 -0.15          0  4e-05  0.054  0.00034   0     0   0  0.000430
3rd Qu. 0.000 0.00     0.000 -0.03          0  1e-03  0.500  0.00685   0     0   0  0.001000
Max.    0.000 0.00     0.000  0.00          0  0e+00  0.000  0.00000   0     0   0  0.000000

As we can see that primary and predicted values has similar central moments. It can mean that we inpute minimal errors in our predicted dataset.

So. We have a new dataset without NA-values. And we can find different patterns in previously missed time intervals.

PS The first version of this post was publicated in my own blog datastory.org.ua

LS0tCnRpdGxlOiAiQmFnZ2luZyBvZiByZWdyZXNzaW9uIHRyZWVzIGZvciBtaXNzaW5nIHZhbHVlcyByZWNvdmVyeSBpbiBSIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tClNvbWV0aW1lcyB3ZSBoYXZlIHNvbWUgcHJvYmxlbXMgd2l0aCBtaXNzaW5nIHZhbHVlcyBpbiByZWFsLWxpZmUgZGF0YXNldHMuIFNvbWV0aW1lcyBtaXNzaW5nIHZhbHVlcyBpcyBhIHJlYWwgcHJvYmxlbSBmb3IgcmVzZWFyY2guIEhvdyB0byByZWNvdmVyIG1pc3NpbmcgdmFsdWVzIGluIG91ciBkYXRhc2V0cz8gSW4gdGhpcyBwb3N0IHdlIHdpbGwgY29uc2lkZXIgb25lIHZlcnkgaW50ZXJlc3RpbmcgbWV0aG9kIG9mIHJlY292ZXJpbmcgbWlzc2luZyB2YWx1ZXMuIEl0IGlzIG1ldGhvZCBhIGJhZ2dpbmcgb2YgcmVncmVzc2lvbiB0cmVlcy4gSXQgcHJvdmlkZXMgdGhlIHJlY292ZXJ5IG9mIG1pc3NpbmcgdmFsdWVzIGZvciBzZXZlcmFsIHZhcmlhYmxlcyBhdCBvbmNlLCBiYXNlZCBvbiByZWdyZXNzaW9uIGRlcGVuZGVuY2llcy4KCkZvciBiYWdnaW5nIG9mIHJlZ3Jlc3Npb24gdHJlZXMgd2Ugd2lsbCB1c2UgY2FyZXQgcGFja2FnZSBmb3IgUiBwcm9ncmFtbWluZyBsYW5ndWFnZS4gQXMgbGVhcm5pbmcgZGF0YXNldCB3ZSB1c2UgZmlsZSB3aXRoIHJlc3VsdHMgYWlyIHBvbGx1dGlvbiBtb25pdG9yaW5nIG9uIG9uZSBvZiBtb25pdG9yaW5nIHN0YXRpb24gaW4gS3J5dnlpIFJpaCBjaXR5IChVa3JhaW5lKS4KCkZvciB3cml0aW5nIHRoaXMgcG9zdCBJIHVzZWQgUiB2ZXJzaW9uIDMuMy4zIGFuZCBjYXJldCBwYWNrYWdlIHZlcnNpb24gNi4wLTc2LgoKU28hIExldOKAmXMgZ28hCgpGaXJzdCBhdCBhbGwgd2UgbmVlZCB0byBpbnN0YWxsIGFuZCBkb3dubG9hZCBjYXJldCBwYWNrYWdlIHRvIHRoZSB3b3JrIGVudmlyb25tZW50OgoKYGBge3J9Cmluc3RhbGwucGFja2FnZXMoImNhcmV0IikKbGlicmFyeShjYXJldCkKYGBgCgpTZWNvbmRseSB3ZSBtdXN0IGRvd25sb2FkIGxlYXJuaW5nIGRhdGFzZXQuIEl0cyBuYW1lIGlzIFtQU1o3MjAxNC5jc3ZdKGh0dHA6Ly93d3cuZGF0YXN0b3J5Lm9yZy51YS8/cD0xMTUxI2dieDIwODI2NzI3MTMpLiBBZnRlciB0aGlzIHdlIGRvd25sb2FkIGRhdGFzZXQgaW50byB0aGUgd29yayBlbnZpcm9ubWVudCBhbmQgdXNlIGZ1bmN0aW9uIHN1bW1hcnkgZm9yIG9ic2VydmF0aW9uOgoKYGBge3J9ClBTWjcyMDE0IDwtIHJlYWQuY3N2KCJQU1o3MjAxNC5jc3YiLCBoZWFkZXIgPSBULCBzZXAgPSAiLCIsIGRlYyA9ICIuIikKYGBgCgpgYGB7cn0Kc3VtbWFyeShQU1o3MjAxNCkKYGBgCgpBbmQgbm93IHdlIGxvb2sgb3VyIGRhdGEuIE91ciBkYXRhc2V0IGNvbnRhaW4gMTMgY29sdW1ucyBhbmQgMTA3NiByb3dzLiBXaGF0IGRvIHdlIHNlZT8gV2Ugc2VlIHRoYXQgc29tZSB2YXJpYWJsZXMgaGF2ZSBvbmx5IHNldmVyYWwgbWlzc2luZyB2YWx1ZXMuIEJ1dCB0d28gdmFyaWFibGVzIHN1Y2ggYXMgQ0gyTyAoZm9ybWFsZGVoeWRlKSBhbmQgQ08gKGNhcmJvbiBtb25vb3h5ZGUpIGhhdmUgNTEzIG1pc3NpbmcgdmFsdWVzIGJvdGggb2YgdGhlbS4gQWxzbyB0d28gdmFyaWFibGVzIHN1Y2ggYXMgU08yIChzdWxwaHVyIGRpb3hpZGUpIGFuZCBOTzIgKG5pdHJpYyBkaW94aWRlKSBoYXZlIDI4MCBtaXNzaW5nIHZhbHVlcy4gV2Ugd2lsbCB0cnkgdG8gcmVjb3ZlciBhbGwgbWlzc2luZyB2YWx1ZXMgaW4gb3VyIGRhdGFzZXQuCgpGaXJzdGx5IHdlIGhhdmUgdG8gZG8gZGF0YSBwcmVwYXJhdGlvbiBmb3IgcmVncmVzc2lvbiB0cmVlcyBiYWdnaW5nIG1vZGVsaW5nOgoKYGBge3J9ClBTWjcyMDE0LnByZSA8LSBwcmVQcm9jZXNzKFBTWjcyMDE0WywgYygyOjEzKV0sIG1ldGhvZCA9ICJiYWdJbXB1dGUiKQpgYGAKCkFmdGVyIGRhdGEgcHJlcGFyYXRpb24gd2UgbWFrZSBhIHNlY29uZGFyeSBkYXRhc2V0LiBJbiB0aGlzIGRhdGFzZXQgd2Ugd2lsbCB3cml0ZSBwcmVkaWN0ZWQgdmFsdWVzLgoKYGBge3J9ClBTWjcyMDE0LmJhZ0ltcHV0ZSA8LSBQU1o3MjAxNApQU1o3MjAxNC5iYWdJbXB1dGVbLCBjKDI6MTMpXSAgPC0gcHJlZGljdChQU1o3MjAxNC5wcmUsIFBTWjcyMDE0LmJhZ0ltcHV0ZVssIGMoMjoxMyldKQpgYGAKCkFmdGVyIHByZWRpY3RpbmcgYWxsIG5vbi1taXNzaW5nIHZhbHVlcyBhcmUgc3RvcmVkIQoKQnV0IHdlIGhhdmUgYSBwcm9ibGVtISBIb3cgY2FuIHdlIHZhbGlkYXRlIG91ciBuZXcgcHJlZGljdGVkIHZhbHVlcz8gT25lIG9mIHRoZSBzaW1wbGllc3Qgd2F5IGlzIGNvbXBhcmlzb24gYSBjZW50cmFsIG1vbWVudHMgZm9yIHZhcmlhYmxlcyBvZiBib3RoIGRhdGFzZXRzLiBXZSB3aWxsIG1ha2UgdHdvIG1hdHJpeGVzIHdpdGggdmFsdWVzIG9mIGNlbnRyYWwgbW9tZW50cyBhbmQgY29tcGFyZSB0aGVzZSB2YWx1ZXMKCmBgYHtyfQp0bXAgPC0gZG8uY2FsbChjYmluZCwgbGFwcGx5KFBTWjcyMDE0WywgYygyOjEzKV0sIHN1bW1hcnkpKQp0bXAyIDwtIGRvLmNhbGwoY2JpbmQsIGxhcHBseShQU1o3MjAxNC5iYWdJbXB1dGVbLCBjKDI6MTMpXSwgc3VtbWFyeSkpCnRtcFsxOjYsXSAtIHRtcDIKYGBgCgpBcyB3ZSBjYW4gc2VlIHRoYXQgcHJpbWFyeSBhbmQgcHJlZGljdGVkIHZhbHVlcyBoYXMgc2ltaWxhciBjZW50cmFsIG1vbWVudHMuIEl0IGNhbiBtZWFuIHRoYXQgd2UgaW5wdXRlIG1pbmltYWwgZXJyb3JzIGluIG91ciBwcmVkaWN0ZWQgZGF0YXNldC4KClNvLiBXZSBoYXZlIGEgbmV3IGRhdGFzZXQgd2l0aG91dCBOQS12YWx1ZXMuIEFuZCB3ZSBjYW4gZmluZCBkaWZmZXJlbnQgcGF0dGVybnMgaW4gcHJldmlvdXNseSBtaXNzZWQgdGltZSBpbnRlcnZhbHMuCgpQUyBUaGUgZmlyc3QgdmVyc2lvbiBvZiB0aGlzIHBvc3Qgd2FzIHB1YmxpY2F0ZWQgaW4gbXkgb3duIGJsb2cgW2RhdGFzdG9yeS5vcmcudWFdKGh0dHA6Ly93d3cuZGF0YXN0b3J5Lm9yZy51YS8/cD0xMTUxKQ==