Blog: "Thema: R", deel 5

In deze blog wordt R code gepresenteerd die benut kan worden om een datastructuur te transformeren.
Blog: "Thema: R", deel 5

Zoals ongetwijfeld bekend is de vector R’s meest basale datastructuur. Niet onbelangrijk, in R moeten alle elementen van een vector behoren tot hetzelfde type. Van minst tot meest flexibel zijn dit achtereenvolgens:  logical, integer, double en character.  Bij type-verschillen binnen een vector zal R uniformiteit afdwingen tot het meest flexibele type voorhanden.  Bijvoorbeeld,  vector c(2, 3 , 4, F) wordt  c(“2”, ”3”, ”4”, ”F”).

Een net zo fundamentele R – structuur als de vector is de list. Is een vector perse niet flexibel, de list is dit juist wel.  Verschillende typen vectoren van verschillende lengte kunnen worden opgeslagen in dezelfde list.  Veel van de statistische functies in R rapporteren daarom de bevindingen in een list.

Een bekende R functional,  lapply, neemt een functie, past deze toe op alle elementen van een list, en rapporteert resultaten als list.

Stel nu eens dat we, als specifiek moment in multivariate analyse, numerieke vectoren van ongelijke lengte bijeennemen in een list. Bijvoorbeeld in geval van een agglomeratieve clusteranalyse zullen we, tijdens iedere iteratie,  een distance matrix opstellen en vectoren van  rij_ en kolom coördinaten construeren.  We willen deze list natuurlijk transformeren tot een vector, omdat dit een simpeler structuur is, om dezelfde informatie weer te geven.

De mtcars dataset heeft 32 rij-vectoren. Dit zijn de automerken. De agglomerative clusteranalyse levert de volgende lijst op:

str(list_cluster)
 List of 6
 $ : num [1:7] 1  2  4  6  10  11  30
 $ : num [1:5] 12  13  14  22  23
 $ : num [1:4] 18  19  20  26
 $ : num [1:4] 15  16  17  31
 $ : num [1:7] 3  8  9  21  27  28  32
 $ : num [1:5] 5  7  24  25  29

We constateren dat 6 numerieke vectoren lengtes hebben variërend van 4 tot 7 elementen. Hoe krijgen we de bedoelde lijst in 1 vector zonder informatieverlies?  Dan willen dus dat rijvectoren 1, 2, 4, 6, 10, 11  en  30 in de vector het cijfer 1 krijgen.

Eerst maken we onze nieuwe vector, V1, via:

V1 <- rep(1 : length(list_cluster), lapply(list_cluster, length)

Vervolgens ordenen we V1 door:

 V1[order(unlist(list_cluster))]
[1] 1 1 5 1 6 1 6 5 5 1 1 2 2 2 4 4 4 3 3 3 5 2 2 6 6 3 5 5 6 1 4 5

 

Vanzelfsprekend maken we de hier gepresenteerde rep – lapply – order – unlist combinatie generiek toepasbaar in de volgende functie:

cluster_vector <- function(x) rep(1 : length(x),  lapply(x,  length)) [order(unlist(x))]

asis van de correlatiematrix.

C  <- hclust(dist(cor(mtcars)))
cluster_vector <- cutree(C, 4)
cluster_vector
mpg  cyl  disp  hp drat  wt  qsec  vs  am  gear  carb 
 1    2     2    3    4    2   1    1   4    4     3

  1. De tweede stap is construeren van een gewichtenmatrix. Hiervoor gebruiken we de functies cw1 en mb.

    mb <-  function(x) {
    x1 = x / x
    x1[is.na(x1)] = 0
    x1
    }
    cw1 <-  function(x){
    v = as.numeric(as.factor(x))
    s = 1:length(unique(x))
    f = function(x) (v == s[x]) * v
    m1 = vapply(s, f, v)
    m2 = mb(m1)
    }
    W <- cw1(cluster_vector)
    dim(W)
    [1] 11  4

    De gewichtenmatrix heeft 11 rijen en 4 kolommen en bevat exact de informatie van clustervector, maar dan in binaire vorm.

    III. De derde stap bestaat uit de factoranalyse van Harman.  Op blz 236 beschrijft hij de oblique factoranalyse die is gebaseerd op de square_root methode.  Op Blz 239 – 241 wordt de orthogonale oplossing beschreven. We gaan beide algoritmes toepassen op de mtcars datset met behulp van de gewichtenmatrix W.

    Het R script voor oblique factoranalyse:

    factorise_oblique <- function(x,  W){
    Q = x %% W
    P = t(W) %
    % Q
    R = solve(diag(sqrt(diag(P))))
    S = Q %*% R
    }
    F <- factorise_oblique(cor(mtcars) , W)

    Het R script voor de orthogonale factoranalyse:

    factorise_orth_c <- function(x, W){
    Q = x %% W
    P = t(W) %
    % Q
    R = solve(chol(P))
    F = Q %*% R
    }
    F1 <- factorise_orth_c(cor(mtcars) , W)

    We kunnen de passing van de oblique (F) en orthogonale(F1) factoranalyse evalueren door de communaliteiten te berekenen.

    round(rowSums(F^2), digits = 2)
     mpg  cyl  disp   hp  drat    wt  qsec    vs    am  gear  carb 
    2.38  2.57  2.42  2.34  1.64  2.13  1.43  1.97  1.30  1.21  1.58
    round(sum(F^2), digits= 2) / 11
    [1] 1.91
    round(rowSums(F1^2), digits = 2)
     mpg   cyl  disp    hp  drat    wt  qsec    vs    am  gear  carb 
    0.87  0.94  0.96  0.93  0.83  0.92  0.89  0.89  0.87  0.88  0.93
    round(sum(F1^2), digits= 2) / 11
    [1] 0.9

    Het is duidelijk dat de oblique oplossing zeker niet past en de orthogonale juist wel. De kwadratensom van de factorladingen en de rijen mag nooit hoger zijn dan 1.

    rownames( F1) <- colnames(mtcars)
    colnames(F1) <- paste ( "F",  1: dim(F1)[2],   sep = " " )
    round(F1, digits = 2)

    F1 F2 F3 F4
    MPG .81 -.43 -.05 .17
    CYL -.77 .41 -.02 -.08
    DISP -.77 .60 -.05 .08
    HP -.86 .20 .39 -.01
    DRAT .47 -.61 .22 .43
    WT -.62 .73 .07 .0
    QSEC .84 .40 -.1 -.12
    VS .93 .02 .14 -.05
    AM .21 -.77 .19 .45
    GEAR .18 -.7 .46 .38
    CARB -.69 -.12 .66 .01
    Het voordeel van de cluster/factoranalyse is dat we verschillende clustertechnieken kunnen benutten om de correlatiematrix te clusteren, het algoritme zeer eenvoudig is en de berekening zeer snel tot resultaat leidt.