DDC examples

Raymaekers, J. and Rousseeuw, P.J.

2023-10-25

Introduction

This file contains some examples of the functions related to the DDC routine. More specifically, DDC and cellMap will be illustrated.

library("cellWise")
library("gridExtra") # has grid.arrange()

# Default options for DDC:
DDCpars = list(fracNA = 0.5, numDiscrete = 3, precScale = 1e-12,
               cleanNAfirst = "automatic", tolProb = 0.99, 
               corrlim = 0.5, combinRule = "wmean",
               returnBigXimp = FALSE, silent = FALSE,
               nLocScale = 25000, fastDDC = FALSE,
               standType = "1stepM", corrType = "gkwls",
               transFun = "wrap", nbngbrs = 100)

# A small list giving the same results:
DDCpars = list(fastDDC = FALSE)

Example with row and column selection

First we illustrate the selection of columns and rows by DDC. This is actually done by the function checkDataSet which is called by DDC.

i = c(1,2,3,4,5,6,7,8,9) 
name = c("aa","bb","cc","dd","ee","ff","gg","hh","ii") 
logic = c(TRUE, FALSE, TRUE, FALSE, FALSE, TRUE, TRUE, TRUE, FALSE) 
V1 = c(1.3,NaN,4.5,2.7,20.0,4.4,-2.1,1.1,-5)
V2 = c(2.3,NA,5,6,7,8,4,-10,0.5)
V3 = c(2,Inf,3,-4,5,6,7,-2,8)
Vna = c(1,-4,2,NaN,3,-Inf,NA,6,5)
Vdis = c(1,1,2,2,3,3,3,1,2)
V0s = c(1,1.5,2,2,2,2,2,3,2.5) 
datafr = data.frame(i,name,logic,V1,V2,V3,Vna,Vdis,V0s) 
datafr
##   i name logic   V1    V2  V3  Vna Vdis V0s
## 1 1   aa  TRUE  1.3   2.3   2    1    1 1.0
## 2 2   bb FALSE  NaN    NA Inf   -4    1 1.5
## 3 3   cc  TRUE  4.5   5.0   3    2    2 2.0
## 4 4   dd FALSE  2.7   6.0  -4  NaN    2 2.0
## 5 5   ee FALSE 20.0   7.0   5    3    3 2.0
## 6 6   ff  TRUE  4.4   8.0   6 -Inf    3 2.0
## 7 7   gg  TRUE -2.1   4.0   7   NA    3 2.0
## 8 8   hh  TRUE  1.1 -10.0  -2    6    1 3.0
## 9 9   ii FALSE -5.0   0.5   8    5    2 2.5
DDCdatafr = DDC(datafr,DDCpars)
##  
##  The input data has 9 rows and 9 columns.
##  
##  The input data contained 2 non-numeric columns (variables).
##  Their column names are:
##  
## [1] name  logic
##  
##  These columns will be ignored in the analysis.
##  We continue with the remaining 7 numeric columns:
##  
## [1] i    V1   V2   V3   Vna  Vdis V0s 
##  
##  The data contained 1 columns that were identical to the case number
##  (number of the row).
##  Their column names are:
##  
## [1] i
##  
##  These columns will be ignored in the analysis.
##  We continue with the remaining 6 columns:
##  
## [1] V1   V2   V3   Vna  Vdis V0s 
##  
##  The data contained 1 discrete columns with 3 or fewer values.
##  Their column names are:
##  
## [1] Vdis
##  
##  These columns will be ignored in the analysis.
##  We continue with the remaining 5 columns:
##  
## [1] V1  V2  V3  Vna V0s
##  
##  The data contained 1 columns with zero or tiny median absolute deviation.
##  Their column names are:
##  
## [1] V0s
##  
##  These columns will be ignored in the analysis.
##  We continue with the remaining 4 columns:
##  
## [1] V1  V2  V3  Vna
##  
##  The final data set we will analyze has 9 rows and 4 columns.
## 
remX = DDCdatafr$remX; dim(remX)
## [1] 9 4
cellMap(DDCdatafr$stdResid)
plot of chunk unnamed-chunk-3

plot of chunk unnamed-chunk-3

# Red cells have higher value than predicted, blue cells lower,
# white cells are missing values, all other cells are yellow.

Small generated dataset

set.seed(12345) # for reproducibility
n <- 50; d <- 20
A <- matrix(0.9, d, d); diag(A) = 1
round(A[1:10,1:10],1) # true covariance matrix
##       [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
##  [1,]  1.0  0.9  0.9  0.9  0.9  0.9  0.9  0.9  0.9   0.9
##  [2,]  0.9  1.0  0.9  0.9  0.9  0.9  0.9  0.9  0.9   0.9
##  [3,]  0.9  0.9  1.0  0.9  0.9  0.9  0.9  0.9  0.9   0.9
##  [4,]  0.9  0.9  0.9  1.0  0.9  0.9  0.9  0.9  0.9   0.9
##  [5,]  0.9  0.9  0.9  0.9  1.0  0.9  0.9  0.9  0.9   0.9
##  [6,]  0.9  0.9  0.9  0.9  0.9  1.0  0.9  0.9  0.9   0.9
##  [7,]  0.9  0.9  0.9  0.9  0.9  0.9  1.0  0.9  0.9   0.9
##  [8,]  0.9  0.9  0.9  0.9  0.9  0.9  0.9  1.0  0.9   0.9
##  [9,]  0.9  0.9  0.9  0.9  0.9  0.9  0.9  0.9  1.0   0.9
## [10,]  0.9  0.9  0.9  0.9  0.9  0.9  0.9  0.9  0.9   1.0
library(MASS) # only needed for the following line:
x <- mvrnorm(n, rep(0,d), A)
x[sample(1:(n * d), 50, FALSE)] <- NA
x[sample(1:(n * d), 50, FALSE)] <- 10
x[sample(1:(n * d), 50, FALSE)] <- -10

# When not specifying DDCpars in the call to DDC
# all defaults are used:
DDCx <- DDC(x)
##  
##  The input data has 50 rows and 20 columns.
cellMap(DDCx$stdResid)
plot of chunk unnamed-chunk-4

plot of chunk unnamed-chunk-4

# Red cells have higher value than predicted, blue cells lower,
# white cells are missing values, all other cells are yellow.
DDCx$DDCpars # These are the default options:
## $fracNA
## [1] 0.5
## 
## $numDiscrete
## [1] 3
## 
## $precScale
## [1] 1e-12
## 
## $cleanNAfirst
## [1] "automatic"
## 
## $tolProb
## [1] 0.99
## 
## $corrlim
## [1] 0.5
## 
## $combinRule
## [1] "wmean"
## 
## $returnBigXimp
## [1] FALSE
## 
## $silent
## [1] FALSE
## 
## $nLocScale
## [1] 25000
## 
## $fastDDC
## [1] FALSE
## 
## $standType
## [1] "1stepM"
## 
## $corrType
## [1] "gkwls"
## 
## $transFun
## [1] "wrap"
## 
## $nbngbrs
## [1] 100
names(DDCx)
##  [1] "DDCpars"         "colInAnalysis"   "rowInAnalysis"   "namesNotNumeric"
##  [5] "namesCaseNumber" "namesNAcol"      "namesNArow"      "namesDiscrete"  
##  [9] "namesZeroScale"  "remX"            "locX"            "scaleX"         
## [13] "Z"               "nbngbrs"         "ngbrs"           "robcors"        
## [17] "robslopes"       "deshrinkage"     "Xest"            "scalestres"     
## [21] "stdResid"        "indcells"        "Ti"              "medTi"          
## [25] "madTi"           "indrows"         "indall"          "indNAs"         
## [29] "Ximp"
# We will now go through these outputs one by one:

DDCx$colInAnalysis # all columns X1,...,X20 remain:
##  X1  X2  X3  X4  X5  X6  X7  X8  X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 
##   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18  19  20
DDCx$rowInAnalysis # all rows 1,...,50 remain:
##  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 
##  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 
## 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 
## 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
DDCx$namesNotNumeric # no non-numeric columns:
## NULL
DDCx$namesCaseNumber # no column was the case number:
## NULL
DDCx$namesNAcol # no columns with too many NA's:
## NULL
DDCx$namesNArow # no columns with too many NA's:
## NULL
DDCx$namesDiscrete # no discrete columns:
## NULL
DDCx$namesZeroScale # no columns with scale = 0:
## NULL
dim(DDCx$remX) # remaining data matrix
## [1] 50 20
round(DDCx$locX,2) # robust location estimates of all 20 columns:
##    X1    X2    X3    X4    X5    X6    X7    X8    X9   X10   X11   X12   X13 
## -0.22 -0.33 -0.37 -0.23 -0.31 -0.42 -0.40 -0.25 -0.24 -0.17 -0.34 -0.31 -0.42 
##   X14   X15   X16   X17   X18   X19   X20 
## -0.33 -0.26 -0.23 -0.40 -0.30 -0.35 -0.30
round(DDCx$scaleX,2) # robust scale estimates of all 20 columns:
##   X1   X2   X3   X4   X5   X6   X7   X8   X9  X10  X11  X12  X13  X14  X15  X16 
## 1.32 1.36 1.12 1.15 1.23 1.35 1.07 1.45 1.04 0.96 1.47 1.37 1.22 1.40 1.40 1.42 
##  X17  X18  X19  X20 
## 1.47 1.31 1.44 1.35
round(DDCx$Z[1:10,1:10],1)
##      X1   X2   X3   X4   X5   X6   X7   X8   X9  X10
## 1  -0.3  0.1  0.1 -0.4  0.1 -0.4  0.2 -0.2 -0.1 -0.3
## 2   0.0 -0.1 -0.3 -0.4 -0.6 -7.1 -0.1 -0.8 -0.6 -0.3
## 3   0.1  0.3  0.1  0.6  0.7  0.4  0.5  0.4   NA  0.4
## 4   0.4  0.6  0.6   NA   NA  0.7  0.8 -6.7  0.8  0.2
## 5   0.2 -0.2   NA  0.0 -0.1 -0.1 -0.6  0.0  0.1 -0.4
## 6   1.6  1.6  2.0  1.8  1.5  1.6  1.6  1.4  2.2  1.8
## 7    NA -0.1 -0.1 -0.5 -0.2  0.1 -0.2  7.1  0.0 -0.5
## 8   0.5  0.6  0.3  0.6  0.4  0.0 -9.0  0.7  0.6  0.2
## 9   0.3  0.8  0.6  0.4  0.2  0.4  0.3  0.1  0.4  1.0
## 10  0.8  0.6  1.2  1.5  1.3  1.2   NA  0.6  1.1  0.6
# Robustly standardized dataset. Due to the high correlations,
# cells in the same row look similar (except for outlying cells).

DDCx$nbngbrs
## [1] 19
# For each column the code looked for up to 19 non-self neighbors (highly correlated columns).
# It goes through all of them, unless fastDDC is set to TRUE.

DDCx$ngbrs[1:3,]
##      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12] [,13] [,14]
## [1,]    1   14   17    4   12    3   16    8   20    18    19     5    11    15
## [2,]    2   17   18   12   11   15   16   10   13     1     4     6     3    19
## [3,]    3   12   15    1   17    6   11    5    7     4    18    16    10     2
##      [,15] [,16] [,17] [,18] [,19] [,20]
## [1,]     6     2     9    10     7    13
## [2,]    14     7     5     9    20     8
## [3,]    14    19     8    20     9    13
# Shows the neighbors, e.g. the nearest non-self neighbor of X1 is X11, then X2,...

round(DDCx$robcors[1:3,],2)
##      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12] [,13] [,14]
## [1,]    1 0.96 0.94 0.93 0.93 0.93 0.93 0.93 0.93  0.93  0.93  0.92  0.92  0.91
## [2,]    1 0.96 0.94 0.93 0.93 0.92 0.92 0.92 0.91  0.91  0.91  0.91  0.90  0.90
## [3,]    1 0.96 0.93 0.93 0.92 0.92 0.92 0.91 0.91  0.91  0.91  0.91  0.90  0.90
##      [,15] [,16] [,17] [,18] [,19] [,20]
## [1,]  0.91  0.91  0.91  0.91  0.90  0.89
## [2,]  0.89  0.89  0.88  0.88  0.87  0.85
## [3,]  0.90  0.90  0.90  0.90  0.89  0.89
# Robust correlations with these neighbors. In each row the correlations
# are sorted by decreasing absolute value. 

round(DDCx$robslopes[1:3,],2)
##      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12] [,13] [,14]
## [1,]    1 0.99 0.99 0.85 1.03 0.76 1.02 1.02 0.93  0.95  0.93  0.84  0.93  0.99
## [2,]    1 1.00 0.91 0.99 0.91 0.94 0.99 0.77 0.85  0.94  0.82  0.88  0.76  0.94
## [3,]    1 1.18 1.17 1.16 1.07 1.07 1.04 1.01 0.91  0.97  1.14  1.15  0.79  1.08
##      [,15] [,16] [,17] [,18] [,19] [,20]
## [1,]  0.91  0.88  0.70  0.70  0.73  0.82
## [2,]  0.94  0.76  0.85  0.66  0.84  0.89
## [3,]  1.13  1.16  1.07  1.06  0.83  1.02
# For each column, the slope of each neighbor predicting it.
# For instance, X1 is predicted by its first neighbor with 
# slope 0.97 and by its second neighbor with slope 0.81 .

round(DDCx$deshrinkage,2)
##   X1   X2   X3   X4   X5   X6   X7   X8   X9  X10  X11  X12  X13  X14  X15  X16 
## 1.09 1.08 1.08 1.10 1.10 1.08 1.08 1.10 1.11 1.13 1.10 1.07 1.09 1.09 1.08 1.06 
##  X17  X18  X19  X20 
## 1.10 1.06 1.10 1.11
# For each column, the factor by which its prediction is multiplied.
round(DDCx$Xest[1:12,1:10],2) # the estimated cells of remX:
##       X1    X2    X3    X4    X5    X6    X7    X8    X9   X10
## 1  -0.48 -0.58 -0.61 -0.48 -0.55 -0.69 -0.63 -0.52 -0.49 -0.41
## 2  -0.53 -0.63 -0.67 -0.54 -0.63 -0.75 -0.69 -0.59 -0.55 -0.46
## 3   0.22  0.12  0.07  0.22  0.14  0.04  0.02  0.23  0.20  0.26
## 4   0.59  0.50  0.44  0.58  0.51  0.44  0.35  0.63  0.56  0.61
## 5  -0.45 -0.58 -0.61 -0.46 -0.54 -0.66 -0.63 -0.50 -0.46 -0.40
## 6   1.77  1.68  1.61  1.71  1.66  1.66  1.46  1.86  1.75  1.74
## 7  -0.42 -0.53 -0.56 -0.43 -0.51 -0.62 -0.59 -0.46 -0.42 -0.36
## 8   0.34  0.23  0.17  0.32  0.25  0.14  0.10  0.35  0.31  0.36
## 9   0.36  0.27  0.20  0.34  0.26  0.17  0.13  0.35  0.33  0.40
## 10  0.97  0.86  0.80  0.95  0.89  0.82  0.70  1.02  0.92  0.97
## 11  0.18  0.07  0.03  0.16  0.09 -0.01 -0.03  0.17  0.14  0.21
## 12 -1.62 -1.73 -1.73 -1.58 -1.69 -1.83 -1.68 -1.71 -1.59 -1.52
round(DDCx$stdResid[1:12,1:10],1)
##      X1    X2   X3   X4   X5    X6    X7    X8    X9  X10
## 1  -0.5   1.1  1.4 -0.6  1.1  -0.7   1.3  -0.1   0.4 -0.3
## 2   0.9   0.5 -0.1 -0.4 -1.5 -25.3   0.7  -2.0  -0.9  0.2
## 3  -1.0   0.0 -1.1  0.6  1.2   0.2   0.3   0.2    NA -0.3
## 4  -0.8   0.1 -0.7   NA   NA   0.4   0.1 -28.1   0.2 -2.1
## 5   1.5  -0.2   NA  0.8  0.2   0.5  -1.1   0.7   1.0 -0.6
## 6   0.5   0.5  0.8  0.4 -0.4   0.1  -0.6  -0.2   0.8 -0.6
## 7    NA   0.2  0.1 -1.1 -0.3   0.8  -0.1  27.6   0.5 -1.0
## 8   0.3   0.8 -0.6  0.5 -0.1  -1.4 -30.3   0.9   0.2 -1.1
## 9  -0.3   1.4  0.2 -0.4 -0.9   0.0  -0.6  -1.4  -0.5  1.6
## 10 -0.3  -0.8  0.5  1.5  1.1   0.9    NA  -1.0  -0.1 -1.9
## 11   NA  -0.2  1.1 -1.4 -1.0   0.1   0.5   0.1  -0.8 -1.7
## 12 -2.3 -21.1  0.7 -0.6  0.1   1.7   0.8  -0.4 -22.1 -2.0
# The standardized residuals of the cells. Note the NA's and some
# large positive and negative cell residuals.

qqnorm(as.vector(DDCx$stdResid)) # Note the far outliers on both sides:
plot of chunk unnamed-chunk-6

plot of chunk unnamed-chunk-6

as.vector(DDCx$indcells) # indices of the cells that were flagged by DDC:
##   [1]  27  34  35  41  62  76  79  80  90  93 100 114 142 146 164 178 180 195
##  [19] 197 219 225 237 245 252 267 273 281 284 292 297 299 308 342 354 357 367
##  [37] 388 396 397 412 430 433 439 465 482 506 529 534 548 553 561 564 566 585
##  [55] 594 597 628 631 643 648 654 663 669 675 685 696 699 704 705 715 728 736
##  [73] 754 757 758 760 784 806 816 817 831 836 843 868 881 886 887 896 904 905
##  [91] 910 921 925 942 950 956 978 981 992 996
plot(DDCx$Ti) # the Ti values of the rows. None are high.
plot of chunk unnamed-chunk-6

plot of chunk unnamed-chunk-6

qqnorm(DDCx$Ti) 
plot of chunk unnamed-chunk-6

plot of chunk unnamed-chunk-6

DDCx$medTi # median of the raw Ti (used to standardize Ti):
## [1] 0.001298725
DDCx$madTi # median absolute deviation of the raw Ti (used to standardize Ti):
## [1] 0.06241016
DDCx$indrows # numeric(0) means no rows are flagged:
## numeric(0)
as.vector(DDCx$indall) # indices of the flagged cells, including those in flagged rows:
##   [1]  27  34  35  41  62  76  79  80  90  93 100 114 142 146 164 178 180 195
##  [19] 197 219 225 237 245 252 267 273 281 284 292 297 299 308 342 354 357 367
##  [37] 388 396 397 412 430 433 439 465 482 506 529 534 548 553 561 564 566 585
##  [55] 594 597 628 631 643 648 654 663 669 675 685 696 699 704 705 715 728 736
##  [73] 754 757 758 760 784 806 816 817 831 836 843 868 881 886 887 896 904 905
##  [91] 910 921 925 942 950 956 978 981 992 996
as.vector(DDCx$indNAs) # indices of the missing cells:
##  [1]   7  11  28  44  97 105 154 174 176 184 204 249 264 310 348 403 427 443 489
## [20] 491 517 523 550 556 589 602 606 609 615 624 645 646 658 700 725 798 803 814
## [39] 867 870 945 954 979 980 999
round(DDCx$Ximp[1:10,1:10],2)
##       X1    X2    X3    X4    X5    X6    X7    X8    X9   X10
## 1  -0.66 -0.16 -0.22 -0.66 -0.22 -0.94 -0.20 -0.57 -0.32 -0.49
## 2  -0.21 -0.45 -0.69 -0.66 -1.09 -0.75 -0.46 -1.36 -0.90 -0.41
## 3  -0.13  0.13 -0.24  0.43  0.51  0.11  0.11  0.31  0.20  0.19
## 4   0.32  0.53  0.25  0.58  0.51  0.58  0.40  0.63  0.64  0.06
## 5   0.07 -0.65 -0.61 -0.20 -0.48 -0.49 -1.01 -0.24 -0.10 -0.56
## 6   1.93  1.89  1.85  1.85  1.55  1.69  1.27  1.77  2.07  1.59
## 7  -0.42 -0.46 -0.54 -0.78 -0.60 -0.34 -0.62 -0.46 -0.22 -0.63
## 8   0.44  0.53  0.01  0.47  0.23 -0.37  0.10  0.71  0.37  0.07
## 9   0.24  0.81  0.26  0.21 -0.03  0.16 -0.06 -0.16  0.13  0.81
## 10  0.88  0.55  0.94  1.45  1.24  1.17  0.70  0.64  0.88  0.46
# The imputed matrix. Both the cellwise outliers and the missing values
# are replaced by their predicted values.

round((DDCx$Ximp - DDCx$remX)[1:10,1:10],2)
##    X1 X2 X3 X4 X5   X6   X7     X8 X9 X10
## 1   0  0  0  0  0 0.00  0.0   0.00  0   0
## 2   0  0  0  0  0 9.25  0.0   0.00  0   0
## 3   0  0  0  0  0 0.00  0.0   0.00 NA   0
## 4   0  0  0 NA NA 0.00  0.0  10.63  0   0
## 5   0  0 NA  0  0 0.00  0.0   0.00  0   0
## 6   0  0  0  0  0 0.00  0.0   0.00  0   0
## 7  NA  0  0  0  0 0.00  0.0 -10.46  0   0
## 8   0  0  0  0  0 0.00 10.1   0.00  0   0
## 9   0  0  0  0  0 0.00  0.0   0.00  0   0
## 10  0  0  0  0  0 0.00   NA   0.00  0   0
# The nonzero values and the NA's correspond to imputed cells.

TopGear dataset

The Top Gear data contains information on 297 cars.

library(robustHD)
data(TopGear)
dim(TopGear)
## [1] 297  32
rownames(TopGear)[1:13] # "1" to "297" are not useful names
##  [1] "1"  "2"  "3"  "4"  "5"  "6"  "7"  "8"  "9"  "10" "11" "12" "13"
rownames(TopGear) = paste(TopGear[,1],TopGear[,2]) 
# Now the rownames are make and model of the cars.
rownames(TopGear)[165] = "Mercedes-Benz G" # name was too long
myTopGear = TopGear[,-31] # removes the subjective variable `Verdict'

# Transform some variables to get roughly gaussianity in the center:
transTG = myTopGear
transTG$Price = log(myTopGear$Price)
transTG$Displacement = log(myTopGear$Displacement)
transTG$BHP = log(myTopGear$BHP)
transTG$Torque = log(myTopGear$Torque)
transTG$TopSpeed = log(myTopGear$TopSpeed)

# Run the DDC method:
DDCpars = list(fastDDC = FALSE, silent = TRUE)
DDCtransTG = DDC(transTG,DDCpars)
##  
##  The final data set we will analyze has 296 rows and 11 columns.
## 
# With DDCpars = list(fastDDC = FALSE, silent = FALSE) we obtain more information:
# 
# The input data has 297 rows and 31 columns.
# 
# The input data contained 19 non-numeric columns (variables).
# Their column names are:
#   
#  [1] Maker         Model              Type               Fuel              
#  [5] DriveWheel    AdaptiveHeadlights AdjustableSteering AlarmSystem       
#  [9] Automatic     Bluetooth          ClimateControl     CruiseControl     
# [13] ElectricSeats Leather            ParkingSensors     PowerSteering     
# [17] SatNav        ESP                Origin            
# 
# These columns will be ignored in the analysis.
# We continue with the remaining 12 numeric columns:
#   
# [1] Price Cylinders Displacement BHP   Torque Acceleration TopSpeed    
# [8] MPG   Weight    Length       Width Height      
# 
# The data contained 1 rows with over 50% of NAs.
# Their row names are:
#   
#   [1] Citroen C5 Tourer
# 
# These rows will be ignored in the analysis.
# We continue with the remaining 296 rows:
#   
#   [1] Alfa Romeo Giulietta             Alfa Romeo MiTo                 
# .......
# [295] Volvo XC70                       Volvo XC90                      
# 
# The data contained 1 columns with zero or tiny median absolute deviation.
# Their column names are:
# 
# [1] Cylinders
# 
# These columns will be ignored in the analysis.
# We continue with the remaining 11 columns:
# 
# [1] Price  Displacement BHP   Torque Acceleration TopSpeed MPG         
# [8] Weight Length       Width Height      
# 
# The final data set we will analyze has 296 rows and 11 columns.
remX = DDCtransTG$remX # the remaining part of the dataset
dim(remX)
## [1] 296  11
colSums(is.na(remX)) # There are still NAs, mainly in `Weight':
##        Price Displacement          BHP       Torque Acceleration     TopSpeed 
##            0            8            3            3            0            3 
##          MPG       Weight       Length        Width       Height 
##           11           32           10           15           10
# Analyze the data by column:
standX = scale(remX,apply(remX,2,median,na.rm = TRUE), 
               apply(remX,2,mad,na.rm = TRUE))
dim(standX)
## [1] 296  11
round(standX[1:5,],1) # has NAs where remX does
##                          Price Displacement  BHP Torque Acceleration TopSpeed
## Alfa Romeo Giulietta      -0.4         -0.4 -0.7    0.0          0.6     -0.5
## Alfa Romeo MiTo           -0.9         -0.7 -0.7   -1.6          0.4     -0.4
## Aston Martin Cygnet        0.3         -0.8 -0.8   -1.7          0.7     -0.9
## Aston Martin DB9           2.7          2.1  1.9    1.2         -1.2      2.0
## Aston Martin DB9 Volante   2.8          2.1  1.9    1.2         -1.2      2.0
##                           MPG Weight Length Width Height
## Alfa Romeo Giulietta      1.0   -0.3   -0.3  -0.2   -0.2
## Alfa Romeo MiTo           0.1   -1.0   -0.9  -1.1   -0.3
## Aston Martin Cygnet       0.6   -1.3   -3.1  -1.5    0.1
## Aston Martin DB9         -1.7    0.8    0.6    NA   -1.6
## Aston Martin DB9 Volante -1.7    1.0    0.6    NA   -1.6
transTGcol = remX
transTGcol[abs(standX) > sqrt(qchisq(0.99,1))] = NA
round(transTGcol[1:5,],1) # has NAs in outlying cells as well:
##                          Price Displacement BHP Torque Acceleration TopSpeed
## Alfa Romeo Giulietta      10.0          7.4 4.7    5.5         11.3      4.7
## Alfa Romeo MiTo            9.6          7.2 4.7    4.6         10.7      4.8
## Aston Martin Cygnet       10.3          7.2 4.6    4.5         11.8      4.7
## Aston Martin DB9            NA          8.7 6.2    6.1          4.6      5.2
## Aston Martin DB9 Volante    NA          8.7 6.2    6.1          4.6      5.2
##                          MPG Weight Length Width Height
## Alfa Romeo Giulietta      64   1385   4351  1798   1465
## Alfa Romeo MiTo           49   1090   4063  1720   1446
## Aston Martin Cygnet       56    988     NA  1680   1500
## Aston Martin DB9          19   1785   4720    NA   1282
## Aston Martin DB9 Volante  19   1890   4720    NA   1282
# Make untransformed submatrix of X for labeling the cells in the plot:
tempX = myTopGear[DDCtransTG$rowInAnalysis,DDCtransTG$colInAnalysis]
tempX$Price = tempX$Price/1000 # to avoid printing long numbers
dim(tempX)
## [1] 296  11
# Show the following 17 cars in the cellmap:
showrows = c(12,42,56,73,81,94,99,135,150,164,176,198,209,215,234,241,277)

# Make two ggplot2 objects:
ggpcol = cellMap(standX, showcellvalues="D", D=tempX, 
                 mTitle="By column", showrows=showrows,
                 sizecellvalues = 0.6, adjustrowlabels=0.5)  
plot(ggpcol)
plot of chunk unnamed-chunk-9

plot of chunk unnamed-chunk-9

ggpDDC = cellMap(DDCtransTG$stdResid, showcellvalues="D", D=tempX,  
                 mTitle="DetectDeviatingCells", showrows=showrows,
                 sizecellvalues = 0.6, adjustrowlabels=0.5)  
plot(ggpDDC)
plot of chunk unnamed-chunk-10

plot of chunk unnamed-chunk-10

# Creating the pdf:
# pdf("cellMap_TopGear.pdf", width = 12, height = 10)
# gridExtra::grid.arrange(ggpcol,ggpDDC,nrow=1) # combines 2 plots in a figure
# dev.off()

Analyzing new data by DDCpredict

We now consider the 17 cars shown in the cellmap as a `new’ dataset.

# Top Gear dataset: prediction of "new" data
############################################
# For comparison we first remake the cell map of the entire dataset, but now 
# showing the values of the residuals instead of the data values:

dim(remX) # 296 11
## [1] 296  11
ggpDDC = cellMap(DDCtransTG$stdResid, showcellvalues="R", 
                 sizecellvalues = 0.7, mTitle="DetectDeviatingCells", 
                 showrows=showrows, adjustrowlabels=0.5)
plot(ggpDDC)
plot of chunk unnamed-chunk-11

plot of chunk unnamed-chunk-11

Define the “initial” dataset as the rows not in these 17:

initX = remX[-showrows,]
dim(initX) # 279 11
## [1] 279  11
# Fit initX:
DDCinitX = DDC(initX,DDCpars=DDCpars) 
##  
##  The final data set we will analyze has 278 rows and 11 columns.
## 

Define the “new” dataset, and apply DDCpredict to it:

newX = remX[showrows,]
dim(newX) # 17  11 
## [1] 17 11
# Make predictions by DDCpredict. 
# Its inputs are:
#   Xnew       : the new data (test data)
#   InitialDDC : Must be provided.
#   DDCpars    : the input options to be used for the prediction.
#                By default the options of InitialDDC are used. 

predictDDC = DDCpredict(newX,DDCinitX)

names(DDCinitX)
##  [1] "DDCpars"         "colInAnalysis"   "rowInAnalysis"   "namesNotNumeric"
##  [5] "namesCaseNumber" "namesNAcol"      "namesNArow"      "namesDiscrete"  
##  [9] "namesZeroScale"  "remX"            "locX"            "scaleX"         
## [13] "Z"               "nbngbrs"         "ngbrs"           "robcors"        
## [17] "robslopes"       "deshrinkage"     "Xest"            "scalestres"     
## [21] "stdResid"        "indcells"        "Ti"              "medTi"          
## [25] "madTi"           "indrows"         "indall"          "indNAs"         
## [29] "Ximp"
# For comparison with:

names(predictDDC) # Fewer, since DDCpredict does not call checkDataSet:
##  [1] "DDCpars"     "locX"        "scaleX"      "Z"           "nbngbrs"    
##  [6] "ngbrs"       "robcors"     "robslopes"   "deshrinkage" "Xest"       
## [11] "scalestres"  "stdResid"    "indcells"    "Ti"          "medTi"      
## [16] "madTi"       "indrows"     "indNAs"      "indall"      "Ximp"
# If you specify the parameters the result is the same:
predictDDC2 = DDCpredict(newX,DDCinitX,DDCpars=DDCpars)
all.equal(predictDDC,predictDDC2) # TRUE
## [1] TRUE
ggpnew = cellMap(predictDDC$stdResid, showcellvalues="R",
                 sizecellvalues = 0.7, mTitle="DDCpredict",
                 adjustrowlabels=0.5) 
plot(ggpnew) # Looks quite similar to the result using the entire dataset:
plot of chunk unnamed-chunk-14

plot of chunk unnamed-chunk-14

# Creating the pdf:
# pdf("TopGear_DDCpredict.pdf",width=12,height=10)
# gridExtra::grid.arrange(ggpDDC,ggpnew,nrow=1)
# dev.off()

Philips data

The philips data contains 9 measurements of TV parts from a production line.

data(data_philips)
dim(data_philips) 
## [1] 677   9
colnames(data_philips) = c("X1","X2","X3","X4","X5","X6","X7","X8","X9")

DDCphilips = DDC(data_philips)
##  
##  The input data has 677 rows and 9 columns.
qqnorm(as.vector(DDCphilips$Z)) # rather gaussian, here we only see 2 outliers:
plot of chunk unnamed-chunk-15

plot of chunk unnamed-chunk-15

round(DDCphilips$stdResid[1:12,],1) # the standardized residuals:
##      X1   X2   X3   X4   X5   X6  X7   X8   X9
## 1  -0.9  2.2  0.0  1.2  0.8 -2.9 2.1  0.1 -1.4
## 2  -0.5  2.0  0.0  3.9  0.8 -1.1 3.5 -0.4 -1.7
## 3  -1.0  2.3 -0.4  0.7  0.6 -2.0 1.4  0.0 -1.7
## 4  -0.6  2.3 -0.1  3.8  1.1 -0.9 3.4 -0.5 -2.0
## 5  -0.6  2.8 -0.2  3.4  0.9 -0.9 3.4  0.2 -1.9
## 6  -0.7  2.6  0.0  4.4  1.0 -1.1 4.2 -0.1 -2.0
## 7   1.0 -0.1 -1.0 -0.3 -1.2 -1.0 1.4  0.8 -0.6
## 8  -0.4 -1.4 -0.7  1.8 -0.7 -2.8 1.9 -0.3 -1.7
## 9   1.0 -0.2  0.0 -0.1 -1.2 -2.1 1.6  1.0 -0.9
## 10  0.2 -1.3 -0.1  1.0 -0.5 -2.9 2.0 -0.1 -1.5
## 11 -0.1 -0.3 -0.1  0.2 -1.1 -1.5 1.9  0.8 -0.8
## 12 -0.1 -0.4 -1.5  2.5  0.0 -3.4 2.3  0.3 -2.5
DDCphilips$indcells # indices of the cells that were flagged:
##   [1]   55   70   95  104  116  144  151  156  161  170  174  175  324  468  682
##  [16]  683  712  747  750  792  985  986  987  988  989  991  992 1006 1007 1008
##  [31] 1010 1011 1196 1199 1450 1452 1455 1474 1529 1787 2033 2035 2036 2037 2045
##  [46] 2047 2094 2101 2114 2116 2126 2135 2267 2312 2355 2365 2464 2499 2592 2743
##  [61] 2775 2777 3120 3216 3226 3227 3228 3248 3386 3393 3395 3397 3399 3401 3412
##  [76] 3413 3414 3415 3416 3417 3423 3424 3425 3426 3427 3428 3430 3431 3437 3445
##  [91] 3456 3457 3460 3464 3465 3466 3468 3469 3470 3990 4064 4066 4067 4068 4076
## [106] 4078 4088 4145 4147 4792 4794 5036 5037 5168 5169 5170 5171 5172 5174 5175
## [121] 5176 5230 5238 5241 5246 5248 5250 5253 5256 5258 5259 5260 5262 5263 5265
## [136] 5267 5268 5270 5510 5511 5512 5513 5514 5515 5516 5517 5518 5519 5520 5569
## [151] 5869 5873 5882 5890 5902
DDCphilips$indrows # flagged rows:
## [1]  26  56  84 334 491

We also apply the rowwise method MCD to detect outlying rows:

library(robustbase) # for covMcd
MCDphilips = robustbase::covMcd(data_philips)
indrowsMCD = which(mahalanobis(data_philips,MCDphilips$center,
                               MCDphilips$cov) > qchisq(0.975,df=9))

plot(sqrt(mahalanobis(data_philips,MCDphilips$center,MCDphilips$cov)),
     main="Philips data",ylab="Robust distances",xlab="",pch=20)
abline(h=sqrt(qchisq(0.975,df=9))) # this horizontal line is the cutoff.
plot of chunk unnamed-chunk-17

plot of chunk unnamed-chunk-17

# dev.copy(pdf,"Figure_philips_left.pdf",width=10,height=4)
# dev.off()
# cellMaps with rectangular blocks:

ggpMCDphilips = cellMap(data_philips, indrows=indrowsMCD,
                        mTitle="MCD", nrowsinblock=15,
                        ncolumnsinblock=1, drawCircles = TRUE)
## No rowblocklabels were given, so they are constructed automatically.
plot(ggpMCDphilips)
plot of chunk unnamed-chunk-18

plot of chunk unnamed-chunk-18

ggpDDCphilips = cellMap(DDCphilips$stdResid, indrows=DDCphilips$indrows,
                        mTitle="DetectDeviatingCells", nrowsinblock=15,
                        ncolumnsinblock=1, drawCircles = TRUE)
## No rowblocklabels were given, so they are constructed automatically.
plot(ggpDDCphilips)
plot of chunk unnamed-chunk-18

plot of chunk unnamed-chunk-18

# dev.copy(pdf,"Figure_philips_right.pdf",width=6,height=12)
# dev.off()

Mortality dataset

This dataset contains the mortality of French males in the years 1816 to 2013.

data(data_mortality)
dim(data_mortality)
## [1] 198  91
# 198  91
rownames(data_mortality)[1:5] 
## [1] "1816" "1817" "1818" "1819" "1820"
colnames(data_mortality)[1:5] 
## [1] "0" "1" "2" "3" "4"
DDCpars = list(fastDDC = FALSE, silent = TRUE)
DDCmortality = DDC(data_mortality,DDCpars) # 1 second

remX = DDCmortality$remX
dim(remX)
## [1] 198  91

We also apply the rowwise method ROBPCA to detect outlying rows:

library(rrcov) # contains ROBPCA
PCAmortality = rrcov::PcaHubert(data_mortality,alpha=0.75,scale=FALSE)

ggpROBPCA = cellMap(remX, indrows=which(PCAmortality@flag==FALSE),
                    mTitle="By row", nrowsinblock=5,
                    ncolumnsinblock=5, rowtitle = "Years",
                    columntitle = "Age", sizetitles = 1.5,
                    drawCircles = TRUE)
## No rowblocklabels were given, so they are constructed automatically.
## No columnblocklabels were given, so they are constructed automatically.
plot(ggpROBPCA)
plot of chunk unnamed-chunk-21

plot of chunk unnamed-chunk-21

ggpDDC = cellMap(DDCmortality$stdResid, 
                 mTitle="DetectDeviatingCells", nrowsinblock=5,
                 ncolumnsinblock=5, rowtitle = "Years",
                 columntitle = "Age", sizetitles = 1.5)
## No rowblocklabels were given, so they are constructed automatically.
## No columnblocklabels were given, so they are constructed automatically.
plot(ggpDDC) # Leads to a detailed interpretation:
plot of chunk unnamed-chunk-21

plot of chunk unnamed-chunk-21

# pdf("cellmap_mortality.pdf",width=9,height=8)
# gridExtra::grid.arrange(ggpROBPCA,ggpDDC,nrow=1)
# dev.off()

To illustrate the ability to make row blocks and column blocks of arbitrary sizes, we create the following artificial groupings:

rowblocksizes = c(84,14,5,21,6,35,33)
rowlabels = c("19th century","1900-1913","WW1","1919-1939",
"WW2","1946-1980","recent")
colblocksizes = c(5,5,10,10,10,10,10,10,10,10,1)
collabels = c("upto 4","5 to 9","10 to 19","20 to 29","30 to 39","40 to 49","50 to 59","60 to 69","70 to 79","80 to 89","90+")

ggpDDC = cellMap(DDCmortality$stdResid,
                 mTitle = "Cellmap with manual blocks",
                 manualrowblocksizes = rowblocksizes,
                 rowblocklabels = rowlabels,
                 manualcolumnblocksizes = colblocksizes,
                 columnblocklabels = collabels,
                 rowtitle = "Epochs",
                 columntitle = "Age groups",
                 sizetitles = 1.2)
# pdf("cellmap_mortality_manual_blocks.pdf",width=5,height=3)
plot(ggpDDC)
plot of chunk unnamed-chunk-22

plot of chunk unnamed-chunk-22

# dev.off()

Glass dataset

The glass data consists of spectra with 750 wavelengths of 180 archaeological glass samples.

data(data_glass)
DDCpars = list(fastDDC = FALSE, silent = TRUE)
DDCglass = DDC(data_glass,DDCpars) # takes 8 seconds
##  
##  The final data set we will analyze has 180 rows and 737 columns.
## 
remX = DDCglass$remX
# With DDCpars$silent = FALSE we obtain more information:
#
#  The input data has 180 rows and 750 columns.
#
#  The data contained 11 discrete columns with 3 or fewer values.
#  Their column names are:
#
#  [1] V1  V2  V3  V4  V5  V6  V7  V8  V9  V10 V11
#
#  These columns will be ignored in the analysis.
#  We continue with the remaining 739 columns:
#  
#   [1] V12  V13  V14  V15  V16  V17  V18  V19  V20  V21  V22  V23  V24  V25
#    ......
# [729] V740 V741 V742 V743 V744 V745 V746 V747 V748 V749 V750
#
#  The data contained 2 columns with zero or tiny median absolute deviation.
#  Their column names are:
#
# [1] V12 V13
#
#  These columns will be ignored in the analysis.
#  We continue with the remaining 737 columns:
#
#   [1] V14  V15  V16  V17  V18  V19  V20  V21  V22  V23  V24  V25  V26  V27
#    ......
# [729] V742 V743 V744 V745 V746 V747 V748 V749 V750
#
#  The final data set we will analyze has 180 rows and 737 columns.

dim(remX)
## [1] 180 737

We will compare this with the faster approximate algorithm of DDC, obtained by the option fastDDC=TRUE:

fastDDCpars = list(fastDDC = TRUE, silent = TRUE)
fastDDCglass = DDC(data_glass, fastDDCpars) # takes 2 seconds
##  
##  The final data set we will analyze has 180 rows and 737 columns.
## 
remXfast = fastDDCglass$remX
all.equal(remX,remXfast) # The remaining data is the same:
## [1] TRUE

We also apply the rowwise method ROBPCA to detect outlying rows:

library(rrcov) # contains ROBPCA
PCAglass = rrcov::PcaHubert(remX,alpha=0.75,scale=FALSE)

n = nrow(remX)
nrowsinblock = 5
rowtitle = "glass samples"
rowlabels = rep("",floor(n/nrowsinblock));
rowlabels[1] = "1"
rowlabels[floor(n/nrowsinblock)] = "n";

d = ncol(remX)
ncolumnsinblock = 5
columntitle = "wavelengths"
columnlabels = rep("",floor(d/ncolumnsinblock));
columnlabels[1] = "1";
columnlabels[floor(d/ncolumnsinblock)] = "d"

ggpROBPCA = cellMap(matrix(0,n,d),
                    indrows=which(PCAglass@flag==FALSE),
                    rowblocklabels=rowlabels,
                    columnblocklabels=columnlabels,
                    mTitle="By row", nrowsinblock=5,
                    ncolumnsinblock=5,
                    rowtitle="glass samples",
                    columntitle="wavelengths",
                    sizetitles=1.2,
                    columnangle=0,
                    drawCircles = TRUE)
plot(ggpROBPCA)
plot of chunk unnamed-chunk-26

plot of chunk unnamed-chunk-26

ggpDDC = cellMap(DDCglass$stdResid,
                 indrows=DDCglass$indrows,
                 rowblocklabels=rowlabels,
                 columnblocklabels=columnlabels,
                 mTitle="DDC", nrowsinblock=5,
                 ncolumnsinblock=5,
                 rowtitle="glass samples",
                 columntitle="wavelengths",
                 sizetitles=1.2,
                 columnangle=0,
                 drawCircles = TRUE)
plot(ggpDDC)
plot of chunk unnamed-chunk-26

plot of chunk unnamed-chunk-26

# pdf("cellmap_glass_ROBPCA_DDC.pdf",width=8,height=6)
# gridExtra::grid.arrange(ggpROBPCA,ggpDDC,ncol=1)
# dev.off()

ggpfastDDC = cellMap(fastDDCglass$stdResid,
                     indrows=fastDDCglass$indrows,
                     rowblocklabels=rowlabels,
                     columnblocklabels=columnlabels,
                     mTitle="fastDDC", nrowsinblock=5,
                     ncolumnsinblock=5,
                     rowtitle="glass samples",
                     columntitle="wavelengths",
                     sizetitles=1.2,
                     columnangle=0,
                     drawCircles = TRUE)
plot(ggpfastDDC)
plot of chunk unnamed-chunk-26

plot of chunk unnamed-chunk-26

# pdf("cellmap_glass_DDC_fastDDC.pdf",width=8,height=6)
# gridExtra::grid.arrange(ggpDDC,ggpfastDDC,ncol=1)
# dev.off()