Search code examples
arraysrmatrixpca

Multiply 2d matrix with 3d array to get 4d array for component scores


I have a matrix seloas of 4 people x 3 rotated components, and an array seraw of 5 items x 5 items x (the same) 4 people. seraw are raw(ish) co-occurence counts, and seloas are (rotated) loadings from a PCA based on that seraw. The slices (= matrices) of seraw are symmetrical (beacuse co-occurence counts).

seloas <- structure(c(-0.232535340320219, -0.230627299973683, 0.124356407389266, 
-0.0203386851625857, -0.12959177205967, -0.0872107254451076, 
0.349621793484575, -0.081476095636832, -0.180898736708137, -0.0310458270134685, 
0.115458426682197, -0.472159305850741), .Dim = c(4L, 3L), .Dimnames = list(
c("Willy", "Karen", "Kristina", "Stefan"), c("PC1", "PC2", 
"PC3")))

seraw <- structure(c(1, 0, 0, 0, 0, 0, 1, 0, 0.2, 0.2, 0, 0, 1, 0, 0.2, 
0, 0.2, 0, 1, 0.4, 0, 0.2, 0.2, 0.4, 1, 1, 0, 0, 0, 0, 0, 1, 
0, 0, 0.0625, 0, 0, 1, 0, 0.0625, 0, 0, 0, 1, 0, 0, 0.0625, 0.0625, 
0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0.166666666666667, 
0, 0, 0, 0.166666666666667, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 
0, 0, 1, 0, 0.111111111111111, 0, 0, 0, 1, 0.111111111111111, 
0.111111111111111, 0, 0.111111111111111, 0.111111111111111, 1, 
0, 0, 0, 0.111111111111111, 0, 1), .Dim = c(5L, 5L, 4L), .Dimnames = structure(list(
items = c("but-how", "encyclopedia", "alien", "language-of-bees", 
"bad-hen"), items = c("but-how", "encyclopedia", "alien", 
"language-of-bees", "bad-hen"), people = c("Willy", "Karen", 
"Kristina", "Stefan")), .Names = c("items", "items", "people"
)))

I have a matrix seloas of 4 people x 3 rotated components, and an array seraw of 5 items x 5 items x (the same) 4 people. seraw are raw(ish) co-occurence counts, and seloas are (rotated) loadings from a PCA based on that seraw. The slices (= matrices) of seraw are symmetrical (beacuse co-occurence counts).

I now want to multiply the component loadings with each of the array slices, so that I get a new 4d array of 5 items x 5 items x 4 people x 3 rotated components. Each person-element of the component vectors from seloas would be multiplied by the respective array slice seraw for that person. Each cell would be the raw score of some item against some item against for some person for each of the components. To further illustrate, I'd like

# res["encyclopedia", "language-of-bees", "Willy", "PC1"] ==
seloas["Willy", "PC1"] * seraw["encyclopedia", "language-of-bees", "Willy"]

To get the component scores as the loadings-weighted averages, I can then apply() my way around the 4d array, which I'd like to keep around as is for other summary calculations.

Is there an efficient matrix-/tensor-algebraic way of accomplishing this?


Solution

  • This is an effort to clarify what Is unclear to me at the moment. Is this the desired goal? (Note that I could not figure out why the fourth dimension should be 4 if the number of principla compoents was 3)

    res <- array(NA, c(5,5,4,3) )
    dimnames(res)[[4]] <-c("PC1", "PC2" ,"PC3")
    dimnames(res)[[3]] <-c("Willy", "Karen", "Kristina", "Stefan")
    for( sub in c("Willy", "Karen", "Kristina", "Stefan") ) {
     for ( pc in c("PC1", "PC2" ,"PC3") ){
     res[ , ,sub, pc] <- seloas[sub, pc] * seraw[ , , sub] }}
     res[ , , "Willy", 1:2]
    #---------
    , , PC1
    
               [,1]        [,2]        [,3]        [,4]        [,5]
    [1,] -0.2325353  0.00000000  0.00000000  0.00000000  0.00000000
    [2,]  0.0000000 -0.23253534  0.00000000 -0.04650707 -0.04650707
    [3,]  0.0000000  0.00000000 -0.23253534  0.00000000 -0.04650707
    [4,]  0.0000000 -0.04650707  0.00000000 -0.23253534 -0.09301414
    [5,]  0.0000000 -0.04650707 -0.04650707 -0.09301414 -0.23253534
    
    , , PC2
    
               [,1]        [,2]        [,3]        [,4]        [,5]
    [1,] -0.1295918  0.00000000  0.00000000  0.00000000  0.00000000
    [2,]  0.0000000 -0.12959177  0.00000000 -0.02591835 -0.02591835
    [3,]  0.0000000  0.00000000 -0.12959177  0.00000000 -0.02591835
    [4,]  0.0000000 -0.02591835  0.00000000 -0.12959177 -0.05183671
    [5,]  0.0000000 -0.02591835 -0.02591835 -0.05183671 -0.12959177
    

    I think that you can select certain slices from the kronecker cross-product that is built this way:

     kres <- kronecker(seloas , seraw , make.dimnames=TRUE) 
    

    Notice that the "Willy:PC1" items is the same as res[ , , "Willy", "PC1"] values

    > kres[ 1:5, 1:5, 1]
                           PC1:but-how PC1:encyclopedia   PC1:alien PC1:language-of-bees
    Willy:but-how           -0.2325353       0.00000000  0.00000000           0.00000000
    Willy:encyclopedia       0.0000000      -0.23253534  0.00000000          -0.04650707
    Willy:alien              0.0000000       0.00000000 -0.23253534           0.00000000
    Willy:language-of-bees   0.0000000      -0.04650707  0.00000000          -0.23253534
    Willy:bad-hen            0.0000000      -0.04650707 -0.04650707          -0.09301414
                           PC1:bad-hen
    Willy:but-how           0.00000000
    Willy:encyclopedia     -0.04650707
    Willy:alien            -0.04650707
    Willy:language-of-bees -0.09301414
    Willy:bad-hen          -0.23253534
    > res[, ,"Willy", "PC1"]
               [,1]        [,2]        [,3]        [,4]        [,5]
    [1,] -0.2325353  0.00000000  0.00000000  0.00000000  0.00000000
    [2,]  0.0000000 -0.23253534  0.00000000 -0.04650707 -0.04650707
    [3,]  0.0000000  0.00000000 -0.23253534  0.00000000 -0.04650707
    [4,]  0.0000000 -0.04650707  0.00000000 -0.23253534 -0.09301414
    [5,]  0.0000000 -0.04650707 -0.04650707 -0.09301414 -0.23253534
    

    Not all of the items in the cross-product would be useful but this would be the "index" if you wnated to use kronecker:

    > attributes(kronecker(seloas , seraw , make.dimnames=TRUE) )
    $dim
    [1] 20 15  4
    
    $dimnames
    $dimnames[[1]]
     [1] "Willy:but-how"             "Willy:encyclopedia"        "Willy:alien"              
     [4] "Willy:language-of-bees"    "Willy:bad-hen"             "Karen:but-how"            
     [7] "Karen:encyclopedia"        "Karen:alien"               "Karen:language-of-bees"   
    [10] "Karen:bad-hen"             "Kristina:but-how"          "Kristina:encyclopedia"    
    [13] "Kristina:alien"            "Kristina:language-of-bees" "Kristina:bad-hen"         
    [16] "Stefan:but-how"            "Stefan:encyclopedia"       "Stefan:alien"             
    [19] "Stefan:language-of-bees"   "Stefan:bad-hen"           
    
    $dimnames[[2]]
     [1] "PC1:but-how"          "PC1:encyclopedia"     "PC1:alien"           
     [4] "PC1:language-of-bees" "PC1:bad-hen"          "PC2:but-how"         
     [7] "PC2:encyclopedia"     "PC2:alien"            "PC2:language-of-bees"
    [10] "PC2:bad-hen"          "PC3:but-how"          "PC3:encyclopedia"    
    [13] "PC3:alien"            "PC3:language-of-bees" "PC3:bad-hen"         
    
    $dimnames[[3]]
    [1] ":Willy"    ":Karen"    ":Kristina" ":Stefan"