A bar graph and its look - should I add titles, where should values go etc

I didn't use r but I have recently decided to use it to plot graphs because of its great capability to do so. 'd like to plot a graph that shows voter turnout in elections. I know little about proper (i.e. correct in regard of their look) graphs so I found in the Internet one that looks (to me) fine. Here's this graph:

Here's my graph. You can see that it is far from being good (screenshot is from R-Fiddle and I think the graph may look different when compiled from desktop R).

What should I do to make it better (e.g. readible)? Specifically, do I need titles for x and y axis? Does it look better if the values are on top of bars (like in my graph) or within bar (the graph found in the Internet)?

My code is:

# Load packages
library(ggplot2)
library(scales)
# Create dataset
dat <- data.frame(years = c("1991", "1993", "1997", "2001", "2005", "2007", "2011", "2015"),
freq = c(43.20, 52.13, 47.93, 46.29, 40.57, 53.88, 48.92, 50.92))
# Create graph bar
ggplot(dat, aes(years, freq)) +   geom_bar(stat = "identity", width=0.55)
+ geom_text(aes(label=comma(freq), y=freq+1.1))
+ scale_y_continuous(breaks = seq(0,50,10)) +  theme_classic()


EDIT:

I tried to incorporate as many suggestions in comments and answers as I could. I've come up with the following:

What do you think of it?

• Tufte'rize it: ggplot(dat, aes(years, freq)) + geom_bar(stat = "identity", width=0.55, fill="grey")+ scale_y_continuous(breaks = seq(0,50,10)) + geom_hline(yintercept= seq(0,50,10), col="white") + theme_classic(base_size = 16) + labs(x=NULL, y=NULL) + ggtitle("freq per year"). Apr 25, 2016 at 19:47
• Thanks, now the graph looks better. Based on your comment I used ggplot(dat, aes(years, freq)) + geom_bar(stat = "identity", width=0.55, fill="grey64")+ scale_y_continuous(breaks = seq(0,65,10)) + geom_hline(yintercept= seq(0,65,10), col="white") + theme_classic(base_size = 16) + labs(x=NULL, y=NULL) + geom_text(aes(label=format(freq,decimal.mark = ","), y=freq+2.2), size=3). Let me know what you think about it. To me your answer deserves to be given as an answer. Apr 26, 2016 at 9:46
• Bar graph is not a great way to display time series, ideally you should be displaying it as a line chart. Apr 27, 2016 at 3:52
• Issues with your line graph: there are 2 years between some of your observations, 4 years between others, yet all are equally spaced on your x-axis. Better to express years as numeric so spacing represents reality better. There is no label on either axis. Showing values along with points on the graph is redundant; better to use a table if you need that level of detail. If you do want the y-axis to extend down to 0, don't have it go below zero as it presently does (if I interpret the unlabeled axes correctly). I personally prefer a white background. Above all, study what Tufte recommends.
– EdM
Apr 28, 2016 at 13:59
• I see no harm in showing numbers too. People often want to read numbers off graphs just as they (should) want to read numbers off tables. Also, offering graph PLUS table in a paper would often be rejected by reviewers as too much space devoted to the same information, so hybridising graph and table is perfectly defensible. Apr 28, 2016 at 17:27

I agree with EdM's point that "bar plots simply have too much ink for the information conveyed." Here's a ggplot2 version of his answer:

library(ggplot2)

df <- data.frame(years=c(1991, 1993, 1997, 2001, 2005, 2007, 2011, 2015),
freq=c(43.20, 52.13, 47.93, 46.29, 40.57, 53.88, 48.92, 50.92))

p <- (ggplot(df, aes(x=years, y=freq)) +
geom_line(size=1.25, color="#999999") + geom_point(size=3.5, color="black") +
theme_bw() +
theme(panel.border=element_blank(), panel.grid.minor=element_blank(),
axis.title.y=element_text(vjust=1.25)) +
scale_x_continuous("", breaks=seq(1990, 2015, 5), minor_breaks=NULL) +
scale_y_continuous("percentage turnout", limits=c(36, 59),
breaks=seq(40, 55, 5), minor_breaks=NULL))
p
ggsave("percentage_turnout_over_time.png", p, width=10, height=8)


Which produces this:

Edit: here's a version with numbers on the graph:

p <- (ggplot(df, aes(x=years, y=freq, label=freq)) +
geom_line(size=1.25, color="#999999") + geom_point(size=3.5, color="black") +
geom_text(vjust=c(2, -1, -1.5*sign(diff(diff(df$freq))) + 0.5)) + theme_bw() + theme(panel.border=element_blank(), panel.grid.minor=element_blank(), axis.title.y=element_text(vjust=1.25)) + scale_x_continuous("", breaks=seq(1990, 2015, 5), minor_breaks=NULL) + scale_y_continuous("percentage turnout", limits=c(36, 59), breaks=seq(40, 55, 5), minor_breaks=NULL)) p ggsave("percentage_turnout_over_time_with_text.png", p, width=10, height=8)  Nick Cox's comment under the original post is convincing: I see no harm in showing numbers too. People often want to read numbers off graphs just as they (should) want to read numbers off tables. Also, offering graph PLUS table in a paper would often be rejected by reviewers as too much space devoted to the same information, so hybridising graph and table is perfectly defensible. • This looks very interesting, however, the reader doesn't know when each election took place so the dates should be probably added to convey this information. What do you think about this idea? And what about values about frequency? I personally think that they are important because they give the reader the exact value and they do not look for this in other places (tables, article text). Apr 28, 2016 at 9:21 • I think including the exact frequency at two decimal places is overkill. Does it matter whether it was 51.27 versus 52.09? Apr 28, 2016 at 9:27 • What do you think about including frequency at all? If you have a look at my edited question, you will see that I've chosen one decimal place. And what about including the dates of the elections? Apr 28, 2016 at 10:02 • I wouldn't include it at all. If the reader really needs to know the exact percentages, use a table. As for the years, you could adjust the breaks in scale_y_continuous, up to you. Apr 28, 2016 at 11:26 Maybe "Tufterize" your plot: ggplot(dat, aes(years, freq)) + geom_bar(stat = "identity", width=0.55, fill="grey")+ scale_y_continuous(breaks = seq(0,50,10)) + geom_hline(yintercept= seq(0,50,10), col="white") + theme_classic(base_size = 16) + theme(axis.ticks=element_blank()) + labs(x=NULL, y=NULL) + ggtitle("freq per year")  or ggplot(dat, aes(years, freq)) + geom_bar(stat = "identity", width=0.55, fill="grey64")+ scale_y_continuous(breaks = seq(0,65,10)) + theme_classic(base_size = 18) + theme(axis.text.y=element_blank(), axis.ticks=element_blank(), axis.line.y=element_blank()) + labs(x=NULL, y=NULL) + geom_text(aes(label=format(freq,decimal.mark = ",")), vjust=-.3, size = 4)  This is not a good application of a bar plot. Tufte would not be pleased, even with the allegedly "Tufterized" bar plots recommended in another answer. Bar plots simply have too much ink used for the information conveyed. See Tufte's website and books, and if possible attend one of his seminars on how to display data effectively. Displaying these data requires no more than a line plot of turnout versus year. The line plot, with equal spacing of actual years along the x-axis, will also remove the implication contained in the bar plot that the observations are equally spaced in time. Here's an example that emphasizes the changes over the range of observed values: I find that the connecting lines between the points help to keep track of the time relationships, even though there are only 8, unequally spaced, observations. Nick Cox rightly noted in a comment that this might tend to suggest linear changes in between dates. The lines are gray, with black dots at the observations, in an attempt to de-emphasize such a suggestion. If you are more interested in changes with respect to a baseline of 0% turnout you could adjust the y-axis limits accordingly. But have you ever had anything close to 0% turnout for an election? Also, figures are not good places to show results down to 2 decimal places. For that, use a table. Code is all from R base graphics. You can probably do something more elegant with ggplot but I have little experience with that. First, change your "years" into numeric from text: dat$years <- as.numeric(as.character(dat\$years))


Then for the plot:

plot (freq~years,data=dat,xlim=c(1990,2016),xlab="Year",ylab="Percentage Turnout",type="l",axes=FALSE,col="gray")
points (freq~years,data=dat,pch=19)
axis(1,at=seq(1990,2020,10))
axis(2,at=seq(42,54,6))


Standard R graphics put a potentially distracting box around the entire plot, which is omitted here by the axes=FALSE specification, in the plot command that draws the gray lines. The points command then places the points. The separate specifications with axis allows control over where the tick marks are placed and labeled; R may tend to over-label a bit to some people's taste.

• Thanks for this. I tried to incorporate your suggestions. Could you have a look at my edited question? Apr 28, 2016 at 8:05