神刀安全网

ggplot2で字幕 [Subtitles in ggplot2]

Subtitles aren’t always necessary for plots, but I began to use them enough that I whipped up a function for ggplot2 that does a decent job adding a subtitle to a finished plot object. More than a few folks have tried their hand at this in the past and this is just my incremental contribution until there’s proper support in ggplot2 (someone’s bound to add it via PR at some point).

We’ll nigh fully recreate the following plot from this NYTimes article :

ggplot2で字幕 [Subtitles in ggplot2]

Here’s a stab at that w/o the subtitle:

library(ggplot2) library(scales)   data.frame(   yrs=c("1789-90", "1849-50", "1909-10", "1965-66", "2016-16"),   pct=c(0.526, 0.795, 0.713, 0.575, 0.365),   xtralabs=c("", "Highest:/n", "", "", "Lowest:/n") ) -> hill_lawyers   gg <- ggplot(hill_lawyers, aes(yrs, pct)) gg <- gg + geom_bar(stat="identity", width=0.65) gg <- gg + geom_label(aes(label=sprintf("%s%s", xtralabs, percent(pct))),                       vjust=-0.4, family=c(rep("FranklinGothic-Book", 4),"FranklinGothic-Heavy"),                        lineheight=0.9, size=4, label.size=0) gg <- gg + scale_x_discrete() gg <- gg + scale_y_continuous(expand=c(0,0), limits=c(0.0, 1.0), labels=percent) gg <- gg + labs(x=NULL, y=NULL, title="Fewer and fewer lawyers on the Hill") gg <- gg + theme_minimal(base_family="FranklinGothic-Book") gg <- gg + theme(axis.line=element_line(color="#2b2b2b", size=0.5)) gg <- gg + theme(axis.line.y=element_blank()) gg <- gg + theme(axis.text.x=element_text(family=c(rep("FranklinGothic-Book", 4),                                                    "FranklinGothic-Heavy"))) gg <- gg + theme(panel.grid.major.x=element_blank()) gg <- gg + theme(panel.grid.major.y=element_line(color="#b2b2b2", size=0.1)) gg <- gg + theme(panel.grid.minor.y=element_blank()) gg <- gg + theme(plot.title=element_text(hjust=0,                                           family="FranklinGothic-Heavy",                                           margin=margin(b=10))) gg

ggplot2で字幕 [Subtitles in ggplot2]

(There are some “tricks” in that plotting code that may be worth spending an extra minute or two to mull over if you didn’t realize some of the function parameters were vectorized, or that you could get a white background with no border for text labels so grid lines don’t get in the way.)

Ideally, a subtitle would be part of the gtable that gets made underneath the covers so it will “travel well” with the plot object itself. The function below makes a textGrob from whatever text we pass into it and does just that; it inserts the new grob into a new table row.

#' Add a subtitle to a ggplot object and draw plot on current graphics device. #'  #' @param gg ggplot2 object #' @param label subtitle label #' @param fontfamily font family to use. The function doesn't pull any font  #'        information from /code{gg} so you should consider specifying fonts #'        for the plot itself and here. Or send me code to make this smarter :-) #' @param fontsize font size #' @param hjust,vjust horizontal/vertical justification  #' @param bottom_margin space between bottom of subtitle and plot (code{pts}) #' @param newpage draw new (empty) page first? #' @param vp viewport to draw plot in #' @param ... parameters passed to /code{gpar} in call to /code{textGrob} #' @return Invisibly returns the result of /code{/link{ggplot_build}}, which #'   is a list with components that contain the plot itself, the data, #'   information about the scales, panels etc. ggplot_with_subtitle <- function(gg,                                   label="",                                   fontfamily=NULL,                                  fontsize=10,                                  hjust=0, vjust=0,                                   bottom_margin=5.5,                                  newpage=is.null(vp),                                  vp=NULL,                                  ...) {     if (is.null(fontfamily)) {     gpr <- gpar(fontsize=10, ...)   } else {     gpr <- gpar(fontfamily=fontfamily, fontsize=10, ...)   }     subtitle <- textGrob(label, x=unit(hjust, "npc"), y=unit(hjust, "npc"),                         hjust=hjust, vjust=vjust,                        gp=gpr)     data <- ggplot_build(gg)     gt <- ggplot_gtable(data)   gt <- gtable_add_rows(gt, grobHeight(subtitle), 2)   gt <- gtable_add_grob(gt, subtitle, 3, 4, 3, 4, 8, "off", "subtitle")   gt <- gtable_add_rows(gt, grid::unit(bottom_margin, "pt"), 3)     if (newpage) grid.newpage()     if (is.null(vp)) {     grid.draw(gt)   } else {     if (is.character(vp)) seekViewport(vp) else pushViewport(vp)     grid.draw(gt)     upViewport()   }     invisible(data)   }

The roxygen comments should give you an idea of how to work with it, and here it is in action:

subtitle <- "The percentage of Congressional members that are laywers has been/ncontinuously dropping since the 1960s"   ggplot_with_subtitle(gg, subtitle,                      fontfamily="FranklinGothic-Book",                      bottom_margin=20, lineheight=0.9)

ggplot2で字幕 [Subtitles in ggplot2]

It deals with long annotations pretty well, too (I strwrap ped the source text for the below at 100 characters). The text is senseless here, but it’s just for show (I had it handy…don’t judge…you’re getting free code :-):

ggplot2で字幕 [Subtitles in ggplot2]

I think this beats manually re-creating the wheel, even if you only infrequently use subtitles. It definitely beats hand-editing plots and is a bit more elegant and functional than using grid.arrange (et al) to mimic the functionality. It also beats futzing with panel margins and clipping to shoehorn a frankenmashup mess of geom_text or annotation_custom calls.

Kick the tyres, tell me where it breaks and if I can cover enough edge cases (or make it smarter) I’ll add it to my ggalt package.

转载本站任何文章请注明:转载至神刀安全网,谢谢神刀安全网 » ggplot2で字幕 [Subtitles in ggplot2]

分享到:更多 ()

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
分享按钮