Refs:

ggplot2 implements Wilkinson grammar of graphics that describes and conceptually organizes the features that underlie statistical graphics.

The most important concepts are:

library(ggplot2)
library(gridExtra)  # for presenting plots side by side
FALSE Loading required package: grid
set.seed(101)
d <- diamonds[sample(nrow(diamonds), 100), ]  # read some data for next examples
head(d)
FALSE       carat       cut color clarity depth table price    x    y    z
FALSE 20077  1.00 Very Good     F    VVS2  60.0    62  8553 6.43 6.46 3.87
FALSE 2364   0.90   Premium     J     VS1  62.3    56  3175 6.23 6.19 3.87
FALSE 38279  0.21   Premium     D     VS2  59.1    62   386 3.89 3.86 2.29
FALSE 35474  0.39     Ideal     G    VVS2  61.9    56   902 4.68 4.70 2.90
FALSE 13477  1.15     Ideal     I     VS2  61.7    57  5534 6.72 6.74 4.15
FALSE 16184  1.10   Premium     D     SI1  60.7    55  6468 6.74 6.71 4.08

qplot

qplot is ggplot2’s plot function.

plot1 <- qplot(carat,      price,      data = d)
plot2 <- qplot(log(carat), log(price), data = d)
plot3 <- qplot(carat,      x * y * z,  data = d)  # x*y*z gives the volume
grid.arrange(plot1, plot2, plot3, ncol=3)

qplot automates some aesthetics like how to assign colors and shapes to data:

plot1 <- qplot(carat, price, data = d, colour = color, size= carat)
plot2 <- qplot(carat, price, data = d, shape  = cut)
grid.arrange(plot1, plot2, ncol=2)

Notice that the plots also come with a legend. For each aesthetic attribute there is a scale function mapping the data values to the aesthetic values. Eg, in the left plot, the attribute D was associated with red.

qplot accepts different types of geometric objects, geoms, which will make it produce different types of graphics. The default is geom="point", ie, the scatterplots we’ve seen. Other objects are possible:

plot1 <- qplot(carat, price, data = d, geom=c("point", "smooth")) # default smooth by loess regression
plot2 <- qplot(carat, price, data = d, geom=c("point", "boxplot"))
plot3 <- qplot(carat, price, data = d, geom=c("point", "line")) 
grid.arrange(plot1, plot2, plot3, ncol=3)

Geoms for 1D data:

plot1 <- qplot(carat, data = d, geom="histogram") # continuous values
plot2 <- qplot(color, data = d, geom="bar")       # discrete values
grid.arrange(plot1, plot2, ncol=2)  

plot1 <- qplot(carat, data = d, geom="freqpoly")
plot2 <- qplot(carat, data = d, geom="density") 
grid.arrange(plot1, plot2, ncol=2)  

Smooth geoms

The smooth geom can be used with different regression methods:

plot1 <- qplot(carat, price, data = d, geom=c("point", "smooth"), method="lm")
plot2 <- qplot(carat, price, data = d, geom=c("point", "smooth"), method="lm", formula=y~poly(x,3)) # polynomial regression
grid.arrange(plot1, plot2, ncol=2)

library(splines) # using natural splines
plot3 <- qplot(carat, price, data = d, geom=c("point", "smooth"), method="lm", formula=y~ns(x,5)) 
library(MASS) # for robust regression
plot4 <- qplot(carat, price, data = d, geom=c("point", "smooth"), method="rlm") 
grid.arrange(plot3, plot4, ncol=2)

Jitter and opacity

Sometimes the datapoints are too many and a direct plot is unable to transmit an appropriate perspective of the data. One tool is to jitter the points (add small random noise so that many equal data points are spread around its center) and/or define an amount of opacity, ie, stating how many points there must be at area so that the graphic plots without transparency.

plot1 <- qplot(carat, price, data = diamonds)
plot2 <- qplot(carat, price, data = diamonds, alpha=I(1/50)) # 100 pts for total opacity
grid.arrange(plot1, plot2, ncol=2)

plot1 <- qplot(color, price/carat, data = diamonds)
plot2 <- qplot(color, price/carat, data = diamonds, geom = "jitter")
plot3 <- qplot(color, price/carat, data = diamonds, geom = "jitter", alpha = I(1/10))
grid.arrange(plot1, plot2, plot3, ncol=3)

Histograms and density plots

For the histogram geom we can define the bin size:

plot1 <- qplot(carat, data = diamonds, geom = "histogram", binwidth = 1)
plot2 <- qplot(carat, data = diamonds, geom = "histogram", binwidth = 0.1)
plot3 <- qplot(carat, data = diamonds, geom = "histogram", binwidth = 0.05)
grid.arrange(plot1, plot2, plot3, ncol=3)

For density plots we have the adjust parameter:

plot1 <- qplot(carat, data = diamonds, geom = "density", adjust = 4)
plot2 <- qplot(carat, data = diamonds, geom = "density", adjust = 1)
plot3 <- qplot(carat, data = diamonds, geom = "density", adjust = 0.5)
grid.arrange(plot1, plot2, plot3, ncol=3)

Applying a color aesthetic:

plot1 <- qplot(carat, data = diamonds, geom = "histogram", fill = color)
plot2 <- qplot(carat, data = diamonds, geom = "density", colour = color)
grid.arrange(plot1, plot2, ncol=2)

Time series

head(economics)
FALSE         date   pce    pop psavert uempmed unemploy
FALSE 1 1967-06-30 507.8 198712     9.8     4.5     2944
FALSE 2 1967-07-31 510.9 198911     9.8     4.7     2945
FALSE 3 1967-08-31 516.7 199113     9.0     4.6     2958
FALSE 4 1967-09-30 513.3 199311     9.8     4.9     3143
FALSE 5 1967-10-31 518.5 199498     9.7     4.7     3066
FALSE 6 1967-11-30 526.2 199657     9.4     4.8     3018
year <- function(x) as.POSIXlt(x)$year + 1900
economics$year <- year(economics$date)
head(economics)
FALSE         date   pce    pop psavert uempmed unemploy year
FALSE 1 1967-06-30 507.8 198712     9.8     4.5     2944 1967
FALSE 2 1967-07-31 510.9 198911     9.8     4.7     2945 1967
FALSE 3 1967-08-31 516.7 199113     9.0     4.6     2958 1967
FALSE 4 1967-09-30 513.3 199311     9.8     4.9     3143 1967
FALSE 5 1967-10-31 518.5 199498     9.7     4.7     3066 1967
FALSE 6 1967-11-30 526.2 199657     9.4     4.8     3018 1967

We can use geom line for standard time series:

plot1 <- qplot(date, unemploy / pop, data = economics, geom = "line")
plot2 <- qplot(date, uempmed,        data = economics, geom = "line")
grid.arrange(plot1, plot2, ncol=2)

The geom path that joins points adjacent in time (when scatterplots does not provide enough information):

plot1 <- qplot(unemploy/pop, uempmed, data = economics, geom = c("point", "path"))
plot2 <- qplot(unemploy/pop, uempmed, data = economics, geom = c("point", "path"), color=year)
grid.arrange(plot1, plot2, ncol=2)

Faceting

Faceting splits the data into subsets which are present at different graphs for easier comparisation.

plot1 <- qplot(carat, data = diamonds, facets = color ~ ., geom = "histogram", binwidth = 0.1,  xlim = c(0, 3))
plot2 <- qplot(carat, data = diamonds, facets = cut ~ .,   geom = "density",   binwidth = 0.05, xlim = c(0, 3))
grid.arrange(plot1, plot2, ncol=2)

plot1 <- qplot(displ, hwy, data = mpg, facets = . ~ drv)
plot2 <- qplot(hwy,        data = mpg, facets = drv ~ ., binwidth = 2)
grid.arrange(plot1, plot2, ncol=2)

Other options

Here’s the use of some other parameters for qplot:

qplot(carat, price, data=d, 
      xlim=c(0.5,1.5), ylim=c(0,5e3),
      main="Main Title",
      xlab=expression(beta[1] == 1), ylab="some stuff")

ggplot and Layers

Layers are responsible for creating the objects that we perceive on the plot. A layer is composed of four parts:

qplot does everything inside its parameters. If we like to have more control in the creation of the graphic, we need to use ggplot.

ggplot receives two arguments, the data which must be a data frame and which aesthetics mappings we want. An eg:

p <- ggplot(d, aes(carat, price, colour=cut))
p
## Error: No layers in plot

It gave an error because we still not defined geoms so that there is something to see. To add new layers we should use operator +:

plot1 <- p + layer(geom="point")
plot2 <- p + layer(geom="line")
grid.arrange(plot1, plot2, ncol=2)  

ggplot2 has a series of specialized functions that simplify the use of layer with prefixes geom_XXX or stat_XXX

p <- ggplot(diamonds, aes(x=carat)) 
plot1 <- p + layer(geom="bar", 
                   geom_params = list(fill = "steelblue"), 
                   stat = "bin", 
                   stat_params = list(binwidth = 0.25))
plot2 <- p + geom_histogram(binwidth = 0.25, fill = "steelblue")  # same graph
grid.arrange(plot1, plot2, ncol=2)  

We can add multiple layers:

ggplot(msleep, aes(sleep_rem / sleep_total, awake)) +
  geom_point() + 
  geom_smooth()

Data

iIt’s possible to use the same graph objet with another data using operator %+%:

head(mtcars)
##                    mpg cyl disp  hp drat    wt  qsec vs am gear carb
## Mazda RX4         21.0   6  160 110 3.90 2.620 16.46  0  1    4    4
## Mazda RX4 Wag     21.0   6  160 110 3.90 2.875 17.02  0  1    4    4
## Datsun 710        22.8   4  108  93 3.85 2.320 18.61  1  1    4    1
## Hornet 4 Drive    21.4   6  258 110 3.08 3.215 19.44  1  0    3    1
## Hornet Sportabout 18.7   8  360 175 3.15 3.440 17.02  0  0    3    2
## Valiant           18.1   6  225 105 2.76 3.460 20.22  1  0    3    1
plot1 <- ggplot(mtcars, aes(mpg, wt, colour = cyl)) + geom_point()
mtcars2 <- transform(mtcars, wt = wt ^ 2)
plot2 <- plot1 %+% mtcars2 
grid.arrange(plot1, plot2, ncol=2)  

Aesthetics

Function aes describes which data values are assigned to things we see in the graph. The first two parameters are to define which should be considered as x and y. Other mappings can be stated, like colour like in the previous eg aes(mpg, wt, colour = cyl).

p <- ggplot(mtcars) 
plot1 <- p + aes(wt,   hp)  + geom_point(colour="darkblue")             # set colors
plot2 <- p + aes(disp, mpg) + geom_point() + aes(colour = factor(cyl))  # map colors
grid.arrange(plot1, plot2, ncol=2) 

Grouping

We can split the data into distinct groups, ie, datapoints that share something in common, and then plot them differently. This is done with the group mapping.

plot1 <- ggplot(mtcars, aes(wt, hp, colour=factor(cyl), group=cyl)) + 
           geom_smooth(method="lm")
plot2 <- ggplot(mtcars, aes(wt, hp, colour=factor(cyl), group=cyl)) + 
           geom_boxplot() + theme(legend.position="none") # removes legend
grid.arrange(plot1, plot2, ncol=2)
## Warning in loop_apply(n, do.ply): position_dodge requires constant width:
## output may be incorrect
## Warning in loop_apply(n, do.ply): position_dodge requires non-overlapping x
## intervals

Geoms

These dtermine the rendering of the graph. Each geom has a set of aesthetics it understands and a default statistic.

Some egs:

p <- ggplot(mtcars) + aes(wt, hp) + geom_point()
plot1 <- p + 
         geom_abline(intercept=2, slope=20, colour="red", size=1.5, linetype="dashed") +
         geom_hline(yintercept=seq(100, 150, by=15)) +
         geom_text(label="    A", angle=30)
plot2 <- p + geom_path(size = 0.5, lineend = "round")
plot3 <- p + geom_rect(xmin=2, xmax=3, ymin=200, ymax=300, fill="lightblue")
grid.arrange(plot1, plot2, plot3, ncol=3)

df <- data.frame(x = c(3, 1, 5), y = c(2, 4, 6), label = c("a","b","c"))
p <- ggplot(df, aes(x, y, label = label)) + xlab(NULL) + ylab(NULL)
plot1 <- p + geom_point()   + ggtitle("geom_point")
plot2 <- p + geom_bar(stat="identity") + ggtitle("geom_bar(stat=\"identity\")")
plot3 <- p + geom_line()    + ggtitle("geom_line")
plot4 <- p + geom_area()    + ggtitle("geom_area")
plot5 <- p + geom_path()    + ggtitle("geom_path")
plot6 <- p + geom_text()    + ggtitle("geom_text")
plot7 <- p + geom_tile()    + ggtitle("geom_tile")
plot8 <- p + geom_polygon() + ggtitle("geom_polygon")
grid.arrange(plot1, plot2, plot3, plot4, plot5, plot6, plot7, plot8, nrow=2, ncol=4)

Positions

Position adjustments modify the position of elements within a layer.

p <- ggplot(data = diamonds) + aes(x=clarity, fill=cut)
plot1 <- p + geom_bar(position="fill") 
plot2 <- p + geom_bar(position="dodge") 
plot3 <- p + geom_bar(position="stack")
grid.arrange(plot1, plot2, plot3, ncol=3)

Stats

It’s possible to connect geoms to other statistics. Values computed by statistics are surrounded by double points, like ..count..

p <- ggplot(diamonds, aes(carat)) + xlim(0, 3)
plot1 <- p + stat_bin(aes(ymax = ..count..),   binwidth = 0.1, geom = "area")
plot2 <- p + stat_bin(aes(size = ..density..), binwidth = 0.1, geom = "point", position="identity")
grid.arrange(plot1, plot2, ncol=2)

p <- ggplot(diamonds, aes(carat, price)) + xlim(1,3) 
plot1 <- p + stat_bin2d(bins = 25)
plot2 <- p + stat_binhex(bins = 10)
grid.arrange(plot1, plot2, ncol=2)

p <- ggplot(diamonds, aes(carat, price)) + xlim(1,3) 
plot1 <- p + geom_point() + geom_density2d()
plot2 <- p + stat_density2d(geom = "point", aes(size = ..density..), contour = F) +
             scale_size_area(0.2, 1.5)
plot3 <- p + stat_density2d(geom = "tile", aes(fill = ..density..), contour = F)
grid.arrange(plot1, plot2, plot3, ncol=3)

Annotating plots

presdts <- presidential[-(1:3),] # remove the first 3 presidents
presdts$start <- as.Date(presdts$start)
head(presdts,4)
##     name      start        end      party
## 4  Nixon 1969-01-20 1974-08-09 Republican
## 5   Ford 1974-08-09 1977-01-20 Republican
## 6 Carter 1977-01-20 1981-01-20 Democratic
## 7 Reagan 1981-01-20 1989-01-20 Republican
head(economics,4)
##         date   pce    pop psavert uempmed unemploy year
## 1 1967-06-30 507.8 198712     9.8     4.5     2944 1967
## 2 1967-07-31 510.9 198911     9.8     4.7     2945 1967
## 3 1967-08-31 516.7 199113     9.0     4.6     2958 1967
## 4 1967-09-30 513.3 199311     9.8     4.9     3143 1967
p <- qplot(date, unemploy, data=economics, geom="line", 
           xlab = "", ylab = "No. unemployed (1000s)")

p + geom_vline(aes(xintercept = as.numeric(start)), data = presdts) +
    scale_x_date()

yrng <- range(economics$unemploy)
xrng <- range(economics$date)

library(scales) # use: alpha()
p + geom_rect(aes(NULL, NULL, xmin = start, xmax = end, fill = party), 
              ymin = yrng[1], ymax = yrng[2], data = presidential) +
    scale_fill_manual(values = alpha(c("blue", "red"), 0.2))

highest <- subset(economics, unemploy == max(unemploy))
p + geom_point(data = highest, size = 3, colour = alpha("red", 0.5))

p + geom_text(aes(x, y, label = "Unemployment rates of the last 40 years"), 
              data = data.frame(x = xrng[2], y = yrng[2]), 
              hjust = 1, vjust = 1, size = 4)

Transformations

Transformations carried out by the coordinate system change the appearance of the geoms: in polar coordinates a rectangle becomes a slice of a doughnut; in a map projection, the shortest path between two points will no longer be a straight line – Wickham

p <- ggplot(data=data.frame(x=c(1,200),y=c(1,100))) + aes(x,y)
plot1 <- p + geom_hline(yintercept=seq(20, 60, by=15)) +
             geom_rect(xmin=20, xmax=40, ymin=20, ymax=60)
             
plot2 <- plot1 + coord_polar()              # x position mapped to angle
plot3 <- plot1 + coord_polar(theta="y")     # y position mapped to angle
plot4 <- plot1 + coord_flip()               # flip coordinates
plot5 <- plot1 + coord_equal()  
plot6 <- plot1 + coord_trans(x = "log10")
grid.arrange(plot1, plot2, plot3, plot4, plot5, plot6, nrow=2, ncol=3)

Eg with map coordinates:

library("maps")

m <- map_data("italy")
p <- ggplot(m, aes(x=long, y=lat, group=group)) +
            geom_polygon(fill="white", colour="black")
plot1 <- p                # Use cartesian coordinates
plot2 <- p + coord_map()  # With default mercator projection
grid.arrange(plot1, plot2, ncol=2)

Some more complex egs

Let’s see an example with error bars (summarySE is defined in the R markdown)):

# from http://www.cookbook-r.com/Graphs/Plotting_means_and_error_bars_(ggplot2)/
tgc <- summarySE(ToothGrowth, measurevar="len", groupvars=c("supp","dose"))
head(tgc)
##   supp dose  N   len       sd        se       ci
## 1   OJ  0.5 10 13.23 4.459709 1.4102837 3.190283
## 2   OJ  1.0 10 22.70 3.910953 1.2367520 2.797727
## 3   OJ  2.0 10 26.06 2.655058 0.8396031 1.899314
## 4   VC  0.5 10  7.98 2.746634 0.8685620 1.964824
## 5   VC  1.0 10 16.77 2.515309 0.7954104 1.799343
## 6   VC  2.0 10 26.14 4.797731 1.5171757 3.432090
pd <- position_dodge(0.1) # move errorbars to the left and right

ggplot(tgc, aes(x=dose, y=len, colour=supp, group=supp)) + 
    geom_errorbar(aes(ymin=len-se, ymax=len+se), colour="black", width=.1, position=pd) +
    geom_line(position=pd) +
    geom_point(position=pd, size=3, shape=21, fill="white") + # 21 is filled circle
    xlab("Dose (mg)") +
    ylab("Tooth length") +
    scale_colour_hue(name="Supplement type",    # Legend label, use darker colors
                     breaks=c("OJ", "VC"),
                     labels=c("Orange juice", "Ascorbic acid"),
                     l=40) +                    # Use darker colors, lightness=40
    ggtitle("The Effect of Vitamin C on\nTooth Growth in Guinea Pigs") +
    expand_limits(y=0) +                        # Expand y range
    scale_y_continuous(breaks=0:20*4) +         # Set tick every 4
    theme_bw() +
    theme(legend.justification=c(1,0),
          legend.position=c(1,0))               # Position legend in bottom right

Eg of a population pyramid taken from Kyle Walker’s example:

# males are counted with negative numbers, while females are with positive numbers
head(portugal) # check R markdown for code to produce this data frame
##     Age Gender Population
## 1   0-4   Male    -275841
## 2   5-9   Male    -300660
## 3 10-14   Male    -317401
## 4 15-19   Male    -319635
## 5 20-24   Male    -334467
## 6 25-29   Male    -351088
ggplot(portugal, aes(x = Age, y = Population, fill = Gender)) + 
  geom_bar(subset = .(Gender == "Female"), stat = "identity") + 
  geom_bar(subset = .(Gender == "Male"),   stat = "identity") + 
  scale_y_continuous(breaks = seq(-5e5, 5e5, 1.25e5), 
                     labels = paste0(as.character(c(seq(0.5, 0, -.125), seq(0.125, 0.5, .125))), "m")) + 
  coord_flip() +   # rotates 90º
  scale_fill_brewer(palette = "Set1") + 
  theme_bw()

Using with maps

This next eg in taken from ggmap, Spatial Visualization with ggplot2 vignette:

library(ggmap)

head(crime)  # compiled from Houston Police Department's website
##                      time     date hour premise            offense  beat
## 82729 2010-01-01 06:00:00 1/1/2010    0     18A             murder 15E30
## 82730 2010-01-01 06:00:00 1/1/2010    0     13R            robbery 13D10
## 82731 2010-01-01 06:00:00 1/1/2010    0     20R aggravated assault 16E20
## 82732 2010-01-01 06:00:00 1/1/2010    0     20R aggravated assault  2A30
## 82733 2010-01-01 06:00:00 1/1/2010    0     20A aggravated assault 14D20
## 82734 2010-01-01 06:00:00 1/1/2010    0     20R           burglary 18F60
##           block    street type suffix number   month    day
## 82729 9600-9699   marlive   ln      -      1 january friday
## 82730 4700-4799 telephone   rd      -      1 january friday
## 82731 5000-5099  wickview   ln      -      1 january friday
## 82732 1000-1099   ashland   st      -      1 january friday
## 82733 8300-8399    canyon           -      1 january friday
## 82734 9300-9399     rowan   ln      -      1 january friday
##                       location           address       lon      lat
## 82729    apartment parking lot   9650 marlive ln -95.43739 29.67790
## 82730 road / street / sidewalk 4750 telephone rd -95.29888 29.69171
## 82731        residence / house  5050 wickview ln -95.45586 29.59922
## 82732        residence / house   1050 ashland st -95.40334 29.79024
## 82733                apartment       8350 canyon -95.37791 29.67063
## 82734        residence / house     9350 rowan ln -95.54830 29.70223
murders <- crime[crime$offense == "murder",]

# get map of Houston (this is also a ggplot object)
houstonMap <- qmap("houston", zoom = 11, color = "bw", legend = "topleft")

plot1 <- houstonMap +
          geom_point(aes(x = lon, y = lat, colour = "red", size = 2), data = murders) +
          theme(legend.position="none")

plot2 <- houstonMap +
          stat_bin2d(aes(x = lon, y = lat, colour = offense, fill = offense),
                     size = .5, bins = 30, alpha = 1/2, data = murders)

plot3 <- houstonMap +
          stat_density2d(aes(x = lon, y = lat, fill = ..level.., alpha = ..level..),
                         size = 2, bins = 4, data = murders, geom = "polygon") +
          guides(alpha=FALSE) # remove alpha legend
grid.arrange(plot1, plot2, plot3, ncol=3)

This next eg is based on Robin Lovelace and James Cheshire’s tutorial (the Rpubs page is here and also here).

library(rgdal)

sport <- readOGR(dsn = "files", "london_sport") # open files/london_sport
## OGR data source with driver: ESRI Shapefile 
## Source: "files", layer: "london_sport"
## with 33 features
## It has 4 fields
# sport.f <- fortify(sport, region = "ons_label") # format shapefile to plot
# head(sport.f)
# # add back attribute information associated with sport object (merge is a data join)
# sport.f <- merge(sport.f, sport@data, by.x = "id", by.y = "ons_label")
# head(sport.f)

sport <- SpatialPolygonsDataFrame(Sr = spTransform(sport, 
                                                   CRSobj = CRS("+init=epsg:4326")),
                                  data = sport@data)
sport.f  <- fortify(sport)    # format shapefile to be ploted
sport$id <- row.names(sport)  # provide same column names for join
sport.f  <- join(sport.f, sport@data)
head(sport.f)
##         long      lat order  hole piece group id ons_label    name
## 1 0.03163909 51.44288     1 FALSE     1   0.1  0      00AF Bromley
## 2 0.04152608 51.44046     2 FALSE     1   0.1  0      00AF Bromley
## 3 0.06333280 51.42321     3 FALSE     1   0.1  0      00AF Bromley
## 4 0.07694588 51.43151     4 FALSE     1   0.1  0      00AF Bromley
## 5 0.10922623 51.41360     5 FALSE     1   0.1  0      00AF Bromley
## 6 0.13119092 51.41437     6 FALSE     1   0.1  0      00AF Bromley
##   Partic_Per Pop_2001
## 1       21.7   295535
## 2       21.7   295535
## 3       21.7   295535
## 4       21.7   295535
## 5       21.7   295535
## 6       21.7   295535

With a well defined dataframe, gglopt can plot it using polygons (each London borough is called group):

p <- ggplot(sport.f, aes(long, lat, group = group, fill = Partic_Per)) +
      geom_polygon() + 
      coord_equal() + 
      labs(x = "Easting (m)", y = "Northing (m)", fill = "% Sport Partic.") + 
      ggtitle("London Sports Participation")
p + scale_fill_gradient(low = "white", high = "black") # black&white version

# ggsave("plot.png", scale = 3, dpi = 400) # to save image

Several operations over the London map:

p <- ggplot() + 
       geom_polygon(data = sport.f, aes(x = long, y = lat, group = group)) +
       coord_map() # this line of code ensures the plot is to scale
plot1 <- p
plot2 <- p + geom_point(aes(x = coordinates(sport)[, 1], y = coordinates(sport)[,2]))
plot3 <- plot2 + 
  geom_path(data = sport.f, aes(x = long, y = lat, group = group), color = "white") + 
  theme_classic()  # this line removes the distracting grey background

new_theme <- theme(axis.line = element_blank(), axis.ticks = element_blank(), 
                   axis.title.x = element_blank(), axis.title.y = element_blank(),
                   axis.text.y = element_blank(), axis.text.x = element_blank(),
                   axis.text.y = element_blank(), axis.text.y = element_blank(), 
                   panel.background = element_rect(fill = "lightgreen"))
plot4 <- p + new_theme
grid.arrange(plot1, plot2, plot3, plot4, nrow=2, ncol=2)

These objects can also be layered in ggmap objects:

library(ggmap) 
b <- bbox(sport)
p <- ggmap(get_map(location = b))
plot1 <- p
plot2 <- p + 
  geom_polygon(data=sport.f, aes(x=long, y=lat, group=group, fill=Partic_Per), alpha=0.5) + 
  scale_fill_continuous(low = "green", high = "red") 

# using a new map source
p <- ggmap(get_map(location = b, source = "stamen", maptype = "toner", crop = T))
plot3 <- p + geom_polygon(data = sport.f, 
                     aes(x=long, y=lat, group=group, fill=Partic_Per), alpha = 0.5)
grid.arrange(plot1, plot2, plot3, nrow=2, ncol=2)