Activity 3.4 - Multiple Correspondence Analysis KEY

Problem 1

Consider the following table summarizing students:

Variable Category Count
Part-time job Yes 110
No 90
Major Science 80
Arts 60
Business 50
Education 10
Commute Car 100
Bus 50
Bike 36
Walk 14
Housing On campus 70
Off campus 90
With family 40

A)

What is the total inertia in this table?

(13-4)/4 = 2.25

B)

How many dimensions are needed to explain 100% of the inertia?

13 - 4 = 9

C)

The code below creates a microdata version of this table, one row per student:

library(tidyverse)
set.seed(1)
students <- data.frame(
  Part_time_job = sample(rep(c("Yes", "No"), c(110, 90))),
  Major         = sample(rep(c("Science", "Arts", "Business", "Education"),
                             c(80, 60, 50, 10))),
  Commute       = sample(rep(c("Car", "Bus", "Bike", "Walk"),
                             c(100, 50, 36, 14))),
  Housing       = sample(rep(c("On campus", "Off campus", "With family"),
                             c(70, 90, 40)))
)

head(students)
  Part_time_job    Major Commute     Housing
1           Yes Business     Car With family
2            No     Arts    Bike  Off campus
3            No     Arts     Car  Off campus
4            No  Science     Car   On campus
5           Yes     Arts    Bike  Off campus
6           Yes  Science     Car With family

Perform MCA on this data set, setting ncp= to the number of dimensions you specified in part B. Verify that the sum of the eigenvalues equal your answer to part A.

library(FactoMineR)

(students_mca <- students
  %>% mutate(across(everything(), as.factor))
  %>% MCA(ncp = 9, graph = FALSE)
)
**Results of the Multiple Correspondence Analysis (MCA)**
The analysis was performed on 200 individuals, described by 4 variables
*The results are available in the following objects:

   name              description                       
1  "$eig"            "eigenvalues"                     
2  "$var"            "results for the variables"       
3  "$var$coord"      "coord. of the categories"        
4  "$var$cos2"       "cos2 for the categories"         
5  "$var$contrib"    "contributions of the categories" 
6  "$var$v.test"     "v-test for the categories"       
7  "$var$eta2"       "coord. of variables"             
8  "$ind"            "results for the individuals"     
9  "$ind$coord"      "coord. for the individuals"      
10 "$ind$cos2"       "cos2 for the individuals"        
11 "$ind$contrib"    "contributions of the individuals"
12 "$call"           "intermediate results"            
13 "$call$marge.col" "weights of columns"              
14 "$call$marge.li"  "weights of rows"                 
(eigs <- students_mca$eig
  %>% data.frame()
  %>% rownames_to_column("dim")
  %>% select(dim, eigenvalue)
)
    dim eigenvalue
1 dim 1  0.3292198
2 dim 2  0.3088978
3 dim 3  0.2884426
4 dim 4  0.2628430
5 dim 5  0.2512049
6 dim 6  0.2259281
7 dim 7  0.2073488
8 dim 8  0.2004296
9 dim 9  0.1756854
(eigs
  %>% summarise(total_inertia = sum(eigenvalue),
                theoretical = (13 - 4)/4)
)
  total_inertia theoretical
1          2.25        2.25

Problem 2

In this problem, we consider the hobbies data set which comes preloaded with the FactoMineR package. The data used here concern a questionnaire on hobbies. 8403 individuals were asked questions about their hobbies, as well as demographic information. Use the following subset of the data, called hobbies_use:

library(FactoMineR)
library(tidyverse)
library(factoextra)
Welcome to factoextra!
Want to learn more? See two factoextra-related books at https://www.datanovia.com/library/principal-component-methods
library(patchwork)
library(ggrepel)
data(hobbies)
hobbies_use <- hobbies %>% select(Reading:Fishing, Sex:`Marital status`)
head(hobbies_use)
         Reading Listening music Cinema Show Exhibition Computer Sport Walking
11000210       1               1      1    1          1        0     1       1
11000410       1               0      0    0          1        0     1       1
11000610       1               1      0    0          1        0     0       0
11000710       1               0      0    0          1        0     1       0
11000810       1               1      1    1          0        0     0       1
11000910       0               0      0    0          0        0     0       0
         Travelling Playing music Collecting Volunteering Mechanic Gardening
11000210          1             0          0            1        1         0
11000410          0             0          1            1        1         1
11000610          1             0          0            0        0         0
11000710          1             0          0            0        0         0
11000810          0             0          0            0        0         0
11000910          0             0          0            0        1         0
         Knitting Cooking Fishing Sex     Age Marital status
11000210        0       0       0   F (55,65]        Married
11000410        0       0       0   M (45,55]        Married
11000610        0       0       0   F (25,35]      Remarried
11000710        0       0       0   M (75,85]        Married
11000810        0       0       0   M (55,65]        Married
11000910        0       0       0   M (45,55]        Married

A)

What percent of the inertia is explained by the first two dimensions?

(hobbies_mca <- hobbies_use
  %>% MCA(graph = FALSE)
)
**Results of the Multiple Correspondence Analysis (MCA)**
The analysis was performed on 8403 individuals, described by 20 variables
*The results are available in the following objects:

   name              description                       
1  "$eig"            "eigenvalues"                     
2  "$var"            "results for the variables"       
3  "$var$coord"      "coord. of the categories"        
4  "$var$cos2"       "cos2 for the categories"         
5  "$var$contrib"    "contributions of the categories" 
6  "$var$v.test"     "v-test for the categories"       
7  "$var$eta2"       "coord. of variables"             
8  "$ind"            "results for the individuals"     
9  "$ind$coord"      "coord. for the individuals"      
10 "$ind$cos2"       "cos2 for the individuals"        
11 "$ind$contrib"    "contributions of the individuals"
12 "$call"           "intermediate results"            
13 "$call$marge.col" "weights of columns"              
14 "$call$marge.li"  "weights of rows"                 
(hobbies_mca$eig
  %>% data.frame()
  %>% rownames_to_column("dim")
  %>% head(2)
)
    dim eigenvalue percentage.of.variance cumulative.percentage.of.variance
1 dim 1  0.1884281              12.995044                          12.99504
2 dim 2  0.1073312               7.402152                          20.39720

20.40%

B)

Perform MCA on this data set. For each of the first two dimensions of the MCA, find the top-10 variables contributing to these dimensions. Discuss your findings.

hobbies_mca <- MCA(hobbies_use, ncp = 5, graph = FALSE)

(fviz_contrib(hobbies_mca, choice = "var", axes = 1, top = 10)
  + fviz_contrib(hobbies_mca, choice = "var", axes = 2, top = 10)
)

(hobbies_mca$var$coord[, 1:2]
  %>% data.frame()
  %>% rownames_to_column("category")
  %>% left_join(hobbies_mca$var$contrib[, 1:2]
                %>% data.frame()
                %>% rownames_to_column("category"),
                by = "category", suffix = c("_coord", "_contrib"))
  %>% arrange(desc(Dim.1_contrib))
)
            category  Dim.1_coord Dim.2_coord Dim.1_contrib Dim.2_contrib
1           Cinema_1  0.817187683 -0.24068066  7.083428e+00   1.078703332
2             Show_1  0.939465481  0.15772085  6.758702e+00   0.334425636
3         Computer_1  0.810406277 -0.32577068  6.549492e+00   1.858000103
4       Exhibition_1  0.861779461  0.42891056  6.085826e+00   2.646552165
5  Listening music_0 -0.852459218  0.11888148  5.635928e+00   0.192427164
6            Sport_1  0.748812860 -0.21573159  5.480209e+00   0.798541965
7       Travelling_1  0.671278653  0.28123382  4.785446e+00   1.474590962
8           Cinema_0 -0.544197746  0.16027882  4.717136e+00   0.718351406
9    Playing music_1  0.986452289  0.10205496  4.486368e+00   0.084300607
10        Computer_0 -0.487943379  0.19614563  3.943431e+00   1.118696726
11         Reading_0 -0.615505416 -0.38421794  3.298302e+00   2.256324974
12           Sport_0 -0.436619405  0.12578924  3.195412e+00   0.465615558
13      Travelling_0 -0.447918672 -0.18765661  3.193146e+00   0.983938374
14            Show_0 -0.381097991 -0.06398010  2.741695e+00   0.135661119
15      Exhibition_0 -0.385040927 -0.19163618  2.719132e+00   1.182472946
16 Listening music_1  0.352049746 -0.04909583  2.327533e+00   0.079468827
17            Single  0.557590763 -1.13553599  2.101042e+00  15.297666738
18        Mechanic_1  0.419222462  0.14303276  1.964082e+00   0.401385071
19           Widower -0.910861514  0.69972445  1.923050e+00   1.992323113
20    Volunteering_1  0.676476341  0.56722100  1.856942e+00   2.292014416
21         Walking_1  0.364631070  0.36109700  1.752886e+00   3.017962554
22         Walking_0 -0.360060245 -0.35657047  1.730913e+00   2.980130951
23           (75,85] -1.054161904  0.36424864  1.691420e+00   0.354529621
24         Cooking_1  0.374086818  0.31914901  1.628883e+00   2.081382775
25         Reading_1  0.300557639  0.18761758  1.610595e+00   1.101786743
26        Mechanic_0 -0.305022264 -0.10406927  1.429047e+00   0.292043949
27           [15,25]  0.723757513 -1.75543874  1.417610e+00  14.640698271
28           (65,75] -0.686469438  0.55906720  1.394350e+00   1.623593242
29         Cooking_0 -0.292322241 -0.24939225  1.272857e+00   1.626452599
30   Playing music_0 -0.207434876 -0.02146050  9.434102e-01   0.017727047
31      Collecting_1  0.578143554  0.32004475  9.098473e-01   0.489483780
32           (25,35]  0.447337834 -0.64218919  8.227580e-01   2.976778884
33          (85,100] -1.493182905 -0.02029633  5.984596e-01   0.000194117
34           (35,45]  0.279625766  0.02467223  4.064191e-01   0.005554644
35    Volunteering_0 -0.122123082 -0.10239941  3.352304e-01   0.413773325
36       Gardening_1  0.173297818  0.59511062  3.182717e-01   6.589113076
37           (55,65] -0.281273608  0.64540437  3.140387e-01   2.902743710
38       Gardening_0 -0.115234293 -0.39571849  2.116346e-01   4.381427280
39           Married -0.113083397  0.35072690  1.749751e-01   2.954856919
40      Collecting_0 -0.066086692 -0.03658382  1.040032e-01   0.055952131
41        Knitting_1  0.105325998  0.99708185  4.949977e-02   7.787774986
42         Fishing_1  0.071026451 -0.14801701  1.505435e-02   0.114779598
43        Knitting_0 -0.021291221 -0.20155603  1.000618e-02   1.574266961
44          Divorcee -0.043019215  0.26373029  4.628497e-03   0.305390003
45         Fishing_0 -0.008999731  0.01875517  1.907530e-03   0.014543674
46                 M  0.012354949 -0.32079994  1.825438e-03   2.160595533
47           (45,55] -0.016956280  0.43057644  1.667862e-03   1.888075212
48                 F -0.010136090  0.26318661  1.497603e-03   1.772568302
49         Remarried -0.001510653  0.46503770  2.911393e-06   0.484358913

For Dim 1, the top contributors are almost all hobbies….Cinema_1, Show_1, Computer_1, Exhibition_1, Sport_1, Travelling_1, and Playing music_1. The “no” versions (Listening music_0, Cinema_0, Computer_0) are on the other side. So Dim 1 is active vs. inactive people.

Dim 2 is mostly about age and marital status. Single and [15,25] are by far the biggest, around 15% each, which is way more than anything else. Then there’s Knitting_1, Gardening, and Walking, plus some age groups and Married. So Dim 2 splits younger single people from older married people who do more home-type hobbies like gardening and knitting.

C)

Use fviz_mca_var() with repel=TRUE to plot the coordinates of the active variables and supplementary variables. Discuss the shortcomings of this plot.

hobbies_mca_sup <- MCA(hobbies_use,
                       quali.sup = c("Sex", "Age", "Marital status"),
                       graph = FALSE)

fviz_mca_var(hobbies_mca_sup, repel = TRUE)

Some of the shortcomings of this graph is that there are so many labels that it is tough to disect, as well as having both the yes and no version of each variable pop up…which makes doubles the clutter.

D)

Note that the plot from C has too many labels and isn’t doing a good job repelling them even with repel = TRUE. Let’s use ggplot layers to improve this plot; unfortunately this will require some data wrangling to create the data set we need “from scratch.”

Use the MCA() output to create active_df and supp_df, containing the coordinates of the active and supplementary variables, respectively. Keep only the “top 10” levels of the active variables, as measured by the sum-of-squared coordinates. The first 6 rows of each data frame are shown:

head(active_df)
               level       Dim.1       Dim.2 sum_sq_coord        category
1    Playing music_1  1.00952955 -0.12540594    1.0348766 active_variable
2             Show_1  0.97841609 -0.25138634    1.0204931 active_variable
3       Exhibition_1  0.94785218  0.03055321    0.8993572 active_variable
4         Knitting_1  0.23757658  0.89493428    0.8573500 active_variable
5        Gardening_1  0.26247657  0.84523534    0.7833167 active_variable
head(supp_df)
   level       Dim.1       Dim.2 sum_sq_coord category
1       M -0.02387308 -0.04419050  0.002522725      Sex
2       F  0.01958565  0.03625421  0.001697965      Sex
3 [15,25]  0.37173828 -0.87241775  0.899302081      Age
4 (25,35]  0.26667450 -0.32338199  0.175691199      Age
5 (35,45]  0.20477079 -0.01052354  0.042041822      Age
6 (45,55]  0.01785855  0.22042748  0.048907200      Age

Then row-bind these together:

final_plot_df <- bind_rows(active_df, supp_df)

Use final_plot_df to reproduce this plot:

hobbies_mca_sup <- MCA(hobbies_use,
                       quali.sup = c("Sex", "Age", "Marital status"),
                       graph = FALSE)

(active_df <- hobbies_mca_sup$var$coord[, 1:2]
  %>% data.frame()
  %>% rownames_to_column("level")
  %>% mutate(sum_sq_coord = Dim.1^2 + Dim.2^2)
  %>% arrange(desc(sum_sq_coord))
  %>% slice_head(n = 10)
  %>% mutate(category = "active_variable")
)
               level       Dim.1       Dim.2 sum_sq_coord        category
1    Playing music_1  1.00952955 -0.12540594    1.0348766 active_variable
2             Show_1  0.97841609 -0.25138634    1.0204931 active_variable
3       Exhibition_1  0.94785218  0.03055321    0.8993572 active_variable
4         Knitting_1  0.23757658  0.89493428    0.8573500 active_variable
5        Gardening_1  0.26247657  0.84523534    0.7833167 active_variable
6           Cinema_1  0.76615410 -0.42991210    0.7718165 active_variable
7  Listening music_0 -0.81515243  0.27844241    0.7420037 active_variable
8     Volunteering_1  0.78411354  0.28507103    0.6960995 active_variable
9          Fishing_1  0.02979718  0.81683572    0.6681085 active_variable
10        Computer_1  0.73540439 -0.31584446    0.6405773 active_variable
(supp_df <- hobbies_mca_sup$quali.sup$coord[, 1:2]
  %>% data.frame()
  %>% rownames_to_column("level")
  %>% mutate(sum_sq_coord = Dim.1^2 + Dim.2^2,
             category = rep(c("Sex", "Age", "Marital status"),
                            times = sapply(hobbies_use[, c("Sex", "Age", "Marital status")],
                                           nlevels)))
)
       level       Dim.1       Dim.2 sum_sq_coord       category
1          M -0.02387308 -0.04419050  0.002522725            Sex
2          F  0.01958565  0.03625421  0.001697965            Sex
3    [15,25]  0.37173828 -0.87241775  0.899302081            Age
4    (25,35]  0.26667450 -0.32338199  0.175691199            Age
5    (35,45]  0.20477079 -0.01052354  0.042041822            Age
6    (45,55]  0.01785855  0.22042748  0.048907200            Age
7    (55,65] -0.15470944  0.38096000  0.169065535            Age
8    (65,75] -0.44418733  0.29627493  0.285081225            Age
9    (75,85] -0.70206643  0.09105498  0.501188275            Age
10  (85,100] -1.01858200 -0.22662368  1.088867582            Age
11    Single  0.29382920 -0.52163478  0.358438448 Marital status
12   Married -0.05818755  0.21315834  0.048822269 Marital status
13   Widower -0.50773741  0.20769195  0.300933220 Marital status
14  Divorcee -0.02902247 -0.03993476  0.002437089 Marital status
15 Remarried  0.04702282  0.17788562  0.033854439 Marital status
final_plot_df <- bind_rows(active_df, supp_df)

(ggplot(final_plot_df, aes(x = Dim.1, y = Dim.2, color = category, label = level))
  + geom_point()
  + geom_text_repel(size = 3, show.legend = FALSE)
  + geom_hline(yintercept = 0, linetype = "dashed")
  + geom_vline(xintercept = 0, linetype = "dashed")
  + scale_color_discrete(labels = c("Active variable", "Age", "Marital status", "Sex"))
  + labs(x = "Dimension 1", y = "Dimension 2", color = NULL)
  + theme_classic()
)

E)

What does this plot suggest about the relationships between hobbies, age, marital status, and sex? Credit will be awarded proportional to thoroughness of response.

Dimension 1 (20.7%) mostly measures overall hobby activity, with people on the right doing more outgoing activities like music, shows, exhibitions, and movies, while people on the left tend to do fewer hobbies. Dimension 2 (8.5%) separates home-based hobbies like knitting, gardening, and fishing from entertainment hobbies like movies and computers. Age seems to have the strongest relationship, with younger people leaning toward more outgoing and entertainment hobbies, middle-aged people leaning toward home-based hobbies, and the oldest groups doing fewer hobbies overall. Marital status seems to follow a similar pattern, with single people looking more like younger groups and widowed people looking more like older groups, although this could mostly be due to age. Sex doesn’t seem to have much of a relationship with either dimension since men and women are both very close to the center

Problem 3

On the public television show The Joy of Painting Bob Ross taught people how to paint lovely scenic landscapes consisting of “snowcapped peaks” and “happy little clouds.” This dataset is a table consisting of different elements (e.g. mountain, river, waterfall, etc.) commonly found within in his paintings. Thus each painting can be thought of as survey respondent where they are asked a series of yes (1) or no (0) questions. This data set contains characteristics of paintings created in 316 of the 403 “Joy of Painting” episodes. Note that MCA requires everything to be factors (aka “categorical”) variables, hence the mutate() command below.

bobross <- read.csv('Data/BobRossCommon.csv') %>% 
  column_to_rownames('TITLE') %>% 
  mutate(across(everything(), as.factor))

Our goal is to understand what elements Ross tended to use together in paintings, and to find representative paintings with various characteristics.

A)

Perform MCA on the data set, and plot the first two dimensions of the variable coordinates. How would you characterize paintings in each of the four quadrants of this plot? Where are the “summer” and “winter” paintings?

bobross_mca <- MCA(bobross, graph = FALSE)

fviz_mca_var(bobross_mca, repel = TRUE)

Upper right: Cabins

Bottom right: Mountain/Lake

Upper left: Summer

Lower left: Water

Winter: Far right

B)

Plot the MCA scores, the coordinates of the individual paintings, filtering to paintings that are “most representative” of each quadrant using the cos2 values. From here, Google image search to find one painting that is representative of each quadrant. Include screenshots of these in your submission, along with discussion of how your selected paintings exemplify the quadrant.

(rep_df <- bobross_mca$ind$coord[, 1:2]
  %>% data.frame()
  %>% rownames_to_column("title")
  %>% mutate(cos2 = rowSums(bobross_mca$ind$cos2[, 1:2]),
             quadrant = case_when(Dim.1 > 0 & Dim.2 > 0 ~ "Upper right",
                                  Dim.1 < 0 & Dim.2 > 0 ~ "Upper left",
                                  Dim.1 < 0 & Dim.2 < 0 ~ "Lower left",
                                  Dim.1 > 0 & Dim.2 < 0 ~ "Lower right"))
  %>% group_by(quadrant)
  %>% slice_max(cos2, n = 3)
  %>% ungroup()
)

(ggplot(rep_df, aes(x = Dim.1, y = Dim.2, color = quadrant, label = title))
  + geom_point(data = bobross_mca$ind$coord[, 1:2] %>% data.frame(),
               aes(x = Dim.1, y = Dim.2), inherit.aes = FALSE,
               color = "grey80")
  + geom_point(size = 2)
  + geom_text_repel(size = 3, show.legend = FALSE)
  + geom_hline(yintercept = 0, linetype = "dashed")
  + geom_vline(xintercept = 0, linetype = "dashed")
  + labs(x = "Dimension 1", y = "Dimension 2", color = NULL)
  + theme_classic()
)

# A tibble: 12 × 5
   title                 Dim.1   Dim.2  cos2 quadrant   
   <chr>                 <dbl>   <dbl> <dbl> <chr>      
 1 FINAL_GRACE         -0.0162 -0.709  0.717 Lower left 
 2 DOUBLE_OVAL_FANTASY -0.0162 -0.709  0.717 Lower left 
 3 MOUNTAIN_WATERFALL1 -0.157  -0.719  0.532 Lower left 
 4 ARCTIC_WINTER_DAY    0.886  -0.359  0.801 Lower right
 5 FROZEN_SOLITUDE      0.840  -0.434  0.751 Lower right
 6 PERFECT_WINTERS_DAY  0.816  -0.353  0.684 Lower right
 7 LAZY_RIVER          -0.328   0.373  0.566 Upper left 
 8 HIDDEN_CREEK        -0.328   0.373  0.566 Upper left 
 9 CYPRESS_CREEK       -0.328   0.373  0.566 Upper left 
10 WINTER_NIGHT         0.826   0.145  0.699 Upper right
11 SNOWY_MORN           0.826   0.145  0.699 Upper right
12 PURPLE_SPLENDOR      0.850   0.0636 0.698 Upper right

Upper left: - Hidden creek, lazy river, and cypress creek are the top 3 representative paintings. In part A I described his quadrant as summer. I think this creek encapsulates this well.

Upper right: - Snowy morning, winter night. These are close to the D2 origin but strong along D1. I kind of combined the two into a snowy winter night.

Bottom left: - Mountain waterfall. I described this in part a as the water themes.

Bottom right: - Winter day. The winter themes were all along the right side, so this is the second winter picture I choose.