HH (version 2.2-17)

likert: Diverging stacked barcharts for Likert, semantic differential, and rating scale data.

Description

Constructs and plots diverging stacked barcharts for Likert, semantic differential, and rating scale data.

Usage

## S3 method for class 'likert':
plot(x, ...)
## S3 method for class 'default':
plot.likert(x,
            positive.order=FALSE,
            ylab=names(dimnames(x)[1]),
            xlab="Frequency",
            main=xName,
            BrewerPaletteName="RdBu",
            ## These are the diverging palettes in RColorBrewer
            ## c("RdBu", "BrBG", "PiYG", "PRGn", "PuOr",
            ## "RdGy", "RdYlBu", "RdYlGn", "Spectral"),
            reference.line.col="gray65",
            middle.col="gray90",  ## "#F7F7F7" is the RColorBrewer default for middle.col in the RdBu scheme
            col.strip.background="gray97",
            col=mybrewer(x, middle.col),
            as.percent=FALSE,
            ...,
            xName=deparse(substitute(x)),
            rightAxisLabels=rowSums(abs(x)),
            rightAxis=!missing(rightAxisLabels),
            ylab.right=if (rightAxis) "Row Totals" else NULL,
            panel=panel.likert)

## S3 method for class 'array':
plot.likert(x,
            condlevelsName=paste(names(dimnames(x))[-(1:2)], collapse="."),
            xName=deparse(substitute(x)),
            main=paste("layers of", xName, "by", condlevelsName),
            layout=c(1, length(dim(x))-2),
            positive.order=FALSE,
            strip.left=TRUE,
            strip.left.values=rev(names(tt.pl)), ## constructed from dimnames(x)[-(1:2)]
            strip.left.par=list(cex=1, lines=1),
            ...)

## S3 method for class 'list':
plot.likert(x,  ## named list of matrices or 2D tables, ftables, structables
            condlevelsName="ListNames",
            xName=deparse(substitute(x)),
            main=paste("List items of", xName, "by", condlevelsName),
            layout=c(1, length(x)),
            positive.order=FALSE,
            strip.left=TRUE,
            strip.left.values=rev(names(x)),
            strip.left.par=list(cex=1, lines=1),
            main.middle=.5,
            ...,
            rightAxisLabels=sapply(x, rowSums, simplify=FALSE),
            rightAxis=!missing(rightAxisLabels),
            resize.height.tuning=-.5)

## S3 method for class 'table':
plot.likert(x, ..., xName=deparse(substitute(x)))
## S3 method for class 'ftable':
plot.likert(x, ..., xName=deparse(substitute(x)))
## S3 method for class 'structable':
plot.likert(x, ..., xName=deparse(substitute(x)))## S3 method for class 'data.frame':
plot.likert(x, ..., xName=deparse(substitute(x)))

panel.likert(..., rightAxisLabels, rightAxis)

Arguments

Value

A "trellis" object containing the plot. The plot will be automatically displayed unless the result is assigned to an object.

Details

The counts (or percentages) of respondents on each row who agree with the statement are shown to the right of the zero line; the counts (or percentages) who disagree are shown to the left. The counts (or percentages) for respondents who neither agree nor disagree are split down the middle and are shown in a neutral color. The neutral category is omitted when the scale has an even number of choices. The categories within each sector are ordered by the counts (or percentages) who agree (by default). It is difficult to compare lengths without a common baseline. In this situation, we are primarily interested in the total count (or percent) to the right or left of the zero line; the breakdown into strongly or not is of lesser interest so that the primary comparisons do have a common baseline of zero. Diverging stacked barcharts are also called "two-directional stacked barcharts". Some authors use the term "floating barcharts" for vertical diverging stacked barcharts and the term "sliding barcharts" for horizontal diverging stacked barcharts. All items in a list of named two-dimensional objects must have the same number of columns. If the items have different column names, the column names of the last item in the list will be used in the key. If the dimnames of the matrices are named, the names will be used in the plot. It is possible to produce a likert plot with a list of objects with different numbers of columns, but not with the plot.likert.list method. These must be done manually by using the ResizeEtc function on each of the individual likert plots. The difficulty is that the legend is based on the last item in the list and will have the wrong number of values for some of the panels. A single data.frame x will be plotted as data.matrix(x); therefore factor columns will be converted to integers and character columns will become NA and will be plotted as if they had value 0. A data.frame with only numeric columns will work in a named list. A data.frame with factors or characters won't work in a named list. ftable and structable arguments x will be plotted as as.table(x). This changes the display sequence. Therefore the user will probably want to use aperm on the ftable or structable before using plot.likert.

References

Naomi Robbins , "Visualizing Data: Challenges to Presentation of Quality Graphics---and Solutions", Amstat News, September 2011, 28--30. Naomi B. Robbins and Richard M. Heiberger (2011). Plotting Likert and Other Rating Scales. In JSM Proceedings, Section on Survey Research Methods. Alexandria, VA: American Statistical Association. Luo, Amy and Tim Keyes (2005). "Second Set of Results in from the Career Track Member Survey," Amstat News. Arlington, VA: American Statistical Association.

See Also

barchart, ResizeEtc, as.likert, as.matrix.listOfNamedMatrices

Examples

Run this code
require(grid)
require(lattice)
require(latticeExtra)
require(HH)

data(ProfChal)  ## List of matrices.  See below for discussion of the dataset.
##
## ProfChal[[2]]
##
## The as.matrix.listOfNamedMatrices display is easier to read when the
## rownames are very long, as in this example.  Because the default
## print of the matrix repeats the rownames several times, with only a
## few columns of the data shown in each repetition, the actual matrix
## structure of the data values is obscured.
##
as.matrix.listOfNamedMatrices(ProfChal[2])

## Count plot
plot.likert(ProfChal[[2]],
            main='Is your job professionally challenging?',
            sub="This plot looks better in a 9in x 4in window.")

## Percent plot calculated automatically from Count data
plot.likert(ProfChal[[2]], as.percent=TRUE,
            main='Is your job professionally challenging?',
            sub="This plot looks better in a 9in x 4in window.")

## Examples with higher-dimensional arrays.
tmp2 <- array(1:12, dim=c(3,4), dimnames=list(B=LETTERS[3:5], C=letters[6:9]))
tmp3 <- array(1:24, dim=c(2,3,4),
              dimnames=list(A=letters[1:2], B=LETTERS[3:5], C=letters[6:9]))
tmp4 <- array(1:120, dim=5:2,
              dimnames=list(
                W=LETTERS[10:14],
                X=letters[6:9],
                Y=LETTERS[3:5],
                Z=letters[1:2]))

## positive.order=FALSE is the default.  With arrays
## the rownames within each item of an array are identical.
plot.likert(tmp2)
## plot.likert(tmp3)
plot.likert(tmp3, layout=c(1,4), auto.key=list(columns=3))
plot.likert(tmp4, layout=c(3,2), auto.key=list(columns=4))
plot.likert(tmp4, layout=c(3,2),
            auto.key=list(columns=4),
            rightAxis=TRUE, between=list(x=c(4,4)))
plot.likert(tmp4, layout=c(3,2),
            auto.key=list(columns=4, border=TRUE, padding.text=4, height=.5))
plot.likert(tmp4, layout=c(3,2), auto.key=list(columns=4), strip.left=FALSE)
plot.likert(tmp4, layout=c(3,2), strip.left=FALSE,
            auto.key=list(columns=4, border=TRUE, padding.text=4, height=.5))


## plot.likert interprets vectors as single-row matrices.
## http://survey.cvent.com/blog/customer-insights-2/box-scores-are-not-just-for-baseball
Responses <- c(15, 13, 12, 25, 35)
names(Responses) <- c("Strongly Disagree", "Disagree", "No Opinion",
                      "Agree", "Strongly Agree")
plot.likert(Responses, main="Retail-R-Us offers the best everyday prices.",
            sub="This plot looks better in a 9in x 2.6in window.")

## plot.likert will plot a single-column matrix as if it were entirely
## "No Opinion".
plot.likert(as.matrix(Responses),
            main="This is silly, but it doesn't give an error")


## Vertical diverging stacked barcharts are possible.
## They usually do not look good and are not recommended.
plot.likert(Responses, horizontal=FALSE,
            aspect=1.5,
            main="Retail-R-Us offers the best everyday prices.")
##
## reverse=TRUE  is needed for a single-column key with
## horizontal=FALSE and with space="right"
plot.likert(Responses, horizontal=FALSE,
            aspect=1.5,
            main="Retail-R-Us offers the best everyday prices.",
            auto.key=list(space="right", columns=1,
                          reverse=TRUE, padding.text=2),
            sub="This plot looks better in a 4in x 3in window.")
plot.likert(Responses, horizontal=FALSE,
            aspect=1.5,
            main="Retail-R-Us offers the best everyday prices.",
            auto.key=list(size=3, columns=5, between=.5,
	                  between.columns=2, just=.5),
            sub="This plot looks better in a 7in x 3in window.")

plot.likert(ProfChal[[2]],
            horizontal=FALSE,
            scales=list(x=list(rot=90)),
            auto.key=list(columns=1, space="right", reverse=TRUE),
            main="Vertical diverging stacked barcharts are possible.
They usually do not look good and are not recommended.",
            sub="This plot looks less bad in a 8.5in x 7in window.")


## Diverging stacked bar charts have many uses in addition to plotting
## rating scales.  The AudiencePercent example, provided by Naomi
## B. Robbins, illustrates the age distribution of users of different
## brands of a product.  The original source for this example used
## multiple pie charts.

data(AudiencePercent)
AudiencePercent

plot.likert(AudiencePercent,
            auto.key=list(columns=4, between=1, between.columns=2),
            xlab="Percentage of audience younger than 35 (left of zero) and older than 35 (right of zero)",
            main="Target Audience",
            sub="This plot looks better in a 7in x 3.5in window.")

## Color palettes from RColorBrewer can be named.
##
## Since age is always positive and increases in a single direction,
## this example uses colors from a sequential palette for the age
## groups.  In this example we do not use a diverging palette that is
## appropriate when groups are defined by a characteristic, such as
## strength of agreement or disagreement, that can increase in two directions.
## Initially we use the "Blues" palette in the BrewerPaletteName argument.
plot.likert(AudiencePercent,
            auto.key=list(columns=4, between=1, between.columns=2),
            xlab="Percentage of audience younger than 35 (left of zero) and older than 35 (right of zero)",
            main="Target Audience",
            BrewerPaletteName="Blues",
            sub="This plot looks better in a 7in x 3.5in window.")

## The really light colors in the previous example are too light.
## Therefore we use the col argument directly.  We chose to use an
## intermediate set of Blue colors selected from the "Blues" palette.
plot.likert(AudiencePercent,
            auto.key=list(columns=4, between=1, between.columns=2),
            xlab="Percentage of audience younger than 35 (left of zero) and older than 35 (right of zero)",
            main="Target Audience",
            col=brewer.pal(9, "Blues")[4:7],
            sub="This plot looks better in a 7in x 3.5in window.")


## Professional Challenges example.
##
## The data for this example is a list of related likert scales, with
## each item in the list consisting of differently named rows.  The data
## is from a questionnaire analyzed in a recent Amstat News article.
## The study population was partitioned in several ways.  Data from one
## of the partitions (Employment sector) was used in the first example
## in this help file.  The examples here show various options for
## displaying all partitions on the same plot.
##
data(ProfChal)
## ProfChal
as.matrix.listOfNamedMatrices(ProfChal, minlength=6)

## 1. Plot counts with rows in each panel sorted by positive counts.
##
## Strip labels on left (the default for plot.likert).
## positive.order=TRUE is the recommended (but not default) setting for
## lists because the rownames within each item of the list are usually
## different.
##
ProfChalCountsPlot <-
plot.likert(ProfChal, box.width=unit(.4,"cm"),
            strip.left.par=list(cex=.7, lines=5),
            main='Is your job professionally challenging?',
            main.middle=.65,
            xlab="Counts",
            positive.order=TRUE,
            rightAxis=TRUE,  ## display row count totals
            sub="This plot looks better in a 10in x 7in window.")
ProfChalCountsPlot

## Strip labels on top (the default for most lattice plots).
## Change line spacing
names(ProfChal)[6] <- "Attitude toward Professional Recognition"
plot.likert(ProfChal, box.width=unit(.3,"cm"),
            strip.left=FALSE,
            main='Is your job professionally challenging?',
            main.middle=.65,
            xlab="Counts",
            positive.order=TRUE)
## Restore original line spacing
names(ProfChal)[6] <- "Attitude
toward
Professional
Recognition"

## 2. Plot percents with rows in each panel sorted by positive percents.
##    This is a different sequence than the counts.  Row Counts are
##    displayed on the right axis.
ProfChalPctPlot <-
plot.likert(ProfChal,
            as.percent=TRUE,    ## implies display Row Count Totals
            box.width=unit(.4,"cm"),
            strip.left.par=list(cex=.7, lines=5),
            main='Is your job professionally challenging?',
            main.middle=.65,
            positive.order=TRUE,
            sub="This plot looks better in a 10in x 7in window.")
ProfChalPctPlot



## 3. Putting both percents and counts on the same plot requires
##    coordination The easiest way is to order the original tables of
##    counts by the percents.

## Manually construct RowTotals and Tables of Row percents
rowTotals <- sapply(ProfChal, rowSums, simplify=FALSE)
rowTotals
ProfChalPct <- sapply(ProfChal, function(x) x / rowSums(x) * 100,
                      simplify=FALSE)
as.matrix.listOfNamedMatrices(ProfChalPct, minlength=6)

## Find sort order for each matrix in the list
PctOrders <- sapply(ProfChalPct,
                    function(x) attr(as.likert(x), "positive.order"),
                    simplify=FALSE)
PctOrders

## Original data on Count scale, but ordered as for Percents
ProfChalOrdered <- mapply(FUN=function(L,O) L[order(O),,drop=FALSE],
                          ProfChal, PctOrders,
                          SIMPLIFY=FALSE)
as.matrix.listOfNamedMatrices(ProfChalOrdered, minlength=6)

## Counts plot in Pct order
CountsPlot <-
plot.likert(ProfChalOrdered,
            box.width=unit(.4,"cm"),
            strip.left.par=list(cex=.7, lines=5),
            main='Is your job professionally challenging?',
            main.middle=.65,
            xlab="Counts", ylab.right="Row Count Totals",
            rightAxis=TRUE,
            sub="This plot looks better in a 10in x 7in window.")
CountsPlot

## Pct plot in Pct order
PctPlot <-
plot.likert(ProfChalOrdered,
            as.percent="noRightAxis", ## suppress right axis
            box.width=unit(.4,"cm"),
            strip.left.par=list(cex=.7, lines=5),
            main='Is your job professionally challenging?',
            main.middle=.65,
            xlab="Percent",
            sub="This plot looks better in a 10in x 7in window.")
PctPlot

## Remove main title, left axis tick labels, left strip, bottom legend
## from plot and keep the vertical spacing allocated to those items.
CountsPlotRevised <- emptyMainLeftAxisLeftStripBottomLegend(CountsPlot)
CountsPlotRevised

## Manual stretching of the graph window may be needed to get the
## alignment right.  For the standard 7x7 window, this aligns well
## (using either the pdf device or the windows device).
##
PctPlotUpdate <-
update(PctPlot,
       sub="The 'position' arguments are set for a 7in x 7in window")
print(PctPlotUpdate, position=c(0,0,.83,1), more=TRUE)
print(CountsPlotRevised, position=c(.76,0,1,1), more=FALSE)

## This graph looks better in a wider window.
## Manual stretching of graph window may be needed to get the alignment right.
## The x position values (.8 and .75) are by trial and error.
## They depend on the current configuration of the plotting window.
print(PctPlot, position=c(0,0,.8,1), more=TRUE)
print(CountsPlotRevised, position=c(.75,0,1,1), more=FALSE)

## The ProfChal data is done again with explicit use of ResizeEtc
## in ?HH:::ResizeEtc

Run the code above in your browser using DataLab