Visualisation des réponses Likert à l'aide de R ou SPSS

19

J'ai 82 répondants dans 2 groupes (43 dans le groupe A et 39 dans le groupe B) qui ont répondu à un sondage de 65 questions Likert allant de 1 à 5 (tout à fait d'accord - tout à fait en désaccord). J'ai donc un dataframe avec 66 colonnes (1 pour chaque question + 1 indiquant la répartition des groupes) et 82 lignes (1 pour chaque répondant).

L'utilisation de R ou SPSS permet à quiconque de visualiser une bonne façon de visualiser ces données.

J'ai besoin de quelque chose comme ça: entrez la description de l'image ici
(de Jason Bryer )

Mais je ne peux pas faire fonctionner la section initiale de code. Alternativement, j'ai trouvé de très bons exemples de la façon de visualiser les données de Likert à partir d'un post précédent de validation croisée: Visualisation des données de réponse de l'article de Likert, mais il n'y a pas de guides ou d'instructions sur la façon de créer ces graphiques de comptage centrés ou ces graphiques à barres empilés à l'aide de R ou SPSS.

Adam
la source
1
Bonjour Adam, pour clarifier davantage, vouliez-vous utiliser les visualisations pour montrer les différences entre les groupes? Si c'est le cas, ce n'est pas une méthode recommandée.
Michelle
Le package de Jason Bryer ne fonctionnait pas pour moi, mais je pense qu'il l'a mis à jour, et cela fonctionne très bien en ce moment. J'ai également ajouté une pull-request avec une fonctionnalité supplémentaire pour stocker les noms de colonnes en tant qu'attributs et groupes. En utilisant cela, je peux facilement visualiser un questionnaire Likert de 45 questions divisé en groupes, voire divisé en fonction d'une autre variable si je le souhaite. (Je produis en utilisant knitr, donc cela se termine avec autant de sous-parcelles sur un site Web, pas une gigantesque parcelle). J'ai fait un article détaillé ici: reganmian.net/blog/2013/10/02/…
Stian Håklev
Juste pour info, pour ceux d'entre vous qui liront ces réponses à l'avenir, il semble que certaines des caractéristiques et fonctionnalités d'irutils concernant les données likert ont été déplacées dans le package Likert R ( voir CRAN ici ).
firefly2442
Le lien bryer.org/2011/visualizing-likert-items semble rompu. Une correction ou un remplacement serait le bienvenu.
Nick Cox
1
Ce genre de question - avec sa forte concentration sur un code spécifique - est moins bienvenu en 2018 qu'en 2012. Quoi qu'il en soit, certaines références croisées pour toute personne intéressée par ceci sont stats.stackexchange.com/questions/56322/ … Et stats.stackexchange.com/questions/148554/…
Nick Cox

Réponses:

30

Si vous voulez vraiment utiliser des diagrammes à barres empilées avec un si grand nombre d'articles, voici deux solutions possibles.

En utilisant irutils

J'ai rencontré ce paquet il y a quelques mois.

À partir de la validation 0573195c07 sur Github , le code ne fonctionnera pas avec un grouping=argument. C'est parti pour la session de débogage de vendredi.

Commencez par télécharger une version zippée depuis Github. Vous devrez pirater le R/likert.Rfichier, en particulier les fonctions likertet plot.likert. Tout d'abord, dans likert, cast()est utilisé mais le reshapepaquet n'est jamais chargé (bien qu'il y ait une import(reshape)instruction dans le NAMESPACEfichier). Vous pouvez le charger vous-même au préalable. Deuxièmement, il y a une instruction incorrecte pour récupérer les étiquettes des éléments, où a ipendille autour de la ligne 175. Cela doit également être corrigé, par exemple en remplaçant toutes les occurrences de likert$items[,i]avec likert$items[,1]. Ensuite, vous pouvez installer le package comme vous le faites sur votre machine. Sur mon Mac, je l'ai fait

% tar -czf irutils.tar.gz jbryer-irutils-0573195
% R CMD INSTALL irutils.tar.gz

Ensuite, avec R, essayez ce qui suit:

library(irutils)
library(reshape)

# Simulate some data (82 respondents x 66 items)
resp <- data.frame(replicate(66, sample(1:5, 82, replace=TRUE)))
resp <- data.frame(lapply(resp, factor, ordered=TRUE, 
                          levels=1:5, 
                          labels=c("Strongly disagree","Disagree",
                                   "Neutral","Agree","Strongly Agree")))
grp <- gl(2, 82/2, labels=LETTERS[1:2]) # say equal group size for simplicity

# Summarize responses by group
resp.likert <- likert(resp, grouping=grp)

Cela devrait fonctionner, mais le rendu visuel sera horrible en raison du nombre élevé d'éléments. plot(likert(resp))Cependant, cela fonctionne sans regroupement (par exemple ).

entrez la description de l'image ici

Je suggérerais donc de réduire votre ensemble de données à de plus petits sous-ensembles d'éléments. Par exemple, en utilisant 12 éléments,

plot(likert(resp[,1:12], grouping=grp))

Je reçois un diagramme à barres empilées «lisible». Vous pouvez probablement les traiter par la suite. (Ce sont des ggplot2objets, mais vous ne pourrez pas les organiser sur une seule page à gridExtra::grid.arrange()cause d'un problème de lisibilité!)

entrez la description de l'image ici

Solution alternative

Je voudrais attirer votre attention sur un autre package, HH , qui permet de représenter les échelles de Likert sous forme de diagrammes à barres empilées divergentes. Nous pourrions réutiliser le code ci-dessus comme indiqué ci-dessous:

resp.likert <- likert(resp)
detach(package:irutils)
library(HH)
plot.likert(resp.likert$results[,-6]*82/100, main="")

mais cela compliquera un peu les choses car nous devons convertir les fréquences en nombres, sous-définir l' likertobjet produit par irutils, détacher le package, etc. Commençons donc avec de nouvelles statistiques (nombres):

plot.likert(t(apply(resp, 2, table)), main="", as.percent=TRUE,
            rightAxisLabels=NULL, rightAxis=NULL, ylab.right="", 
            positive.order=TRUE)

entrez la description de l'image ici

Pour utiliser une variable de regroupement, vous devrez travailler avec une arraydes valeurs numériques.

# compute responses frequencies separately by grp
resp.array <- array(NA, dim=c(66, 5, 2))
resp.array[,,1] <- t(apply(subset(resp, grp=="A"), 2, table))
resp.array[,,2] <- t(apply(subset(resp, grp=="B"), 2, table))
dimnames(resp.array) <- list(NULL, NULL, group=levels(grp))
plot.likert(resp.array, layout=c(2,1), main="")

Cela produira deux panneaux distincts, mais il tient sur une seule page.

entrez la description de l'image ici

Modifier 2016-6-3

  1. À partir de maintenant, likert est disponible en package séparé.
  2. Vous n'avez pas besoin de remodeler la bibliothèque ou de détacher les deux irutils et de remodeler
chl
la source
Le dernier graphique me rappelle les pyramides des âges. Nous devrions obtenir de vraies données pour voir comment elles fonctionnent "à l'état sauvage", avec des données qui ne sont pas si ordonnées. Je dois admettre qu'ils sont accrocheurs et jolis.
Andy W
@Andy C'est le cas, en effet. Tu vois HH::as.pyramidLikert.
chl
1
+1, la bibliothèque (HH) est définitivement la voie à suivre. Mais quelque chose a mal tourné avec votre avant-dernière intrigue dans l'ordre d'accord / en désaccord, etc.
Peter Ellis
@PeterEllis Yup, il semble que les catégories de réponses soient dans le mauvais ordre, en effet. (L'ordre des étiquettes a été perdu lors de la tabulation des données, et les noms des tables sont organisés selon l'ordre lexicographique.) Pour un hack rapide, nous pouvons simplement remplacer t(apply(resp, 2, table))par t(apply(resp, 2, table))[,levels(resp[,1])]. Et +1 à vous aussi!
chl
7

J'ai commencé à écrire un article de blog sur la recréation de nombreux graphiques dans l'article que vous mentionnez ( Visualizing Likert Item Response Data ) dans SPSS, donc je suppose que ce sera une bonne motivation pour le terminer.

Comme le note Michelle, le fait que vous ayez des groupes est une nouvelle tournure par rapport aux questions précédentes. Et bien que les groupes puissent être pris en compte à l'aide des graphiques à barres empilées, l'OMI sont beaucoup plus facilement incorporés dans l'exemple de tracé de points dans le post original de chl. J'ai inclus le code SPSS pour générer cela à la fin du message, cela implique essentiellement de savoir comment remodeler vos données dans le format approprié pour générer ledit tracé (annotation fournie dans le code pour, espérons-le, effacer une partie de cela). Ici, j'ai utilisé un encodage redondant (couleur et forme) pour distinguer les points provenant des deux groupes, et j'ai rendu les points semi-transparents pour que vous puissiez dire quand ils se chevauchent (une autre option serait d'esquiver les points lorsqu'ils se chevauchent).

Figure 1: Tracés de points par groupe

Pourquoi est-ce mieux que les graphiques à barres empilées? Les graphiques à barres empilées codent les informations dans la longueur des barres. Lorsque vous essayez de faire des comparaisons entre les longueurs de barres, soit dans la même catégorie d'axe, soit entre les panneaux, l'empilement empêche les barres d'avoir une échelle commune. Par exemple, j'ai fourni une image sur la figure 2 dans laquelle deux barres sont placées dans un tracé dans lequel leur emplacement de départ est différent, quelle barre est la plus large (le long de l'axe horizontal)?

Figure 2: barres sans échelle commune

Comparez cela au tracé de la figure 3 ci-dessous, dans lequel les deux barres (de même longueur) sont tracées à partir du même point de départ. J'ai intentionnellement rendu la tâche difficile, mais vous devriez pouvoir dire laquelle est plus longue.

Figure 3: barres avec une échelle commune

Les graphiques à barres empilées font essentiellement ce qui est affiché dans la figure 2. Les tracés de points peuvent être considérés comme plus similaires à ceux qui sont affichés dans la figure 3, il suffit de remplacer la barre par un point à la fin de la barre.

Je ne vais pas dire ne pas générer de graphique particulier pour l'analyse exploratoire des données, mais je suggérerais d'éviter les graphiques à barres empilées lorsque vous utilisez autant de catégories. Les graphiques à points ne sont pas non plus une panacée, mais je pense que faire des comparaisons entre les panneaux avec les graphiques à points est beaucoup plus facile qu'avec les graphiques à barres empilées. Considérez également certains des conseils que je donne sur mon blog ici pour les tableaux, essayez de classer et / ou de séparer les graphiques en catégories significatives, et assurez-vous que les éléments que vous souhaitez regarder en tandem sont plus proches les uns des autres dans les graphiques. Bien que certaines des méthodes de traçage puissent être adaptées à de nombreuses questions (les cartes thermiques catégorielles en sont un exemple), sans trier, il sera toujours difficile d'identifier des modèles significatifs (en dehors des valeurs aberrantes évidentes).

Une note sur l'utilisation de SPSS. SPSS peut générer n'importe lequel des précédents liés aux graphiques, bien qu'il implique souvent de savoir comment façonner vos données (il en va de même pour ggplot, mais les gens ont développé des packages pour essentiellement faire le remodelage pour vous). Pour comprendre comment le langage GPL de SPSS fonctionne mieux, je suggère de lire le livre de Hadley Wickham sur ggplot2dans Use R! séries. Il présente la grammaire nécessaire pour comprendre comment fonctionne la GPL de SPSS et est beaucoup plus facile à lire que le manuel de programmation GPL fourni avec SPSS! Si vous avez des questions sur la génération de graphiques spécifiques dans SPSS, il serait préférable de poser une question pour un graphique (j'en ai assez parlé ici!). Je mettrai à jour cette réponse avec un lien, si jamais j'arrive à faire mon article de blog répliquant certains des autres graphiques. Pour une preuve de concept des cartes de chaleur ou des graphiques de fluctuation, vous pouvez voir un autre article de mon blog, Quelques exemples de Corrgrams dans SPSS .

Code SPSS utilisé pour générer la figure 1

****************************************.
input program. */making fake data similar to yours.
loop #i = 1 to 82.
compute case_num = #i.
end case.
end loop.
end file.
end input program.
execute.
dataset name likert.

*making number in groups.
compute group = 1.
if case_num > 43 group = 2.
value labels group
1 'A'
2 'B'.

*this makes 5 variables with categories between 0 and 5 (similar to Likert data with 5 categories plus missing data).
vector V(5).
do repeat V = V1 to V5.
compute V = TRUNC(RV.UNIFORM(0,6)).
end repeat.
execute.

value labels V1 to V5
0 'missing'
1 'very disagree'
2 'disagree'
3 'neutral'
4 'agree'
5 'very agree'.
formats case_num group V1 to V5 (F1.0).
*****************************************.

*Because I want to panel by variable, I am going to reshape my data so all of the "V" variables are in one column (stacking them in long format).
varstocases
/make V from V1 to V5
/index orig (V).

*I am going to plot the points, so I aggregate that information (you could aggregate total counts as well if you wanted to plot percentages.
DATASET DECLARE agg_lik.
AGGREGATE
  /OUTFILE='agg_lik'
  /BREAK=orig V group
  /count_lik=N.
dataset activate agg_lik.


*now the fun part, generating the chart.
*The X axis, dim(1) is the count of likert responses within each category for each original question.
*The Y axis, dim(2) is the likert responses, and the third axis is used to panel the observations by the original questions, dim(4) here beacause I want to panel
by rows instead of columns.
DATASET ACTIVATE agg_lik.
* Chart Builder.
GGRAPH
  /GRAPHDATASET NAME="graphdataset" VARIABLES=count_lik V group orig 
    MISSING=LISTWISE REPORTMISSING=NO
  /GRAPHSPEC SOURCE=INLINE.
BEGIN GPL
  SOURCE: s=userSource(id("graphdataset"))
  DATA: count_lik=col(source(s), name("count_lik"))
  DATA: V=col(source(s), name("V"), unit.category())
  DATA: group=col(source(s), name("group"), unit.category())
  DATA: orig=col(source(s), name("orig"), unit.category())
  GUIDE: axis(dim(1), label("Count"))
  GUIDE: axis(dim(2))
  GUIDE: axis(dim(4))
  GUIDE: legend(aesthetic(aesthetic.color.exterior), label("group"))
  GUIDE: text.title(label("Figure 1: Dot Plots by Group"))
  SCALE: cat(aesthetic(aesthetic.color.exterior), include("1", "2"))
  SCALE: cat(aesthetic(aesthetic.shape), map(("1", shape.circle), ("2", shape.square)))
  ELEMENT: point(position(count_lik*V*1*orig), color.exterior(group), color.interior(group), transparency.interior(transparency."0.7"), size(size."8px"), shape(group))
END GPL.
*The "SCALE: cat" statements map different shapes which I use to assign to the two groups in the plot, and I plot the interior of the points as partially transparent.
*With some post hoc editing you should be able to make the chart look like what I have in the stats post.
****************************************.
Andy W
la source
Fort avantage de ma part pour avoir discuté poliment mais de manière pénétrante des lacunes des graphiques à barres empilées, qui sont faciles à comprendre en principe mais souvent beaucoup moins faciles à décoder dans la pratique.
Nick Cox
5

Eh bien, j'ai trouvé le code avant que vous ne clarifiiez. J'aurais dû attendre mais j'ai pensé que je devrais l'afficher pour que toute personne qui vient ici puisse réutiliser ce code.

Données factices pour la visualisation

# Response for http://stats.stackexchange.com/questions/25109/visualizing-likert-responses-using-r-or-spss
# Load libraries
library(reshape2)
library(ggplot2)

# Functions
CreateRowsColumns <- function(noofrows, noofcolumns) {
createcolumnnames <- paste("Q", 1:noofcolumns, sep ="")
df <- sapply(1:noofcolumns, function(i) assign(createcolumnnames[i], matrix(sample(1:5, noofrows, replace = TRUE))))
df <- sapply(1:noofcolumns, function(i) df[,i] <- as.factor(df[,i]))
colnames(df) <- createcolumnnames
return(df)}

# Generate dummy dataframe
LikertResponse <- CreateRowsColumns(82, 65)
LikertResponse[LikertResponse == 1] <- "Strongly agree"
LikertResponse[LikertResponse == 2] <- "Agree"
LikertResponse[LikertResponse == 3] <- "Neutral"
LikertResponse[LikertResponse == 4] <- "Disagree"
LikertResponse[LikertResponse == 5] <- "Strongly disagree"

Code pour heatmap

# Prepare data
LikertResponseSummary <- do.call(rbind, lapply(data.frame(LikertResponse), table))
LikertResponseSummaryPercent <- prop.table(LikertResponseSummary,1)

# Melt data
LikertResponseSummary <- melt(LikertResponseSummary)
LikertResponseSummaryPercent <- melt(LikertResponseSummaryPercent)

# Merge counts with proportions
LikertResponsePlotData <- merge(LikertResponseSummary, LikertResponseSummaryPercent, by = c("Var1","Var2"))

# Plot heatmap!
# Use the "geom_tile(aes(fill = value.y*100), colour = "white")" to control how you want the heatmap colours to map to.
ggplot(LikertResponsePlotData, aes(x = Var2, y = Var1)) +
    geom_tile(aes(fill = value.y*100), colour = "white") +
    scale_fill_gradient(low = "white", high = "steelblue", name = "% of Respondents") +
    scale_x_discrete(name = 'Response') +
    scale_y_discrete(name = 'Questions') +
    geom_text(aes(label = paste(format(round(value.y*100), width = 3), '% (', format(round(value.x), width = 3), ')')), size = 3) 

Il s'agit essentiellement d'un modèle de visualisation des éléments Likert sur une carte thermique du site Web de Jason Bryon.

RJ-
la source
1
github.com/jbryer/irutils/blob/master/R/likert.R est la source des graphiques à barres empilées que vous souhaitez.
RJ-
Pour clarifier, je ne veux pas comparer entre les groupes. Juste pour présenter les réponses des deux groupes de manière sophistiquée. Ceci est une excellente réponse. J'apprécie beaucoup. Merci.
Adam
3

Le code de @ RJ produit un tracé comme celui-ci, qui est en réalité un tableau avec des cellules ombrées. C'est plutôt occupé et un peu difficile à déchiffrer. Un tableau simple sans ombrage pourrait être plus efficace (et vous pouvez également placer les données dans un ordre plus significatif).

entrez la description de l'image ici

Bien sûr, cela dépend du message principal que vous essayez de communiquer, mais je pense que c'est plus simple et un peu plus facile à comprendre. Il a également les questions et les réponses dans un ordre logique (surtout!).

    library(stringr)
    LikertResponseSummary$Var1num <- 
      as.numeric(str_extract(LikertResponseSummary$Var1, "[0-9]+"))
    LikertResponseSummary$Var2 <- 
      factor(LikertResponseSummary$Var2, 
      levels =  c("Strongly disagree", "Disagree", "Neutral", "Agree", "Strongly agree"))

ggplot(LikertResponseSummary, 
       aes(factor(Var1num), value, fill = factor(Var2))) + 
       geom_bar(position="fill") +
       scale_x_discrete(name = 'Question', breaks=LikertResponseSummary$Var1num,
                        labels=LikertResponseSummary$Var1) +
       scale_y_continuous(name = 'Proportion') +
       scale_fill_discrete(name = 'Response') +
       coord_flip()

entrez la description de l'image ici

Ben
la source
Convenu que le graphique semble occupé. Il serait toutefois utile que les questions soient regroupées dans un ordre quelconque, par exemple Q1 - 10 pose des questions sur une certaine dimension, etc. En un coup d'œil, si les tendances sont évidentes, les couleurs le diraient.
RJ-