Visualisierung von Likert-Antworten mit R oder SPSS

19

Ich habe 82 Befragte in 2 Gruppen (43 in Gruppe A und 39 in Gruppe B), die eine Umfrage unter 65 Likert-Fragen im Bereich von 1 bis 5 abgeschlossen haben (stimme voll und ganz zu - stimme überhaupt nicht zu). Ich habe daher einen Datenrahmen mit 66 Spalten (1 für jede Frage + 1 für die Gruppenzuordnung) und 82 Zeilen (1 für jeden Befragten).

Mit R oder SPSS kann jeder diese Daten gut visualisieren.

Ich brauche so etwas: Bildbeschreibung hier eingeben
(von Jason Bryer )

Ich kann den ersten Codeabschnitt jedoch nicht zum Laufen bringen. Alternativ habe ich wirklich gute Beispiele für die Visualisierung von Likert-Daten aus einem früheren Cross Validated-Beitrag gefunden: Visualisierung von Likert-Artikel-Antwortdaten. Es gibt jedoch keine Anleitungen oder Anweisungen zum Erstellen dieser zentrierten Zählungsdiagramme oder gestapelten Balkendiagramme mit R oder SPSS.

Adam
quelle
1
Hallo Adam, um es weiter zu verdeutlichen, wolltest du die Visualisierungen verwenden, um Unterschiede zwischen den Gruppen zu zeigen? Wenn ja, ist dies keine empfohlene Methode.
Michelle
Das Paket von Jason Bryer hat bei mir nicht funktioniert, aber ich denke, er hat es aktualisiert und es funktioniert jetzt wunderbar. Ich habe auch eine Pull-Anfrage mit einer zusätzlichen Funktion zum Speichern von Spaltennamen als Attribute und Gruppen hinzugefügt. Auf diese Weise kann ich auf einfache Weise einen Likert-Fragebogen mit 45 Fragen visualisieren, der in Gruppen aufgeteilt ist. Wenn ich möchte, kann ich ihn auch gegen eine andere Variable aufteilen. (Ich gebe mit knitr aus, so dass es so viele Untergrundstücke auf einer Website gibt, kein einziges gigantisches Grundstück). Ich habe hier ausführlich geschrieben: reganmian.net/blog/2013/10/02/…
Stian Håklev
Nur zu Ihrer Information, für diejenigen unter Ihnen, die diese Antworten in Zukunft lesen, sieht es so aus, als ob einige der Merkmale und Funktionen von irutils in Bezug auf Likert-Daten in das Likert R-Paket verschoben wurden ( siehe CRAN hier ).
Firefly2442
Der Link bryer.org/2011/visualizing-likert-items scheint defekt zu sein. Eine Korrektur oder Ersetzung wäre willkommen.
Nick Cox
1
Diese Art von Frage - mit einem starken Fokus auf spezifischen Code - ist 2018 weniger willkommen als 2012. Unabhängig davon sind einige Querverweise für jeden, der daran interessiert ist, stats.stackexchange.com/questions/56322/. … Und stats.stackexchange.com/questions/148554/…
Nick Cox

Antworten:

30

Wenn Sie wirklich gestapelte Balkendiagramme mit einer so großen Anzahl von Elementen verwenden möchten, gibt es zwei mögliche Lösungen.

Verwenden irutils

Ich bin vor einigen Monaten auf dieses Paket gestoßen.

Ab Commit 0573195c07 für Github funktioniert der Code nicht mit einem grouping=Argument. Lassen Sie uns für die Debugging-Sitzung am Freitag gehen.

Laden Sie zunächst eine komprimierte Version von Github herunter. Sie müssen die R/likert.RDatei hacken , insbesondere die Funktionen likertund plot.likert. Zuerst in likert, cast()verwendet wird , aber das reshapePaket geladen werden nie (obwohl es eine import(reshape)Anweisung in der NAMESPACEDatei). Sie können dies vorher selbst laden. Zweitens gibt es eine falsche Anweisung zum Abrufen von Elementbezeichnungen, bei denen a ium die Zeile 175 baumelt. Dies muss ebenfalls behoben werden, z. B. indem alle Vorkommen von likert$items[,i]durch ersetzt werden likert$items[,1]. Anschließend können Sie das Paket so installieren, wie Sie es auf Ihrem Computer gewohnt sind. Auf meinem Mac habe ich getan

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

Versuchen Sie dann mit R Folgendes:

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)

Das sollte funktionieren, aber das visuelle Rendern wird wegen der hohen Anzahl von Elementen schrecklich sein. Es funktioniert jedoch ohne Gruppierung (zB plot(likert(resp))).

Bildbeschreibung hier eingeben

Ich würde daher vorschlagen, Ihren Datensatz auf kleinere Teilmengen von Elementen zu reduzieren. ZB mit 12 Artikeln,

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

Ich bekomme ein 'lesbares' gestapeltes Balkendiagramm. Sie können sie wahrscheinlich später verarbeiten. (Dies sind ggplot2Objekte, aber Sie können sie gridExtra::grid.arrange()aufgrund von Lesbarkeitsproblemen nicht auf einer einzigen Seite anordnen !)

Bildbeschreibung hier eingeben

Alternative Lösung

Ich möchte Ihre Aufmerksamkeit auf ein anderes Paket lenken, HH , mit dem Likert-Skalen als divergierende gestapelte Balkendiagramme dargestellt werden können. Wir könnten den obigen Code wie folgt wiederverwenden:

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

Aber das wird die Sache ein wenig komplizieren, weil wir Frequenzen in Zählwerte umwandeln müssen, um likertdas von erzeugte Objekt zu unterteilenirutils Paket trennen usw. Beginnen wir also erneut mit neuen (Zähl-) Statistiken:

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

Bildbeschreibung hier eingeben

Um eine Gruppierungsvariable zu verwenden, müssen Sie mit einem arbeiten array numerischen Werten arbeiten.

# 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="")

Dadurch werden zwei separate Bedienfelder erstellt, die jedoch auf eine einzelne Seite passen.

Bildbeschreibung hier eingeben

Bearbeiten Sie 2016-6-3

  1. Zum jetzigen Stand likert als separates Paket erhältlich.
  2. Sie brauchen nicht neu zu gestalten Bibliothek oder beide lösen irutils und reshape
chl
quelle
Die letzte Handlung erinnert mich an Bevölkerungspyramiden. Wir sollten einige echte Daten erhalten, um zu sehen, wie sie "in freier Wildbahn" funktionieren, mit einigen Daten, die nicht so ordentlich sind. Ich gebe zu, sie sind auffällig und hübsch.
Andy W
@ Andy Das ist in der Tat der Fall. Sehen HH::as.pyramidLikert.
Chl
1
+1, Bibliothek (HH) ist definitiv der richtige Weg. Aber etwas ist mit Ihrer vorletzten Handlung in der Reihenfolge zustimmen / nicht zustimmen usw.
Peter Ellis
@PeterEllis Yup, es sieht so aus, als wären die Antwortkategorien in der Tat in der falschen Reihenfolge. (Die Reihenfolge der Etiketten verloren ging , als die Daten tabellarisch zusammen und Tabellennamen folgende lexicographic Reihenfolge angeordnet sind.) Für einen schnellen Hack, können wir einfach ersetzen t(apply(resp, 2, table))mit t(apply(resp, 2, table))[,levels(resp[,1])]. Und dir auch +1!
chl
7

Ich habe begonnen, einen Blog-Beitrag über die Neuerstellung vieler Diagramme in dem von Ihnen erwähnten Beitrag zu schreiben ( Visualisierung der Likert-Artikel-Antwortdaten ) in SPSS damit ich davon ausgehe, dass dies eine gute Motivation für die Fertigstellung ist.

Wie Michelle bemerkt, ist die Tatsache, dass Sie Gruppen haben, eine neue Wendung im Vergleich zu den vorherigen Fragen. Und während Gruppen mithilfe der gestapelten Balkendiagramme berücksichtigt werden können, lassen sie sich IMO viel einfacher in das Punktdiagramm-Beispiel im ursprünglichen Beitrag von chl integrieren. Ich habe den SPSS-Code eingefügt, um dies am Ende des Beitrags zu generieren. Im Wesentlichen bedeutet dies, dass Sie wissen, wie Sie Ihre Daten in das entsprechende Format umformen können, um das Diagramm zu generieren (Anmerkung im Code, um hoffentlich einiges davon zu klären). Hier habe ich eine redundante Codierung (Farbe und Form) verwendet, um Punkte aus den beiden Gruppen zu unterscheiden, und die Punkte halbtransparent gemacht, damit Sie erkennen können, wann sie sich überlappen (eine andere Option wäre, den Punkten auszuweichen, wenn sie sich überlappen).

Abbildung 1: Punktdiagramme nach Gruppe

Warum ist das besser als die gestapelten Balkendiagramme? Die gestapelten Balkendiagramme codieren Informationen in der Länge der Balken. Wenn Sie versuchen, Balkenlängen innerhalb derselben Achsenkategorie oder zwischen Feldern zu vergleichen, wird durch das Stapeln verhindert, dass die Balken einen gemeinsamen Maßstab haben. Als Beispiel habe ich in Abbildung 2 ein Bild bereitgestellt, in dem zwei Balken in einem Diagramm platziert sind, in dem sich ihre Anfangsposition unterscheidet. Welcher Balken ist der breitere Balken (entlang der horizontalen Achse)?

Abbildung 2: Balken ohne gemeinsame Skala

Vergleichen Sie dies mit der Darstellung in Abbildung 3 unten, in der die beiden Balken (mit der gleichen Länge) vom gleichen Anfangspunkt aus dargestellt sind. Ich habe die Aufgabe absichtlich schwierig gemacht, aber Sie sollten in der Lage sein zu erkennen, welche länger ist.

Abbildung 3: Balken mit einer gemeinsamen Skala

Gestapelte Balkendiagramme erfüllen im Wesentlichen die in Abbildung 2 dargestellten Aufgaben. Punktdiagramme können als ähnlicher angesehen werden als in Abbildung 3 dargestellt. Ersetzen Sie den Balken einfach durch einen Punkt am Ende des Balkens.

Ich werde nicht sagen, dass Sie kein bestimmtes Diagramm für die explorative Datenanalyse generieren, aber ich würde vorschlagen, die gestapelten Balkendiagramme zu vermeiden, wenn Sie so viele Kategorien verwenden. Die Punktdiagramme sind auch kein Allheilmittel, aber ich glaube, dass es viel einfacher ist, Panels mit den Punktdiagrammen zu vergleichen als mit den gestapelten Balkendiagrammen. Berücksichtigen Sie auch einige der Ratschläge, die ich in meinem Blog-Beitrag hier für Tabellen gebe, und versuchen Sie, die Diagramme in sinnvolle Kategorien zu unterteilen und sicherzustellen, dass die Elemente, die Sie zusammen betrachten möchten, in den Diagrammen näher beieinander liegen. Während einige der Plotmethoden sich gut auf viele Fragen skalieren lassen (die kategorialen Heatmaps sind ein Beispiel), wird es ohne Sortierung immer noch schwierig sein, bedeutungsvolle Muster zu identifizieren (abgesehen von offensichtlichen Ausreißern).

Ein Hinweis zur Verwendung von SPSS. SPSS kann alle zuvor mit Diagrammen verknüpften Elemente generieren, obwohl häufig bekannt ist, wie Ihre Daten zu gestalten sind (dasselbe gilt für ggplot, aber die Leute haben Pakete entwickelt, um im Wesentlichen die Umgestaltung für Sie durchzuführen). Um zu verstehen, wie die GPL-Sprache von SPSS besser funktioniert, würde ich vorschlagen, Hadley Wickhams Buch über ggplot2 zu lesenin der Verwendung R! Serie. Es enthält die Grammatik, die erforderlich ist, um zu verstehen, wie die GPL von SPSS funktioniert, und ist viel einfacher zu lesen als das mit SPSS gelieferte GPL-Programmierhandbuch! Wenn Sie Fragen zum Generieren bestimmter Diagramme in SPSS haben, ist es am besten, eine Frage für ein Diagramm zu stellen (ich habe hier so genug gesprochen wie es ist!) Blog-Post, der einige der anderen Charts repliziert. Für einen Proof-of-Concept der Wärmekarten oder Fluktuationsdiagramme sehen Sie einen anderen Blogbeitrag von mir, Einige Beispiel-Corrgramme in SPSS .

SPSS-Code zum Generieren von Abbildung 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
quelle
Ein starkes Plus von mir für die höfliche, aber eindringliche Erörterung der Mängel von gestapelten Balkendiagrammen, die im Prinzip leicht zu verstehen, in der Praxis jedoch oft viel weniger leicht zu dekodieren sind.
Nick Cox
5

Na ja, ich habe mir den Code ausgedacht, bevor Sie ihn geklärt haben. Ich hätte warten sollen, aber ich dachte, ich sollte es veröffentlichen, damit jeder, der hierher kommt, diesen Code wiederverwenden kann.

Dummy-Daten zur Visualisierung

# 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 für 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) 

Dies ist im Grunde eine Vorlage zur Visualisierung von Likert-Elementen auf einer Heatmap von Jason Bryons Website.

RJ-
quelle
1
github.com/jbryer/irutils/blob/master/R/likert.R ist die Quelle für die gewünschten gestapelten Balkendiagramme.
RJ
Zur Verdeutlichung möchte ich nicht zwischen Gruppen vergleichen. Nur um die Antworten beider Gruppen auf raffinierte Weise zu präsentieren. Dies ist eine großartige Antwort. Schätze es wirklich. Vielen Dank.
Adam
3

Der Code von @ RJ erzeugt einen Plot wie diesen, der eigentlich eine Tabelle mit schattierten Zellen ist. Es ist ziemlich beschäftigt und ein bisschen schwierig zu entziffern. Eine einfache Tabelle ohne Schattierung ist möglicherweise effektiver (und Sie können die Daten auch in eine aussagekräftigere Reihenfolge bringen).

Bildbeschreibung hier eingeben

Natürlich hängt es davon ab, welche Hauptbotschaft Sie zu kommunizieren versuchen, aber ich denke, dies ist einfacher und ein bisschen einfacher zu verstehen. Es hat auch die Fragen und Antworten in einer (meist!) Logischen Reihenfolge.

    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()

Bildbeschreibung hier eingeben

Ben
quelle
Einverstanden, dass das Diagramm beschäftigt aussieht. Es wäre jedoch nützlich, wenn die Fragen in einer bestimmten Reihenfolge gruppiert wären, z. B. Q1 - 10 nach einer bestimmten Dimension fragen und so weiter. Auf einen Blick, wenn die Trends offensichtlich sind, würden die Farben zeigen.
RJ