使用 R 或 SPSS 可视化李克特响应

机器算法验证 r 数据可视化 spss 李克特
2022-01-22 08:43:29

我在 2 组中有 82 名受访者(A 组 43 人,B 组 39 人)完成了 65 个李克特问题的调查,每个问题的范围从 1 到 5(非常同意 - 非常不同意)。因此,我有一个包含 66 列(每个问题 1 列 + 1 表示组分配)和 82 行(每个受访者 1 列)的数据框。

任何人都知道使用 R 或 SPSS 可视化这些数据的好方法。

我需要这样的东西:( 在此处输入图像描述
来自Jason Bryer

但我无法让代码的初始部分工作。或者,我从以前的交叉验证帖子中找到了如何可视化李克特数据的非常好的示例:可视化李克特项目响应数据,但没有关于如何使用 R 或 SPSS 创建这些居中计数图或堆叠条形图的指南或说明。

4个回答

如果您真的想使用包含如此大量项目的堆叠条形图,这里有两种可能的解决方案。

使用irutils

几个月前我遇到了这个包裹。

从Github上的提交 0573195c07 开始,该代码将无法使用grouping=参数。让我们去参加周五的调试会议。

首先从 Github 下载一个压缩版本。您需要破解R/likert.R文件,特别是likertandplot.likert函数。首先,使用 in likertcast()但从reshape不加载包(尽管文件import(reshape)中有说明NAMESPACE)。您可以事先自行加载。其次,获取项目标签的指令不正确,其中 ai在第 175 行附近悬空。这也必须修复,例如通过将所有出现的 替换likert$items[,i]likert$items[,1]然后,您可以按照您在机器上使用的方式安装该软件包。在我的 Mac 上,我做到了

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

然后,使用 R,尝试以下操作:

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)

这应该可以工作,但是由于项目数量众多,视觉渲染会很糟糕。但是,它无需分组(例如,plot(likert(resp)))即可工作。

在此处输入图像描述

因此,我建议将您的数据集减少为较小的项目子集。例如,使用 12 个项目,

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

我得到一个“可读”的堆积条形图。您可能可以在之后处理它们。(这些是对象,但由于可读性问题ggplot2,您将无法将它们排列在单个页面上!)gridExtra::grid.arrange()

在此处输入图像描述

替代解决方案

我想提请您注意另一个包HH,它允许将李克特量表绘制为发散的堆叠条形图。我们可以重用上面的代码,如下所示:

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

但这会使事情变得有点复杂,因为我们需要将频率转换为计数、子集likert生成的对象irutils、分离包等。所以让我们从新的(计数)统计数据重新开始:

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

在此处输入图像描述

要使用分组变量,您需要使用array数值。

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

这将生成两个单独的面板,但它适合单个页面。

在此处输入图像描述

编辑 2016-6-3

  1. 截至目前,likert可作为单独的软件包提供。
  2. 您不需要reshape库或分离irutilsreshape

我开始写一篇关于在 SPSS 中重新创建您提到的帖子(可视化李克特项目响应数据)中的许多图表的博客文章,所以我想这将是完成它的良好动力。

正如 Michelle 所说,与之前的问题相比,您有小组这一事实是一个新的转折。虽然可以使用堆叠条形图考虑组,但 IMO 更容易将它们合并到 chl 原始帖子中的点图示例中。我在文章末尾包含了 SPSS 代码来生成它,本质上它需要知道如何以适当的格式重塑数据以生成所述图(代码中提供的注释希望能清除其中的一些)。在这里,我使用了一些冗余编码(颜色和形状)来区分来自两组的点,并使这些点半透明,这样你就可以知道它们何时重叠(另一种选择是在它们重叠时避开这些点)。

图 1:按组划分的点图

为什么这比堆积条形图更好?堆积条形图以条形的长度对信息进行编码。当您尝试在同一轴类别内或面板之间比较条的长度时,堆叠会阻止条具有共同的比例。例如,我在图 2 中提供了一个图像,其中两个条形图放置在它们的起始位置不同的图中,哪个条形图较宽(沿水平轴)?

图 2:没有通用刻度的条形图

将其与下面的图 3 中的图进行比较,其中两条(相同长度的)条从同一起点绘制。我故意使这项任务变得困难,但你应该能够分辨出哪个更长。

图 3:具有通用比例的条形图

堆叠条形图本质上是在执行图 2 中显示的内容。点图可以认为更类似于图 3 中显示的内容,只需将条形图替换为条形末尾的点即可。

我不会说不要为探索性数据分析生成任何特定图表,但我建议在使用这么多类别时避免使用堆积条形图。点图也不是灵丹妙药,但我相信用点图在面板之间进行比较比使用堆叠条形图要容易得多。考虑一下我在博客文章中为表格提供的一些建议,尝试将图表排序和/或分隔成有意义的类别,并确保您想要串联查看的项目在图表中更靠近。虽然一些绘图方法可以很好地扩展到许多问题(分类热图就是一个例子),但如果不进行排序,仍然很难识别任何有意义的模式(除了明显的异常值)。

关于使用 SPSS 的说明。SPSS 可以生成任何先前链接到图表的内容,尽管它通常需要知道如何塑造数据(ggplot 也是如此,但人们一直在开发包来基本上为您进行重塑)。要了解 SPSS 的 GPL 语言如何更好地工作,我实际上建议阅读 Hadley Wickham 的关于ggplot2的书在使用 R! 系列。它列出了理解 SPSS 的 GPL 如何工作所需的语法,并且比 SPSS 附带的 GPL 编程手册更容易阅读!如果您对在 SPSS 中生成特定图表有任何疑问,最好对一张图表提出一个问题(我在这里已经谈得够多了!)我会用一个链接更新这个答案,但如果我有时间制作我的复制其他一些图表的博客文章。有关热图或波动图的概念证明,您可以查看我的另一篇博客文章,SPSS 中的一些示例 Corrgrams

用于生成图 1 的 SPSS 代码

****************************************.
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.
****************************************.

哦,好吧,在你澄清之前我想出了代码。应该等待,但我想我应该把它贴出来,这样来这里的任何人都可以重用这段代码。

用于可视化的虚拟数据

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

热图代码

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

这基本上是在 Jason Bryon 网站的热图中可视化李克特项目的模板。

@RJ 的代码会生成这样的图,它实际上是一个带有阴影单元格的表格。它相当忙,而且有点难以破译。没有阴影的普通表格可能更有效(您也可以将数据按更有意义的顺序排列)。

在此处输入图像描述

当然,这取决于您要传达的主要信息,但我认为这更简单,也更容易理解。它还以(大部分!)逻辑顺序提供问题和响应。

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

在此处输入图像描述