SachaEpskamp / qgraph

Developmental version of qgraph
GNU General Public License v2.0
68 stars 22 forks source link

pie in qgraph.loadings does not display when there are more than 4 latent factors #79

Closed nadyamajeed closed 1 year ago

nadyamajeed commented 1 year ago

If there are 4 or fewer factors, the pie display works fine. However when there are 5 or more factors, it doesn't show up. See reproducible example below in psychonetrics version 0.11 and qgraph version 1.9.5.

library(dplyr)
library(psychonetrics)
library(qgraph)

# function to do everything
getFitAndPlot = function(data, lambda) {
  # create fit
  fitCurrent = psychonetrics::lnm(
    data = data,
    identification = "variance", standardize = "z",
    lambda = lambda,
    vars = colnames(data),
    latents = colnames(lambda)) %>%
    psychonetrics::runmodel()

  # get matrices
  latent_correlations = psychonetrics::getmatrix(fitCurrent, "omega_zeta")
  factor_loadings     = psychonetrics::getmatrix(fitCurrent, "lambda")
  resid_vcov          = psychonetrics::getmatrix(fitCurrent, "sigma_epsilon")

  # get labels
  groups = lapply(1:ncol(factor_loadings), function(x) which(factor_loadings[,x] != 0))
  names(groups) = colnames(lambda)

  # prepare plot
  p_manualpie = qgraph::qgraph.loadings(
    # more important stuff
    fact = factor_loadings,
    model = "reflective",
    resid = diag(resid_vcov),
    groups = groups,
    factorCors = latent_correlations,
    # graphics to make things prettier
    residSize = 0.3, vsize = c(3, 10), fade = FALSE, cut = 0,
    theme = "colorblind", 
    # don't plot yet - need to fix more things
    DoNotPlot = TRUE)

  # fix edges
  p_manualpie$Edgelist$bidirectional[p_manualpie$Edgelist$to > nrow(factor_loadings)] = FALSE
  p_manualpie$Edgelist$directed[p_manualpie$Edgelist$to > nrow(factor_loadings)] = FALSE

  # manually fix pie
  nNodes = p_manualpie$graphAttributes$Graph$nNodes
  p_manualpie$plotOptions$drawPies = TRUE
  p_manualpie$plotOptions$pieRadius = 1
  p_manualpie$graphAttributes$Nodes$pieStart = rep(0, nNodes)
  p_manualpie$graphAttributes$Nodes$pieDarken = rep(0.25, nNodes)
  p_manualpie$graphAttributes$Nodes$pieBorder = rep(0.15, nNodes)
  p_manualpie$graphAttributes$Nodes$pieColor = rep(NA, nNodes)
  p_manualpie$graphAttributes$Nodes$pieColor2 = rep("white", nNodes)
  # get predictability (R2)
  predR2 = qgraph::centrality(latent_correlations, R2 = TRUE)$R2 %>% as.list()
  predR2 = c(rep(list(NULL), nrow(factor_loadings)), predR2)
  predR2 = predR2[1:nNodes]
  # prepare pie values
  p_manualpie$Arguments$pie = p_manualpie$graphAttributes$Nodes$pie = predR2

  # view plot
  plot(p_manualpie)
}

##### four factor (pie works) -----

# read in data
data4Factor = read.csv("https://github.com/SachaEpskamp/psychonetrics/files/12660505/example.csv")[1:21]

# factor loading matrix
lambda4Factor = psychonetrics::simplestructure(c(
  rep("a", 3), 
  rep("b", 6), 
  rep("c", 6),
  rep("d", 6)))

# run
getFitAndPlot(data4Factor, lambda4Factor)

##### five factor (pie doesn't work) -----

# read in data
data5Factor = read.csv("https://github.com/SachaEpskamp/psychonetrics/files/12660505/example.csv")[1:27]

# factor loading matrix
lambda5Factor = psychonetrics::simplestructure(c(
  rep("a", 3), 
  rep("b", 6), 
  rep("c", 6),
  rep("d", 6),
  rep("e", 6)))

# run
getFitAndPlot(data5Factor, lambda5Factor)
image image
nadyamajeed commented 1 year ago

Update - it doesn't seem to be due to the number of latent factors but maybe due to the total number of nodes instead. See the following code where there are 3 latent factors in both models but a different number of manifest variables. The smaller model (7 manifest variables per factor) shows pie correctly while the larger model (8 manifest variables per factor) does not.

This part of the code is the same as previous, but I'm adding it below for clarity:

library(dplyr)
library(psychonetrics)
library(qgraph)

# function to do everything
getFitAndPlot = function(data, lambda) {
  # create fit
  fitCurrent = psychonetrics::lnm(
    data = data,
    identification = "variance", standardize = "z",
    lambda = lambda,
    vars = colnames(data),
    latents = colnames(lambda)) %>%
    psychonetrics::runmodel()

  # get matrices
  latent_correlations = psychonetrics::getmatrix(fitCurrent, "omega_zeta")
  factor_loadings     = psychonetrics::getmatrix(fitCurrent, "lambda")
  resid_vcov          = psychonetrics::getmatrix(fitCurrent, "sigma_epsilon")

  # get labels
  groups = lapply(1:ncol(factor_loadings), function(x) which(factor_loadings[,x] != 0))
  names(groups) = colnames(lambda)

  # prepare plot
  p_manualpie = qgraph::qgraph.loadings(
    # more important stuff
    fact = factor_loadings,
    model = "reflective",
    resid = diag(resid_vcov),
    groups = groups,
    factorCors = latent_correlations,
    # graphics to make things prettier
    residSize = 0.3, vsize = c(3, 10), fade = FALSE, cut = 0,
    theme = "colorblind", 
    # don't plot yet - need to fix more things
    DoNotPlot = TRUE)

  # fix edges
  p_manualpie$Edgelist$bidirectional[p_manualpie$Edgelist$to > nrow(factor_loadings)] = FALSE
  p_manualpie$Edgelist$directed[p_manualpie$Edgelist$to > nrow(factor_loadings)] = FALSE

  # manually fix pie
  nNodes = p_manualpie$graphAttributes$Graph$nNodes
  p_manualpie$plotOptions$drawPies = TRUE
  p_manualpie$plotOptions$pieRadius = 1
  p_manualpie$graphAttributes$Nodes$pieStart = rep(0, nNodes)
  p_manualpie$graphAttributes$Nodes$pieDarken = rep(0.25, nNodes)
  p_manualpie$graphAttributes$Nodes$pieBorder = rep(0.15, nNodes)
  p_manualpie$graphAttributes$Nodes$pieColor = rep(NA, nNodes)
  p_manualpie$graphAttributes$Nodes$pieColor2 = rep("white", nNodes)
  # get predictability (R2)
  predR2 = qgraph::centrality(latent_correlations, R2 = TRUE)$R2 %>% as.list()
  predR2 = c(rep(list(NULL), nrow(factor_loadings)), predR2)
  predR2 = predR2[1:nNodes]
  # prepare pie values
  p_manualpie$Arguments$pie = p_manualpie$graphAttributes$Nodes$pie = predR2

  # view plot
  plot(p_manualpie)
}

New code for new models:

##### smaller model (pie works) -----

# read in data
dataSmall = read.csv("https://github.com/SachaEpskamp/psychonetrics/files/12660505/example.csv")[1:21]

# factor loading matrix
lambdaSmall = psychonetrics::simplestructure(c(
  rep("a", 7), 
  rep("b", 7), 
  rep("c", 7)))

# run
getFitAndPlot(dataSmall, lambdaSmall)

##### larger model (pie doesn't work) -----

# read in data
dataLarge = read.csv("https://github.com/SachaEpskamp/psychonetrics/files/12660505/example.csv")[1:24]

# factor loading matrix
lambdaLarge = psychonetrics::simplestructure(c(
  rep("a", 8), 
  rep("b", 8), 
  rep("c", 8)))

# run
getFitAndPlot(dataLarge, lambdaLarge)
image image
SachaEpskamp commented 1 year ago

We found out that this is due to the default setting for usePCH, which defaults to TRUE when there are at least 50 nodes and when pie charts were not specified in qgraph(...) originally. This can be solved by setting usePCH = FALSE in the original qgraph call.