简体   繁体   中英

Adding a manual legend in ggplot2?

I'm doing a comparison chart of two different estimates of the same time series data. I'm filling the area between the two series in green if the original estimate is more than the latest estimate, and red otherwise.

I've got that part working, but I'd like to add a legend for the fill colors. I tried scale_fill_manual towards the bottom of the code, but it doesn't seem to be doing anything?

Here's the code:

library(ggplot2)
library(scales)
library(colorspace)

# Return a polygon that only plots between yLower and yUpper when yLower is
# less than yUpper.
getLowerPolygon = function(x, yLower, yUpper) {
    # Create the table of coordinates
    poly = data.frame(
        x = numeric(),
        y = numeric())

    lastReversed = (yUpper[1] < yLower[1])
    for (r in 1:length(x)) {
        reversed = (yUpper[r] < yLower[r])
        if (reversed != lastReversed) {
            # Between points r-1 and r, the series intersected, so we need to
            # change the polygon from visible to invisible or v.v.  In either
            # case, just add the intersection between those two segments to the
            # polygon.  Algorithm from:
            # https://en.wikipedia.org/wiki/Line-line_intersection
            # First line: x1,y1 - x2,y2
            x1 = x[r-1]
            y1 = yLower[r-1]
            x2 = x[r]
            y2 = yLower[r]
            # Second line: x3,y3 - x4,y4
            x3 = x[r-1]
            y3 = yUpper[r-1]
            x4 = x[r]
            y4 = yUpper[r]
            # Calculate determinants
            xy12 = det(matrix(c(x1, y1, x2, y2), ncol = 2))
            xy34 = det(matrix(c(x3, y3, x4, y4), ncol = 2))
            x12  = det(matrix(c(x1,  1, x2,  1), ncol = 2))
            x34  = det(matrix(c(x3,  1, x4,  1), ncol = 2))
            y12  = det(matrix(c(y1,  1, y2,  1), ncol = 2))
            y34  = det(matrix(c(y3,  1, y4,  1), ncol = 2))
            # Calculate fraction pieces
            xn = det(matrix(c(xy12, x12, xy34, x34), ncol = 2))
            yn = det(matrix(c(xy12, y12, xy34, y34), ncol = 2))
            d  = det(matrix(c(x12 , y12,  x34, y34), ncol = 2))
            # Calculate intersection
            xi = xn / d
            yi = yn / d
            # Add the point
            poly[nrow(poly)+1,] = c(xi, yi)
        }
        lastReversed = reversed
        # http://stackoverflow.com/questions/2563824
        poly[nrow(poly)+1,] = c(x[r], min(yLower[r], yUpper[r]))
    }

    poly = rbind(poly, data.frame(
        x = rev(x),
        y = rev(yUpper)))

    return(poly)
}

getComparisonPlot = function(data, title, lower_name, upper_name,
                         x_label, y_label, legend_title = '') {

    lightGreen = '#b0dd8d'
    lightRed   = '#fdba9a'

    darkGray = RGB(.8, .8, .8)
    midGray  = RGB(.5, .5, .5)

    plot = ggplot(data, aes(x = x))

    plot = plot + geom_polygon(
        aes(x = x, y = y),
        data = data.frame(
            x = c(data$x, rev(data$x)),
            y = c(data$yLower, rev(data$yUpper))
        ),
        fill = lightRed)

    coords = getLowerPolygon(data$x, data$yLower, data$yUpper)

    plot = plot + geom_polygon(
        aes(x = x, y = y),
        data = coords,
        fill = lightGreen)

    plot = plot + geom_line(
        aes(y = yUpper, color = 'upper'),
        size = 0.5)

    plot = plot + geom_line(
        aes(y = yLower, color = 'lower'),
        size = 0.5)

    plot = plot +
        ggtitle(paste(title, '\n', sep='')) +
        xlab(x_label) +
        ylab(y_label) +
        scale_y_continuous(labels = comma)

    # http://stackoverflow.com/a/10355844/106302
    plot = plot + scale_color_manual(
        name   = legend_title,
        breaks = c('upper' , 'lower'),
        values = c('gray20', 'gray50'),
        labels = c(upper_name, lower_name))

    plot = plot + scale_fill_manual(
        name   = 'Margin',
        breaks = c('upper', 'lower'),
        values = c(lightGreen, lightRed),
        labels = c('Over', 'Under'))

    return(plot)
}

print(getComparisonPlot(
    data = data.frame(
        x = 1:20,
        yLower = 1:20 %% 5 + 2,
        yUpper = 1:20 %% 7
    ),
    title = 'Comparison Chart',
    lower_name = 'Latest',
    upper_name = 'Original',
    x_label = 'X axis',
    y_label = 'Y axis',
    legend_title = 'Thing'
))

Here's an image of the chart, I think it is a cool technique:

比较表

I'm also open to any other suggestions for improving my ggplot code.

GGplot need you to map polygons fill aesthetic to some variable. OR, in this case, it need just you to "label" the types of polygons (ie 'upper' and 'lower'). You do this by passing a string with the respective label for the fill aesthetic of geom_polygon(). What you are doing is passing a giving colour for each polygon and not mapping to anything that the ggplot will understand. It's kind of a "hard coded colour" =P.

Well, here are the changes inside getComparisonPlot :

plot = plot + geom_polygon(
    aes(x = x, y = y, fill = "upper"),
    data = coords)

plot = plot + geom_polygon(
    aes(x = x, y = y, fill = "lower"),
    data = data.frame(
      x = c(data$x, rev(data$x)),
      y = c(data$yLower, rev(data$yUpper))
    ))

One more thing. Note that the strings passed to fill aesthetic coincides with the breaks passed to the scale_fill_manual. It is necessary to make the legend map things right.

plot = plot + scale_fill_manual(
    name   = 'Margin',
    breaks = c('upper', 'lower'), # <<< corresponds to fill aesthetic labels
    values = c(lightGreen, lightRed),
    labels = c('Over', 'Under'))

Result:

在此输入图像描述

hope it helps.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM