Introduction

Edward Tufte’s famous book “The Visual Display of Quantitative Information” contains a section in which he redesigns the classic bar chart in a way that leaves exactly the ink necessary to convey the desired information. In this post, I’ll show how to replicate his visually-pleasing bar chart in Julia using the Gadfly graphing package.

Here’s what we’ll make.

For reference, Tufte’s redesign is shown below.

Bar charts in Julia using Gadfly

Gadfly is an awesome Julia package that I have been using recently to produce the majority of my scientific visualizations. It is based on Leland Wilkinson’s book The Grammar of Graphics and Hadley Wickhams’s ggplot2 for the R programming language. By default it produces some pretty nice bar charts, but also provides many opportunities for customization.

# Default 

using Gadfly

function plot_default(title, data)
    p = plot(x=1:5, y=data, 
        Geom.bar, 
        Guide.title(title), 
    )
    draw(SVG("default.svg", 9cm, 9cm), p)
end

plot_default("Rate the T.A.'s overall teaching effectiveness", [0, 1, 1, 7, 6])

Customizing Gadfly using Themes

Gadfly allows customization of a number of its components using Theme objects. We’ll define a Theme that modifies the bar color, background color, spacing between bars, and typefaces, and also eliminates the plot’s grid lines. More about customization using Themes can be found in the Gadfly Theme Documentation.

# Add Theme

using Gadfly

tufte_bar = Theme(
    default_color=colorant"lightgray",
    background_color=colorant"white", 
    bar_spacing=25pt,
    grid_line_width=0pt, 
    minor_label_font="Gill Sans",
    major_label_font="Gill Sans", # could be changed to Bembo if you prefer Tufte's serif typeface
)

function plot_theme(title, data)
    p = plot(x=1:5, y=data, 
        Geom.bar, 
        Guide.title(title), 
        tufte_bar
    )
    draw(SVG("theme.svg", 9cm, 9cm), p)
end

plot_theme("Rate the T.A.'s overall teaching effectiveness", [0, 1, 1, 7, 6])

Customizing axes using Guides

Using Themes got us a lot of the way there, but our bar chart is still cluttered by a lot of unnecessary axis elements. We now use Gadfly’s Guides and Coords to remove some of the default axis elements. Here is the Gadfly Guides Documentation and the Gadfly Coords Documentation.

# Customize Guides

using Gadfly

tufte_bar = Theme(
    default_color=colorant"lightgray",
    background_color=colorant"white", 
    bar_spacing=25pt,
    grid_line_width=0pt, 
    minor_label_font="Gill Sans",
    major_label_font="Gill Sans", # could be changed to Bembo if you prefer Tufte's serif typeface
)

function plot_guides(title, data)
    p = plot(x=1:5, y=data, 
        Geom.bar, 
        Guide.title(title), 
        Guide.xlabel(""), 
        Guide.ylabel(""), 
        Guide.xticks(ticks=collect(1:5)),
        Guide.yticks(ticks=nothing),
        Coord.cartesian(xmin=0, xmax=6, ymin=0, ymax=10),
        tufte_bar
    )
    draw(SVG("guides.svg", 9cm, 9cm), p)
end

plot_guides("Rate the T.A.'s overall teaching effectiveness", [0, 1, 1, 7, 6])

Adding a baseline and grid lines using annotations

Finally, we would like to include the baseline and grid lines from Tufte’s bar chart. To do this, we will use Guide.annotation to place annotations consisting of a Compose graphic. Compose is a vector graphics system for Julia, that forms the basis of Gadfly. Guide.annotation can be used to place arbitrary Compose elements into Gadfly plots. Here is the Compose Package Documentation along with the Guide.annotation Documentation.

# Add Annotations 

using Gadfly
using Compose

tufte_bar = Theme(
    default_color=colorant"lightgray",
    background_color=colorant"white", 
    bar_spacing=25pt,
    grid_line_width=0pt, 
    minor_label_font="Gill Sans",
    major_label_font="Gill Sans", # could be changed to Bembo if you prefer Tufte's serif typeface
)

tick_width = 1pt
baseline = compose(context(), line([(0.5, 0), (5.5, 0)]), fill(nothing), stroke(colorant"lightgray"), linewidth(2pt))
line1 = compose(context(), line([(0, 2), (6, 2)]), fill(nothing), stroke(colorant"white"), linewidth(tick_width))
line2 = compose(context(), line([(0, 4), (6, 4)]), fill(nothing), stroke(colorant"white"), linewidth(tick_width))
line3 = compose(context(), line([(0, 6), (6, 6)]), fill(nothing), stroke(colorant"white"), linewidth(tick_width))
line4 = compose(context(), line([(0, 8), (6, 8)]), fill(nothing), stroke(colorant"white"), linewidth(tick_width))

function plot_annotations(title, data)
    p = plot(x=1:5, y=data, 
        Geom.bar, 
        Guide.title(title), 
        Guide.xlabel(""), 
        Guide.ylabel(""), 
        Guide.xticks(ticks=collect(1:5)),
        Guide.yticks(ticks=nothing),
        Coord.cartesian(xmin=0, xmax=6, ymin=0, ymax=10),
        Guide.annotation(baseline),
        Guide.annotation(line1),
        Guide.annotation(line2),
        Guide.annotation(line3),
        Guide.annotation(line4),
        tufte_bar
    )
    draw(SVG("annotations.svg", 9cm, 9cm), p)
end

plot_annotations("Rate the T.A.'s overall teaching effectiveness", [0, 1, 1, 7, 6])

There we go!

Visualizing my TA ratings

We can now use what we created to visualize a data set. Here are the student ratings of my teaching as a TA for EE 120: Signals and Systems at UC Berkeley in the Fall 2015 semester.