Wizualizacja odpowiedzi Likerta przy użyciu R lub SPSS

19

Mam 82 respondentów w 2 grupach (43 w grupie A i 39 w grupie B), którzy wypełnili ankietę obejmującą 65 pytań Likerta w zakresie od 1 do 5 (zdecydowanie się zgadzam - zdecydowanie się nie zgadzam). Mam zatem ramkę danych z 66 kolumnami (1 dla każdego pytania + 1 wskazująca przydział grupy) i 82 wierszami (1 dla każdego respondenta).

Używając R lub SPSS, ktoś zna dobry sposób na wizualizację tych danych.

Potrzebuję czegoś takiego: wprowadź opis zdjęcia tutaj
(od Jasona Bryera )

Ale nie mogę uruchomić początkowej sekcji kodu. Alternatywnie znalazłem naprawdę dobre przykłady wizualizacji danych Likerta z poprzedniego postu Zweryfikowany krzyżowo: Wizualizacja danych odpowiedzi na pytanie Likerta, ale nie ma przewodników ani instrukcji, jak tworzyć te wyśrodkowane wykresy zliczania lub skumulowane wykresy słupkowe za pomocą R lub SPSS.

Adam
źródło
1
Cześć Adam, aby wyjaśnić dalej, czy chciałeś użyć wizualizacji, aby pokazać różnice między grupami? Jeśli tak, to nie jest to zalecana metoda.
Michelle
Pakiet Jasona Bryera nie działał dla mnie, ale myślę, że go zaktualizował i teraz działa pięknie. Dodałem również żądanie ściągnięcia z dodatkową funkcją do przechowywania nazw kolumn jako atrybutów i grup. Korzystając z tego, jestem w stanie łatwo wyobrazić sobie 45 pytań kwestionariusza Likerta podzielonych na grupy, a nawet podzielić na inną zmienną, jeśli zechcę. (Wyprowadzam za pomocą knitr, więc kończy się tyle samo wątków na stronie, a nie jednej gigantycznej). Zrobiłem szczegółowy opis tutaj: reganmian.net/blog/2013/10/02/…
Stian Håklev,
Po prostu FYI, dla tych z was, którzy czytają te odpowiedzi w przyszłości, wygląda na to, że niektóre funkcje i funkcje irutils dotyczące danych Likerta zostały przeniesione do pakietu Likert R ( patrz CRAN tutaj ).
firefly2442,
Link bryer.org/2011/visualizing-likert-items wydaje się być uszkodzony. Mile widziane korekty lub wymiany.
Nick Cox
1
Tego rodzaju pytanie - z silnym naciskiem na konkretny kod - jest mniej mile widziane w 2018 r. Niż w 2012 r. Niezależnie od tego niektóre odsyłacze dla wszystkich zainteresowanych są stats.stackexchange.com/questions/56322/ … I stats.stackexchange.com/questions/148554/…
Nick Cox

Odpowiedzi:

30

Jeśli naprawdę chcesz używać skumulowanych wykresów słupkowych z tak dużą liczbą przedmiotów, oto dwa możliwe rozwiązania.

Za pomocą irutils

Kilka miesięcy temu natknąłem się na ten pakiet.

Począwszy od zatwierdzenia 0573195c07 w Github , kod nie będzie działał z grouping=argumentem. Chodźmy na piątkową sesję debugowania.

Zacznij od pobrania skompresowanej wersji z Github. Musisz zhakować R/likert.Rplik, w szczególności funkcje likerti plot.likert. Najpierw używana jest funkcja in likert, cast()ale reshapepakiet nigdy nie jest ładowany (chociaż import(reshape)w NAMESPACEpliku znajduje się instrukcja ). Możesz to załadować wcześniej. Po drugie, jest błędny dyspozycja sprowadzić pozycje etykiet, gdzie ijest zwisające wokół linii 175. To musi być stała, a także, na przykład poprzez zastąpienie wszystkich wystąpień likert$items[,i]z likert$items[,1]. Następnie możesz zainstalować pakiet w sposób, w jaki jesteś przyzwyczajony na swoim komputerze. Na moim komputerze Mac tak zrobiłem

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

Następnie, używając R, spróbuj wykonać następujące czynności:

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)

To powinno po prostu działać, ale renderowanie wizualne będzie okropne z powodu dużej liczby elementów. Działa jednak bez grupowania (np. plot(likert(resp))).

wprowadź opis zdjęcia tutaj

Proponuję zatem zredukować zestaw danych do mniejszych podzbiorów pozycji. Np. Używając 12 przedmiotów

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

Dostaję „czytelny” skumulowany wykres słupkowy. Prawdopodobnie możesz je później przetworzyć. (Są to ggplot2obiekty, ale z gridExtra::grid.arrange()powodu problemów z czytelnością nie będziesz w stanie rozmieścić ich na jednej stronie !)

wprowadź opis zdjęcia tutaj

Alternatywne rozwiązanie

Chciałbym zwrócić uwagę na inny pakiet, HH , który pozwala na wykreślanie skal Likerta jako rozbieżnych stosów wykresów słupkowych. Możemy ponownie użyć powyższego kodu, jak pokazano poniżej:

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

ale to trochę skomplikuje, ponieważ musimy przekonwertować częstotliwości na liczby, podzbiór likertwytworzonego obiektu irutils, odłączyć pakiet itp. Zacznijmy więc od świeżych (zliczających) statystyk:

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

wprowadź opis zdjęcia tutaj

Aby użyć zmiennej grupującej, musisz pracować z arraywartościami liczbowymi.

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

Spowoduje to utworzenie dwóch oddzielnych paneli, ale zmieści się na jednej stronie.

wprowadź opis zdjęcia tutaj

Edytuj 2016-6-3

  1. Obecnie likert jest dostępny jako osobny pakiet.
  2. Nie trzeba przekształcać biblioteki ani odłączać zarówno irutils, jak i przekształcać
chl
źródło
Ostatnia fabuła przypomina mi piramidy ludności. Powinniśmy zdobyć prawdziwe dane, aby zobaczyć, jak działają „na wolności”, a niektóre dane nie są tak uporządkowane. Przyznaję, że przyciągają wzrok i są ładne.
Andy W
@ Andy Tak właśnie jest. Zobaczyć HH::as.pyramidLikert.
chl
1
+1, biblioteka (HH) to zdecydowanie najlepsza droga. Ale coś poszło nie tak z twoim ostatnim spiskiem w kolejności wyrażania zgody / niezgody itp.
Peter Ellis
@PeterEllis Tak, wygląda na to, że kategorie odpowiedzi są rzeczywiście w niewłaściwej kolejności. (Kolejność etykiet została utracona podczas zestawiania danych, a nazwy tabel są uporządkowane zgodnie z porządkiem leksykograficznym.) Aby szybko zhakować, możemy po prostu zastąpić t(apply(resp, 2, table))je t(apply(resp, 2, table))[,levels(resp[,1])]. I tobie też +1!
chl
7

Zacząłem pisać post na blogu o odtwarzaniu wielu wykresów w poście, o którym wspomniałeś ( Wizualizowanie danych odpowiedzi na pytania Likerta ) w SPSS, więc przypuszczam, że będzie to dobra motywacja do jego ukończenia.

Jak zauważa Michelle, fakt, że masz grupy, stanowi nowy zwrot w porównaniu z poprzednimi pytaniami. I chociaż grupy można wziąć pod uwagę przy użyciu skumulowanych wykresów słupkowych, IMO są one o wiele łatwiej włączone do przykładowego wykresu punktowego w oryginalnym poście chl. Dodałem kod SPSS, aby wygenerować to na końcu postu, w zasadzie wymaga wiedzy, jak przekształcić dane w odpowiednim formacie, aby wygenerować wspomnianą fabułę (adnotacja podana w kodzie, miejmy nadzieję, że część z tego wyczyści). Tutaj użyłem zbędnego kodowania (koloru i kształtu), aby rozróżnić punkty pochodzące z dwóch grup, i sprawiłem, że punkty są półprzezroczyste, abyś mógł określić, kiedy się pokrywają (inną opcją byłoby unikanie punktów, gdy się pokrywają).

Ryc. 1: Wykresy punktowe według grup

Dlaczego jest to lepsze niż skumulowane wykresy słupkowe? Skumulowane wykresy słupkowe kodują informacje o długości słupków. Podczas próby porównania długości prętów, w obrębie tej samej kategorii osi lub między panelami, układanie w stosy wyklucza, aby pręty miały wspólną skalę. Na przykład podałem obraz na ryc. 2, na którym dwa paski są umieszczone na wykresie, w którym ich początkowe położenie jest inne, który pasek jest szerszy (wzdłuż osi poziomej)?

Rysunek 2: Słupki bez wspólnej skali

Porównaj to z wykresem na rycinie 3 poniżej, na którym dwa pręty (o tej samej długości) są wykreślone z tego samego punktu początkowego. Celowo sprawiłem, że zadanie było trudne, ale powinieneś być w stanie stwierdzić, który z nich jest dłuższy.

Rysunek 3: Słupki ze wspólną skalą

Skumulowane wykresy słupkowe robią zasadniczo to, co pokazano na rysunku 2. Wykresy punktowe można uznać za bardziej podobne do tego, co pokazano na rysunku 3, wystarczy zastąpić pasek kropką na końcu paska.

Nie powiem, że nie generuj żadnego konkretnego wykresu do eksploracyjnej analizy danych, ale sugerowałbym unikanie skumulowanych wykresów słupkowych przy użyciu tak wielu kategorii. Wykresy punktowe też nie są panaceum, ale uważam, że porównywanie paneli z wykresami punktowymi jest znacznie łatwiejsze niż w przypadku skumulowanych wykresów słupkowych. Zastanów się również nad niektórymi radami, które zamieszczam na moim blogu, aby znaleźć tabele, spróbuj uporządkować i / lub rozdzielić wykresy na znaczące kategorie i upewnij się, że elementy, które chcesz obejrzeć w tandemie, są bliżej siebie na wykresach. Podczas gdy niektóre metody kreślenia mogą dobrze skalować się do wielu pytań (przykładowe są mapy jakościowe), bez sortowania nadal trudno będzie zidentyfikować jakiekolwiek znaczące wzorce (poza oczywistymi wartościami odstającymi).

Uwaga na temat używania SPSS. SPSS może generować dowolne z poprzednich linków powiązanych z wykresami, chociaż często wymaga znajomości kształtowania danych (to samo dotyczy ggplot, ale ludzie opracowywali pakiety, aby zasadniczo wykonać dla ciebie przekształcenie). Aby zrozumieć, jak działa lepiej język GPL SPSS, sugerowałbym przeczytanie książki Hadleya Wickhama na ggplot2w Use R! seria. Ustanawia gramatykę niezbędną do zrozumienia działania GPL SPSS i jest znacznie łatwiejszy do odczytania niż podręcznik programowania GPL dostarczany z SPSS! Jeśli masz jakieś pytania dotyczące generowania określonych wykresów w SPSS, najlepiej zadać jedno pytanie dla jednego wykresu (mówiłem już wystarczająco dużo tutaj!) Zaktualizuję tę odpowiedź linkiem, ale jeśli kiedykolwiek zacznę tworzyć post na blogu replikujący niektóre inne wykresy. Aby sprawdzić koncepcję map cieplnych lub wykresów fluktuacji, możesz zobaczyć inny mój post na blogu, na przykład Corrgrams w SPSS .

Kod SPSS użyty do wygenerowania rysunku 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.
źródło
Mocny plus ode mnie za grzeczne, ale wnikliwe omawianie braków ułożonych wykresów słupkowych, które są łatwe do zrozumienia w zasadzie, ale często znacznie trudniejsze do odkodowania w praktyce.
Nick Cox
5

No cóż, wymyśliłem kod, zanim wyjaśniłeś. Powinienem poczekać, ale pomyślałem, że powinienem to opublikować, aby każdy, kto tu przyjdzie, mógł ponownie użyć tego kodu.

Atraktyczne dane do wizualizacji

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

Kod dla mapy cieplnej

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

Jest to w zasadzie szablon wizualizacji przedmiotów Likerta na mapie cieplnej ze strony Jasona Bryona.

RJ-
źródło
1
github.com/jbryer/irutils/blob/master/R/likert.R jest źródłem odpowiednich wykresów słupkowych.
RJ-
Aby to wyjaśnić, nie chcę porównywać między grupami. Aby przedstawić odpowiedzi obu grup w wyrafinowany sposób. To świetna odpowiedź. Naprawdę to doceniam. Dzięki.
Adam
3

Kod @ RJ tworzy taki wykres, który jest tak naprawdę tabelą z zacienionymi komórkami. Jest raczej zajęty i nieco trudny do rozszyfrowania. Zwykły stół bez cieniowania może być bardziej skuteczny (i można również umieścić dane w bardziej znaczącej kolejności).

wprowadź opis zdjęcia tutaj

Oczywiście zależy to od głównego komunikatu, który próbujesz przekazać, ale myślę, że jest to prostsze i nieco łatwiejsze do zrozumienia. Ma także pytania i odpowiedzi w (najczęściej!) Logicznej kolejności.

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

wprowadź opis zdjęcia tutaj

Ben
źródło
Zgodził się, że wykres wygląda na zajęty. Przydałoby się jednak, jeśli pytania są pogrupowane w jakiejś kolejności, np. Q1 - 10 pyta o określony wymiar i tak dalej. Na pierwszy rzut oka, jeśli trendy są oczywiste, kolory by powiedziały.
RJ-