[med-svn] [r-cran-ggplot2] 01/10: Imported Upstream version 2.2.0
Andreas Tille
tille at debian.org
Mon Nov 14 09:09:33 UTC 2016
This is an automated email from the git hooks/post-receive script.
tille pushed a commit to branch master
in repository r-cran-ggplot2.
commit fa515262822ba57a92c5cbe612f269a4622911ef
Author: Andreas Tille <tille at debian.org>
Date: Mon Nov 14 09:17:05 2016 +0100
Imported Upstream version 2.2.0
---
DESCRIPTION | 74 +-
LICENSE | 280 ++++++++
MD5 | 613 +++++++++--------
NAMESPACE | 45 +-
NEWS.md | 227 ++++++
R/aes-group-order.r | 2 +-
R/aes.r | 39 +-
R/annotation-custom.r | 9 +-
R/annotation-logticks.r | 2 +-
R/annotation-map.r | 6 +-
R/annotation-raster.r | 11 +-
R/annotation.r | 9 +-
R/axis-secondary.R | 162 +++++
R/bin.R | 2 +-
R/coord-.r | 30 +-
R/coord-cartesian-.r | 3 +-
R/coord-fixed.r | 2 +-
R/coord-flip.r | 4 +-
R/coord-map.r | 100 ++-
R/coord-polar.r | 17 +-
R/coord-transform.r | 8 +-
R/data.R | 172 ++---
R/facet-.r | 448 +++++++++++-
R/facet-grid-.r | 447 ++++++------
R/facet-layout.r | 175 -----
R/facet-locate.r | 84 ---
R/facet-null.r | 113 ++-
R/facet-viewports.r | 50 --
R/facet-wrap.r | 564 ++++++++-------
R/fortify-lm.r | 3 +-
R/fortify-map.r | 16 +-
R/fortify-multcomp.r | 1 +
R/fortify-spatial.r | 1 +
R/fortify.r | 2 +-
R/geom-.r | 9 +-
R/geom-abline.r | 13 +-
R/geom-bar.r | 37 +-
R/geom-bin2d.r | 9 +-
R/geom-blank.r | 8 +-
R/geom-boxplot.r | 45 +-
R/geom-col.r | 48 ++
R/geom-contour.r | 12 +-
R/geom-count.r | 12 +-
R/geom-curve.r | 3 +-
R/geom-defaults.r | 5 +-
R/geom-density.r | 9 +-
R/geom-density2d.r | 14 +-
R/geom-dotplot.r | 37 +-
R/geom-errorbarh.r | 5 +-
R/geom-hex.r | 9 +-
R/geom-histogram.r | 29 +-
R/geom-jitter.r | 11 +-
R/geom-linerange.r | 13 +-
R/geom-map.r | 9 +-
R/geom-path.r | 15 +-
R/geom-point.r | 44 +-
R/geom-polygon.r | 17 +-
R/geom-quantile.r | 7 +-
R/geom-ribbon.r | 15 +-
R/geom-rug.r | 35 +-
R/geom-segment.r | 17 +-
R/geom-smooth.r | 4 +-
R/geom-spoke.r | 7 +-
R/geom-text.r | 40 +-
R/geom-tile.r | 8 +-
R/geom-violin.r | 21 +-
R/ggplot2.r | 3 +
R/ggproto.r | 119 +++-
R/guide-colorbar.r | 27 +-
R/guide-legend.r | 25 +-
R/guides-.r | 101 +--
R/guides-axis.r | 4 +-
R/hexbin.R | 4 +
R/{facet-labels.r => labeller.r} | 75 +-
R/labels.r | 50 +-
R/layer.r | 65 +-
R/layout.R | 214 ++++++
R/limits.r | 55 +-
R/margins.R | 29 +-
R/panel.r | 185 -----
R/plot-build.r | 106 ++-
R/plot-construction.r | 87 +--
R/plot-last.r | 1 +
R/plot.r | 97 +--
R/position-.r | 4 +-
R/position-collide.r | 45 +-
R/position-dodge.r | 49 +-
R/position-fill.r | 24 -
R/position-jitter.r | 31 +-
R/position-jitterdodge.R | 8 +-
R/position-nudge.R | 13 +-
R/position-stack.r | 229 ++++++-
R/quick-plot.r | 14 +-
R/range.r | 4 +-
R/save.r | 19 +-
R/scale-.r | 102 ++-
R/scale-alpha.r | 19 +-
R/scale-brewer.r | 30 +-
R/scale-continuous.r | 161 +++--
R/scale-date.r | 129 +++-
R/scale-discrete-.r | 41 +-
R/scale-gradient.r | 11 +-
R/scale-grey.r | 10 +-
R/scale-hue.r | 27 +-
R/scale-identity.r | 57 +-
R/scale-linetype.r | 19 +-
R/scale-manual.r | 40 +-
R/scale-shape.r | 21 +-
R/scale-size.r | 2 +-
R/scale-type.R | 3 +
R/stat-.r | 32 +-
R/stat-bin.r | 9 +-
R/stat-binhex.r | 9 +-
R/stat-boxplot.r | 2 +-
R/stat-contour.r | 5 +
R/stat-count.r | 23 +-
R/stat-density.r | 39 +-
R/stat-ecdf.r | 22 +-
R/stat-ellipse.R | 2 +-
R/stat-function.r | 10 +-
R/stat-identity.r | 2 +-
R/stat-qq.r | 10 +-
R/stat-smooth.r | 22 +-
R/stat-sum.r | 2 +-
R/stat-summary.r | 45 +-
R/stat-unique.r | 10 +-
R/stat-ydensity.r | 30 +-
R/theme-current.R | 109 +++
R/theme-defaults.r | 271 +++++---
R/theme-elements.r | 160 +++--
R/theme.r | 762 ++++++++++-----------
R/translate-qplot-ggplot.r | 1 +
R/translate-qplot-lattice.r | 1 +
R/utilities-break.r | 2 +-
R/utilities-help.r | 35 +-
R/utilities-resolution.r | 16 +-
R/utilities.r | 25 +-
R/zzz.r | 8 +-
README.md | 66 +-
build/partial.rdb | Bin 186675 -> 172618 bytes
build/vignette.rds | Bin 250 -> 245 bytes
inst/doc/extending-ggplot2.R | 352 +++++++++-
inst/doc/extending-ggplot2.Rmd | 445 +++++++++++-
inst/doc/extending-ggplot2.html | 517 ++++++++++++--
inst/doc/ggplot2-specs.Rmd | 4 +-
inst/doc/ggplot2-specs.html | 23 +-
inst/staticdocs/README.md | 0
inst/staticdocs/footer.html | 5 -
inst/staticdocs/head.html | 27 -
inst/staticdocs/icons.R | 576 ----------------
inst/staticdocs/index.r | 241 -------
man/add_theme.Rd | 4 +-
man/aes.Rd | 28 +-
man/aes_.Rd | 13 +-
man/aes_group_order.Rd | 4 +-
man/annotate.Rd | 13 +-
man/annotation_custom.Rd | 5 +-
man/annotation_map.Rd | 4 +-
man/annotation_raster.Rd | 8 +-
man/as.list.ggproto.Rd | 1 +
man/as_labeller.Rd | 3 +-
man/borders.Rd | 7 +-
man/calc_element.Rd | 2 +-
man/combine_vars.Rd | 28 +
man/continuous_scale.Rd | 8 +-
man/coord_cartesian.Rd | 2 +-
man/coord_fixed.Rd | 2 +-
man/coord_flip.Rd | 4 +-
man/coord_map.Rd | 57 +-
man/coord_polar.Rd | 4 +-
man/coord_trans.Rd | 8 +-
man/cut_interval.Rd | 2 +-
man/diamonds.Rd | 24 +-
man/discrete_scale.Rd | 16 +-
man/economics.Rd | 26 +-
man/element.Rd | 120 ++++
man/element_blank.Rd | 14 -
man/element_line.Rd | 24 -
man/element_rect.Rd | 24 -
man/element_text.Rd | 41 --
man/expand_limits.Rd | 3 +-
man/facet.Rd | 18 -
man/facet_grid.Rd | 6 +-
man/facet_null.Rd | 1 +
man/facet_wrap.Rd | 36 +-
man/find_panel.Rd | 29 +
man/format.ggproto.Rd | 21 -
man/fortify-multcomp.Rd | 1 +
man/fortify.Rd | 2 +-
man/fortify.lm.Rd | 3 +-
man/fortify.map.Rd | 3 +-
man/fortify.sp.Rd | 1 +
man/geom_abline.Rd | 17 +-
man/geom_bar.Rd | 45 +-
man/geom_bin2d.Rd | 13 +-
man/geom_blank.Rd | 5 +-
man/geom_boxplot.Rd | 50 +-
man/geom_contour.Rd | 16 +-
man/geom_count.Rd | 17 +-
man/geom_density.Rd | 34 +-
man/geom_density_2d.Rd | 18 +-
man/geom_dotplot.Rd | 14 +-
man/geom_errorbarh.Rd | 11 +-
man/geom_hex.Rd | 13 +-
man/geom_histogram.Rd | 45 +-
man/geom_jitter.Rd | 15 +-
man/geom_linerange.Rd | 17 +-
man/geom_map.Rd | 13 +-
man/geom_path.Rd | 15 +-
man/geom_point.Rd | 46 +-
man/geom_polygon.Rd | 21 +-
man/{stat_qq.Rd => geom_qq.Rd} | 16 +-
man/geom_quantile.Rd | 11 +-
man/geom_ribbon.Rd | 19 +-
man/geom_rug.Rd | 38 +-
man/geom_segment.Rd | 22 +-
man/geom_smooth.Rd | 21 +-
man/geom_spoke.Rd | 11 +-
man/geom_text.Rd | 44 +-
man/geom_tile.Rd | 12 +-
man/geom_violin.Rd | 30 +-
man/gg-add.Rd | 81 +--
man/ggplot.Rd | 74 +-
man/ggplot2-ggproto.Rd | 106 ++-
man/ggplot2-package.Rd | 15 +
man/ggproto.Rd | 51 +-
man/ggsave.Rd | 18 +-
man/ggtheme.Rd | 29 +-
man/graphical-units.Rd | 2 +-
man/guide_colourbar.Rd | 4 +-
man/guide_legend.Rd | 2 +-
man/guides.Rd | 13 +-
man/hmisc.Rd | 27 +-
man/is.ggproto.Rd | 15 -
man/is.rel.Rd | 1 +
man/is.theme.Rd | 1 +
man/label_bquote.Rd | 6 +-
man/labeller.Rd | 4 +-
man/labellers.Rd | 4 +-
man/labs.Rd | 47 +-
man/last_plot.Rd | 1 +
man/layer.Rd | 9 +-
man/lims.Rd | 51 +-
man/luv_colours.Rd | 4 +-
man/macros/aesthetics.Rd | 1 +
man/map_data.Rd | 6 +-
man/margin.Rd | 24 -
man/max_height.Rd | 22 +
man/mean_se.Rd | 12 +-
man/midwest.Rd | 60 +-
man/mpg.Rd | 24 +-
man/msleep.Rd | 26 +-
man/position_dodge.Rd | 55 +-
man/position_identity.Rd | 4 +-
man/position_jitter.Rd | 31 +-
man/position_jitterdodge.Rd | 6 +-
man/position_nudge.Rd | 17 +-
man/position_stack.Rd | 131 +++-
man/presidential.Rd | 2 +-
man/print.ggplot.Rd | 22 +-
man/print.ggproto.Rd | 14 +-
man/qplot.Rd | 12 +-
man/rel.Rd | 21 -
man/render_axes.Rd | 33 +
man/render_strips.Rd | 26 +
man/resolution.Rd | 17 +-
man/scale_alpha.Rd | 24 +-
man/scale_brewer.Rd | 26 +-
man/scale_continuous.Rd | 119 ++--
man/scale_date.Rd | 48 +-
man/scale_discrete.Rd | 15 +-
man/scale_gradient.Rd | 8 +-
man/scale_grey.Rd | 7 +-
man/scale_hue.Rd | 24 +-
man/scale_identity.Rd | 13 +-
man/scale_linetype.Rd | 19 +-
man/scale_manual.Rd | 37 +-
man/scale_shape.Rd | 21 +-
man/scale_size.Rd | 2 +-
man/seals.Rd | 2 +-
man/sec_axis.Rd | 69 ++
man/stat_ecdf.Rd | 21 +-
man/stat_ellipse.Rd | 6 +-
man/stat_function.Rd | 14 +-
man/stat_identity.Rd | 2 +-
man/stat_summary.Rd | 12 +-
man/stat_summary_2d.Rd | 4 +-
man/stat_unique.Rd | 16 +-
man/theme.Rd | 580 ++++++++--------
man/theme_get.Rd | 92 +++
man/theme_update.Rd | 69 --
man/translate_qplot_ggplot.Rd | 1 +
man/translate_qplot_lattice.Rd | 1 +
man/txhousing.Rd | 4 +-
man/update_defaults.Rd | 1 +
man/update_labels.Rd | 1 +
man/wrap_dims.Rd | 21 +
tests/testthat/Rplots.pdf | Bin 3831 -> 3833 bytes
tests/testthat/helper-plot-data.r | 8 +-
tests/testthat/test-add.R | 6 +
tests/testthat/test-annotate.r | 20 +
tests/testthat/test-boxplot.r | 4 +-
tests/testthat/test-build.r | 13 +-
tests/testthat/test-data.r | 7 +-
tests/testthat/test-dotplot.r | 14 +
tests/testthat/test-facet-labels.r | 20 +-
tests/testthat/test-facet-layout.r | 73 +-
.../{test-facet-locate.r => test-facet-map.r} | 52 +-
tests/testthat/test-facet-strips.r | 88 ++-
tests/testthat/test-function-args.r | 4 +-
tests/testthat/test-geom-hex.R | 12 +
tests/testthat/test-geom-violin.R | 23 +-
tests/testthat/test-guides.R | 9 +
tests/testthat/test-labels.r | 10 +
tests/testthat/test-layer.r | 8 +-
tests/testthat/test-position-stack.R | 54 ++
tests/testthat/test-scale-date.R | 46 ++
tests/testthat/test-scale-discrete.R | 53 ++
tests/testthat/test-scales.r | 25 +-
tests/testthat/test-stat-bin.R | 33 +-
tests/testthat/test-stat-hex.R | 8 +
tests/testthat/test-stats.r | 5 +
tests/testthat/test-theme.r | 33 +-
tests/testthat/test-utilities.r | 14 +
vignettes/extending-ggplot2.Rmd | 445 +++++++++++-
vignettes/ggplot2-specs.Rmd | 4 +-
vignettes/releases/ggplot2-1.0.0.Rmd | 15 +
vignettes/releases/ggplot2-2.0.0.Rmd | 327 +++++++++
vignettes/releases/ggplot2-2.1.0.Rmd | 74 ++
vignettes/releases/ggplot2-2.2.0.Rmd | 227 ++++++
330 files changed, 10084 insertions(+), 5896 deletions(-)
diff --git a/DESCRIPTION b/DESCRIPTION
index c1584ef..2d23dc8 100644
--- a/DESCRIPTION
+++ b/DESCRIPTION
@@ -1,28 +1,25 @@
Package: ggplot2
-Version: 2.1.0
+Version: 2.2.0
+Title: Create Elegant Data Visualisations Using the Grammar of Graphics
+Description: A system for 'declaratively' creating graphics,
+ based on "The Grammar of Graphics". You provide the data, tell 'ggplot2'
+ how to map variables to aesthetics, what graphical primitives to use,
+ and it takes care of the details.
Authors at R: c(
person("Hadley", "Wickham", , "hadley at rstudio.com", c("aut", "cre")),
person("Winston", "Chang", , "winston at rstudio.com", "aut"),
- person("RStudio", role = "cph")
+ person("RStudio", role = c("cph"))
)
-Title: An Implementation of the Grammar of Graphics
-Description: An implementation of the grammar of graphics in R. It combines the
- advantages of both base and lattice graphics: conditioning and shared axes
- are handled automatically, and you can still build up a plot step by step
- from multiple data sources. It also implements a sophisticated
- multidimensional conditioning system and a consistent interface to map
- data to aesthetic attributes. See http://ggplot2.org for more information,
- documentation and examples.
Depends: R (>= 3.1)
Imports: digest, grid, gtable (>= 0.1.1), MASS, plyr (>= 1.7.1),
- reshape2, scales (>= 0.3.0), stats
+ reshape2, scales (>= 0.4.1), stats, tibble, lazyeval
Suggests: covr, ggplot2movies, hexbin, Hmisc, lattice, mapproj, maps,
maptools, mgcv, multcomp, nlme, testthat (>= 0.11.0), quantreg,
knitr, rpart, rmarkdown, svglite
Enhances: sp
-License: GPL-2
-URL: http://ggplot2.org, https://github.com/hadley/ggplot2
-BugReports: https://github.com/hadley/ggplot2/issues
+License: GPL-2 | file LICENSE
+URL: http://ggplot2.tidyverse.org, https://github.com/tidyverse/ggplot2
+BugReports: https://github.com/tidyverse/ggplot2/issues
LazyData: true
Collate: 'ggproto.r' 'aaa-.r' 'aes-calculated.r'
'aes-colour-fill-alpha.r' 'aes-group-order.r'
@@ -30,31 +27,30 @@ Collate: 'ggproto.r' 'aaa-.r' 'aes-calculated.r'
'aes.r' 'legend-draw.r' 'geom-.r' 'annotation-custom.r'
'annotation-logticks.r' 'geom-polygon.r' 'geom-map.r'
'annotation-map.r' 'geom-raster.r' 'annotation-raster.r'
- 'annotation.r' 'autoplot.r' 'bench.r' 'bin.R' 'coord-.r'
- 'coord-cartesian-.r' 'coord-fixed.r' 'coord-flip.r'
- 'coord-map.r' 'coord-munch.r' 'coord-polar.r'
+ 'annotation.r' 'autoplot.r' 'axis-secondary.R' 'bench.r'
+ 'bin.R' 'coord-.r' 'coord-cartesian-.r' 'coord-fixed.r'
+ 'coord-flip.r' 'coord-map.r' 'coord-munch.r' 'coord-polar.r'
'coord-quickmap.R' 'coord-transform.r' 'data.R' 'facet-.r'
- 'facet-grid-.r' 'facet-labels.r' 'facet-layout.r'
- 'facet-locate.r' 'facet-null.r' 'facet-viewports.r'
- 'facet-wrap.r' 'fortify-lm.r' 'fortify-map.r'
- 'fortify-multcomp.r' 'fortify-spatial.r' 'fortify.r' 'stat-.r'
- 'geom-abline.r' 'geom-rect.r' 'geom-bar.r' 'geom-bin2d.r'
- 'geom-blank.r' 'geom-boxplot.r' 'geom-path.r' 'geom-contour.r'
- 'geom-count.r' 'geom-crossbar.r' 'geom-segment.r'
- 'geom-curve.r' 'geom-defaults.r' 'geom-ribbon.r'
- 'geom-density.r' 'geom-density2d.r' 'geom-dotplot.r'
- 'geom-errorbar.r' 'geom-errorbarh.r' 'geom-freqpoly.r'
- 'geom-hex.r' 'geom-histogram.r' 'geom-hline.r' 'geom-jitter.r'
+ 'facet-grid-.r' 'facet-null.r' 'facet-wrap.r' 'fortify-lm.r'
+ 'fortify-map.r' 'fortify-multcomp.r' 'fortify-spatial.r'
+ 'fortify.r' 'stat-.r' 'geom-abline.r' 'geom-rect.r'
+ 'geom-bar.r' 'geom-bin2d.r' 'geom-blank.r' 'geom-boxplot.r'
+ 'geom-col.r' 'geom-path.r' 'geom-contour.r' 'geom-count.r'
+ 'geom-crossbar.r' 'geom-segment.r' 'geom-curve.r'
+ 'geom-defaults.r' 'geom-ribbon.r' 'geom-density.r'
+ 'geom-density2d.r' 'geom-dotplot.r' 'geom-errorbar.r'
+ 'geom-errorbarh.r' 'geom-freqpoly.r' 'geom-hex.r'
+ 'geom-histogram.r' 'geom-hline.r' 'geom-jitter.r'
'geom-label.R' 'geom-linerange.r' 'geom-point.r'
'geom-pointrange.r' 'geom-quantile.r' 'geom-rug.r'
'geom-smooth.r' 'geom-spoke.r' 'geom-text.r' 'geom-tile.r'
'geom-violin.r' 'geom-vline.r' 'ggplot2.r' 'grob-absolute.r'
'grob-dotstack.r' 'grob-null.r' 'grouping.r' 'guide-colorbar.r'
'guide-legend.r' 'guides-.r' 'guides-axis.r' 'guides-grid.r'
- 'hexbin.R' 'labels.r' 'layer.r' 'limits.r' 'margins.R'
- 'panel.r' 'plot-build.r' 'plot-construction.r' 'plot-last.r'
- 'plot.r' 'position-.r' 'position-collide.r' 'position-dodge.r'
- 'position-fill.r' 'position-identity.r' 'position-jitter.r'
+ 'hexbin.R' 'labeller.r' 'labels.r' 'layer.r' 'layout.R'
+ 'limits.r' 'margins.R' 'plot-build.r' 'plot-construction.r'
+ 'plot-last.r' 'plot.r' 'position-.r' 'position-collide.r'
+ 'position-dodge.r' 'position-identity.r' 'position-jitter.r'
'position-jitterdodge.R' 'position-nudge.R' 'position-stack.r'
'quick-plot.r' 'range.r' 'save.r' 'scale-.r' 'scale-alpha.r'
'scale-brewer.r' 'scale-continuous.r' 'scale-date.r'
@@ -69,18 +65,18 @@ Collate: 'ggproto.r' 'aaa-.r' 'aes-calculated.r'
'stat-smooth-methods.r' 'stat-smooth.r' 'stat-sum.r'
'stat-summary-2d.r' 'stat-summary-bin.R' 'stat-summary-hex.r'
'stat-summary.r' 'stat-unique.r' 'stat-ydensity.r' 'summary.r'
- 'theme-defaults.r' 'theme-elements.r' 'theme.r'
- 'translate-qplot-ggplot.r' 'translate-qplot-lattice.r'
- 'utilities-break.r' 'utilities-grid.r' 'utilities-help.r'
- 'utilities-matrix.r' 'utilities-resolution.r'
- 'utilities-table.r' 'zxx.r' 'zzz.r'
+ 'theme-elements.r' 'theme.r' 'theme-defaults.r'
+ 'theme-current.R' 'translate-qplot-ggplot.r'
+ 'translate-qplot-lattice.r' 'utilities-break.r'
+ 'utilities-grid.r' 'utilities-help.r' 'utilities-matrix.r'
+ 'utilities-resolution.r' 'utilities-table.r' 'zxx.r' 'zzz.r'
VignetteBuilder: knitr
RoxygenNote: 5.0.1
NeedsCompilation: no
-Packaged: 2016-02-29 20:47:22 UTC; hadley
+Packaged: 2016-11-09 13:44:31 UTC; hadley
Author: Hadley Wickham [aut, cre],
Winston Chang [aut],
RStudio [cph]
Maintainer: Hadley Wickham <hadley at rstudio.com>
Repository: CRAN
-Date/Publication: 2016-03-01 15:47:24
+Date/Publication: 2016-11-11 12:24:31
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..d8cf7d4
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,280 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
diff --git a/MD5 b/MD5
index 68242b5..dd94f80 100644
--- a/MD5
+++ b/MD5
@@ -1,177 +1,177 @@
-c6072e44c868630c920dc1ce36693a4a *DESCRIPTION
-f513b3f13d3b51210d938a711ee41b14 *NAMESPACE
-b6159543e84fe4d451aef0cbee94b7df *NEWS.md
+aec5cb1b5a8cfef6ca82a6d08f84325e *DESCRIPTION
+4641e94ec96f98fabc56ff9cc48be14b *LICENSE
+72a2b956dfe5844fedf26fc59a5e1059 *NAMESPACE
+f9809c53ccd5705f73c0be99cd32d186 *NEWS.md
7171046778fbb6a06b96a48bb9c6cb75 *R/aaa-.r
41c9ba0c55c5b2dac5c2ba80a9e6f0f0 *R/aes-calculated.r
a744d7b67886c9a66068cffb5e71699a *R/aes-colour-fill-alpha.r
-d901c095dc98fd19f788ed7814152f8d *R/aes-group-order.r
+8e1baf8ac5987592e1f5646d4c41569b *R/aes-group-order.r
53f129179804c8dbeb9a5fba52d5b056 *R/aes-linetype-size-shape.r
9b2ae79db47a3528738807199529ff3c *R/aes-position.r
-8a425278ff1b9e474098872a0c5e9493 *R/aes.r
-bb54062d751148cfc3c9fe4a2827a537 *R/annotation-custom.r
-1c56152690873e5cb80a9ede93222a45 *R/annotation-logticks.r
-23a65ca6c3e4ce2ffa1d11b94171e524 *R/annotation-map.r
-39f637bb6e438cfbe001e2f86eedc1f2 *R/annotation-raster.r
-f74649f9a228d8953f2b7bdf2f47ecb6 *R/annotation.r
+5a22db2c2782b3f44651a3f84cd35481 *R/aes.r
+25bb15e59d22f0776b6928b193da036e *R/annotation-custom.r
+117a827bc40c7c6e002b843a194219a8 *R/annotation-logticks.r
+4350257038de69c0d79b72d2217ec051 *R/annotation-map.r
+2a72caec0b9aff72ce077df1e3de3401 *R/annotation-raster.r
+361e5f3e3bab8aba9d12484842b2220d *R/annotation.r
eb4a7f643fd1a9c47fb64ed576a10e87 *R/autoplot.r
+da780ad50af3e05ff9db94c09986e9a8 *R/axis-secondary.R
57939239ee7146de73a2aba6de11f5a0 *R/bench.r
-c008fbcae7264267eb84acb684e18ec7 *R/bin.R
-8081549496f51360a79570c233a1929f *R/coord-.r
-7f06acc0754ed9be0e8499cf393c1d25 *R/coord-cartesian-.r
-09cadc86b4cc436f073d468b5024b7bc *R/coord-fixed.r
-c7a07694ef7c686f232fe3c0ba4e673a *R/coord-flip.r
-0dd48578dbceafa78fc2e170032790a2 *R/coord-map.r
+75b3c0cbcdc8569a1c0950aa5d21482e *R/bin.R
+392bc48426e5cc7ead1a305fbc9be5d3 *R/coord-.r
+8ff9cf4958c614558c710c46d48cbb09 *R/coord-cartesian-.r
+61bf85230648adf5a0e0c8d632dc92dd *R/coord-fixed.r
+df8b9d096865b986388b89931feb1226 *R/coord-flip.r
+7366b8aaf87749d31444f649a8b572b6 *R/coord-map.r
3cd117fab4fb175c188202dc7078b951 *R/coord-munch.r
-5a85a120c7c97d7528035e105a76a24b *R/coord-polar.r
+0bdd51a8975d4410c29697061f066fb4 *R/coord-polar.r
e042de925de133ab9978221438997b4e *R/coord-quickmap.R
-5b7efc37f850dba08b4dffb964122000 *R/coord-transform.r
-75a46d8776159cc4d2290536a459d524 *R/data.R
-1cc34cbdb97662693545d1f09f53c080 *R/facet-.r
-f6db1c26ddf5b3c7452b5252b7f04fb3 *R/facet-grid-.r
-455f53f8eeafdb025e1923f8fafc1105 *R/facet-labels.r
-bf8ddb7258fdc55f31454b5f6b82ab1e *R/facet-layout.r
-10fead8ab5f75e313766eef213ca175d *R/facet-locate.r
-9fabb1104100303c97d6132cf7aceb64 *R/facet-null.r
-97efe28b15c4317698b0ec49e5c2b279 *R/facet-viewports.r
-2e72831c1bb7a5c459f5acc2d0d66fae *R/facet-wrap.r
-185ec0bbe6e5fb2900d3bcb8b3a98947 *R/fortify-lm.r
-5ab10594c4dae94d6118c0a2655d5736 *R/fortify-map.r
-1e6a486f18bfe39121a436f47922f67c *R/fortify-multcomp.r
-c48d8fa6657357fbaaca6335107e0fc9 *R/fortify-spatial.r
-b6b4ce36f91452eddf970ab0f84360e3 *R/fortify.r
-fa9c44fc3c22fbfd1a9f352cfec46845 *R/geom-.r
-a77a946e74889047cc19b28693cbf4f5 *R/geom-abline.r
-a96676255590b096e27992e915a8447f *R/geom-bar.r
-c88130f984e10e44374e3c35eda15fa0 *R/geom-bin2d.r
-101aa2d1ab6e1b7508b8f62021fad144 *R/geom-blank.r
-6c26af5529d24130f4310186e8f99c31 *R/geom-boxplot.r
-2cce6a316c345907fbcbf31e24d3a2bd *R/geom-contour.r
-891b1fe7dce6e7e8b9bb87e38f40f541 *R/geom-count.r
+29d2067f711abf273f4819d600c25d5a *R/coord-transform.r
+a235479d31f2afb1a8fb0b15c275095b *R/data.R
+60424a7a66288196af3093e3fd30c8ad *R/facet-.r
+a0b70e01c464b263e7d58ab73e64df56 *R/facet-grid-.r
+1539a36fd3e5ddcb08b81adcd86ba965 *R/facet-null.r
+f3c2e3c3dfc6936eef2ef2718c03e2d4 *R/facet-wrap.r
+1dc876bfe72707568d943358617e306e *R/fortify-lm.r
+97b9d11d1105d13b7ccbf49f762e98e3 *R/fortify-map.r
+bb468dbfb06340d329fb0d79b02201a2 *R/fortify-multcomp.r
+a795cac7619299ea44aabf7fabeaa5b5 *R/fortify-spatial.r
+8c1a1ac77480bb8d59ad041a6bc5f50d *R/fortify.r
+c82d27a9b9f2513fd9486ada20397640 *R/geom-.r
+04cb3f7e10d83b022eba2cb77906c0cf *R/geom-abline.r
+6fbb9fab05b04e28075fa0315871f27b *R/geom-bar.r
+4abce7fdb6c87f2d8255f12710c3163d *R/geom-bin2d.r
+4dd34fb7d2492d8aae4ca41d44671d00 *R/geom-blank.r
+c4a9d472e2bf2682d5020e954921ef7e *R/geom-boxplot.r
+d3691fb8a8cacad72449aeac3085ae3c *R/geom-col.r
+765765703a05ee92f663bf7b7a7495eb *R/geom-contour.r
+3fbe0e03444d6ac030d4a007a731c3b6 *R/geom-count.r
3505ee228499af84d0e284e0208ad4e5 *R/geom-crossbar.r
-41365dd70a4fcc77afe2f53aa2d900df *R/geom-curve.r
-55172848e9beb54df54b8d6d2d2f91ae *R/geom-defaults.r
-33391cdb47cecb97415532e78cda470e *R/geom-density.r
-b3befdd4d6f980a4f531b2e7f28864d0 *R/geom-density2d.r
-ce66841aba9df11233df963837056900 *R/geom-dotplot.r
+7c4cd4b338eea7bf1fb8ca1302d44586 *R/geom-curve.r
+1db3037e9d7d4aff02c00dc2fe1699f4 *R/geom-defaults.r
+b29797f936ccac2c0ce4869354315091 *R/geom-density.r
+6389193dc86762cadaad02a6a682cf05 *R/geom-density2d.r
+8004028aee4c51a3d055cc8073ece181 *R/geom-dotplot.r
3bbf79cbeaccc53bc972d3319a516156 *R/geom-errorbar.r
-8fd6a340781ae915960ac7ddc7fc327e *R/geom-errorbarh.r
+b8b5349b44d96f294fac78a355671ba6 *R/geom-errorbarh.r
8129ffb0edb8c565179efe069dfb1f26 *R/geom-freqpoly.r
-f00e72a4a83b7ea9196379d7dadc37a7 *R/geom-hex.r
-2f32e6222a2f78fc94cad3cb759f6eac *R/geom-histogram.r
+e13dee39a744c95d3d16902556007352 *R/geom-hex.r
+ba1cd567c6649ace8dc19804af1494df *R/geom-histogram.r
ecfc67b8ccd284fac23b27874bd0e936 *R/geom-hline.r
-539e1374e446d7adc6964aaca1e8a36b *R/geom-jitter.r
+f6a52a3d46c3e2468f70adf22768001a *R/geom-jitter.r
1e701b7fca39e919d4d97e5b084c53c1 *R/geom-label.R
-29bcde9102012f50218481e7d97dc781 *R/geom-linerange.r
-bba9be6242a5126a96740fdb97f0641f *R/geom-map.r
-ae5dce0abc8a0243754835a42caa684c *R/geom-path.r
-3c3c64fae74f4a918045eacd0349b445 *R/geom-point.r
+74cd669b84a5da6c9d86415879492601 *R/geom-linerange.r
+aaaf1ba1d85cc3eb058f9bd8dd4f09da *R/geom-map.r
+aecde3f39e244983b8706cfd58bd7f7e *R/geom-path.r
+4d799401b32976dfcbbb45f5414789a6 *R/geom-point.r
4f8fca315a6bae9c1856e7f36f3b4c13 *R/geom-pointrange.r
-c70f5781c5a783a3f688370de92eb31b *R/geom-polygon.r
-256da5b4c9516af4885be6a1a2532a3b *R/geom-quantile.r
+5480f4749347b506fa931cb5a3b865a1 *R/geom-polygon.r
+02db392a143d015be46c7dbca877448d *R/geom-quantile.r
4318136c00615ababd58b3ad9dd8e3a3 *R/geom-raster.r
f5acd7b513a158d5a7115a82a69330ed *R/geom-rect.r
-96547a86a31fa51e6ef700fc2037c513 *R/geom-ribbon.r
-4f193539fd6749b699ce7dbbae6ac6a9 *R/geom-rug.r
-e2253a84b9cdcf7f4adff3089587f253 *R/geom-segment.r
-aedf4458c989202a6a4e8fde9d35ece0 *R/geom-smooth.r
-b4bc0d601f337458a08b2dc255bfcd60 *R/geom-spoke.r
-840a2068c875c932a1e20adbc4b75e6e *R/geom-text.r
-838fdf45748355c8a074512d6f936120 *R/geom-tile.r
-6c76d3fbd3008fe71db78ba0e4f872f4 *R/geom-violin.r
+769ee61e4cb2fa53d78d231125684a8d *R/geom-ribbon.r
+fd2429a3e3ae66076b2bbbbaa8860e5b *R/geom-rug.r
+42fd227eb2b32a4d922502c52f1effb2 *R/geom-segment.r
+f524621a61505402faef875c8639aa4d *R/geom-smooth.r
+817f2fb2dcd63751f38418f98e989188 *R/geom-spoke.r
+e5362d67113f6fd7bddebeed14346066 *R/geom-text.r
+6a67cb38076a5212cfbc465b1b7c4ef2 *R/geom-tile.r
+827fceb88a85027bf156c9af9311669f *R/geom-violin.r
a0ce9da0fa8dac5d5b3311513e15406c *R/geom-vline.r
-7fbb4a1b3a36deea08d5990dc9b82bb6 *R/ggplot2.r
-f147370b7f531891339d35260af1f633 *R/ggproto.r
+e648399f6192cc0f925180b41f6c6d01 *R/ggplot2.r
+88e63c858972aba5e24fdff09ac4fb88 *R/ggproto.r
abe3e869859c7f73fa4a9721df2ef29e *R/grob-absolute.r
57c5dee7337f17082fde9a43d071cc2c *R/grob-dotstack.r
95d44bd0d180dca5892db5674bec6362 *R/grob-null.r
a432566834bd4321476e8c8e1a5993f6 *R/grouping.r
-a81c889fb0d614d49262f7f627c6d314 *R/guide-colorbar.r
-1fad1b1dbe111530a190525ffaffe5ca *R/guide-legend.r
-4ffffa04a45a0fdc98d0b4a86b3964fd *R/guides-.r
-a6a7662bace821f91da3ababf1f42c11 *R/guides-axis.r
+f1e355b26ddb7e6b2b8e580ae9e8372c *R/guide-colorbar.r
+fd2197610f73f7df3b1f0fad77d46fbb *R/guide-legend.r
+d3ee693756d8b31cf70903905991a618 *R/guides-.r
+10b34c424016b2ee09d70210f7a5cd5c *R/guides-axis.r
91319825178764869b02d5ed527637e8 *R/guides-grid.r
-4516ab6dab4a8799e669a5c64b917a92 *R/hexbin.R
-d3c091b2ddf6c29197d5dd13f9f83d21 *R/labels.r
-e9012d6b78f7a735a48cd04389025580 *R/layer.r
+7959750b1fdc84a554988ca16a2fbbd3 *R/hexbin.R
+3f5ac8c3ec652eccc65cbbf14918b135 *R/labeller.r
+88e77bce46b26da3629469e6383d906f *R/labels.r
+a2d09e05122d77d032185afff0f10c5e *R/layer.r
+3bee76fa566fea533ad4429d988b2846 *R/layout.R
cb041a872c511f7508ccf9e0b7a2b68e *R/legend-draw.r
-22ac4e1d41ee5809fd19a21347000af6 *R/limits.r
-a12685c9806c50b576b3fa6edea4c95d *R/margins.R
-103f267c62ad9ce85913913147077918 *R/panel.r
-33234cd1d8699b1a1020e6162e64a761 *R/plot-build.r
-21d0eb8d4b914162038f04585ad70b09 *R/plot-construction.r
-e7d2e29ecfc09bffcf8518894eec414b *R/plot-last.r
-3ddfd4215c08fbf122599b2c34a888b2 *R/plot.r
-636580e38110490dd2f38b09bba0152c *R/position-.r
-9bb9cfaa59dd9d64dfcf2388f4ba0cdf *R/position-collide.r
-29baf4dc9462147822635b22109fb527 *R/position-dodge.r
-d30ffc9364ffa8b08256f601b7d67fcd *R/position-fill.r
+fcb8b3d4b07b175826838b88e8f80e6d *R/limits.r
+b8fd2602d0286b7fa47889847357c404 *R/margins.R
+c347cf27c8ea43aad5e51d9be9db7eab *R/plot-build.r
+db44a395a397b96cfeb6f2fe438c16b1 *R/plot-construction.r
+17a5687edb51a5d01f7cad17974916d7 *R/plot-last.r
+3c3b0d16a57d76bb08a8ee5ea599963a *R/plot.r
+7aff1851315fda3989a433a6ce551cd1 *R/position-.r
+94e8b39fb07be177d0b19423224f5ffc *R/position-collide.r
+f3072c581a7c9b6932d30ab28c56535c *R/position-dodge.r
8d8c6f5432e9f61de2fe222922b93142 *R/position-identity.r
-fe1e1022e11f937a360352a26255e8ac *R/position-jitter.r
-e4e46d5e81375b783697b672ce55ae2c *R/position-jitterdodge.R
-95c9046e151200799b13f4eb53b9a8e0 *R/position-nudge.R
-3bf43bb214de063904c603ce4f4909d2 *R/position-stack.r
-d88b979e879b28ea83929c9d410fb1bf *R/quick-plot.r
-42d7a80d2fdd0e3b2a53b58a0cbf9be9 *R/range.r
-c4fa493797bfd91074ac48614b09ec63 *R/save.r
-514ce47a8a2a62077636b7682b2a8d2b *R/scale-.r
-1b99c2f65e2b210e26f686a379629341 *R/scale-alpha.r
-a9d9dad9fd80dd209ebe50d8ea1d5558 *R/scale-brewer.r
-04a1eb6b2c8144a6528882dc64f95934 *R/scale-continuous.r
-61e114a0c99a03328d6dabcacc0ec9ad *R/scale-date.r
-0ca691dbd7734540ca50eabfdc4781d4 *R/scale-discrete-.r
-d577e4befed18b3306ff53f9c2a917e7 *R/scale-gradient.r
-bf1d0a9a8698b092799af1c0f966034a *R/scale-grey.r
-d0248ff685ce6bda7eba6fb9272e13dd *R/scale-hue.r
-e96f1acf5f333a0e2d3fb94ba0ac1682 *R/scale-identity.r
-7501deaed0c692d084705890995952df *R/scale-linetype.r
-6bf98c26a2f17a42134941151ef25af9 *R/scale-manual.r
-bf779de05e42c3302d62bab0c58658ef *R/scale-shape.r
-200ff19fd355ca908deade85210e4276 *R/scale-size.r
-ff4dea73d4aaa6641d22380863a6c947 *R/scale-type.R
+88c081f05771d686f2a2af1020d8bf8c *R/position-jitter.r
+3a7d86d7fc3270baa85fbf276a5003e9 *R/position-jitterdodge.R
+354314296e9708a975923aac6db1f480 *R/position-nudge.R
+bf5cf4d6514cb435eccb2b4e62656f6c *R/position-stack.r
+693ab67459dc0cb0e4b7435a3e69dbfe *R/quick-plot.r
+3b0a08e9a42ff183bda57179ce3ddad3 *R/range.r
+fe5929bb1247b8645cb03d54c14eb8d7 *R/save.r
+65d8529295f1babd51c9e0252cda7b70 *R/scale-.r
+cc5a5b298b06451970ef1859582bf0f4 *R/scale-alpha.r
+71410d9d620ea19af5961affeb9d7d79 *R/scale-brewer.r
+7785edd56e2841cb77e927b6b89539bf *R/scale-continuous.r
+c7cbbb506ba1966703ae868f9291907a *R/scale-date.r
+70871e4f1ce226c0cbe20e280f430fe1 *R/scale-discrete-.r
+c3e2d61778985b8d9f2f8cc60cf7e68e *R/scale-gradient.r
+ed8cf54545f3396657182ec8cfe4cd4c *R/scale-grey.r
+6bbe7be8e96b787cc2502aa139ffcbbf *R/scale-hue.r
+bb12149220242c7e5f402f181ce919bd *R/scale-identity.r
+fa46729fb574c00adc57d8685e2e7bb4 *R/scale-linetype.r
+c9a163672157dd0ab59e8147bb928bed *R/scale-manual.r
+c30347d699c78a5c2c0808e90c7d2eb8 *R/scale-shape.r
+c9419762f11c701dfc3510be004883f5 *R/scale-size.r
+28c4b4b85482c7d5cf59303562ba1dc6 *R/scale-type.R
9033369440f516d0cd7c1c2a80b8c2bf *R/scales-.r
-002772e3a128c3074f4d3100c9f63d9d *R/stat-.r
-17e1006a3af82af8d0d8ef106022c933 *R/stat-bin.r
+b02a94a2837d08c29b95f55b7d61504b *R/stat-.r
+ea9fd54ce1bda809ecc2ef5b3a6697ad *R/stat-bin.r
1635bcde69b4e9d6d87129bdf637262a *R/stat-bin2d.r
1df733e7ef7c92dc3830dc8575ee03b0 *R/stat-bindot.r
-50fb7e01c7f17addc242332d7a5d9974 *R/stat-binhex.r
-7cd8ec280a69afc9b965de0a1f8c1746 *R/stat-boxplot.r
-c50f8f9a9a7a94da06143bd39b19534d *R/stat-contour.r
-37fab9d9152ce0bb0262fffef3376f6d *R/stat-count.r
+a7e7c767f2f06f87e067a1754cc19a7e *R/stat-binhex.r
+e379850ef4fd6ab1c3358238bf667d0a *R/stat-boxplot.r
+a124ff852a4fd8a2bdf9cba235904654 *R/stat-contour.r
+a47e7c1e0e8cd125ddc403d3dcb4eb08 *R/stat-count.r
00a2bc6012bf166b8847fac2db06acd6 *R/stat-density-2d.r
-530636e52afa89032e39aaa0523cf9b2 *R/stat-density.r
-5369f50577747f1c1e89415e5c462a87 *R/stat-ecdf.r
-bf0e515f6799c217df737d25445bfc13 *R/stat-ellipse.R
-e6e47cbcac2f096948480ba01e92f28f *R/stat-function.r
-eb36628791a1918308e2a458a3e77aea *R/stat-identity.r
-cb854afda9c9cdc4578b0bac2c949d5d *R/stat-qq.r
+60af67e571677450dccbc8f03d506f88 *R/stat-density.r
+0f7bee01c4df60ee3920e53cfac864c3 *R/stat-ecdf.r
+6efb3263a0f4a9b314864e929db9b5f1 *R/stat-ellipse.R
+18a029945a4c3f158d1e1ccf7e5e7527 *R/stat-function.r
+e17c32034528eb1df308bd2aa8c1511f *R/stat-identity.r
+4b44390bbfabf3128227ca8416273905 *R/stat-qq.r
b9542dc7c675d1992487d0c49334b557 *R/stat-quantile.r
12924f87b366aa163300ee99f39c8dbf *R/stat-smooth-methods.r
-c10a2141b526b6ee7a74e1459526b6a2 *R/stat-smooth.r
-3fd5fcea86e72471b6b05e1f06d8fe1e *R/stat-sum.r
+8aeca4faeb291ce1700a053449eb50c4 *R/stat-smooth.r
+68cde82be89cf853171bebd405755555 *R/stat-sum.r
f50adf8c33ca45f41d2cc69a7450efbe *R/stat-summary-2d.r
f5b6045a5814dd15cc858d6ac792686c *R/stat-summary-bin.R
a7e0fd3364dd4842108043b76c7bc096 *R/stat-summary-hex.r
-9dd83fee5edeeb8315179fc82ad93dc2 *R/stat-summary.r
-407275f26e16aee2fd8547f61d1b2fd9 *R/stat-unique.r
-eeecedcc273617abb811c886f161b4fb *R/stat-ydensity.r
+392c56db2c8b9b2a7eee86f78bfbc262 *R/stat-summary.r
+ead27b711540674f4165dc1640a725ba *R/stat-unique.r
+2f0f62e77aa29c77b797937067887b41 *R/stat-ydensity.r
c92420a879310b86c1bcbb1e6fb45425 *R/summary.r
-9b50db9df88063e9fd1d56e2fc69de86 *R/theme-defaults.r
-7beceb23e6e35270cdf25d7df940fedd *R/theme-elements.r
-f6297c7f47ca260c98948aefa8e33254 *R/theme.r
-b97bd5e0d2c69643142fb2b4529a0cad *R/translate-qplot-ggplot.r
-ba2fc9a3d891a2991792efc0bdd25649 *R/translate-qplot-lattice.r
-31fad80a907f5ec247287296b5ffa4a4 *R/utilities-break.r
+951e981b94ea71edf1c78d9e38ddc6de *R/theme-current.R
+2119521ebfded436037c1992fcd76243 *R/theme-defaults.r
+e03b71fec1335001df05f4da7d941f36 *R/theme-elements.r
+66a0a906c695126852761f8a3bdd69f4 *R/theme.r
+33a3e6f7f309e53b462dba734646f71c *R/translate-qplot-ggplot.r
+81a2f1934f0f93e586108d1521e2abd7 *R/translate-qplot-lattice.r
+0a3fe0985172319afe38df44af6a652d *R/utilities-break.r
c27187f963d056888cde8428c0ca9530 *R/utilities-grid.r
-6f55c444360fe627ccd3669b74f122da *R/utilities-help.r
+2bed0f2b37a0e270092ffa31f971941f *R/utilities-help.r
3bef847b27dc1a7182eff846527f6fc6 *R/utilities-matrix.r
-a2f6c224351ced0c10b6f55dcd941fd3 *R/utilities-resolution.r
+1752e4a9d0671ef2f511ff19da46f2ea *R/utilities-resolution.r
e72a2689dc0d2076b7492a6ebd165b62 *R/utilities-table.r
-ae40757394892074687c9d11a7744f20 *R/utilities.r
+10d383d8ee481be06a3446673bcd1981 *R/utilities.r
eeec75de61894ddc1238ba5c894879b2 *R/zxx.r
-9ba24b6237d2e367c3f4c30d8cd968d1 *R/zzz.r
-0eb9195324e6152acd04d0e853a8b455 *README.md
-9918255ea02dd0585fec4061d6f70950 *build/partial.rdb
-0d9454fbd1cab8dc1e4d63a8ddf1ac2c *build/vignette.rds
+8727fae979ba198dd4960aa4d703d017 *R/zzz.r
+5b63dae9880edf74ecf2075c350eb90a *README.md
+3a37980418f6014d9230e47ab05b5086 *build/partial.rdb
+62b5a3a09eb3d7562b0d20e7f3808df2 *build/vignette.rds
7fd28ad1feab3e3c773313672cd1b3be *data/diamonds.rda
1c883c4bb873c21cd428435264c456d7 *data/economics.rda
3cc1cdb7f53ac95b7346683e44e225d1 *data/economics_long.rda
@@ -184,230 +184,235 @@ e1ef68be302f256fe79e1e2833f5f50f *data/presidential.rda
d419c85de9cb971b7ebec959a836f72c *data/seals.rda
2012af9687f6f5813b8aa6aa86773c67 *data/txhousing.rda
478b5d9f6c7d31a55ff4a26eab66578f *inst/CITATION
-3cd9eb0fa04c028287aecdac2bf95cdd *inst/doc/extending-ggplot2.R
-a0a183c6edece97cf73fbf564fbfa5b5 *inst/doc/extending-ggplot2.Rmd
-760149398ece2e28f72ece41bb53e32e *inst/doc/extending-ggplot2.html
+46cae52ecb13d7c4290be96f046b9f19 *inst/doc/extending-ggplot2.R
+a5c24ca25dc3ca0de9498de80fd597bd *inst/doc/extending-ggplot2.Rmd
+1be0b240bb65cd85b07ef00d24532df7 *inst/doc/extending-ggplot2.html
fd4fffee04dfb833e03497c4701be1b4 *inst/doc/ggplot2-specs.R
-7d2ce3bdee71e694250a1c764cfa5f8c *inst/doc/ggplot2-specs.Rmd
-0f97122414df042259e702415a821322 *inst/doc/ggplot2-specs.html
-d41d8cd98f00b204e9800998ecf8427e *inst/staticdocs/README.md
-7a09459ca2898424c3714a03f20665fd *inst/staticdocs/footer.html
-923198a65466326dfb62fec20e793e3c *inst/staticdocs/head.html
-8a0e4eb3cf19379924a78bc3f6fd3d7d *inst/staticdocs/icons.R
-a45c45d397ec9594b1abad195094f45a *inst/staticdocs/index.r
+4214125f454891075c8d8d34626a5fee *inst/doc/ggplot2-specs.Rmd
+40bef156a5e34809c88baa682eb4203d *inst/doc/ggplot2-specs.html
56c75450069dc75bc81998ee2ec1e689 *man/absoluteGrob.Rd
-ff67ba09d9d3541a10c519c3ed0d055c *man/add_theme.Rd
-20f0528eb3688034fab93fc721b65409 *man/aes.Rd
-efeed95b7b79a95a6186466d46869cec *man/aes_.Rd
+f5266d4b1769b3d868ce02f039373b89 *man/add_theme.Rd
+65c7b45903695a87f0080ffc652908e2 *man/aes.Rd
+000dc4ab73b2773b2df3de8d652d5409 *man/aes_.Rd
7712e3e5b0ab03a32404a8f034aa10f0 *man/aes_all.Rd
ef8ce354cb3b609f4c33f53cd26821cc *man/aes_auto.Rd
03b05c2b533b5a88d81a6ac37cd5de77 *man/aes_colour_fill_alpha.Rd
-48368f5dea136a4ec910b3c2e77cbf11 *man/aes_group_order.Rd
+39e0fb7caf3b0342f48949eab472a7b2 *man/aes_group_order.Rd
d7a853f2e395f95617f5b6da138cec65 *man/aes_linetype_size_shape.Rd
a99dcba86e888d8d6641653c7a5b972d *man/aes_position.Rd
-b012b63a37cd9c98384f13129d533360 *man/annotate.Rd
-5652e80103710fabd05920fcc372a8a2 *man/annotation_custom.Rd
+f157c937a70109cbe559b23e83733420 *man/annotate.Rd
+1a96a32d671dc0d2237ef1d1ddd5c699 *man/annotation_custom.Rd
e3f138140bdadb64a8aa2038218f4e25 *man/annotation_logticks.Rd
-8c8c2ba1b4e343fb916b354a6560fcb5 *man/annotation_map.Rd
-9a00f65c63148ff18f9fd46067bbdb4e *man/annotation_raster.Rd
-f428bf30bcd593b2b6940df5e04b7f58 *man/as.list.ggproto.Rd
-a01a9827585d5254017ca6ce9fbbb599 *man/as_labeller.Rd
+799c215b3fe64e942ed5bdecb1dfee83 *man/annotation_map.Rd
+d43ea00879a7581560ec2ee5196b691f *man/annotation_raster.Rd
+d68c76559552db3af0c5e93c5abd6d40 *man/as.list.ggproto.Rd
+0f8cde7a6c155aa0229db8c4162c8445 *man/as_labeller.Rd
43589aa720efd687662e0de7354eb4b0 *man/autoplot.Rd
9f6abc675b3e32de763c9d53fa9f77d0 *man/benchplot.Rd
-1a343455cf735e38d3fabfa499221ad5 *man/borders.Rd
-63c959e3e092bcf3a6fcc2366d3f1430 *man/calc_element.Rd
-3e26b171206c01912b6d838c222eb49f *man/continuous_scale.Rd
-f6d9adf026157a00fd92df166ac675fa *man/coord_cartesian.Rd
-61af3e0c70d351e48110df56657ca13e *man/coord_fixed.Rd
-ede8ed39297e89fe4dc8d9bb0eeb422a *man/coord_flip.Rd
-79cd96819a42d0185ce072d5ca853dfa *man/coord_map.Rd
+6647896c706573d085ac72f80aa8e521 *man/borders.Rd
+765bd43fbac050cc20fffdd191201fc5 *man/calc_element.Rd
+42b9460b04c0b7da5be4832f41d891c7 *man/combine_vars.Rd
+798dfb2d95d1096ea115cfe1f22d5dcb *man/continuous_scale.Rd
+4ed446a72797ad6ab4702673139194e1 *man/coord_cartesian.Rd
+c4da2d12b1c8ebbc61f67fbdd900d3c8 *man/coord_fixed.Rd
+1b37faa881028d55d2ee59dff1347a89 *man/coord_flip.Rd
+69322c26430c5324e2df87358dab7050 *man/coord_map.Rd
03ea1bbb888bb836e26d1d04ce859a76 *man/coord_munch.Rd
-3af68f29c13c188fc422415813089498 *man/coord_polar.Rd
-1025087210f72dabf5ef6308790b2c6e *man/coord_trans.Rd
-b3087f2c77e447cd21e6898615bfbf81 *man/cut_interval.Rd
-b3ce84b7cf66c2930d2a6ee63873f72d *man/diamonds.Rd
-7f99b154408e3c3de0ba90c584dcbb90 *man/discrete_scale.Rd
+7dc9548422b6a4148b097bdbaa5e0c3f *man/coord_polar.Rd
+346fa45ed2ca8acfa065c2f4bfacad28 *man/coord_trans.Rd
+d1a4575bab1fe0ff0cf2544b46970a05 *man/cut_interval.Rd
+f6e5d43960dc5fa64758524f4be2dbc0 *man/diamonds.Rd
+a68902d001113f1af7408df13b33f888 *man/discrete_scale.Rd
6be3e22abc22f2b478bbf7bf6f79e10c *man/draw_key.Rd
-4e7e5a544d058f424a52f3c2dba4cd91 *man/economics.Rd
-c4df9291f631ea3cd24f69c97f02f1e2 *man/element_blank.Rd
+92490fdfddee2a7bd4a4e0f647c2c2ca *man/economics.Rd
+c00e5ebd37cae86802178f5981d559b2 *man/element.Rd
1296c043ef8b9ed396cf6d0103f6f7ba *man/element_grob.Rd
-0e777f87e5497e22240fcc4febf0edda *man/element_line.Rd
-325bd1005487d98b4e7a39e2878c22ac *man/element_rect.Rd
-f5778a7dbfad57f255816ee8c590abfb *man/element_text.Rd
-5d9d25976953ec27acfc6428556eb3aa *man/expand_limits.Rd
-df0176ef30dbfdde4cce9cea8381d99d *man/facet.Rd
-b8d3b3cdac4a62f7fbd1882b767bc94e *man/facet_grid.Rd
-b79ffe497fda049f9f7718a4efa86867 *man/facet_null.Rd
-772a5f2d18d65842decf6d7fce27df91 *man/facet_wrap.Rd
+6135f2185dbf5070b954678af6042366 *man/expand_limits.Rd
+6feb2e27b5fed5489ded4a469299fb7e *man/facet_grid.Rd
+7d463ee66fa1ec8bd4a9a778caa753ad *man/facet_null.Rd
+677fa0628519920d06f7075afa8c8bb9 *man/facet_wrap.Rd
49314a7e941068d810878ceee132c904 *man/faithfuld.Rd
-294388d79ebc5fef1db58f4c06d12523 *man/format.ggproto.Rd
-84300d3fcdd22030ca8d1ac5d9f1df62 *man/fortify-multcomp.Rd
-60662763b7a08678979a92f218c9537c *man/fortify.Rd
-72f9d74ebc7f9b4354a702a02ac8acd4 *man/fortify.lm.Rd
-720abd47d0d039c70334c93b327f3141 *man/fortify.map.Rd
-7602d9d65104b9bc4e204bcb7894f445 *man/fortify.sp.Rd
-750ced57460204f1252f23f49b8387aa *man/geom_abline.Rd
-d01f490cacf87215fac4fb4c7724ee04 *man/geom_bar.Rd
-648d1c3cc181137034baa755dd3754d2 *man/geom_bin2d.Rd
-76fe6f24e9bec643e2a924803060fb8d *man/geom_blank.Rd
-e349223241134c76ab500f617bf0f0e5 *man/geom_boxplot.Rd
-d870e7d46d5131fd6e4335b6c872753e *man/geom_contour.Rd
-639a2a0990bf02bc0c183a302577136d *man/geom_count.Rd
-741c9f98418db946e303b90d5887743b *man/geom_density.Rd
-3ba68b899f9b46f08bb6aefe28f2d450 *man/geom_density_2d.Rd
-fc2e473b6c0e5d3448902902241863f3 *man/geom_dotplot.Rd
-2736238f9142b5e96fef4d88fd093258 *man/geom_errorbarh.Rd
-e3555ec1eab4e060e0d0bc8d5852c480 *man/geom_hex.Rd
-f93cd5e514d918af0a984f4918f14633 *man/geom_histogram.Rd
-cc3f124a7051f2069416dd768d7bb803 *man/geom_jitter.Rd
-29484d3543d6681f84b66683a088328c *man/geom_linerange.Rd
-992fd4b38d2a988b24dfec8fb9704052 *man/geom_map.Rd
-ab8fb39b0eb8d4aad16bc763e36502c3 *man/geom_path.Rd
-7bc71b4ce3b94cffbdc03b9da6543aa3 *man/geom_point.Rd
-0f77fb216b5f5a40fe0ac7ec4aa29b25 *man/geom_polygon.Rd
-10945610bcc44a55521d2b2590084cc5 *man/geom_quantile.Rd
-9de21040d79f19d12fed435c6d4ba94c *man/geom_ribbon.Rd
-26ed0bb5db5051598ce661b2a4dcf939 *man/geom_rug.Rd
-de5656f17d3bac3bf5cfef7e84d09281 *man/geom_segment.Rd
-295a6d67caf1c97c1ac1c62dd50049d8 *man/geom_smooth.Rd
-7c27a32863bf85f83d11f8bfd339668e *man/geom_spoke.Rd
-2dc58e50a0313a8d9809ac0acd38f468 *man/geom_text.Rd
-dbce3474b37580aeb473afd430e684e5 *man/geom_tile.Rd
-7025ef5ca55fcca57d003c494cf13b49 *man/geom_violin.Rd
-d22c6fff5fffed1dc41e1130a2ca56a5 *man/gg-add.Rd
+2e9f76d8db863fb7699ab16deecf8a09 *man/find_panel.Rd
+4fa2527f68b6d7c5a7e69c6b388c8603 *man/fortify-multcomp.Rd
+0c6159643f0ff8eee6d18394621660ed *man/fortify.Rd
+b6f07b4030874ed1e334e77ca0555c42 *man/fortify.lm.Rd
+6e59e6e47681b7741a292a25dc00c4ab *man/fortify.map.Rd
+8641f9bdfb7e5824969f80f1bb76e96b *man/fortify.sp.Rd
+87ca8d4f1bc1029b8e84cb33feb34498 *man/geom_abline.Rd
+a59a6aa7f847e043a753f9e1f69b89f6 *man/geom_bar.Rd
+c43cf1cef27a2e3438a48c24db37f06f *man/geom_bin2d.Rd
+a5c0aa7a4eabda93157060c4de27c6f4 *man/geom_blank.Rd
+0fc5acc51bd2709ec8083465811e0e0c *man/geom_boxplot.Rd
+1c0522f2552104eb7374c752b1b71d07 *man/geom_contour.Rd
+45561df383f17ad8dcaefcf3938349e1 *man/geom_count.Rd
+4b5a42a2dfce8df3f3ae0f8c373f637a *man/geom_density.Rd
+1f8ee579e699a69796a887f8d0eb5afd *man/geom_density_2d.Rd
+25cbaf6e4b4495968e612b1cf51d8fe6 *man/geom_dotplot.Rd
+637f63bba5d4698c72bbeb77a4049dda *man/geom_errorbarh.Rd
+78acb95e6a3e7a0d616b565a7fd447eb *man/geom_hex.Rd
+fbb36c8bcbc42dc602c1aad77b249970 *man/geom_histogram.Rd
+6160607614bf80f79e72f99ce4a9976e *man/geom_jitter.Rd
+230a79c3de26edd960fc80dc9cc3c39c *man/geom_linerange.Rd
+a43f471e8c1c467d48949168069389b6 *man/geom_map.Rd
+71c3546254b6f73d0c71a2ed1a7b534e *man/geom_path.Rd
+8848420356b3a6e9344d4dfcd640e9ad *man/geom_point.Rd
+deeffee79f47bd1d365c42cf18272470 *man/geom_polygon.Rd
+4bec9bf609dcfd418fbeb775b4c5eab9 *man/geom_qq.Rd
+763f2632481008dc2f05ffb5cfb04b54 *man/geom_quantile.Rd
+4b8563c07efb8f6b5ad2af6a10e1d6d9 *man/geom_ribbon.Rd
+7ce669c7077fc8ed4ad8a5ea4fedf3ab *man/geom_rug.Rd
+a361427422418a99d5695caaa440233f *man/geom_segment.Rd
+79e1e3763a565ba57cbc985db1856444 *man/geom_smooth.Rd
+7abbb406670202998dedfc5fcba30e87 *man/geom_spoke.Rd
+8f1f76b6ca1e244acab2220e73618118 *man/geom_text.Rd
+dd72a0b7a849af365ca1989bfdc85036 *man/geom_tile.Rd
+9eef3020494bd809fd45350d26e2a644 *man/geom_violin.Rd
+edb144634461f675eb2327f90967939a *man/gg-add.Rd
f1075176114c6b91fdbf223719cd981a *man/gg_dep.Rd
-c63db10ccb04b87aedfdda2119ef9b5c *man/ggplot.Rd
-238ef3805c533dc74575a0853d06c636 *man/ggplot2-ggproto.Rd
+63d7cb98e9eda74ddf46e46761b5ec91 *man/ggplot.Rd
+39daf04da2137137c18346a90f8d45e7 *man/ggplot2-ggproto.Rd
+0c536abe136279334bf9ac63672b9fd8 *man/ggplot2-package.Rd
a4bef608dde830e27ac03f259c102cef *man/ggplotGrob.Rd
449d55cacfef6d03d889df009992894e *man/ggplot_build.Rd
7aedf2453e47d8d2bebd38923501c60b *man/ggplot_gtable.Rd
-db86dd2f3896ef6a5950d64b0a4d2766 *man/ggproto.Rd
-7fadc3359fc39669f9e3b3bd59dea9a0 *man/ggsave.Rd
-33dc8fe57712ee1ca53e7373cff3690c *man/ggtheme.Rd
-7a6498b8983bc96328ce0846ce0e877c *man/graphical-units.Rd
-6759e461416f3ce513ad249dd15fa106 *man/guide_colourbar.Rd
-92cb04f0845d6d89bad331c61b44de84 *man/guide_legend.Rd
-81243640ba87fee316373900d47bd170 *man/guides.Rd
-7e0bd3d61fe1df07da190c3ced288fdf *man/hmisc.Rd
+d34b5628c9843a2a09c88a7730af110d *man/ggproto.Rd
+f3da1018d84cdbb5ac8717db05995c71 *man/ggsave.Rd
+474c0bb30892409140b691c864e52028 *man/ggtheme.Rd
+3645847b616286988de7845d45982d7c *man/graphical-units.Rd
+69cafbf47b9600caacf9a9668d075a48 *man/guide_colourbar.Rd
+38450bfb9b92aa79734c45216f6dcc20 *man/guide_legend.Rd
+68e979d1d908e485434bd51bd54b2430 *man/guides.Rd
+f9e92e47f9910dfb7ab261e2bb5504bc *man/hmisc.Rd
630598fb99d0a7a34bbb146074dfacc0 *man/is.Coord.Rd
15366e61452b09ab99fe3c4d76402aad *man/is.facet.Rd
8656337fbb445f7df31749f916df4879 *man/is.ggplot.Rd
-b6171db99a23771ff09509040977439c *man/is.ggproto.Rd
-9d8a05937e7e0b8637eded383b367400 *man/is.rel.Rd
-9dd92b4dd7a88320e6d1486625f7f018 *man/is.theme.Rd
-6d35b6f8b4fef5a3757a999d2294cadb *man/label_bquote.Rd
-489c778eadaba0fc2ea1779450e723d1 *man/labeller.Rd
-86f29d63e2e2434a227e2de707b8f54e *man/labellers.Rd
-b5821e641bcc949b92d3b27871ff5ff3 *man/labs.Rd
-ec27029cac6b31ea9157ef0feb154b07 *man/last_plot.Rd
-e551b1f98d545489785941488546a0f1 *man/layer.Rd
+c406c823b077c991495dc95a1da5d108 *man/is.rel.Rd
+e4c98acc6af3eed7aeaa7af63606b5a0 *man/is.theme.Rd
+1ffb78b895eb68ef57d617244fd3c539 *man/label_bquote.Rd
+822209ea5af04175de032af178bcf54e *man/labeller.Rd
+08f003c7080e79471eb8fb3673b8f4ff *man/labellers.Rd
+53c32b6e2264f8e7ffaf36a800537f1a *man/labs.Rd
+ae2e3fb68a21250aea51d382a9a465ad *man/last_plot.Rd
+91f9e7041bad2a09befc9c2c9535b6bc *man/layer.Rd
115cedc87e2e5383505475fe2cf63054 *man/limits.Rd
-377da418fcc446ccf31c060c52bc0d7a *man/lims.Rd
-3ed091075707aa13724a9043b3ed2aa1 *man/luv_colours.Rd
-21a16a89724a7fb35b1e4a83497bef74 *man/map_data.Rd
-449492299ada55b08955ca2efcb2c73a *man/margin.Rd
-95ceb14a8b8d499aa1e3a13318fc2b98 *man/mean_se.Rd
-230020b63a1ed18e271efbb407269e64 *man/midwest.Rd
-ed26f5ff992715b558e465f84f9a5ee6 *man/mpg.Rd
-1db47d6001687f68ad3ae1cb53cb1c0a *man/msleep.Rd
-a2e7c0d524aa4ddb36a5c3800adc00b2 *man/position_dodge.Rd
-ae3e6c3a943617e4d58eb0954a0c76e0 *man/position_identity.Rd
-a6e474584d501fccbc8f44c7b5cfc477 *man/position_jitter.Rd
-17959133179918180ec83f11c4cbc621 *man/position_jitterdodge.Rd
-605df25d4d57fa0117b5247aa1c15205 *man/position_nudge.Rd
-bec4dbb370a26eb76d196c9208f95e94 *man/position_stack.Rd
-7e72c18b1ffb5adfb4f691f8074bbd35 *man/presidential.Rd
-3214de68f499011f976c396ae519e6de *man/print.ggplot.Rd
-f130ffe874ab878cfba9e43a145df148 *man/print.ggproto.Rd
-2618d742cecc1748f77cd870aade6839 *man/qplot.Rd
+672f94600157b9eeb6e9529beb532c3d *man/lims.Rd
+873720c015c2ed92083ba70b634867bb *man/luv_colours.Rd
+ba25f6f853fe1a301293418f3df51f07 *man/macros/aesthetics.Rd
+06465b0db964bf023fcdd16ab62a8043 *man/map_data.Rd
+9dbda53ad3f4dfc4431ae0f5c3d08dbc *man/max_height.Rd
+27e97c7a3de848c93f2bd710625e53af *man/mean_se.Rd
+ac74fc5cdcb5861d20230c4922c56734 *man/midwest.Rd
+a121bce16b9e3de30419dd99b7290ddf *man/mpg.Rd
+e56ded2ad64c557132120c46646ebf29 *man/msleep.Rd
+99a80a6e079d96d7a3b61a45f77e4245 *man/position_dodge.Rd
+43f1322bf3d8825be79471f5b98be7fe *man/position_identity.Rd
+9abcc66043f37323f065c5f5d065d756 *man/position_jitter.Rd
+2833d368a93686f1c5bb081a0c01134d *man/position_jitterdodge.Rd
+985d1bd1b9103520eabdb529435ad23e *man/position_nudge.Rd
+0231d3ba231572735b9c3af58dc5c369 *man/position_stack.Rd
+7a3de6cba1805a72f89154a9ae1d1ea7 *man/presidential.Rd
+04fa16f24a5071210a5f1bf673c1489b *man/print.ggplot.Rd
+66648d4b930a8a31c598409f32f75010 *man/print.ggproto.Rd
+e1cf05ab2bcf0ee198b89214e8f2da63 *man/qplot.Rd
774af14fcd1baed19c001ae81a31f2aa *man/reexports.Rd
-2a3d7cca6cadd2c1c919f77f97fa118e *man/rel.Rd
d7f87c2dfc2d156fb61cba090a620299 *man/remove_missing.Rd
-0945809c10e67d31c5337b7300d14d42 *man/resolution.Rd
-7646f1860ccfcfdef643a36a96838422 *man/scale_alpha.Rd
-89d50c11d76e3222056ca9bef1eee95c *man/scale_brewer.Rd
-1d97e27509e6d0ac39fc87e598d337a9 *man/scale_continuous.Rd
-445d9ce9a48c7656f7e0e196a76edc29 *man/scale_date.Rd
-c210a92df4262fb598bd058f0fee5c52 *man/scale_discrete.Rd
-7b31f2ec29b6f02264d9be81a841d7a0 *man/scale_gradient.Rd
-cf572e09e6ec62dc446f6ee845713751 *man/scale_grey.Rd
-2315b109bc3814b19fe1eca32d71fc45 *man/scale_hue.Rd
-f6a2295e5df163391b3759359def22cb *man/scale_identity.Rd
-cdf000a350d3bc9df114f5d73889becb *man/scale_linetype.Rd
-7b1d99d5983f282fd910e9b53378f0c6 *man/scale_manual.Rd
-baefd2a18b730ad3b123ae8973e3accc *man/scale_shape.Rd
-73d9bf4e9ca971c2e09809f9ff989342 *man/scale_size.Rd
-b59ea5ba748f6cff46c84679997afc9b *man/seals.Rd
+5cfb6f6ba399cce08e9bca839e68dcdd *man/render_axes.Rd
+16c93e1f87ea8e5f5261146675f51e25 *man/render_strips.Rd
+7577d34cefb50bda9c8ea34ada441f1a *man/resolution.Rd
+f576eef76424d5312ad3a25c79902df3 *man/scale_alpha.Rd
+be0b130a29a4a061a49021957dc54491 *man/scale_brewer.Rd
+4a6d03840addd503c696b0f0842b1c5c *man/scale_continuous.Rd
+acf3ab11ce211a0457d75e1aa25dec8d *man/scale_date.Rd
+6626e77d0bca78ae6bbe0ed21c41369a *man/scale_discrete.Rd
+53f3e4baa6c9f5e7ef8e12d76b8e11a7 *man/scale_gradient.Rd
+7204f6152cb4a7a30ffa303d5425faf4 *man/scale_grey.Rd
+61abbec81e558f63589dfb71cede809c *man/scale_hue.Rd
+f37d4c79a4b5f1e08f43702f9da0d3b6 *man/scale_identity.Rd
+9391ddb834a734e6dc5d8e62815a296e *man/scale_linetype.Rd
+4109806dc8f87fabd826dd0ad7ccd218 *man/scale_manual.Rd
+e80f081f7b5a9163cf156fe4fd482559 *man/scale_shape.Rd
+9d1e6f1f0fca8eba2b7c73e0ce0c1abe *man/scale_size.Rd
+a1f55c889ce5a4b474d6ce0c866e7dac *man/seals.Rd
+996011ba402ecf59d27bd0fd7a09007a *man/sec_axis.Rd
1056d55d33485453dd04e35394dddbac *man/should_stop.Rd
-deffe971dc96a2ad0af9bc540dd24120 *man/stat_ecdf.Rd
-a44557727af8ce2613f543ed208754c5 *man/stat_ellipse.Rd
-b14f79b80724a6aa3bea34a00113482b *man/stat_function.Rd
-2eea6067b69ce41c0c6060ff8dec13e8 *man/stat_identity.Rd
-237ac5e2002e510db181269c677c3278 *man/stat_qq.Rd
-951594691eb93b454f362a284cea4603 *man/stat_summary.Rd
-20ae95e1741a7e4c828ef715e047ed04 *man/stat_summary_2d.Rd
-f221aeef4b1a78a6b408097385d36fe8 *man/stat_unique.Rd
+7bc63cbd82e32cb8886d711f28a5bcfa *man/stat_ecdf.Rd
+dea8f052d2032d9f6a37fb4c1bd2de89 *man/stat_ellipse.Rd
+00d904ce8d93419ab6d57efea3724e0d *man/stat_function.Rd
+2511dbecb658334fbd365f8119f29286 *man/stat_identity.Rd
+bb56c18ca8f111360beb2d0046efe47d *man/stat_summary.Rd
+f350d8fbfb96f104b5769a85c011fed6 *man/stat_summary_2d.Rd
+03c4c087ba66b23cc0289a54740d64f5 *man/stat_unique.Rd
ab1fa2e5b12d61345c6df1c55d057290 *man/summary.ggplot.Rd
-9850ada2b758839395edf07c936cdd0e *man/theme.Rd
-414ae32e79a1cd06063d9b524503346e *man/theme_update.Rd
+df1f15c673de0613bb682812bcfdf79d *man/theme.Rd
+3232b5a6e2ab94fe9877a5faf1b1975a *man/theme_get.Rd
ec58a310165674abab536ca72c129ac5 *man/transform_position.Rd
-8feeb91c8e5d9077dc5a37ed1808af46 *man/translate_qplot_ggplot.Rd
-8b3e7fb1c8204cabc0eae18979880e4b *man/translate_qplot_lattice.Rd
-d8ce22b7718402206269b27a8f954b22 *man/txhousing.Rd
-efdbab8bf593c178c64d6e1fc22205cb *man/update_defaults.Rd
-df4c14b5a8d448e6bab888cab8831378 *man/update_labels.Rd
+6d49c3b4a6b16db5db560d767b8f2bce *man/translate_qplot_ggplot.Rd
+730cf379f91d5f51083a8f47b19af4a4 *man/translate_qplot_lattice.Rd
+4effd00599df94055c75ee30108691ba *man/txhousing.Rd
+94862eb34388d40dc5b01223b440f179 *man/update_defaults.Rd
+81f6205b20f2cde5882dbf1f6e7714f0 *man/update_labels.Rd
00df4f81231118534f870304920f88db *man/waiver.Rd
+423cd1c258a41686727ffb77f095e961 *man/wrap_dims.Rd
9ef186947b49d31af9fec818c266e4ce *man/zeroGrob.Rd
d61ade7569d3176888310444bed0e4ef *tests/testthat.R
-30b231302ed86d4a6a45c339d7780561 *tests/testthat/Rplots.pdf
-d3a921ea3008eada9d101257f4f15594 *tests/testthat/helper-plot-data.r
+c5e766e860663cd1c2b0007b088a43dd *tests/testthat/Rplots.pdf
+76b33d4c5897b19cd3f9deecb0abaa51 *tests/testthat/helper-plot-data.r
+461f0fcb435bd455024afae489a1e211 *tests/testthat/test-add.R
1076ad0ef6acd3a78dd8db456f9bba04 *tests/testthat/test-aes-grouping.r
ae2c17740d61430bc7c28953b49e61d2 *tests/testthat/test-aes-setting.r
56417865028605cba1c09e3a5f39663a *tests/testthat/test-aes.r
-1093aca9aab9868183b5357b945ed379 *tests/testthat/test-annotate.r
-ebd4f529f2d6e6c75fd386bf4dd32c18 *tests/testthat/test-boxplot.r
-dfb45a5e2f1c12d8ac1c5021a7d642af *tests/testthat/test-build.r
+880254f7c7c356d17cf9de1e1c7a4ec5 *tests/testthat/test-annotate.r
+15007da53259edd2b0ea0c8c852328d2 *tests/testthat/test-boxplot.r
+04763961d870fb9a0dcf92e3bf89adde *tests/testthat/test-build.r
a61803e15d2625dc599faa8f3f6b1fb2 *tests/testthat/test-coord-polar.r
6e70a3a306f7272bb7391af0af9cb84e *tests/testthat/test-coord-train.r
-9ec3f36295915b84878da970f38610cc *tests/testthat/test-data.r
-d0b20527ddaca6ab1f99c1cfc449e15d *tests/testthat/test-dotplot.r
+daad32ca40517d97ef4f4d89a19af9c0 *tests/testthat/test-data.r
+e9b8b346b28946475feb137f3827ea31 *tests/testthat/test-dotplot.r
2b3c216d68ac31f27e027669bb17dbf6 *tests/testthat/test-empty-data.r
6728e5c8edd8d12a05308e12b82197b6 *tests/testthat/test-facet-.r
-fafcc66255d651a5d3c8819fb0d069fd *tests/testthat/test-facet-labels.r
-32772170a1a135a200a9748c63ddfbcc *tests/testthat/test-facet-layout.r
-14ad2ef393e2b8c42ed26caae00faf1a *tests/testthat/test-facet-locate.r
-da4133fc542d63cc560632c272a8fead *tests/testthat/test-facet-strips.r
+24943dc463a3147dbf5ef4387bbd305d *tests/testthat/test-facet-labels.r
+c462388ef1f8e65b4029361b51931532 *tests/testthat/test-facet-layout.r
+7f3b5fcd30b44095b68991c37da71d6f *tests/testthat/test-facet-map.r
+43509c86e6bc7d338c7c2d4fc494d5e1 *tests/testthat/test-facet-strips.r
24307e570b5c3a261fbac843622f6dfb *tests/testthat/test-fortify.r
-77262eb0c26b03cdcb6a7a610d584ccf *tests/testthat/test-function-args.r
+e6c13f5d5a1a3ad8159ae56719356c96 *tests/testthat/test-function-args.r
a313873083761be05009614b1af7f85b *tests/testthat/test-geom-boxplot.R
30a99868232c5278af513e0d220f30bf *tests/testthat/test-geom-freqpoly.R
+da4f831a250b1f2921e9cef443ddd517 *tests/testthat/test-geom-hex.R
7d5ddcaf54c7b4cf75a4448edc954ccf *tests/testthat/test-geom-ribbon.R
3a8b464d19174e4eece703c99839174c *tests/testthat/test-geom-rule.R
b7060e5ccf4c447b38e855310bfd140c *tests/testthat/test-geom-text.R
ab3e947e4450a3a42d10b25d2fbffc98 *tests/testthat/test-geom-tile.R
-11b564057fd03011bca4861fbf2c4798 *tests/testthat/test-geom-violin.R
+c59f1a565ca2c3eb0a4504de2eca64ec *tests/testthat/test-geom-violin.R
f7de4f656b033dab01630f1ed918c7b5 *tests/testthat/test-ggsave.R
493eb5e788ec25e42e2b67c03ef074df *tests/testthat/test-grid-utils.R
-cb855e4c0ec9892813c5f86f838b3f52 *tests/testthat/test-guides.R
-6318ef949123e4e13c938ffc0e3d4010 *tests/testthat/test-labels.r
-98f8d9fc455c3c781d5f9f3bbb6e3ff7 *tests/testthat/test-layer.r
+fe35756cf49c81c0016df276f7231310 *tests/testthat/test-guides.R
+766c84d55d27324b0b30e251dcf14858 *tests/testthat/test-labels.r
+3876e0fe8a2653c4e7ca42b70e0300aa *tests/testthat/test-layer.r
c2b1f4b1d448df295563ae1ed9dc1886 *tests/testthat/test-munch.r
+7d18911043f7a714758019078e938418 *tests/testthat/test-position-stack.R
972abcb47b4a68ac9f086af1c8198da9 *tests/testthat/test-qplot.r
99ee168c3f25007fca5e93c76453038c *tests/testthat/test-range.r
d2f2d93cd9cfda4de1268614593e15f9 *tests/testthat/test-sanitise-dim.r
-e8d8d6c6cf4d7cc4df05a43eecdd1fe7 *tests/testthat/test-scale-discrete.R
+2ee3dd85a4c4bee9e143026769e0bc53 *tests/testthat/test-scale-date.R
+96f11e5a569046c4133f8019a4bf453b *tests/testthat/test-scale-discrete.R
926a7d6e1dd028761c51e1926c605c82 *tests/testthat/test-scale-manual.r
11af33c3989010aec22c5c5ebb34108f *tests/testthat/test-scales-breaks-labels.r
-cc078e4e9973263342f51212c2ecb71f *tests/testthat/test-scales.r
-21b7159e833753f4ea9389f7c9a17807 *tests/testthat/test-stat-bin.R
+2bb289dca8a4df61fb94dcc424e35b6e *tests/testthat/test-scales.r
+86e0b22fd008b74243aa76ffc209dcea *tests/testthat/test-stat-bin.R
6d202b2200dd789edfc46aa1751f34d6 *tests/testthat/test-stat-bin2d.R
264a54ed95553c9d2bef6fa83a8245c9 *tests/testthat/test-stat-density.R
10744c4e51dd7cd19728e845f4171880 *tests/testthat/test-stat-density2d.R
+ad642a876a84a6d71acb68ccb701a9bb *tests/testthat/test-stat-hex.R
547fbe507cc42321b6257125ba33f1c3 *tests/testthat/test-stat-sum.R
078bb7203f95d2e0b961f494ab73a0d2 *tests/testthat/test-stats-function.r
-4b6d4c25b74a97b1e27a0e169126e37f *tests/testthat/test-stats.r
-03ac3fdccc2a146db16f700f1b657cb6 *tests/testthat/test-theme.r
-ce26fb5635464c1ddc29cd992f03108f *tests/testthat/test-utilities.r
+1de1deca86597fc1b2dc9187c9b887b1 *tests/testthat/test-stats.r
+02e1ecf45e2dcc1a786a9b9047350ded *tests/testthat/test-theme.r
+a4593abadd232ec41f81017ac93c2a95 *tests/testthat/test-utilities.r
ac880a6aa04fc8644fed280c88b5d674 *vignettes/car.png
-a0a183c6edece97cf73fbf564fbfa5b5 *vignettes/extending-ggplot2.Rmd
-7d2ce3bdee71e694250a1c764cfa5f8c *vignettes/ggplot2-specs.Rmd
+a5c24ca25dc3ca0de9498de80fd597bd *vignettes/extending-ggplot2.Rmd
+4214125f454891075c8d8d34626a5fee *vignettes/ggplot2-specs.Rmd
+d7381f8e67eed0e54419e6cae59868c9 *vignettes/releases/ggplot2-1.0.0.Rmd
+33bf4ee5885c22e2cffeca986be98e41 *vignettes/releases/ggplot2-2.0.0.Rmd
+ec5ef98db8656ee6556953f737e91bb9 *vignettes/releases/ggplot2-2.1.0.Rmd
+05db68898eb6b9b4898823d6c973d4ac *vignettes/releases/ggplot2-2.2.0.Rmd
diff --git a/NAMESPACE b/NAMESPACE
index 5297ffb..855d9f0 100644
--- a/NAMESPACE
+++ b/NAMESPACE
@@ -13,26 +13,7 @@ S3method(element_grob,element_blank)
S3method(element_grob,element_line)
S3method(element_grob,element_rect)
S3method(element_grob,element_text)
-S3method(facet_axes,grid)
-S3method(facet_axes,wrap)
-S3method(facet_map_layout,grid)
-S3method(facet_map_layout,null)
-S3method(facet_map_layout,wrap)
-S3method(facet_panels,grid)
-S3method(facet_panels,wrap)
-S3method(facet_render,grid)
-S3method(facet_render,null)
-S3method(facet_render,wrap)
-S3method(facet_strips,grid)
-S3method(facet_strips,wrap)
-S3method(facet_train_layout,grid)
-S3method(facet_train_layout,null)
-S3method(facet_train_layout,wrap)
-S3method(facet_vars,grid)
-S3method(facet_vars,null)
-S3method(facet_vars,wrap)
S3method(finite.cases,data.frame)
-S3method(format,facet)
S3method(format,ggproto)
S3method(format,ggproto_method)
S3method(fortify,"NULL")
@@ -89,7 +70,6 @@ S3method(predictdf,glm)
S3method(predictdf,locfit)
S3method(predictdf,loess)
S3method(print,element)
-S3method(print,facet)
S3method(print,ggplot)
S3method(print,ggplot2_bins)
S3method(print,ggproto)
@@ -103,6 +83,7 @@ S3method(scale_type,POSIXt)
S3method(scale_type,character)
S3method(scale_type,default)
S3method(scale_type,factor)
+S3method(scale_type,hms)
S3method(scale_type,logical)
S3method(scale_type,numeric)
S3method(scale_type,ordered)
@@ -115,6 +96,7 @@ export("%+%")
export("%+replace%")
export(.pt)
export(.stroke)
+export(AxisSecondary)
export(Coord)
export(CoordCartesian)
export(CoordFixed)
@@ -123,6 +105,10 @@ export(CoordMap)
export(CoordPolar)
export(CoordQuickmap)
export(CoordTrans)
+export(Facet)
+export(FacetGrid)
+export(FacetNull)
+export(FacetWrap)
export(Geom)
export(GeomAbline)
export(GeomAnnotationMap)
@@ -130,6 +116,7 @@ export(GeomArea)
export(GeomBar)
export(GeomBlank)
export(GeomBoxplot)
+export(GeomCol)
export(GeomContour)
export(GeomCrossbar)
export(GeomCurve)
@@ -223,6 +210,7 @@ export(autoplot)
export(benchplot)
export(borders)
export(calc_element)
+export(combine_vars)
export(continuous_scale)
export(coord_cartesian)
export(coord_equal)
@@ -236,6 +224,7 @@ export(coord_trans)
export(cut_interval)
export(cut_number)
export(cut_width)
+export(derive)
export(discrete_scale)
export(draw_key_abline)
export(draw_key_blank)
@@ -252,16 +241,17 @@ export(draw_key_smooth)
export(draw_key_text)
export(draw_key_vline)
export(draw_key_vpath)
+export(dup_axis)
export(element_blank)
export(element_grob)
export(element_line)
export(element_rect)
export(element_text)
export(expand_limits)
-export(facet)
export(facet_grid)
export(facet_null)
export(facet_wrap)
+export(find_panel)
export(fortify)
export(geom_abline)
export(geom_area)
@@ -269,6 +259,7 @@ export(geom_bar)
export(geom_bin2d)
export(geom_blank)
export(geom_boxplot)
+export(geom_col)
export(geom_contour)
export(geom_count)
export(geom_crossbar)
@@ -340,11 +331,15 @@ export(layer_scales)
export(lims)
export(map_data)
export(margin)
+export(max_height)
+export(max_width)
export(mean_cl_boot)
export(mean_cl_normal)
export(mean_sdl)
export(mean_se)
export(median_hilow)
+export(panel_cols)
+export(panel_rows)
export(position_dodge)
export(position_fill)
export(position_identity)
@@ -356,6 +351,8 @@ export(qplot)
export(quickplot)
export(rel)
export(remove_missing)
+export(render_axes)
+export(render_strips)
export(resolution)
export(scale_alpha)
export(scale_alpha_continuous)
@@ -425,6 +422,7 @@ export(scale_x_discrete)
export(scale_x_log10)
export(scale_x_reverse)
export(scale_x_sqrt)
+export(scale_x_time)
export(scale_y_continuous)
export(scale_y_date)
export(scale_y_datetime)
@@ -432,6 +430,8 @@ export(scale_y_discrete)
export(scale_y_log10)
export(scale_y_reverse)
export(scale_y_sqrt)
+export(scale_y_time)
+export(sec_axis)
export(should_stop)
export(stat_bin)
export(stat_bin2d)
@@ -480,6 +480,7 @@ export(update_geom_defaults)
export(update_labels)
export(update_stat_defaults)
export(waiver)
+export(wrap_dims)
export(xlab)
export(xlim)
export(ylab)
@@ -488,6 +489,8 @@ export(zeroGrob)
import(grid)
import(gtable)
import(scales)
+importFrom(lazyeval,f_eval)
importFrom(plyr,as.quoted)
importFrom(plyr,defaults)
importFrom(stats,setNames)
+importFrom(tibble,tibble)
diff --git a/NEWS.md b/NEWS.md
index 9e622fd..8729447 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -1,3 +1,227 @@
+# ggplot2 2.2.0
+
+## Major new features
+
+### Subtitle and caption
+
+Thanks to @hrbrmstr plots now have subtitles and captions, which can be set with the `subtitle` and `caption` arguments to `ggtitle()` and `labs()`. You can control their appearance with the theme settings `plot.caption` and `plot.subtitle`. The main plot title is now left-aligned to better work better with a subtitle. The caption is right-aligned (@hrbrmstr).
+
+### Stacking
+
+`position_stack()` and `position_fill()` now sort the stacking order to match grouping order. This allows you to control the order through grouping, and ensures that the default legend matches the plot (#1552, #1593). If you want the opposite order (useful if you have horizontal bars and horizontal legend), you can request reverse stacking by using `position = position_stack(reverse = TRUE)` (#1837).
+
+`position_stack()` and `position_fill()` now accepts negative values which will create stacks extending below the x-axis (#1691).
+
+`position_stack()` and `position_fill()` gain a `vjust` argument which makes it easy to (e.g.) display labels in the middle of stacked bars (#1821).
+
+### Layers
+
+`geom_col()` was added to complement `geom_bar()` (@hrbrmstr). It uses `stat="identity"` by default, making the `y` aesthetic mandatory. It does not support any other `stat_()` and does not provide fallback support for the `binwidth` parameter. Examples and references in other functions were updated to demonstrate `geom_col()` usage.
+
+When creating a layer, ggplot2 will warn if you use an unknown aesthetic or an unknown parameter. Compared to the previous version, this is stricter for aesthetics (previously there was no message), and less strict for parameters (previously this threw an error) (#1585).
+
+### Facetting
+
+The facet system, as well as the internal panel class, has been rewritten in ggproto. Facets are now extendable in the same manner as geoms and stats, as described in `vignette("extending-ggplot2")`.
+
+We have also added the following new fatures.
+
+* `facet_grid()` and `facet_wrap()` now allow expressions in their facetting
+ formulas (@DanRuderman, #1596).
+
+* When `facet_wrap()` results in an uneven number of panels, axes will now be
+ drawn underneath the hanging panels (fixes #1607)
+
+* Strips can now be freely positioned in `facet_wrap()` using the
+ `strip.position` argument (deprecates `switch`).
+
+* The relative order of panel, strip, and axis can now be controlled with
+ the theme setting `strip.placement` that takes either `inside` (strip between
+ panel and axis) or `outside` (strip after axis).
+
+* The theme option `panel.margin` has been deprecated in favour of
+ `panel.spacing` to more clearly communicate intent.
+
+### Extensions
+
+Unfortunately there was a major oversight in the construction of ggproto which lead to extensions capturing the super object at package build time, instead of at package run time (#1826). This problem has been fixed, but requires re-installation of all extension packages.
+
+## Scales
+
+* The position of x and y axes can now be changed using the `position` argument
+ in `scale_x_*`and `scale_y_*` which can take `top` and `bottom`, and `left`
+ and `right` respectively. The themes of top and right axes can be modified
+ using the `.top` and `.right` modifiers to `axis.text.*` and `axis.title.*`.
+
+### Continuous scales
+
+* `scale_x_continuous()` and `scale_y_continuous()` can now display a secondary
+ axis that is a __one-to-one__ transformation of the primary axis (e.g. degrees
+ Celcius to degrees Fahrenheit). The secondary axis will be positioned opposite
+ to the primary axis and can be controlled with the `sec.axis` argument to
+ the scale constructor.
+
+* Scales worry less about having breaks. If no breaks can be computed, the
+ plot will work instead of throwing an uninformative error (#791). This
+ is particularly helpful when you have facets with free scales, and not
+ all panels contain data.
+
+* Scales now warn when transformation introduces infinite values (#1696).
+
+### Date time
+
+* `scale_*_datetime()` now supports time zones. It will use the timezone
+ attached to the varaible by default, but can be overridden with the
+ `timezone` argument.
+
+* New `scale_x_time()` and `scale_y_time()` generate reasonable default
+ breaks and labels for hms vectors (#1752).
+
+### Discrete scales
+
+The treatment of missing values by discrete scales has been thoroughly overhauled (#1584). The underlying principle is that we can naturally represent missing values on discrete variables (by treating just like another level), so by default we should.
+
+This principle applies to:
+
+* character vectors
+* factors with implicit NA
+* factors with explicit NA
+
+And to all scales (both position and non-position.)
+
+Compared to the previous version of ggplot2, there are three main changes:
+
+1. `scale_x_discrete()` and `scale_y_discrete()` always show discrete NA,
+ regardless of their source
+
+1. If present, `NA`s are shown in discete legends.
+
+1. All discrete scales gain a `na.translate` argument that allows you to
+ control whether `NA`s are translated to something that can be visualised,
+ or should be left as missing. Note that if you don't translate (i.e.
+ `na.translate = FALSE)` the missing values will passed on to the layer,
+ which will warning that it's dropping missing values. To suppress the
+ warnings, you'll also need to add `na.rm = TRUE` to the layer call.
+
+There were also a number of other smaller changes
+
+* Correctly use scale expansion factors.
+* Don't preserve space for dropped levels (#1638).
+* Only issue one warning when when asking for too many levels (#1674).
+* Unicode labels work better on Windows (#1827).
+* Warn when used with only continuous data (#1589)
+
+## Themes
+
+* The `theme()` constructor now has named arguments rather than ellipses. This
+ should make autocomplete substantially more useful. The documentation
+ (including exampes) has been considerably improved.
+
+* Built-in themes are more visually homogeneous, and match `theme_grey` better.
+ (@jiho, #1679)
+
+* When computing the height of titles, ggplot2 now includes the height of the
+ descenders (i.e. the bits of `g` and `y` that hang beneath the baseline). This
+ improves the margins around titles, particularly the y axis label (#1712).
+ I have also very slightly increased the inner margins of axis titles, and
+ removed the outer margins.
+
+* Theme element inheritance is now easier to work with as modification now
+ overrides default `element_blank` elements (#1555, #1557, #1565, #1567)
+
+* Horizontal legends (i.e. legends on the top or bottom) are horizontally
+ aligned by default (#1842). Use `legend.box = "vertical"` to switch back
+ to the previous behaviour.
+
+* `element_line()` now takes an `arrow` argument to specify arrows at the end of
+ lines (#1740)
+
+There were a number of tweaks to the theme elements that control legends:
+
+* `legend.justification` now controls appearance will plotting the legend
+ outside of the plot area. For example, you can use
+ `theme(legend.justification = "top")` to make the legend align with the
+ top of the plot.
+
+* `panel.margin` and `legend.margin` have been renamed to `panel.spacing` and
+ `legend.spacing` respectively, to better communicate intent (they only
+ affect spacing between legends and panels, not the margins around them)
+
+* `legend.margin` now controls margin around individual legends.
+
+* New `legend.box.background`, `legend.box.spacing`, and `legend.box.margin`
+ control the background, spacing, and margin of the legend box (the region
+ that contains all legends).
+
+## Bug fixes and minor improvements
+
+* ggplot2 now imports tibble. This ensures that all built-in datasets print
+ compactly even if you haven't explicitly loaded tibble or dplyr (#1677).
+
+* Class of aesthetic mapping is preserved when adding `aes()` objects (#1624).
+
+* `+.gg` now works for lists that include data frames.
+
+* `annotation_x()` now works in the absense of global data (#1655)
+
+* `geom_*(show.legend = FALSE)` now works for `guide_colorbar`.
+
+* `geom_boxplot()` gains new `outlier.alpha` (@jonathan-g) and
+ `outlier.fill` (@schloerke, #1787) parameters to control the alpha/fill of
+ outlier points independently of the alpha of the boxes.
+
+* `position_jitter()` (and hence `geom_jitter()`) now correctly computes
+ the jitter width/jitter when supplied by the user (#1775, @has2k1).
+
+* `geom_contour()` more clearly describes what inputs it needs (#1577).
+
+* `geom_curve()` respects the `lineend` paramater (#1852).
+
+* `geom_histogram()` and `stat_bin()` understand the `breaks` parameter once
+ more. (#1665). The floating point adjustment for histogram bins is now
+ actually used - it was previously inadvertently ignored (#1651).
+
+* `geom_violin()` no longer transforms quantile lines with the alpha aesthetic
+ (@mnbram, #1714). It no longer errors when quantiles are requested but data
+ have zero range (#1687). When `trim = FALSE` it once again has a nice
+ range that allows the density to reach zero (by extending the range 3
+ bandwidths to either side of the data) (#1700).
+
+* `geom_dotplot()` works better when facetting and binning on the y-axis.
+ (#1618, @has2k1).
+
+* `geom_hexbin()` once again supports `..density..` (@mikebirdgeneau, #1688).
+
+* `geom_step()` gives useful warning if only one data point in layer (#1645).
+
+* `layer()` gains new `check.aes` and `check.param` arguments. These allow
+ geom/stat authors to optional suppress checks for known aesthetics/parameters.
+ Currently this is used only in `geom_blank()` which powers `expand_limits()`
+ (#1795).
+
+* All `stat_*()` display a better error message when required aesthetics are
+ missing.
+
+* `stat_bin()` and `stat_summary_hex()` now accept length 1 `binwidth` (#1610)
+
+* `stat_density()` gains new argument `n`, which is passed to underlying function
+ `stats::density` ("number of equally spaced points at which the
+ density is to be estimated"). (@hbuschme)
+
+* `stat_binhex()` now again returns `count` rather than `value` (#1747)
+
+* `stat_ecdf()` respects `pad` argument (#1646).
+
+* `stat_smooth()` once again informs you about the method it has chosen.
+ It also correctly calculates the size of the largest group within facets.
+
+* `x` and `y` scales are now symmetric regarding the list of
+ aesthetics they accept: `xmin_final`, `xmax_final`, `xlower`,
+ `xmiddle` and `xupper` are now valid `x` aesthetics.
+
+* `Scale` extensions can now override the `make_title` and `make_sec_title`
+ methods to let the scale modify the axis/legend titles.
+
# ggplot2 2.1.0
## New features
@@ -123,6 +347,9 @@
* Fixed a compatibility issue with `ggproto` and R versions prior to 3.1.2.
(#1444)
+* Fixed issue where `coord_map()` fails when given an explicit `parameters`
+ argument (@tdmcarthur, #1729)
+
# ggplot2 2.0.0
## Major changes
diff --git a/R/aes-group-order.r b/R/aes-group-order.r
index a97956a..d6bd151 100644
--- a/R/aes-group-order.r
+++ b/R/aes-group-order.r
@@ -1,4 +1,4 @@
-#' Aesthetics: group
+#' Aesthetics: grouping
#'
#' @name aes_group_order
#' @aliases group
diff --git a/R/aes.r b/R/aes.r
index 410317c..ec9ef46 100644
--- a/R/aes.r
+++ b/R/aes.r
@@ -22,23 +22,21 @@ NULL
"max" = "ymax"
)
-#' Define aesthetic mappings.
+#' Construct aesthetic mappings
#'
-#' Generate aesthetic mappings that describe how variables in the data are
-#' mapped to visual properties (aesthetics) of geoms. This function also
-#' standardise aesthetic names by performs partial name matching, converting
-#' color to colour, and old style R names to ggplot names (eg. pch to shape,
-#' cex to size)
+#' Aesthetic mappings describe how variables in the data are mapped to visual
+#' properties (aesthetics) of geoms. Aesthetic mappings can be set in
+#' \code{\link{ggplot2}} and in individual layers.
+#'
+#' This function also standardise aesthetic names by performing partial
+#' matching, converting color to colour, and translating old style R names to
+#' ggplot names (eg. pch to shape, cex to size)
#'
#' @param x,y,... List of name value pairs giving aesthetics to map to
-#' variables. The names for x and y aesthetics can be omitted (because
-#' they are so common); all other aesthetics must be named.
-#' @seealso See \code{\link{aes_q}}/\code{\link{aes_string}} for standard
-#' evaluation versions of \code{aes}.
-#' @seealso See
-#' \code{\link{aes_colour_fill_alpha}}, \code{\link{aes_group_order}},
-#' \code{\link{aes_linetype_size_shape}} and \code{\link{aes_position}}
-#' for more specific examples with different aesthetics.
+#' variables. The names for x and y aesthetics are typically omitted because
+#' they are so common; all other aesthetics must be named.
+#' @seealso See \code{\link{aes_}} for a version of \code{aes} that is
+#' more suitable for programming with.
#' @export
#' @examples
#' aes(x = mpg, y = wt)
@@ -105,24 +103,25 @@ is_position_aes <- function(vars) {
aes_to_scale(vars) %in% c("x", "y")
}
-#' Define aesthetic mappings from strings, or quoted calls and formulas.
+#' Define aesthetic mappings programatically
#'
#' Aesthetic mappings describe how variables in the data are mapped to visual
#' properties (aesthetics) of geoms. \code{\link{aes}} uses non-standard
#' evaluation to capture the variable names. \code{aes_} and \code{aes_string}
#' require you to explicitly quote the inputs either with \code{""} for
#' \code{aes_string()}, or with \code{quote} or \code{~} for \code{aes_()}.
-#' (\code{aes_q} is an alias to \code{aes_})
-#'
-#' It's better to use \code{aes_q()}, because there's no easy way to create the
-#' equivalent to \code{aes(colour = "my colour")} or \code{aes{x = `X$1`}}
-#' with \code{aes_string()}.
+#' (\code{aes_q} is an alias to \code{aes_}). This makes \code{aes_} and
+#' \code{aes_string} easy to program with.
#'
#' \code{aes_string} and \code{aes_} are particularly useful when writing
#' functions that create plots because you can use strings or quoted
#' names/calls to define the aesthetic mappings, rather than having to use
#' \code{\link{substitute}} to generate a call to \code{aes()}.
#'
+#' I recommend using \code{aes_()}, because creating the equivalents of
+#' \code{aes(colour = "my colour")} or \code{aes{x = `X$1`}}
+#' with \code{aes_string()} is quite clunky.
+#'
#' @param x,y,... List of name value pairs. Elements must be either
#' quoted calls, strings, one-sided formulas or constants.
#' @seealso \code{\link{aes}}
diff --git a/R/annotation-custom.r b/R/annotation-custom.r
index 77404b5..5597d22 100644
--- a/R/annotation-custom.r
+++ b/R/annotation-custom.r
@@ -1,12 +1,13 @@
#' @include geom-.r
NULL
-#' Annotation: Custom grob.
+#' Annotation: Custom grob
#'
#' This is a special geom intended for use as static annotations
#' that are the same in every panel. These annotations will not
#' affect scales (i.e. the x and y axes will not grow to cover the range
-#' of the grob, and the grob will not be modified by any ggplot settings or mappings).
+#' of the grob, and the grob will not be modified by any ggplot settings
+#' or mappings).
#'
#' Most useful for adding tables, inset plots, and other grid-based decorations.
#'
@@ -42,11 +43,11 @@ NULL
#' annotation_custom(grob = g, xmin = 1, xmax = 10, ymin = 8, ymax = 10)
annotation_custom <- function(grob, xmin = -Inf, xmax = Inf, ymin = -Inf, ymax = Inf) {
layer(
- data = NULL,
+ data = dummy_data(),
stat = StatIdentity,
position = PositionIdentity,
geom = GeomCustomAnn,
- inherit.aes = TRUE,
+ inherit.aes = FALSE,
params = list(
grob = grob,
xmin = xmin,
diff --git a/R/annotation-logticks.r b/R/annotation-logticks.r
index 56d909a..ab4d20b 100644
--- a/R/annotation-logticks.r
+++ b/R/annotation-logticks.r
@@ -81,7 +81,7 @@ annotation_logticks <- function(base = 10, sides = "bl", scaled = TRUE,
colour <- color
layer(
- data = data.frame(x = NA),
+ data = dummy_data(),
mapping = NULL,
stat = StatIdentity,
geom = GeomLogticks,
diff --git a/R/annotation-map.r b/R/annotation-map.r
index c7476a0..2180be0 100644
--- a/R/annotation-map.r
+++ b/R/annotation-map.r
@@ -1,7 +1,9 @@
#' @include geom-map.r
NULL
-#' Annotation: maps.
+#' Annotation: a maps
+#'
+#' Display a fixed map on a plot.
#'
#' @param map data frame representing a map. Most map objects can be
#' converted into the right format by using \code{\link{fortify}}
@@ -34,7 +36,7 @@ annotation_map <- function(map, ...) {
stopifnot(all(c("x", "y", "id") %in% names(map)))
layer(
- data = NULL,
+ data = dummy_data(),
stat = StatIdentity,
geom = GeomAnnotationMap,
position = PositionIdentity,
diff --git a/R/annotation-raster.r b/R/annotation-raster.r
index 45b49cb..d2d3945 100644
--- a/R/annotation-raster.r
+++ b/R/annotation-raster.r
@@ -2,14 +2,13 @@
#' @include geom-raster.r
NULL
-#' Annotation: High-performance rectangular tiling.
+#' Annotation: high-performance rectangular tiling
#'
#' This is a special version of \code{\link{geom_raster}} optimised for static
#' annotations that are the same in every panel. These annotations will not
#' affect scales (i.e. the x and y axes will not grow to cover the range
-#' of the raster, and the raster must already have its own colours).
-#'
-#' Most useful for adding bitmap images.
+#' of the raster, and the raster must already have its own colours). This
+#' is useful for adding bitmap images.
#'
#' @param raster raster object to display
#' @param xmin,xmax x location (in data coordinates) giving horizontal
@@ -43,12 +42,12 @@ annotation_raster <- function(raster, xmin, xmax, ymin, ymax,
raster <- grDevices::as.raster(raster)
layer(
- data = NULL,
+ data = dummy_data(),
mapping = NULL,
stat = StatIdentity,
position = PositionIdentity,
geom = GeomRasterAnn,
- inherit.aes = TRUE,
+ inherit.aes = FALSE,
params = list(
raster = raster,
xmin = xmin,
diff --git a/R/annotation.r b/R/annotation.r
index d810024..a98439e 100644
--- a/R/annotation.r
+++ b/R/annotation.r
@@ -1,6 +1,6 @@
-#' Create an annotation layer.
+#' Create an annotation layer
#'
-#' This function adds geoms to a plot. Unlike typical a geom function,
+#' This function adds geoms to a plot, but unlike typical a geom function,
#' the properties of the geoms are not mapped from variables of a data frame,
#' but are instead passed in as vectors. This is useful for adding small annotations
#' (such as text labels) or if you have your data in vectors, and for some
@@ -29,6 +29,11 @@
#' colour = "red", size = 1.5)
#'
#' p + annotate("text", x = 2:3, y = 20:21, label = c("my label", "label 2"))
+#'
+#' p + annotate("text", x = 4, y = 25, label = "italic(R) ^ 2 == 0.75",
+#' parse = TRUE)
+#' p + annotate("text", x = 4, y = 25,
+#' label = "paste(italic(R) ^ 2, \" = .75\")", parse = TRUE)
annotate <- function(geom, x = NULL, y = NULL, xmin = NULL, xmax = NULL,
ymin = NULL, ymax = NULL, xend = NULL, yend = NULL, ...,
na.rm = FALSE) {
diff --git a/R/axis-secondary.R b/R/axis-secondary.R
new file mode 100644
index 0000000..9b808a8
--- /dev/null
+++ b/R/axis-secondary.R
@@ -0,0 +1,162 @@
+#' Specify a secondary axis
+#'
+#' This function is used in conjunction with a position scale to create a
+#' secondary axis, positioned opposite of the primary axis. All secondary
+#' axes must be based on a one-to-one transformation of the primary axes.
+#'
+#' @param trans A transformation formula
+#'
+#' @param name The name of the secondary axis
+#'
+#' @param breaks One of:
+#' \itemize{
+#' \item{\code{NULL} for no breaks}
+#' \item{\code{waiver()} for the default breaks computed by the transformation object}
+#' \item{A numeric vector of positions}
+#' \item{A function that takes the limits as input and returns breaks as output}
+#' }
+#'
+#' @param labels One of:
+#' \itemize{
+#' \item{\code{NULL} for no labels}
+#' \item{\code{waiver()} for the default labels computed by the transformation object}
+#' \item{A character vector giving labels (must be same length as \code{breaks})}
+#' \item{A function that takes the breaks as input and returns labels as output}
+#' }
+#'
+#' @details
+#' \code{sec_axis} is used to create the specifications for a secondary axis.
+#' Except for the \code{trans} argument any of the arguments can be set to
+#' \code{derive()} which would result in the secondary axis inheriting the
+#' settings from the primary axis.
+#'
+#' \code{dup_axis} is provide as a shorthand for creating a secondary axis that
+#' is a duplication of the primary axis, effectively mirroring the primary axis.
+#'
+#' @examples
+#' p <- ggplot(mtcars, aes(cyl, mpg)) +
+#' geom_point()
+#'
+#' # Create a simple secondary axis
+#' p + scale_y_continuous(sec.axis = sec_axis(~.+10))
+#'
+#' # Inherit the name from the primary axis
+#' p + scale_y_continuous("Miles/gallon", sec.axis = sec_axis(~.+10, name = derive()))
+#'
+#' # Duplicate the primary axis
+#' p + scale_y_continuous(sec.axis = dup_axis())
+#'
+#' # You can pass in a formula as a shorthand
+#' p + scale_y_continuous(sec.axis = ~.^2)
+#'
+#' @export
+sec_axis <- function(trans = NULL, name = waiver(), breaks = waiver(), labels = waiver()) {
+ if (!is.formula(trans)) stop("transformation for secondary axes must be a formula", call. = FALSE)
+ ggproto(NULL, AxisSecondary,
+ trans = trans,
+ name = name,
+ breaks = breaks,
+ labels = labels
+ )
+}
+#' @rdname sec_axis
+#'
+#' @export
+dup_axis <- function(trans = ~., name = derive(), breaks = derive(), labels = derive()) {
+ sec_axis(trans, name, breaks, labels)
+}
+is.sec_axis <- function(x) {
+ inherits(x, "AxisSecondary")
+}
+#' @rdname sec_axis
+#'
+#' @export
+derive <- function() {
+ structure(list(), class = "derived")
+}
+is.derived <- function(x) {
+ inherits(x, "derived")
+}
+#' @importFrom lazyeval f_eval
+#'
+#' @rdname ggplot2-ggproto
+#' @format NULL
+#' @usage NULL
+#' @export
+AxisSecondary <- ggproto("AxisSecondary", NULL,
+ trans = NULL,
+ axis = NULL,
+ name = waiver(),
+ breaks = waiver(),
+ labels = waiver(),
+
+ # This determines the quality of the remapping from the secondary axis and
+ # back to the primary axis i.e. the exactness of the placement of the
+ # breakpoints of the secondary axis.
+ detail = 1000,
+
+ empty = function(self) {
+ is.null(self$trans)
+ },
+
+ # Inherit settings from the primary axis/scale
+ init = function(self, scale) {
+ if (self$empty()) return()
+ if (!is.formula(self$trans)) stop("transformation for secondary axes must be a formula", call. = FALSE)
+ if (is.derived(self$name) && !is.waive(scale$name)) self$name <- scale$name
+ if (is.derived(self$breaks)) self$breaks <- scale$breaks
+ if (is.derived(self$labels)) self$labels <- scale$labels
+ },
+
+ transform_range = function(self, range) {
+ range <- structure(data.frame(range), names = '.')
+ f_eval(self$trans, range)
+ },
+
+
+ break_info = function(self, range, scale) {
+ if (self$empty()) return()
+
+ # Get original range before transformation
+ inv_range <- scale$trans$inverse(range)
+
+ # Create mapping between primary and secondary range
+ old_range <- seq(inv_range[1], inv_range[2], length.out = self$detail)
+ full_range <- self$transform_range(old_range)
+
+ # Test for monotony
+ if (length(unique(sign(diff(full_range)))) != 1)
+ stop("transformation for secondary axes must be monotonous")
+
+ # Get break info for the secondary axis
+ new_range <- range(full_range, na.rm = TRUE)
+ temp_scale <- self$create_scale(new_range)
+ range_info <- temp_scale$break_info()
+
+ # Map the break values back to their correct position on the primary scale
+ old_val <- lapply(range_info$major_source, function(x) which.min(abs(full_range - x)))
+ old_val <- old_range[unlist(old_val)]
+ old_val_trans <- scale$trans$transform(old_val)
+ range_info$major[] <- round(rescale(scale$map(old_val_trans, range(old_val_trans)), from = range), digits = 3)
+
+ names(range_info) <- paste0("sec.", names(range_info))
+ range_info
+ },
+
+ # Temporary scale for the purpose of calling break_info()
+ create_scale = function(self, range) {
+ scale <- ggproto(NULL, ScaleContinuousPosition,
+ name = self$name,
+ breaks = self$breaks,
+ labels = self$labels,
+ limits = range,
+ expand = c(0, 0),
+ trans = identity_trans()
+ )
+ scale$train(range)
+ scale
+ },
+ make_title = function(title) {
+ title
+ }
+)
diff --git a/R/bin.R b/R/bin.R
index 1d58d3e..c325122 100644
--- a/R/bin.R
+++ b/R/bin.R
@@ -124,7 +124,7 @@ bin_vector <- function(x, bins, weight = NULL, pad = FALSE) {
weight[is.na(weight)] <- 0
}
- bin_idx <- cut(x, bins$breaks, right = bins$right_closed,
+ bin_idx <- cut(x, bins$fuzzy, right = bins$right_closed,
include.lowest = TRUE)
bin_count <- as.numeric(tapply(weight, bin_idx, sum, na.rm = TRUE))
bin_count[is.na(bin_count)] <- 0
diff --git a/R/coord-.r b/R/coord-.r
index ce3ae43..aae2be9 100644
--- a/R/coord-.r
+++ b/R/coord-.r
@@ -18,8 +18,8 @@
#' \item \code{labels}: Returns a list containing labels for x and y.
#' \item \code{render_fg}: Renders foreground elements.
#' \item \code{render_bg}: Renders background elements.
-#' \item \code{render_axis_h}: Renders the horizontal axis.
-#' \item \code{render_axis_v}: Renders the vertical axis.
+#' \item \code{render_axis_h}: Renders the horizontal axes.
+#' \item \code{render_axis_v}: Renders the vertical axes.
#' \item \code{range}: Returns the x and y ranges
#' \item \code{train}: Return the trained scale ranges.
#' \item \code{transform}: Transforms x and y coordinates.
@@ -50,11 +50,21 @@ Coord <- ggproto("Coord",
},
render_axis_h = function(scale_details, theme) {
- guide_axis(scale_details$x.major, scale_details$x.labels, "bottom", theme)
+ arrange <- scale_details$x.arrange %||% c("secondary", "primary")
+
+ list(
+ top = render_axis(scale_details, arrange[1], "x", "top", theme),
+ bottom = render_axis(scale_details, arrange[2], "x", "bottom", theme)
+ )
},
render_axis_v = function(scale_details, theme) {
- guide_axis(scale_details$y.major, scale_details$y.labels, "left", theme)
+ arrange <- scale_details$y.arrange %||% c("primary", "secondary")
+
+ list(
+ left = render_axis(scale_details, arrange[1], "y", "left", theme),
+ right = render_axis(scale_details, arrange[2], "y", "right", theme)
+ )
},
range = function(scale_details) {
@@ -79,3 +89,15 @@ is.Coord <- function(x) inherits(x, "Coord")
expand_default <- function(scale, discrete = c(0, 0.6), continuous = c(0.05, 0)) {
scale$expand %|W|% if (scale$is_discrete()) discrete else continuous
}
+
+# Renders an axis with the correct orientation or zeroGrob if no axis should be
+# generated
+render_axis <- function(scale_details, axis, scale, position, theme) {
+ if (axis == "primary") {
+ guide_axis(scale_details[[paste0(scale, ".major")]], scale_details[[paste0(scale, ".labels")]], position, theme)
+ } else if (axis == "secondary" && !is.null(scale_details[[paste0(scale, ".sec.major")]])) {
+ guide_axis(scale_details[[paste0(scale, ".sec.major")]], scale_details[[paste0(scale, ".sec.labels")]], position, theme)
+ } else {
+ zeroGrob()
+ }
+}
diff --git a/R/coord-cartesian-.r b/R/coord-cartesian-.r
index 5ae6e49..f77d425 100644
--- a/R/coord-cartesian-.r
+++ b/R/coord-cartesian-.r
@@ -1,4 +1,4 @@
-#' Cartesian coordinates.
+#' Cartesian coordinates
#'
#' The Cartesian coordinate system is the most familiar, and common, type of
#' coordinate system. Setting limits on the coordinate system will zoom the
@@ -91,6 +91,7 @@ CoordCartesian <- ggproto("CoordCartesian", Coord,
}
out <- scale_details$break_info(range)
+ out$arrange <- scale_details$axis_order()
names(out) <- paste(name, names(out), sep = ".")
out
}
diff --git a/R/coord-fixed.r b/R/coord-fixed.r
index 98eb933..eeda01d 100644
--- a/R/coord-fixed.r
+++ b/R/coord-fixed.r
@@ -1,4 +1,4 @@
-#' Cartesian coordinates with fixed relationship between x and y scales.
+#' Cartesian coordinates with fixed "aspect ratio"
#'
#' A fixed scale coordinate system forces a specified ratio between the
#' physical representation of data units on the axes. The ratio represents the
diff --git a/R/coord-flip.r b/R/coord-flip.r
index b42d64e..2167020 100644
--- a/R/coord-flip.r
+++ b/R/coord-flip.r
@@ -1,6 +1,6 @@
-#' Flipped cartesian coordinates.
+#' Cartesian coordinates with x and y flipped
#'
-#' Flipped cartesian coordinates so that horizontal becomes vertical, and
+#' Flip cartesian coordinates so that horizontal becomes vertical, and
#' vertical, horizontal. This is primarily useful for converting geoms and
#' statistics which display y conditional on x, to x conditional on y.
#'
diff --git a/R/coord-map.r b/R/coord-map.r
index 5c1c5ce..ee84505 100644
--- a/R/coord-map.r
+++ b/R/coord-map.r
@@ -1,34 +1,35 @@
-#' Map projections.
+#' Map projections
#'
-#' The representation of a portion of the earth, which is approximately spherical,
-#' onto a flat 2D plane requires a projection. This is what
-#' \code{\link{coord_map}} does. These projections account for the fact that the
-#' actual length (in km) of one degree of longitude varies between the equator
-#' and the pole. Near the equator, the ratio between the lengths of one degree
-#' of latitude and one degree of longitude is approximately 1. Near the pole, it
-#' is tends towards infinity because the length of one degree of longitude tends
-#' towards 0. For regions that span only a few degrees and are not too close to
-#' the poles, setting the aspect ratio of the plot to the appropriate lat/lon
-#' ratio approximates the usual mercator projection. This is what
-#' \code{coord_quickmap} does. With \code{\link{coord_map}} all elements of the
-#' graphic have to be projected which is not the case here. So
-#' \code{\link{coord_quickmap}} has the advantage of being much faster, in
-#' particular for complex plots such as those using with
-#' \code{\link{geom_tile}}, at the expense of correctness in the projection.
-#' This coordinate system provides the full range of map projections available
-#' in the mapproj package.
+#' \code{coord_map} projects a portion of the earth, which is approximately
+#' spherical, onto a flat 2D plane using any projection defined by the
+#' \code{mapproj} package. Map projections do not, in general, preserve straight
+#' lines, so this requires considerable computation. \code{coord_quickmap} is a
+#' quick approximation that does preserve straight lines. It works best for
+#' smaller areas closer to the equator.
+#'
+#' In general, map projections must account for the fact that the actual length
+#' (in km) of one degree of longitude varies between the equator and the pole.
+#' Near the equator, the ratio between the lengths of one degree of latitude and
+#' one degree of longitude is approximately 1. Near the pole, it is tends
+#' towards infinity because the length of one degree of longitude tends towards
+#' 0. For regions that span only a few degrees and are not too close to the
+#' poles, setting the aspect ratio of the plot to the appropriate lat/lon ratio
+#' approximates the usual mercator projection. This is what
+#' \code{coord_quickmap} does, and is much faster (particularly for complex
+#' plots like \code{\link{geom_tile}}) at the expense of correctness.
#'
-#' @export
#' @param projection projection to use, see
#' \code{\link[mapproj]{mapproject}} for list
-#' @param ... other arguments passed on to
-#' \code{\link[mapproj]{mapproject}}
+#' @param ...,parameters Other arguments passed on to
+#' \code{\link[mapproj]{mapproject}}. Use \code{...} for named parameters to
+#' the projection, and \code{parameters} for unnamed parameters.
+#' \code{...} is ignored if the \code{parameters} argument is present.
#' @param orientation projection orientation, which defaults to
-#' \code{c(90, 0, mean(range(x)))}. This is not optimal for many
-#' projections, so you will have to supply your own. See
-#' \code{\link[mapproj]{mapproject}} for more information.
-#' @param xlim manually specific x limits (in degrees of longitude)
-#' @param ylim manually specific y limits (in degrees of latitude)
+#' \code{c(90, 0, mean(range(x)))}. This is not optimal for many
+#' projections, so you will have to supply your own. See
+#' \code{\link[mapproj]{mapproject}} for more information.
+#' @param xlim,ylim Manually specific x/y limits (in degrees of
+#' longitude/latitude)
#' @export
#' @examples
#' if (require("maps")) {
@@ -46,7 +47,8 @@
#'
#' # Other projections
#' nzmap + coord_map("cylindrical")
-#' nzmap + coord_map("azequalarea",orientation=c(-36.92,174.6,0))
+#' nzmap + coord_map("azequalarea", orientation = c(-36.92, 174.6, 0))
+#' nzmap + coord_map("lambert", parameters = c(-37, -44))
#'
#' states <- map_data("state")
#' usamap <- ggplot(states, aes(long, lat, group = group)) +
@@ -71,7 +73,7 @@
#'
#' # World map, using geom_path instead of geom_polygon
#' world <- map_data("world")
-#' worldmap <- ggplot(world, aes(x=long, y=lat, group=group)) +
+#' worldmap <- ggplot(world, aes(x = long, y = lat, group = group)) +
#' geom_path() +
#' scale_y_continuous(breaks = (-2:2) * 30) +
#' scale_x_continuous(breaks = (-4:4) * 45)
@@ -83,12 +85,18 @@
#' # Centered on New York (currently has issues with closing polygons)
#' worldmap + coord_map("ortho", orientation = c(41, -74, 0))
#' }
-coord_map <- function(projection="mercator", ..., orientation = NULL, xlim = NULL, ylim = NULL) {
+coord_map <- function(projection="mercator", ..., parameters = NULL, orientation = NULL, xlim = NULL, ylim = NULL) {
+ if (is.null(parameters)) {
+ params <- list(...)
+ } else {
+ params <- parameters
+ }
+
ggproto(NULL, CoordMap,
projection = projection,
orientation = orientation,
limits = list(x = xlim, y = ylim),
- params = list(...)
+ params = params
)
}
@@ -215,7 +223,14 @@ CoordMap <- ggproto("CoordMap", Coord,
},
render_axis_h = function(self, scale_details, theme) {
- if (is.null(scale_details$x.major)) return(zeroGrob())
+ arrange <- scale_details$x.arrange %||% c("primary", "secondary")
+
+ if (is.null(scale_details$x.major)) {
+ return(list(
+ top = zeroGrob(),
+ bottom = zeroGrob()
+ ))
+ }
x_intercept <- with(scale_details, data.frame(
x = x.major,
@@ -223,11 +238,23 @@ CoordMap <- ggproto("CoordMap", Coord,
))
pos <- self$transform(x_intercept, scale_details)
- guide_axis(pos$x, scale_details$x.labels, "bottom", theme)
+ axes <- list(
+ bottom = guide_axis(pos$x, scale_details$x.labels, "bottom", theme),
+ top = guide_axis(pos$x, scale_details$x.labels, "top", theme)
+ )
+ axes[[which(arrange == "secondary")]] <- zeroGrob()
+ axes
},
render_axis_v = function(self, scale_details, theme) {
- if (is.null(scale_details$y.major)) return(zeroGrob())
+ arrange <- scale_details$y.arrange %||% c("primary", "secondary")
+
+ if (is.null(scale_details$y.major)) {
+ return(list(
+ left = zeroGrob(),
+ right = zeroGrob()
+ ))
+ }
x_intercept <- with(scale_details, data.frame(
x = x.range[1],
@@ -235,7 +262,12 @@ CoordMap <- ggproto("CoordMap", Coord,
))
pos <- self$transform(x_intercept, scale_details)
- guide_axis(pos$y, scale_details$y.labels, "left", theme)
+ axes <- list(
+ left = guide_axis(pos$y, scale_details$y.labels, "left", theme),
+ right = guide_axis(pos$y, scale_details$y.labels, "right", theme)
+ )
+ axes[[which(arrange == "secondary")]] <- zeroGrob()
+ axes
}
)
diff --git a/R/coord-polar.r b/R/coord-polar.r
index aa62111..2103a68 100644
--- a/R/coord-polar.r
+++ b/R/coord-polar.r
@@ -1,4 +1,4 @@
-#' Polar coordinates.
+#' Polar coordinates
#'
#' The polar coordinate system is most commonly used for pie charts, which
#' are a stacked bar chart in polar coordinates.
@@ -36,7 +36,7 @@
#' value = c(20, 80)
#' )
#' ggplot(df, aes(x = "", y = value, fill = variable)) +
-#' geom_bar(width = 1, stat = "identity") +
+#' geom_col(width = 1) +
#' scale_fill_manual(values = c("red", "yellow")) +
#' coord_polar("y", start = pi / 3) +
#' labs(title = "Pac man")
@@ -148,12 +148,23 @@ CoordPolar <- ggproto("CoordPolar", Coord,
},
render_axis_v = function(self, scale_details, theme) {
+ arrange <- scale_details$y.arrange %||% c("primary", "secondary")
+
x <- r_rescale(self, scale_details$r.major, scale_details) + 0.5
guide_axis(x, scale_details$r.labels, "left", theme)
+ axes <- list(
+ left = guide_axis(x, scale_details$r.labels, "left", theme),
+ right = guide_axis(x, scale_details$r.labels, "right", theme)
+ )
+ axes[[which(arrange == "secondary")]] <- zeroGrob()
+ axes
},
render_axis_h = function(scale_details, theme) {
- guide_axis(NA, "", "bottom", theme)
+ list(
+ top = zeroGrob(),
+ bottom = guide_axis(NA, "", "bottom", theme)
+ )
},
render_bg = function(self, scale_details, theme) {
diff --git a/R/coord-transform.r b/R/coord-transform.r
index cc506b1..29344ed 100644
--- a/R/coord-transform.r
+++ b/R/coord-transform.r
@@ -1,12 +1,12 @@
-#' Transformed cartesian coordinate system.
+#' Transformed Cartesian coordinate system
#'
#' \code{coord_trans} is different to scale transformations in that it occurs after
#' statistical transformation and will affect the visual appearance of geoms - there is
#' no guarantee that straight lines will continue to be straight.
#'
-#' All current transformations only work with continuous values - see
-#' \code{\link[scales]{trans_new}} for list of transformations, and instructions on
-#' how to create your own.
+#' Transformations only work with continuous values: see
+#' \code{\link[scales]{trans_new}} for list of transformations, and instructions
+#' on how to create your own.
#'
#' @param x,y transformers for x and y axes
#' @param xtrans,ytrans Deprecated; use \code{x} and \code{y} instead.
diff --git a/R/data.R b/R/data.R
index 211d113..47896b4 100644
--- a/R/data.R
+++ b/R/data.R
@@ -4,41 +4,41 @@
#' diamonds. The variables are as follows:
#'
#' @format A data frame with 53940 rows and 10 variables:
-#' \itemize{
-#' \item price: price in US dollars (\$326--\$18,823)
-#' \item carat: weight of the diamond (0.2--5.01)
-#' \item cut: quality of the cut (Fair, Good, Very Good, Premium, Ideal)
-#' \item color: diamond colour, from J (worst) to D (best)
-#' \item clarity: a measurement of how clear the diamond is
-#' (I1 (worst), SI1, SI2, VS1, VS2, VVS1, VVS2, IF (best))
-#' \item x: length in mm (0--10.74)
-#' \item y: width in mm (0--58.9)
-#' \item z: depth in mm (0--31.8)
-#' \item depth: total depth percentage = z / mean(x, y) = 2 * z / (x + y) (43--79)
-#' \item table: width of top of diamond relative to widest point (43--95)
+#' \describe{
+#' \item{price}{price in US dollars (\$326--\$18,823)}
+#' \item{carat}{weight of the diamond (0.2--5.01)}
+#' \item{cut}{quality of the cut (Fair, Good, Very Good, Premium, Ideal)}
+#' \item{color}{diamond colour, from J (worst) to D (best)}
+#' \item{clarity}{a measurement of how clear the diamond is (I1 (worst), SI1,
+#' SI2, VS1, VS2, VVS1, VVS2, IF (best))}
+#' \item{x}{length in mm (0--10.74)}
+#' \item{y}{width in mm (0--58.9)}
+#' \item{z}{depth in mm (0--31.8)}
+#' \item{depth}{total depth percentage = z / mean(x, y) = 2 * z / (x + y) (43--79)}
+#' \item{table}{width of top of diamond relative to widest point (43--95)}
#' }
"diamonds"
-#' US economic time series.
+#' US economic time series
#'
#' This dataset was produced from US economic time series data available from
#' \url{http://research.stlouisfed.org/fred2}. \code{economics} is in "wide"
#' format, \code{economics_long} is in "long" format.
#'
#' @format A data frame with 478 rows and 6 variables
-#' \itemize{
-#' \item date. Month of data collection
-#' \item psavert, personal savings rate,
-#' \url{http://research.stlouisfed.org/fred2/series/PSAVERT/}
-#' \item pce, personal consumption expenditures, in billions of dollars,
-#' \url{http://research.stlouisfed.org/fred2/series/PCE}
-#' \item unemploy, number of unemployed in thousands,
-#' \url{http://research.stlouisfed.org/fred2/series/UNEMPLOY}
-#' \item uempmed, median duration of unemployment, in week,
-#' \url{http://research.stlouisfed.org/fred2/series/UEMPMED}
-#' \item pop, total population, in thousands,
-#' \url{http://research.stlouisfed.org/fred2/series/POP}
+#' \describe{
+#' \item{date}{Month of data collection}
+#' \item{psavert}{personal savings rate,
+#' \url{http://research.stlouisfed.org/fred2/series/PSAVERT/}}
+#' \item{pce}{personal consumption expenditures, in billions of dollars,
+#' \url{http://research.stlouisfed.org/fred2/series/PCE}}
+#' \item{unemploy}{number of unemployed in thousands,
+#' \url{http://research.stlouisfed.org/fred2/series/UNEMPLOY}}
+#' \item{uempmed}{median duration of unemployment, in weeks,
+#' \url{http://research.stlouisfed.org/fred2/series/UEMPMED}}
+#' \item{pop}{total population, in thousands,
+#' \url{http://research.stlouisfed.org/fred2/series/POP}}
#' }
#'
"economics"
@@ -46,40 +46,40 @@
#' @rdname economics
"economics_long"
-#' Midwest demographics.
+#' Midwest demographics
#'
#' Demographic information of midwest counties
#'
#' @format A data frame with 437 rows and 28 variables
-#' \itemize{
-#' \item PID
-#' \item county
-#' \item state
-#' \item area
-#' \item poptotal. Total population
-#' \item popdensity. Population density
-#' \item popwhite. Number of whites.
-#' \item popblack. Number of blacks.
-#' \item popamerindian. Number of American Indians.
-#' \item popasian. Number of Asians.
-#' \item popother. Number of other races.
-#' \item percwhite. Percent white.
-#' \item percblack. Percent black.
-#' \item percamerindan. Percent American Indian.
-#' \item percasian. Percent Asian.
-#' \item percother. Percent other races.
-#' \item popadults. Number of adults.
-#' \item perchsd.
-#' \item percollege. Percent college educated.
-#' \item percprof. Percent profession.
-#' \item poppovertyknown.
-#' \item percpovertyknown
-#' \item percbelowpoverty
-#' \item percchildbelowpovert
-#' \item percadultpoverty
-#' \item percelderlypoverty
-#' \item inmetro. In a metro area.
-#' \item category'
+#' \describe{
+#' \item{PID}{}
+#' \item{county}{}
+#' \item{state}{}
+#' \item{area}{}
+#' \item{poptotal}{Total population}
+#' \item{popdensity}{Population density}
+#' \item{popwhite}{Number of whites.}
+#' \item{popblack}{Number of blacks.}
+#' \item{popamerindian}{Number of American Indians.}
+#' \item{popasian}{Number of Asians.}
+#' \item{popother}{Number of other races.}
+#' \item{percwhite}{Percent white.}
+#' \item{percblack}{Percent black.}
+#' \item{percamerindan}{Percent American Indian.}
+#' \item{percasian}{Percent Asian.}
+#' \item{percother}{Percent other races.}
+#' \item{popadults}{Number of adults.}
+#' \item{perchsd}{}
+#' \item{percollege}{Percent college educated.}
+#' \item{percprof}{Percent profession.}
+#' \item{poppovertyknown}{}
+#' \item{percpovertyknown}{}
+#' \item{percbelowpoverty}{}
+#' \item{percchildbelowpovert}{}
+#' \item{percadultpoverty}{}
+#' \item{percelderlypoverty}{}
+#' \item{inmetro}{In a metro area.}
+#' \item{category}{}
#' }
#'
"midwest"
@@ -93,22 +93,22 @@
#' proxy for the popularity of the car.
#'
#' @format A data frame with 234 rows and 11 variables
-#' \itemize{
-#' \item manufacturer.
-#' \item model.
-#' \item displ. engine displacement, in litres
-#' \item year.
-#' \item cyl. number of cylinders
-#' \item trans. type of transmission
-#' \item drv. f = front-wheel drive, r = rear wheel drive, 4 = 4wd
-#' \item cty. city miles per gallon
-#' \item hwy. highway miles per gallon
-#' \item fl.
-#' \item class.
+#' \describe{
+#' \item{manufacturer}{}
+#' \item{model}{model name}
+#' \item{displ}{engine displacement, in litres}
+#' \item{year}{year of manufacture}
+#' \item{cyl}{number of cylinders}
+#' \item{trans}{type of transmission}
+#' \item{drv}{f = front-wheel drive, r = rear wheel drive, 4 = 4wd}
+#' \item{cty}{city miles per gallon}
+#' \item{hwy}{highway miles per gallon}
+#' \item{fl}{fuel type}
+#' \item{class}{"type" of car}
#' }
"mpg"
-#' An updated and expanded version of the mammals sleep dataset.
+#' An updated and expanded version of the mammals sleep dataset
#'
#' This is an updated and expanded version of the mammals sleep dataset.
#' Updated sleep times and weights were taken from V. M. Savage and G. B.
@@ -120,22 +120,22 @@
#' wikipedia.
#'
#' @format A data frame with 83 rows and 11 variables
-#' \itemize{
-#' \item name. common name
-#' \item genus.
-#' \item vore. carnivore, omnivore or herbivore?
-#' \item order.
-#' \item conservation. the conservation status of the animal
-#' \item sleep\_total. total amount of sleep, in hours
-#' \item sleep\_rem. rem sleep, in hours
-#' \item sleep\_cycle. length of sleep cycle, in hours
-#' \item awake. amount of time spent awake, in hours
-#' \item brainwt. brain weight in kilograms
-#' \item bodywt. body weight in kilograms
+#' \describe{
+#' \item{name}{common name}
+#' \item{genus}{}
+#' \item{vore}{carnivore, omnivore or herbivore?}
+#' \item{order}{}
+#' \item{conservation}{the conservation status of the animal}
+#' \item{sleep_total}{total amount of sleep, in hours}
+#' \item{sleep_rem}{rem sleep, in hours}
+#' \item{sleep_cycle}{length of sleep cycle, in hours}
+#' \item{awake}{amount of time spent awake, in hours}
+#' \item{brainwt}{brain weight in kilograms}
+#' \item{bodywt}{body weight in kilograms}
#' }
"msleep"
-#' Terms of 11 presidents from Eisenhower to Obama.
+#' Terms of 11 presidents from Eisenhower to Obama
#'
#' The names of each president, the start and end date of their term, and
#' their party of 11 US presidents from Eisenhower to Obama.
@@ -143,7 +143,7 @@
#' @format A data frame with 11 rows and 4 variables
"presidential"
-#' Vector field of seal movements.
+#' Vector field of seal movements
#'
#' This vector field was produced from the data described in Brillinger, D.R.,
#' Preisler, H.K., Ager, A.A. and Kie, J.G. "An exploratory data analysis
@@ -164,24 +164,24 @@
#' @format A data frame with 5,625 observations and 3 variables.
"faithfuld"
-#' \code{colors()} in Luv space.
+#' \code{colors()} in Luv space
#'
#' All built-in \code{\link{colors}()} translated into Luv colour space.
#'
#' @format A data frame with 657 observations and 4 variables:
-#' \itemize{
+#' \describe{
#' \item{L,u,v}{Position in Luv colour space}
#' \item{col}{Colour name}
#' }
"luv_colours"
-#' Housing sales in TX.
+#' Housing sales in TX
#'
#' Information about the housing market in Texas provided by the TAMU
#' real estate center, \url{http://recenter.tamu.edu/}.
#'
#' @format A data frame with 8602 observations and 9 variables:
-#' \itemize{
+#' \describe{
#' \item{city}{Name of MLS area}
#' \item{year,month,date}{Date}
#' \item{sales}{Number of sales}
diff --git a/R/facet-.r b/R/facet-.r
index 029ec06..29bf001 100644
--- a/R/facet-.r
+++ b/R/facet-.r
@@ -1,62 +1,436 @@
-#' Facet specification.
+#' @include ggproto.r
+NULL
+
+#' @section Facets:
#'
-#' Create new facetting specification. For internal use only.
+#' All \code{facet_*} functions returns a \code{Facet} object or an object of a
+#' \code{Facet} subclass. This object describes how to assign data to different
+#' panels, how to apply positional scales and how to lay out the panels, once
+#' rendered.
#'
-#' @param ... object fields
-#' @param shrink shrink scales to fit output of statistics, not raw data
-#' @keywords internal
+#' Extending facets can range from the simple modifications of current facets,
+#' to very laborious rewrites with a lot of \code{\link{gtable}} manipulation.
+#' For some examples of both, please see the extension vignette.
+#'
+#' \code{Facet} subclasses, like other extendible ggproto classes, have a range
+#' of methods that can be modified. Some of these are required for all new
+#' subclasses, while other only need to be modified if need arises.
+#'
+#' The required methods are:
+#'
+#' \itemize{
+#' \item \code{compute_layout}: Based on layer data compute a mapping between
+#' panels, axes, and potentially other parameters such as faceting variable
+#' level etc. This method must return a data.frame containing at least the
+#' columns \code{PANEL}, \code{SCALE_X}, and \code{SCALE_Y} each containing
+#' integer keys mapping a PANEL to which axes it should use. In addition the
+#' data.frame can contain whatever other information is necessary to assign
+#' observations to the correct panel as well as determining the position of
+#' the panel.
+#'
+#' \item \code{map_data}: This method is supplied the data for each layer in
+#' turn and is expected to supply a \code{PANEL} column mapping each row to a
+#' panel defined in the layout. Additionally this method can also add or
+#' subtract data points as needed e.g. in the case of adding margins to
+#' \code{facet_grid}.
+#'
+#' \item \code{draw_panels}: This is where the panels are assembled into a
+#' \code{gtable} object. The method recieves, among others, a list of grobs
+#' defining the content of each panel as generated by the Geoms and Coord
+#' objects. The responsibility of the method is to decorate the panels with
+#' axes and strips as needed, as well as position them relative to each other
+#' in a gtable. For some of the automatic functions to work correctly, each
+#' panel, axis, and strip grob name must be prefixed with "panel", "axis", and
+#' "strip" respectively.
+#' }
+#'
+#' In addition to the methods described above, it is also possible to override
+#' the default behaviour of one or more of the following methods:
+#'
+#' \itemize{
+#' \item \code{setup_params}:
+#' \item \code{init_scales}: Given a master scale for x and y, create panel
+#' specific scales for each panel defined in the layout. The default is to
+#' simply clone the master scale.
+#'
+#' \item \code{train_scales}: Based on layer data train each set of panel
+#' scales. The default is to train it on the data related to the panel.
+#'
+#' \item \code{finish_data}: Make last-minute modifications to layer data
+#' before it is rendered by the Geoms. The default is to not modify it.
+#'
+#' \item \code{draw_back}: Add a grob in between the background defined by the
+#' Coord object (usually the axis grid) and the layer stack. The default is to
+#' return an empty grob for each panel.
+#'
+#' \item \code{draw_front}: As above except the returned grob is placed
+#' between the layer stack and the foreground defined by the Coord object
+#' (usually empty). The default is, as above, to return an empty grob.
+#'
+#' \item \code{draw_labels}: Given the gtable returned by \code{draw_panels},
+#' add axis titles to the gtable. The default is to add one title at each side
+#' depending on the position and existance of axes.
+#' }
+#'
+#' All extension methods recieve the content of the params field as the params
+#' argument, so the constructor function will generally put all relevant
+#' information into this field. The only exception is the \code{shrink}
+#' parameter which is used to determine if scales are retrained after Stat
+#' transformations has been applied.
+#'
+#' @rdname ggplot2-ggproto
+#' @format NULL
+#' @usage NULL
#' @export
-facet <- function(..., shrink = TRUE, subclass = c()) {
- structure(list(..., shrink = shrink), class = c(subclass, "facet"))
-}
+Facet <- ggproto("Facet", NULL,
+ shrink = FALSE,
+ params = list(),
+
+
+# Layout interface --------------------------------------------------------
+
+ train = function(self, data) {
+ self$compute_layout(data, self$params)
+ },
+ map = function(self, data, layout) {
+ self$map_data(data, layout, self$params)
+ },
+ render_back = function(self, data, layout, x_scales, y_scales, theme) {
+ self$draw_back(data, layout, x_scales, y_scales, theme, self$params)
+ },
+ render_front = function(self, data, layout, x_scales, y_scales, theme) {
+ self$draw_front(data, layout, x_scales, y_scales, theme, self$params)
+ },
+ render_panels = function(self, panels, layout, x_scales, y_scales, ranges, coord, data, theme, labels) {
+ panels <- self$draw_panels(panels, layout, x_scales, y_scales, ranges, coord, data, theme, self$params)
+ self$draw_labels(panels, layout, x_scales, y_scales, ranges, coord, data, theme, labels, self$params)
+ },
+ train_positions = function(self, x_scales, y_scales, layout, data) {
+ self$train_scales(x_scales, y_scales, layout, data, self$params)
+ },
+
+
+# Extension interface -----------------------------------------------------
+
+ compute_layout = function(data, params) {
+ stop("Not implemented", call. = FALSE)
+ },
+ map_data = function(data, layout, params) {
+ stop("Not implemented", call. = FALSE)
+ },
+ init_scales = function(layout, x_scale = NULL, y_scale = NULL, params) {
+ scales <- list()
+ if (!is.null(x_scale)) {
+ scales$x <- plyr::rlply(max(layout$SCALE_X), x_scale$clone())
+ }
+ if (!is.null(y_scale)) {
+ scales$y <- plyr::rlply(max(layout$SCALE_Y), y_scale$clone())
+ }
+ scales
+ },
+ train_scales = function(x_scales, y_scales, layout, data, params) {
+ # loop over each layer, training x and y scales in turn
+ for (layer_data in data) {
+ match_id <- match(layer_data$PANEL, layout$PANEL)
+
+ if (!is.null(x_scales)) {
+ x_vars <- intersect(x_scales[[1]]$aesthetics, names(layer_data))
+ SCALE_X <- layout$SCALE_X[match_id]
+
+ scale_apply(layer_data, x_vars, "train", SCALE_X, x_scales)
+ }
+
+ if (!is.null(y_scales)) {
+ y_vars <- intersect(y_scales[[1]]$aesthetics, names(layer_data))
+ SCALE_Y <- layout$SCALE_Y[match_id]
+
+ scale_apply(layer_data, y_vars, "train", SCALE_Y, y_scales)
+ }
+ }
+ },
+ draw_back = function(data, layout, x_scales, y_scales, theme, params) {
+ rep(list(zeroGrob()), length(unique(layout$PANEL)))
+ },
+ draw_front = function(data, layout, x_scales, y_scales, theme, params) {
+ rep(list(zeroGrob()), length(unique(layout$PANEL)))
+ },
+ draw_panels = function(panels, layout, x_scales, y_scales, ranges, coord, data, theme, params) {
+ stop("Not implemented", call. = FALSE)
+ },
+ draw_labels = function(panels, layout, x_scales, y_scales, ranges, coord, data, theme, labels, params) {
+ panel_dim <- find_panel(panels)
+
+ xlab_height_top <- grobHeight(labels$x[[1]])
+ panels <- gtable_add_rows(panels, xlab_height_top, pos = 0)
+ panels <- gtable_add_grob(panels, labels$x[[1]], name = "xlab-t",
+ l = panel_dim$l, r = panel_dim$r, t = 1, clip = "off")
+
+ xlab_height_bottom <- grobHeight(labels$x[[2]])
+ panels <- gtable_add_rows(panels, xlab_height_bottom, pos = -1)
+ panels <- gtable_add_grob(panels, labels$x[[2]], name = "xlab-b",
+ l = panel_dim$l, r = panel_dim$r, t = -1, clip = "off")
+
+ panel_dim <- find_panel(panels)
+
+ ylab_width_left <- grobWidth(labels$y[[1]])
+ panels <- gtable_add_cols(panels, ylab_width_left, pos = 0)
+ panels <- gtable_add_grob(panels, labels$y[[1]], name = "ylab-l",
+ l = 1, b = panel_dim$b, t = panel_dim$t, clip = "off")
+
+ ylab_width_right <- grobWidth(labels$y[[2]])
+ panels <- gtable_add_cols(panels, ylab_width_right, pos = -1)
+ panels <- gtable_add_grob(panels, labels$y[[2]], name = "ylab-r",
+ l = -1, b = panel_dim$b, t = panel_dim$t, clip = "off")
+
+ panels
+ },
+ setup_params = function(data, params) {
+ params
+ },
+ setup_data = function(data, params) {
+ data
+ },
+ finish_data = function(data, layout, x_scales, y_scales, params) {
+ data
+ }
+)
+
+# Helpers -----------------------------------------------------------------
#' Is this object a facetting specification?
#'
#' @param x object to test
#' @keywords internal
#' @export
-is.facet <- function(x) inherits(x, "facet")
+is.facet <- function(x) inherits(x, "Facet")
+# A "special" value, currently not used but could be used to determine
+# if faceting is active
+NO_PANEL <- -1L
-# Figure out layout from data from plot and all layers.
-#
-# This creates the layout data frame which maps from data values to
-# panel coordinates: ROW, COL and PANEL. It also records the panels that
-# contribute to each x and y scale.
-#
-# @param data a list of data frames (one for the plot and one for each
-# layer)
-facet_train_layout <- function(facet, data)
- UseMethod("facet_train_layout")
+unique_combs <- function(df) {
+ if (length(df) == 0) return()
-facet_map_layout <- function(facet, data, layout)
- UseMethod("facet_map_layout")
+ unique_values <- plyr::llply(df, ulevels)
+ rev(expand.grid(rev(unique_values), stringsAsFactors = FALSE,
+ KEEP.OUT.ATTRS = TRUE))
+}
-facet_render <- function(facet, panels_grob, coord, theme, geom_grobs)
- UseMethod("facet_render")
+df.grid <- function(a, b) {
+ if (is.null(a) || nrow(a) == 0) return(b)
+ if (is.null(b) || nrow(b) == 0) return(a)
-facet_strips <- function(facet, panel, theme)
- UseMethod("facet_strips")
+ indexes <- expand.grid(
+ i_a = seq_len(nrow(a)),
+ i_b = seq_len(nrow(b))
+ )
+ plyr::unrowname(cbind(
+ a[indexes$i_a, , drop = FALSE],
+ b[indexes$i_b, , drop = FALSE]
+ ))
+}
-facet_panels <- function(facet, panel, coord, theme, geom_grobs)
- UseMethod("facet_panels")
+# When evaluating variables in a facet specification, we evaluate bare
+# variables and expressions slightly differently. Bare variables should
+# always succeed, even if the variable doesn't exist in the data frame:
+# that makes it possible to repeat data across multiple factors. But
+# when evaluating an expression, you want to see any errors. That does
+# mean you can't have background data when facetting by an expression,
+# but that seems like a reasonable tradeoff.
+eval_facet_vars <- function(vars, data, env = emptyenv()) {
+ nms <- names(vars)
+ out <- list()
-facet_axes <- function(facet, panel, coord, theme)
- UseMethod("facet_axes")
+ for (i in seq_along(vars)) {
+ out[[ nms[[i]] ]] <- eval_facet_var(vars[[i]], data, env = env)
+ }
-# Text description of facetting variables
-facet_vars <- function(facet)
- UseMethod("facet_vars")
+ tibble::as_tibble(out)
+}
+eval_facet_var <- function(var, data, env = emptyenv()) {
+ if (is.name(var)) {
+ var <- as.character(var)
+ if (var %in% names(data)) {
+ data[[var]]
+ } else {
+ NULL
+ }
+ } else if (is.call(var)) {
+ eval(var, envir = data, enclos = env)
+ } else {
+ stop("Must use either variable name or expression when facetting",
+ call. = FALSE)
+ }
+}
+layout_null <- function() {
+ data.frame(PANEL = 1, ROW = 1, COL = 1, SCALE_X = 1, SCALE_Y = 1)
+}
+#' Get the maximal width/length of a list of grobs
+#'
+#' @param grobs A list of grobs
+#'
+#' @return The largest value. measured in cm as a unit object
+#'
+#' @keywords internal
+#' @export
+max_height <- function(grobs) {
+ unit(max(unlist(lapply(grobs, height_cm))), "cm")
+}
+#' @rdname max_height
+#' @export
+max_width <- function(grobs) {
+ unit(max(unlist(lapply(grobs, width_cm))), "cm")
+}
+#' Find panels in a gtable
+#'
+#' These functions help detect the placement of panels in a gtable, if they are
+#' named with "panel" in the beginning. \code{find_panel} returns the extend of
+#' the panel area, while \code{panel_cols} and \code{panel_rows} returns the
+#' columns and rows that contains panels respectively.
+#'
+#' @param table A gtable
+#'
+#' @return A data.frame with some or all of the columns t(op), r(ight),
+#' b(ottom), and l(eft)
+#'
+#' @keywords internal
#' @export
-format.facet <- function(x, ...) {
- name <- paste(rev(class(x)), collapse = "_")
+find_panel <- function(table) {
+ layout <- table$layout
+ panels <- layout[grepl("^panel", layout$name), , drop = FALSE]
- paste(name, "(", facet_vars(x), ")", sep = "")
+ data.frame(
+ t = min(panels$t),
+ r = max(panels$r),
+ b = max(panels$b),
+ l = min(panels$l)
+ )
+}
+#' @rdname find_panel
+#' @export
+panel_cols = function(table) {
+ panels <- table$layout[grepl("^panel", table$layout$name), , drop = FALSE]
+ unique(panels[, c('l', 'r')])
+}
+#' @rdname find_panel
+#' @export
+panel_rows <- function(table) {
+ panels <- table$layout[grepl("^panel", table$layout$name), , drop = FALSE]
+ unique(panels[, c('t', 'b')])
}
+#' Take input data and define a mapping between facetting variables and ROW,
+#' COL and PANEL keys
+#'
+#' @param data A list of data.frames, the first being the plot data and the
+#' subsequent individual layer data
+#' @param env The environment the vars should be evaluated in
+#' @param vars A list of quoted symbols matching columns in data
+#' @param drop should missing combinations/levels be dropped
+#'
+#' @return A data.frame with columns for PANEL, ROW, COL, and facetting vars
+#'
+#' @keywords internal
+#' @export
+combine_vars <- function(data, env = emptyenv(), vars = NULL, drop = TRUE) {
+ if (length(vars) == 0) return(data.frame())
+ # For each layer, compute the facet values
+ values <- compact(plyr::llply(data, eval_facet_vars, vars = vars, env = env))
+
+ # Form the base data frame which contains all combinations of facetting
+ # variables that appear in the data
+ has_all <- unlist(plyr::llply(values, length)) == length(vars)
+ if (!any(has_all)) {
+ stop("At least one layer must contain all variables used for facetting")
+ }
+
+ base <- unique(plyr::ldply(values[has_all]))
+ if (!drop) {
+ base <- unique_combs(base)
+ }
+
+ # Systematically add on missing combinations
+ for (value in values[!has_all]) {
+ if (empty(value)) next;
+
+ old <- base[setdiff(names(base), names(value))]
+ new <- unique(value[intersect(names(base), names(value))])
+ if (drop) {
+ new <- unique_combs(new)
+ }
+ base <- rbind(base, df.grid(old, new))
+ }
+
+ if (empty(base)) {
+ stop("Faceting variables must have at least one value", call. = FALSE)
+ }
+
+ base
+}
+#' Render panel axes
+#'
+#' These helpers facilitates generating theme compliant axes when
+#' building up the plot.
+#'
+#' @param x,y A list of ranges as available to the draw_panel method in
+#' \code{Facet} subclasses.
+#' @param coord A \code{Coord} object
+#' @param theme A \code{theme} object
+#' @param transpose Should the output be transposed?
+#'
+#' @return A list with the element "x" and "y" each containing axis
+#' specifications for the ranges passed in. Each axis specification is a list
+#' with a "top" and "bottom" element for x-axes and "left" and "right" element
+#' for y-axis, holding the respective axis grobs. Depending on the content of x
+#' and y some of the grobs might be zeroGrobs. If \code{transpose=TRUE} the
+#' content of the x and y elements will be transposed so e.g. all left-axes are
+#' collected in a left element as a list of grobs.
+#'
+#' @keywords internal
+#' @export
+#'
+render_axes <- function(x = NULL, y = NULL, coord, theme, transpose = FALSE) {
+ axes <- list()
+ if (!is.null(x)) {
+ axes$x <- lapply(x, coord$render_axis_h, theme)
+ }
+ if (!is.null(y)) {
+ axes$y <- lapply(y, coord$render_axis_v, theme)
+ }
+ if (transpose) {
+ axes <- list(
+ x = list(
+ top = lapply(axes$x, `[[`, "top"),
+ bottom = lapply(axes$x, `[[`, "bottom")
+ ),
+ y = list(
+ left = lapply(axes$y, `[[`, "left"),
+ right = lapply(axes$y, `[[`, "right")
+ )
+ )
+ }
+ axes
+}
+#' Render panel strips
+#'
+#' All positions are rendered and it is up to the facet to decide which to use
+#'
+#' @param x,y A data.frame with a column for each variable and a row for each
+#' combination to draw
+#' @param labeller A labeller function
+#' @param theme a \code{theme} object
+#'
+#' @return A list with an "x" and a "y" element, each containing a "top" and
+#' "bottom" or "left" and "right" element respectively. These contains a list of
+#' rendered strips as gtables.
+#'
+#' @keywords internal
#' @export
-print.facet <- function(x, ...) {
- cat(format(x, ...), "\n")
+render_strips <- function(x = NULL, y = NULL, labeller, theme) {
+ list(
+ x = build_strip(x, labeller, theme, TRUE),
+ y = build_strip(y, labeller, theme, FALSE)
+ )
}
diff --git a/R/facet-grid-.r b/R/facet-grid-.r
index 4d8f941..d5a7b9f 100644
--- a/R/facet-grid-.r
+++ b/R/facet-grid-.r
@@ -1,4 +1,11 @@
-#' Lay out panels in a grid.
+#' @include facet-.r
+NULL
+
+#' Lay out panels in a grid
+#'
+#' \code{facet_grid} forms a matrix of panels defined by row and column
+#' facetting variables. It is most useful when you have two discrete
+#' variables, and all combinations of the variables exist in the data.
#'
#' @param facets a formula with the rows (of the tabular display) on the LHS
#' and the columns (of the tabular display) on the RHS; the dot in the
@@ -131,10 +138,14 @@ facet_grid <- function(facets, margins = FALSE, scales = "fixed", space = "fixed
space <- match.arg(space, c("fixed", "free_x", "free_y", "free"))
space_free <- list(
- x = any(space %in% c("free_x", "free")),
- y = any(space %in% c("free_y", "free"))
+ x = any(space %in% c("free_x", "free")),
+ y = any(space %in% c("free_y", "free"))
)
+ if (!is.null(switch) && !switch %in% c("both", "x", "y")) {
+ stop("switch must be either 'both', 'x', or 'y'", call. = FALSE)
+ }
+
# Facets can either be a formula, a string, or a list of things to be
# convert to quoted
if (is.character(facets)) {
@@ -160,256 +171,236 @@ facet_grid <- function(facets, margins = FALSE, scales = "fixed", space = "fixed
# Check for deprecated labellers
labeller <- check_labeller(labeller)
- facet(
- rows = rows, cols = cols, margins = margins, shrink = shrink,
- free = free, space_free = space_free, labeller = labeller,
- as.table = as.table, switch = switch, drop = drop,
- subclass = "grid"
+ ggproto(NULL, FacetGrid,
+ shrink = shrink,
+ params = list(rows = rows, cols = cols, margins = margins,
+ free = free, space_free = space_free, labeller = labeller,
+ as.table = as.table, switch = switch, drop = drop)
)
}
-
+#' @rdname ggplot2-ggproto
+#' @format NULL
+#' @usage NULL
#' @export
-facet_train_layout.grid <- function(facet, data) {
- layout <- layout_grid(data, facet$rows, facet$cols, facet$margins,
- drop = facet$drop, as.table = facet$as.table)
+FacetGrid <- ggproto("FacetGrid", Facet,
+ shrink = TRUE,
- # Relax constraints, if necessary
- layout$SCALE_X <- if (facet$free$x) layout$COL else 1L
- layout$SCALE_Y <- if (facet$free$y) layout$ROW else 1L
+ compute_layout = function(data, params) {
+ rows <- as.quoted(params$rows)
+ cols <- as.quoted(params$cols)
- layout
-}
+ base_rows <- combine_vars(data, params$plot_env, rows, drop = params$drop)
+ if (!params$as.table) {
+ rev_order <- function(x) factor(x, levels = rev(ulevels(x)))
+ base_rows[] <- lapply(base_rows, rev_order)
+ }
+ base_cols <- combine_vars(data, params$plot_env, cols, drop = params$drop)
+ base <- df.grid(base_rows, base_cols)
+
+ # Add margins
+ base <- reshape2::add_margins(base, list(names(rows), names(cols)), params$margins)
+ # Work around bug in reshape2
+ base <- unique(base)
+
+ # Create panel info dataset
+ panel <- plyr::id(base, drop = TRUE)
+ panel <- factor(panel, levels = seq_len(attr(panel, "n")))
+
+ rows <- if (is.null(names(rows))) 1L else plyr::id(base[names(rows)], drop = TRUE)
+ cols <- if (is.null(names(cols))) 1L else plyr::id(base[names(cols)], drop = TRUE)
+
+ panels <- data.frame(PANEL = panel, ROW = rows, COL = cols, base,
+ check.names = FALSE, stringsAsFactors = FALSE)
+ panels <- panels[order(panels$PANEL), , drop = FALSE]
+ rownames(panels) <- NULL
+
+ panels$SCALE_X <- if (params$free$x) panels$COL else 1L
+ panels$SCALE_Y <- if (params$free$y) panels$ROW else 1L
+
+ panels
+ },
+ map_data = function(data, layout, params) {
+ if (empty(data)) {
+ return(cbind(data, PANEL = integer(0)))
+ }
+ rows <- as.quoted(params$rows)
+ cols <- as.quoted(params$cols)
+ vars <- c(names(rows), names(cols))
-#' @export
-facet_map_layout.grid <- function(facet, data, layout) {
- locate_grid(data, layout, facet$rows, facet$cols, facet$margins)
-}
+ # Compute facetting values and add margins
+ margin_vars <- list(intersect(names(rows), names(data)),
+ intersect(names(cols), names(data)))
+ data <- reshape2::add_margins(data, margin_vars, params$margins)
-#' @export
-facet_render.grid <- function(facet, panel, coord, theme, geom_grobs) {
- axes <- facet_axes(facet, panel, coord, theme)
- strips <- facet_strips(facet, panel, theme)
- panels <- facet_panels(facet, panel, coord, theme, geom_grobs)
-
- # adjust the size of axes to the size of panel
- axes$l$heights <- panels$heights
- axes$b$widths <- panels$widths
-
- # adjust the size of the strips to the size of the panels
- strips$r$heights <- panels$heights
- strips$t$widths <- panels$widths
-
- # Check if switch is consistent with grid layout
- switch_x <- !is.null(facet$switch) && facet$switch %in% c("both", "x")
- switch_y <- !is.null(facet$switch) && facet$switch %in% c("both", "y")
- if (switch_x && length(strips$t) == 0) {
- facet$switch <- if (facet$switch == "both") "y" else NULL
- switch_x <- FALSE
- warning("Cannot switch x axis strips as they do not exist", call. = FALSE)
- }
- if (switch_y && length(strips$r) == 0) {
- facet$switch <- if (facet$switch == "both") "x" else NULL
- switch_y <- FALSE
- warning("Cannot switch y axis strips as they do not exist", call. = FALSE)
- }
+ facet_vals <- eval_facet_vars(c(rows, cols), data, params$plot_env)
+ # If any facetting variables are missing, add them in by
+ # duplicating the data
+ missing_facets <- setdiff(vars, names(facet_vals))
+ if (length(missing_facets) > 0) {
+ to_add <- unique(layout[missing_facets])
- # Combine components into complete plot
- if (is.null(facet$switch)) {
- top <- strips$t
- top <- gtable_add_cols(top, strips$r$widths)
- top <- gtable_add_cols(top, axes$l$widths, pos = 0)
+ data_rep <- rep.int(1:nrow(data), nrow(to_add))
+ facet_rep <- rep(1:nrow(to_add), each = nrow(data))
- center <- cbind(axes$l, panels, strips$r, z = c(2, 1, 3))
- bottom <- axes$b
- bottom <- gtable_add_cols(bottom, strips$r$widths)
- bottom <- gtable_add_cols(bottom, axes$l$widths, pos = 0)
+ data <- plyr::unrowname(data[data_rep, , drop = FALSE])
+ facet_vals <- plyr::unrowname(cbind(
+ facet_vals[data_rep, , drop = FALSE],
+ to_add[facet_rep, , drop = FALSE]))
+ }
- complete <- rbind(top, center, bottom, z = c(1, 2, 3))
+ # Add PANEL variable
+ if (nrow(facet_vals) == 0) {
+ # Special case of no facetting
+ data$PANEL <- NO_PANEL
+ } else {
+ facet_vals[] <- lapply(facet_vals[], as.factor)
+ facet_vals[] <- lapply(facet_vals[], addNA, ifany = TRUE)
- } else {
- # Add padding between the switched strips and the axes
- padding <- convertUnit(theme$strip.switch.pad.grid, "cm")
+ keys <- plyr::join.keys(facet_vals, layout, by = vars)
- if (switch_x) {
- t_heights <- c(padding, strips$t$heights)
- gt_t <- gtable(widths = strips$t$widths, heights = unit(t_heights, "cm"))
- gt_t <- gtable_add_grob(gt_t, strips$t, name = strips$t$name, clip = "off",
- t = 1, l = 1, b = -1, r = -1)
+ data$PANEL <- layout$PANEL[match(keys$x, keys$y)]
}
- if (switch_y) {
- r_widths <- c(strips$r$widths, padding)
- gt_r <- gtable(widths = unit(r_widths, "cm"), heights = strips$r$heights)
- gt_r <- gtable_add_grob(gt_r, strips$r, name = strips$r$name, clip = "off",
- t = 1, l = 1, b = -1, r = -1)
+ data[order(data$PANEL), , drop = FALSE]
+ },
+ draw_panels = function(panels, layout, x_scales, y_scales, ranges, coord, data, theme, params) {
+ cols <- which(layout$ROW == 1)
+ rows <- which(layout$COL == 1)
+ axes <- render_axes(ranges[cols], ranges[rows], coord, theme, transpose = TRUE)
+
+ col_vars <- unique(layout[names(params$cols)])
+ row_vars <- unique(layout[names(params$rows)])
+ # Adding labels metadata, useful for labellers
+ attr(col_vars, "type") <- "cols"
+ attr(col_vars, "facet") <- "grid"
+ attr(row_vars, "type") <- "rows"
+ attr(row_vars, "facet") <- "grid"
+ strips <- render_strips(col_vars, row_vars, params$labeller, theme)
+
+ aspect_ratio <- theme$aspect.ratio
+ if (is.null(aspect_ratio) && !params$free$x && !params$free$y) {
+ aspect_ratio <- coord$aspect(ranges[[1]])
}
-
- # Combine plot elements according to strip positions
- if (switch_x && switch_y) {
- center <- cbind(gt_r, axes$l, panels, z = c(3, 2, 1))
-
- bottom <- rbind(axes$b, gt_t)
- bottom <- gtable_add_cols(bottom, axes$l$widths, pos = 0)
- bottom <- gtable_add_cols(bottom, gt_r$widths, pos = 0)
-
- complete <- rbind(center, bottom, z = c(1, 2))
- } else if (switch_x) {
- center <- cbind(axes$l, panels, strips$r, z = c(2, 1, 3))
-
- bottom <- rbind(axes$b, gt_t)
- bottom <- gtable_add_cols(bottom, strips$r$widths)
- bottom <- gtable_add_cols(bottom, axes$l$widths, pos = 0)
-
- complete <- rbind(center, bottom, z = c(1, 2))
- } else if (switch_y) {
- top <- strips$t
- top <- gtable_add_cols(top, axes$l$widths, pos = 0)
- top <- gtable_add_cols(top, gt_r$widths, pos = 0)
-
- center <- cbind(gt_r, axes$l, panels, z = c(3, 2, 1))
- bottom <- axes$b
- bottom <- gtable_add_cols(bottom, axes$l$widths, pos = 0)
- bottom <- gtable_add_cols(bottom, gt_r$widths, pos = 0)
-
- complete <- rbind(top, center, bottom, z = c(1, 2, 3))
+ if (is.null(aspect_ratio)) {
+ aspect_ratio <- 1
+ respect <- FALSE
} else {
- stop("`switch` must be either NULL, 'both', 'x', or 'y'",
- call. = FALSE)
+ respect <- TRUE
}
- }
-
- complete$respect <- panels$respect
- complete$name <- "layout"
- bottom <- axes$b
-
- complete
-}
-
-#' @export
-facet_strips.grid <- function(facet, panel, theme) {
- col_vars <- unique(panel$layout[names(facet$cols)])
- row_vars <- unique(panel$layout[names(facet$rows)])
-
- # Adding labels metadata, useful for labellers
- attr(col_vars, "type") <- "cols"
- attr(col_vars, "facet") <- "grid"
- attr(row_vars, "type") <- "rows"
- attr(row_vars, "facet") <- "grid"
-
- dir <- list(r = "r", t = "t")
- if (!is.null(facet$switch) && facet$switch %in% c("both", "x")) {
- dir$t <- "b"
- }
- if (!is.null(facet$switch) && facet$switch %in% c("both", "y")) {
- dir$r <- "l"
- }
-
- strips <- list(
- r = build_strip(panel, row_vars, facet$labeller,
- theme, dir$r, switch = facet$switch),
- t = build_strip(panel, col_vars, facet$labeller,
- theme, dir$t, switch = facet$switch)
- )
-
- Map(function(strip, side) {
- if (side %in% c("t", "b")) {
- gtable_add_col_space(strip, theme$panel.margin.x %||% theme$panel.margin)
+ ncol <- max(layout$COL)
+ nrow <- max(layout$ROW)
+ panel_table <- matrix(panels, nrow = nrow, ncol = ncol, byrow = TRUE)
+
+ # @kohske
+ # Now size of each panel is calculated using PANEL$ranges, which is given by
+ # coord_train called by train_range.
+ # So here, "scale" need not to be referred.
+ #
+ # In general, panel has all information for building facet.
+ if (params$space_free$x) {
+ ps <- layout$PANEL[layout$ROW == 1]
+ widths <- vapply(ps, function(i) diff(ranges[[i]]$x.range), numeric(1))
+ panel_widths <- unit(widths, "null")
} else {
- gtable_add_row_space(strip, theme$panel.margin.y %||% theme$panel.margin)
+ panel_widths <- rep(unit(1, "null"), ncol)
}
- }, strips, dir)
-}
-
-#' @export
-facet_axes.grid <- function(facet, panel, coord, theme) {
- axes <- list()
-
- # Horizontal axes
- cols <- which(panel$layout$ROW == 1)
- grobs <- lapply(panel$ranges[cols], coord$render_axis_h, theme = theme)
- axes$b <- gtable_add_col_space(gtable_row("axis-b", grobs),
- theme$panel.margin.x %||% theme$panel.margin)
-
- # Vertical axes
- rows <- which(panel$layout$COL == 1)
- grobs <- lapply(panel$ranges[rows], coord$render_axis_v, theme = theme)
- axes$l <- gtable_add_row_space(gtable_col("axis-l", grobs),
- theme$panel.margin.y %||% theme$panel.margin)
-
- axes
-}
-
-#' @export
-facet_panels.grid <- function(facet, panel, coord, theme, geom_grobs) {
-
- # If user hasn't set aspect ratio, and we have fixed scales, then
- # ask the coordinate system if it wants to specify one
- aspect_ratio <- theme$aspect.ratio
- if (is.null(aspect_ratio) && !facet$free$x && !facet$free$y) {
- aspect_ratio <- coord$aspect(panel$ranges[[1]])
- }
- if (is.null(aspect_ratio)) {
- aspect_ratio <- 1
- respect <- FALSE
- } else {
- respect <- TRUE
- }
-
- # Add background and foreground to panels
- panels <- panel$layout$PANEL
- ncol <- max(panel$layout$COL)
- nrow <- max(panel$layout$ROW)
-
- panel_grobs <- lapply(panels, function(i) {
- fg <- coord$render_fg(panel$ranges[[i]], theme)
- bg <- coord$render_bg(panel$ranges[[i]], theme)
-
- geom_grobs <- lapply(geom_grobs, `[[`, i)
-
- if (theme$panel.ontop) {
- panel_grobs <- c(geom_grobs, list(bg), list(fg))
+ if (params$space_free$y) {
+ ps <- layout$PANEL[layout$COL == 1]
+ heights <- vapply(ps, function(i) diff(ranges[[i]]$y.range), numeric(1))
+ panel_heights <- unit(heights, "null")
} else {
- panel_grobs <- c(list(bg), geom_grobs, list(fg))
+ panel_heights <- rep(unit(1 * aspect_ratio, "null"), nrow)
}
- gTree(children = do.call("gList", panel_grobs))
- })
-
- panel_matrix <- matrix(panel_grobs, nrow = nrow, ncol = ncol, byrow = TRUE)
-
- # @kohske
- # Now size of each panel is calculated using PANEL$ranges, which is given by
- # coord_train called by train_range.
- # So here, "scale" need not to be referred.
- #
- # In general, panel has all information for building facet.
- if (facet$space_free$x) {
- ps <- panel$layout$PANEL[panel$layout$ROW == 1]
- widths <- vapply(ps, function(i) diff(panel$ranges[[i]]$x.range), numeric(1))
- panel_widths <- unit(widths, "null")
- } else {
- panel_widths <- rep(unit(1, "null"), ncol)
- }
- if (facet$space_free$y) {
- ps <- panel$layout$PANEL[panel$layout$COL == 1]
- heights <- vapply(ps, function(i) diff(panel$ranges[[i]]$y.range), numeric(1))
- panel_heights <- unit(heights, "null")
- } else {
- panel_heights <- rep(unit(1 * aspect_ratio, "null"), nrow)
+ panel_table <- gtable_matrix("layout", panel_table,
+ panel_widths, panel_heights, respect = respect, clip = "on", z = matrix(1, ncol = ncol, nrow = nrow))
+ panel_table$layout$name <- paste0('panel-', rep(seq_len(ncol), nrow), '-', rep(seq_len(nrow), each = ncol))
+
+ panel_table <- gtable_add_col_space(panel_table,
+ theme$panel.spacing.x %||% theme$panel.spacing)
+ panel_table <- gtable_add_row_space(panel_table,
+ theme$panel.spacing.y %||% theme$panel.spacing)
+
+ # Add axes
+ panel_table <- gtable_add_rows(panel_table, max_height(axes$x$top), 0)
+ panel_table <- gtable_add_rows(panel_table, max_height(axes$x$bottom), -1)
+ panel_table <- gtable_add_cols(panel_table, max_width(axes$y$left), 0)
+ panel_table <- gtable_add_cols(panel_table, max_width(axes$y$right), -1)
+ panel_pos_col <- panel_cols(panel_table)
+ panel_pos_rows <- panel_rows(panel_table)
+
+ panel_table <- gtable_add_grob(panel_table, axes$x$top, 1, panel_pos_col$l, clip = "off", name = paste0("axis-t-", seq_along(axes$x$top)), z = 3)
+ panel_table <- gtable_add_grob(panel_table, axes$x$bottom, -1, panel_pos_col$l, clip = "off", name = paste0("axis-b-", seq_along(axes$x$bottom)), z = 3)
+ panel_table <- gtable_add_grob(panel_table, axes$y$left, panel_pos_rows$t, 1, clip = "off", name = paste0("axis-l-", seq_along(axes$y$left)), z = 3)
+ panel_table <- gtable_add_grob(panel_table, axes$y$right, panel_pos_rows$t, -1, clip = "off", name = paste0("axis-r-", seq_along(axes$y$right)), z= 3)
+
+ # Add strips
+ switch_x <- !is.null(params$switch) && params$switch %in% c("both", "x")
+ switch_y <- !is.null(params$switch) && params$switch %in% c("both", "y")
+ inside_x <- (theme$strip.placement.x %||% theme$strip.placement %||% "inside") == "inside"
+ inside_y <- (theme$strip.placement.y %||% theme$strip.placement %||% "inside") == "inside"
+ strip_padding <- convertUnit(theme$strip.switch.pad.grid, "cm")
+ panel_pos_col <- panel_cols(panel_table)
+ if (switch_x) {
+ if (!is.null(strips$x$bottom)) {
+ if (inside_x) {
+ panel_table <- gtable_add_rows(panel_table, max_height(strips$x$bottom), -2)
+ panel_table <- gtable_add_grob(panel_table, strips$x$bottom, -2, panel_pos_col$l, clip = "on", name = paste0("strip-b-", seq_along(strips$x$bottom)), z = 2)
+ } else {
+ panel_table <- gtable_add_rows(panel_table, strip_padding, -1)
+ panel_table <- gtable_add_rows(panel_table, max_height(strips$x$bottom), -1)
+ panel_table <- gtable_add_grob(panel_table, strips$x$bottom, -1, panel_pos_col$l, clip = "on", name = paste0("strip-b-", seq_along(strips$x$bottom)), z = 2)
+ }
+ }
+ } else {
+ if (!is.null(strips$x$top)) {
+ if (inside_x) {
+ panel_table <- gtable_add_rows(panel_table, max_height(strips$x$top), 1)
+ panel_table <- gtable_add_grob(panel_table, strips$x$top, 2, panel_pos_col$l, clip = "on", name = paste0("strip-t-", seq_along(strips$x$top)), z = 2)
+ } else {
+ panel_table <- gtable_add_rows(panel_table, strip_padding, 0)
+ panel_table <- gtable_add_rows(panel_table, max_height(strips$x$top), 0)
+ panel_table <- gtable_add_grob(panel_table, strips$x$top, 1, panel_pos_col$l, clip = "on", name = paste0("strip-t-", seq_along(strips$x$top)), z = 2)
+ }
+ }
+ }
+ panel_pos_rows <- panel_rows(panel_table)
+ if (switch_y) {
+ if (!is.null(strips$y$left)) {
+ if (inside_y) {
+ panel_table <- gtable_add_cols(panel_table, max_width(strips$y$left), 1)
+ panel_table <- gtable_add_grob(panel_table, strips$y$left, panel_pos_rows$t, 2, clip = "on", name = paste0("strip-l-", seq_along(strips$y$left)), z = 2)
+ } else {
+ panel_table <- gtable_add_cols(panel_table, strip_padding, 0)
+ panel_table <- gtable_add_cols(panel_table, max_width(strips$y$left), 0)
+ panel_table <- gtable_add_grob(panel_table, strips$y$left, panel_pos_rows$t, 1, clip = "on", name = paste0("strip-l-", seq_along(strips$y$left)), z = 2)
+ }
+ }
+ } else {
+ if (!is.null(strips$y$right)) {
+ if (inside_y) {
+ panel_table <- gtable_add_cols(panel_table, max_width(strips$y$right), -2)
+ panel_table <- gtable_add_grob(panel_table, strips$y$right, panel_pos_rows$t, -2, clip = "on", name = paste0("strip-r-", seq_along(strips$y$right)), z = 2)
+ } else {
+ panel_table <- gtable_add_cols(panel_table, strip_padding, -1)
+ panel_table <- gtable_add_cols(panel_table, max_width(strips$y$right), -1)
+ panel_table <- gtable_add_grob(panel_table, strips$y$right, panel_pos_rows$t, -1, clip = "on", name = paste0("strip-r-", seq_along(strips$y$right)), z = 2)
+ }
+ }
+ }
+ panel_table
}
+)
- panels <- gtable_matrix("panel", panel_matrix,
- panel_widths, panel_heights, respect = respect)
- panels <- gtable_add_col_space(panels, theme$panel.margin.x %||% theme$panel.margin)
- panels <- gtable_add_row_space(panels, theme$panel.margin.y %||% theme$panel.margin)
-
- panels
-}
+# Helpers -----------------------------------------------------------------
-#' @export
-facet_vars.grid <- function(facet) {
- paste(lapply(list(facet$rows, facet$cols), paste, collapse = ", "),
- collapse = " ~ ")
+ulevels <- function(x) {
+ if (is.factor(x)) {
+ x <- addNA(x, TRUE)
+ factor(levels(x), levels(x), exclude = NULL)
+ } else {
+ sort(unique(x))
+ }
}
diff --git a/R/facet-layout.r b/R/facet-layout.r
deleted file mode 100644
index ef312b4..0000000
--- a/R/facet-layout.r
+++ /dev/null
@@ -1,175 +0,0 @@
-# Layout panels in a 2d grid.
-#
-# @params data list of data frames, one for each layer
-# @params rows variables that form the rows
-# @params cols variables that form the columns
-# @return a data frame with columns \code{PANEL}, \code{ROW} and \code{COL},
-# that match the facetting variable values up with their position in the
-# grid
-layout_grid <- function(data, rows = NULL, cols = NULL, margins = NULL,
- drop = TRUE, as.table = TRUE) {
- if (length(rows) == 0 && length(cols) == 0) return(layout_null())
- rows <- as.quoted(rows)
- cols <- as.quoted(cols)
-
- base_rows <- layout_base(data, rows, drop = drop)
- if (!as.table) {
- rev_order <- function(x) factor(x, levels = rev(ulevels(x)))
- base_rows[] <- lapply(base_rows, rev_order)
- }
- base_cols <- layout_base(data, cols, drop = drop)
- base <- df.grid(base_rows, base_cols)
-
- # Add margins
- base <- reshape2::add_margins(base, list(names(rows), names(cols)), margins)
- # Work around bug in reshape2
- base <- unique(base)
-
- # Create panel info dataset
- panel <- plyr::id(base, drop = TRUE)
- panel <- factor(panel, levels = seq_len(attr(panel, "n")))
-
- rows <- if (is.null(names(rows))) 1L else plyr::id(base[names(rows)], drop = TRUE)
- cols <- if (is.null(names(cols))) 1L else plyr::id(base[names(cols)], drop = TRUE)
-
- panels <- data.frame(PANEL = panel, ROW = rows, COL = cols, base,
- check.names = FALSE, stringsAsFactors = FALSE)
- panels <- panels[order(panels$PANEL), , drop = FALSE]
- rownames(panels) <- NULL
- panels
-}
-
-# Layout out panels in a 1d ribbon.
-#
-# @params drop should missing combinations be excluded from the plot?
-# @keywords internal
-layout_wrap <- function(data, vars = NULL, nrow = NULL, ncol = NULL,
- as.table = TRUE, drop = TRUE, dir = "h") {
- vars <- as.quoted(vars)
- if (length(vars) == 0) return(layout_null())
-
- base <- plyr::unrowname(layout_base(data, vars, drop = drop))
-
- id <- plyr::id(base, drop = TRUE)
- n <- attr(id, "n")
-
- dims <- wrap_dims(n, nrow, ncol)
- layout <- data.frame(PANEL = factor(id, levels = seq_len(n)))
-
- if (as.table) {
- layout$ROW <- as.integer((id - 1L) %/% dims[2] + 1L)
- } else {
- layout$ROW <- as.integer(dims[1] - (id - 1L) %/% dims[2])
- }
- layout$COL <- as.integer((id - 1L) %% dims[2] + 1L)
-
- # For vertical direction, flip row and col
- if (identical(dir, "v")) {
- layout[c("ROW", "COL")] <- layout[c("COL", "ROW")]
- }
-
- panels <- cbind(layout, plyr::unrowname(base))
- panels <- panels[order(panels$PANEL), , drop = FALSE]
- rownames(panels) <- NULL
- panels
-}
-
-layout_null <- function() {
- data.frame(PANEL = 1, ROW = 1, COL = 1)
-}
-
-# Base layout function that generates all combinations of data needed for
-# facetting
-# The first data frame in the list should be the default data for the plot.
-# Other data frames in the list are ones that are added to layers.
-#
-# @params data list of data frames (one for each layer)
-# @keywords internal
-layout_base <- function(data, vars = NULL, drop = TRUE) {
- if (length(vars) == 0) return(data.frame())
-
- # For each layer, compute the facet values
- values <- compact(plyr::llply(data, quoted_df, vars = vars))
-
- # Form the base data frame which contains all combinations of facetting
- # variables that appear in the data
- has_all <- unlist(plyr::llply(values, length)) == length(vars)
- if (!any(has_all)) {
- stop("At least one layer must contain all variables used for facetting")
- }
-
- base <- unique(plyr::ldply(values[has_all]))
- if (!drop) {
- base <- unique_combs(base)
- }
-
- # Systematically add on missing combinations
- for (value in values[!has_all]) {
- if (empty(value)) next;
-
- old <- base[setdiff(names(base), names(value))]
- new <- unique(value[intersect(names(base), names(value))])
- if (drop) {
- new <- unique_combs(new)
- }
- base <- rbind(base, df.grid(old, new))
- }
-
- if (empty(base)) {
- stop("Faceting variables must have at least one value", call. = FALSE)
- }
-
- base
-}
-
-ulevels <- function(x) {
- if (is.factor(x)) {
- x <- addNA(x, TRUE)
- factor(levels(x), levels(x), exclude = NULL)
- } else {
- sort(unique(x))
- }
-}
-
-unique_combs <- function(df) {
- if (length(df) == 0) return()
-
- unique_values <- plyr::llply(df, ulevels)
- rev(expand.grid(rev(unique_values), stringsAsFactors = FALSE,
- KEEP.OUT.ATTRS = TRUE))
-}
-
-df.grid <- function(a, b) {
- if (nrow(a) == 0) return(b)
- if (nrow(b) == 0) return(a)
-
- indexes <- expand.grid(
- i_a = seq_len(nrow(a)),
- i_b = seq_len(nrow(b))
- )
- plyr::unrowname(cbind(
- a[indexes$i_a, , drop = FALSE],
- b[indexes$i_b, , drop = FALSE]
- ))
-}
-
-quoted_df <- function(data, vars) {
- values <- plyr::eval.quoted(vars, data, emptyenv(), try = TRUE)
- as.data.frame(compact(values), optional = TRUE, stringsAsFactors = FALSE)
-}
-
-# Arrange 1d structure into a grid
-wrap_dims <- function(n, nrow = NULL, ncol = NULL) {
- if (is.null(ncol) && is.null(nrow)) {
- rc <- grDevices::n2mfrow(n)
- nrow <- rc[2]
- ncol <- rc[1]
- } else if (is.null(ncol)) {
- ncol <- ceiling(n / nrow)
- } else if (is.null(nrow)) {
- nrow <- ceiling(n / ncol)
- }
- stopifnot(nrow * ncol >= n)
-
- c(nrow, ncol)
-}
diff --git a/R/facet-locate.r b/R/facet-locate.r
deleted file mode 100644
index 57483e4..0000000
--- a/R/facet-locate.r
+++ /dev/null
@@ -1,84 +0,0 @@
-# A "special" value, currently not used but could be used to determine
-# if faceting is active
-NO_PANEL <- -1L
-
-# Take single layer of data and combine it with panel information to split
-# data into different panels. Adds in extra data for missing facetting
-# levels and for margins.
-#
-# @params data a data frame
-locate_grid <- function(data, panels, rows = NULL, cols = NULL, margins = FALSE) {
- if (empty(data)) {
- return(cbind(data, PANEL = integer(0)))
- }
-
- rows <- as.quoted(rows)
- cols <- as.quoted(cols)
- vars <- c(names(rows), names(cols))
-
- # Compute facetting values and add margins
- margin_vars <- list(intersect(names(rows), names(data)),
- intersect(names(cols), names(data)))
- data <- reshape2::add_margins(data, margin_vars, margins)
-
- facet_vals <- quoted_df(data, c(rows, cols))
-
- # If any facetting variables are missing, add them in by
- # duplicating the data
- missing_facets <- setdiff(vars, names(facet_vals))
- if (length(missing_facets) > 0) {
- to_add <- unique(panels[missing_facets])
-
- data_rep <- rep.int(1:nrow(data), nrow(to_add))
- facet_rep <- rep(1:nrow(to_add), each = nrow(data))
-
- data <- plyr::unrowname(data[data_rep, , drop = FALSE])
- facet_vals <- plyr::unrowname(cbind(
- facet_vals[data_rep, , drop = FALSE],
- to_add[facet_rep, , drop = FALSE]))
- }
-
- # Add PANEL variable
- if (nrow(facet_vals) == 0) {
- # Special case of no facetting
- data$PANEL <- NO_PANEL
- } else {
- facet_vals[] <- lapply(facet_vals[], as.factor)
- facet_vals[] <- lapply(facet_vals[], addNA, ifany = TRUE)
-
- keys <- plyr::join.keys(facet_vals, panels, by = vars)
-
- data$PANEL <- panels$PANEL[match(keys$x, keys$y)]
- }
-
- data[order(data$PANEL), , drop = FALSE]
-}
-
-locate_wrap <- function(data, panels, vars) {
- if (empty(data)) {
- return(cbind(data, PANEL = integer(0)))
- }
- vars <- as.quoted(vars)
-
- facet_vals <- quoted_df(data, vars)
- facet_vals[] <- lapply(facet_vals[], as.factor)
-
- missing_facets <- setdiff(names(vars), names(facet_vals))
- if (length(missing_facets) > 0) {
-
- to_add <- unique(panels[missing_facets])
-
- data_rep <- rep.int(1:nrow(data), nrow(to_add))
- facet_rep <- rep(1:nrow(to_add), each = nrow(data))
-
- data <- plyr::unrowname(data[data_rep, , drop = FALSE])
- facet_vals <- plyr::unrowname(cbind(
- facet_vals[data_rep, , drop = FALSE],
- to_add[facet_rep, , drop = FALSE]))
- }
-
- keys <- plyr::join.keys(facet_vals, panels, by = names(vars))
-
- data$PANEL <- panels$PANEL[match(keys$x, keys$y)]
- data[order(data$PANEL), ]
-}
diff --git a/R/facet-null.r b/R/facet-null.r
index a320537..91bb5f9 100644
--- a/R/facet-null.r
+++ b/R/facet-null.r
@@ -1,76 +1,75 @@
+#' @include facet-.r
+NULL
+
#' Facet specification: a single panel.
#'
#' @inheritParams facet_grid
+#' @keywords internal
#' @export
#' @examples
#' # facet_null is the default facetting specification if you
#' # don't override it with facet_grid or facet_wrap
#' ggplot(mtcars, aes(mpg, wt)) + geom_point()
facet_null <- function(shrink = TRUE) {
- facet(shrink = shrink, subclass = "null")
+ ggproto(NULL, FacetNull,
+ shrink = shrink
+ )
}
+#' @rdname ggplot2-ggproto
+#' @format NULL
+#' @usage NULL
#' @export
-facet_train_layout.null <- function(facet, data) {
- data.frame(
- PANEL = 1L, ROW = 1L, COL = 1L,
- SCALE_X = 1L, SCALE_Y = 1L)
-}
+FacetNull <- ggproto("FacetNull", Facet,
+ shrink = TRUE,
-#' @export
-facet_map_layout.null <- function(facet, data, layout) {
- # Need the is.waive check for special case where no data, but aesthetics
- # are mapped to vectors
- if (is.waive(data) || empty(data))
- return(cbind(data, PANEL = integer(0)))
- data$PANEL <- 1L
- data
-}
+ compute_layout = function(data, params) {
+ layout_null()
+ },
+ map_data = function(data, layout, params) {
+ # Need the is.waive check for special case where no data, but aesthetics
+ # are mapped to vectors
+ if (is.waive(data) || empty(data))
+ return(cbind(data, PANEL = integer(0)))
+ data$PANEL <- 1L
+ data
+ },
+ draw_panels = function(panels, layout, x_scales, y_scales, ranges, coord, data, theme, params) {
-#' @export
-facet_render.null <- function(facet, panel, coord, theme, geom_grobs) {
- range <- panel$ranges[[1]]
+ range <- ranges[[1]]
- # Figure out aspect ratio
- aspect_ratio <- theme$aspect.ratio %||% coord$aspect(range)
- if (is.null(aspect_ratio)) {
- aspect_ratio <- 1
- respect <- FALSE
- } else {
- respect <- TRUE
- }
+ # Figure out aspect ratio
+ aspect_ratio <- theme$aspect.ratio %||% coord$aspect(range)
+ if (is.null(aspect_ratio)) {
+ aspect_ratio <- 1
+ respect <- FALSE
+ } else {
+ respect <- TRUE
+ }
+ axis_h <- coord$render_axis_h(range, theme)
+ axis_v <- coord$render_axis_v(range, theme)
- fg <- coord$render_fg(range, theme)
- bg <- coord$render_bg(range, theme)
+ all <- matrix(list(
+ zeroGrob(), axis_h$top, zeroGrob(),
+ axis_v$left, panels[[1]], axis_v$right,
+ zeroGrob(), axis_h$bottom, zeroGrob()
+ ), ncol = 3, byrow = TRUE)
+ z_matrix <- matrix(c(5, 6, 4, 7, 1, 8, 3, 9, 2), ncol = 3, byrow = TRUE)
+ grob_widths <- unit.c(grobWidth(axis_v$left), unit(1, "null"), grobWidth(axis_v$right))
+ grob_heights <- unit.c(grobHeight(axis_h$top), unit(aspect_ratio, "null"), grobHeight(axis_h$bottom))
+ grob_names <- c("spacer", "axis-l", "spacer", "axis-t", "panel", "axis-b", "spacer", "axis-r", "spacer")
+ grob_clip <- c("off", "off", "off", "off", "on", "off", "off", "off", "off")
- # Flatten layers - we know there's only one panel
- geom_grobs <- lapply(geom_grobs, "[[", 1)
+ layout <- gtable_matrix("layout", all,
+ widths = grob_widths, heights = grob_heights,
+ respect = respect, clip = grob_clip,
+ z = z_matrix
+ )
+ layout$layout$name <- grob_names
- if (theme$panel.ontop) {
- panel_grobs <- c(geom_grobs, list(bg), list(fg))
- } else {
- panel_grobs <- c(list(bg), geom_grobs, list(fg))
+ layout
+ },
+ vars = function(self) {
+ ""
}
-
- panel_grob <- gTree(children = do.call("gList", panel_grobs))
- axis_h <- coord$render_axis_h(range, theme)
- axis_v <- coord$render_axis_v(range, theme)
-
- all <- matrix(list(
- axis_v, panel_grob,
- zeroGrob(), axis_h
- ), ncol = 2, byrow = TRUE)
-
- layout <- gtable_matrix("layout", all,
- widths = unit.c(grobWidth(axis_v), unit(1, "null")),
- heights = unit.c(unit(aspect_ratio, "null"), grobHeight(axis_h)),
- respect = respect, clip = c("off", "off", "on", "off"),
- z = matrix(c(3, 2, 1, 4), ncol = 2, byrow = TRUE)
- )
- layout$layout$name <- c("axis-l", "spacer", "panel", "axis-b")
-
- layout
-}
-
-#' @export
-facet_vars.null <- function(facet) ""
+)
diff --git a/R/facet-viewports.r b/R/facet-viewports.r
deleted file mode 100644
index 6f41bf5..0000000
--- a/R/facet-viewports.r
+++ /dev/null
@@ -1,50 +0,0 @@
-# Assign viewports to a matrix of grobs
-#
-# Uses the structure (and names) of the matrix of grobs, to automatically
-# assign each grob to the appropriate viewport
-assign_viewports <- function(grobs) {
- make_grid <- function(type) {
- data.frame(
- type = type,
- x = c(row(grobs[[type]])),
- y = c(col(grobs[[type]]))
- )
- }
-
- assign_vp <- function(type, x, y) {
- ggname(type, editGrob(grobs[[type]][[x, y]], vp = vp_path(x, y, type)))
- }
-
- grid <- plyr::ldply(names(grobs), make_grid)
- plyr::mlply(grid, assign_vp)
-}
-
-# Setup matrix of viewports for a layout with given parameters
-setup_viewports <- function(type, data, offset = c(0,0), clip = "on") {
- rows <- nrow(data)
- cols <- ncol(data)
-
- vp <- function(x,y) {
- # cat(vp_name(x, y, type), ": ", x + offset[1], ", ", y + offset[2], "\n", sep="")
- viewport(
- name = vp_name(x, y, type),
- layout.pos.row = x + offset[1],
- layout.pos.col = y + offset[2],
- clip = clip
- )
- }
- pos <- expand.grid(x = seq_len(rows), y = seq_len(cols))
- do.call("vpList", plyr::mlply(pos, vp))
-}
-
-# Calculate viewport path.
-# Helps ensure a common naming scheme throughout ggplot.
-vp_path <- function(row, col, type) {
- vpPath("panels", vp_name(row, col, type))
-}
-
-# Compute viewport name.
-# Helps ensure a common naming scheme throughout ggplot.
-vp_name <- function(row, col, type) {
- paste(type, row, col, sep = "_")
-}
diff --git a/R/facet-wrap.r b/R/facet-wrap.r
index 66004b1..1eb598a 100644
--- a/R/facet-wrap.r
+++ b/R/facet-wrap.r
@@ -1,9 +1,11 @@
-#' Wrap a 1d ribbon of panels into 2d.
+#' @include facet-.r
+NULL
+
+#' Wrap a 1d ribbon of panels into 2d
#'
-#' Most displays are roughly rectangular, so if you have a categorical
-#' variable with many levels, it doesn't make sense to try and display them
-#' all in one row (or one column). To solve this dilemma, \code{facet_wrap}
-#' wraps a 1d sequence of panels into 2d, making best use of screen real estate.
+#' \code{facet_wrap} wraps a 1d sequence of panels into 2d. This is generally
+#' a better use of screen space than \code{\link{facet_grid}} because most
+#' displays are roughly rectangular.
#'
#' @param facets Either a formula or character vector. Use either a
#' one sided formula, \code{~a + b}, or a character vector, \code{c("a", "b")}.
@@ -11,10 +13,10 @@
#' @param scales should Scales be fixed (\code{"fixed"}, the default),
#' free (\code{"free"}), or free in one dimension (\code{"free_x"},
#' \code{"free_y"}).
-#' @param switch By default, the labels are displayed on the top of
-#' the plot. If \code{switch} is \code{"x"}, they will be displayed
-#' to the bottom. If \code{"y"}, they will be displayed to the
-#' left, near the y axis.
+#' @param strip.position By default, the labels are displayed on the top of
+#' the plot. Using \code{strip.position} it is possible to place the labels on
+#' either of the four sides by setting \code{strip.position = c("top",
+#' "bottom", "left", "right")}
#' @param dir Direction: either "h" for horizontal, the default, or "v", for
#' vertical.
#' @inheritParams facet_grid
@@ -66,24 +68,29 @@
#' geom_point() +
#' facet_wrap(~class)
#'
-#' # Use `switch` to display the facet labels near an axis, acting as
-#' # a subtitle for this axis. This is typically used with free scales
-#' # and a theme without boxes around strip labels.
+#' # Use `strip.position` to display the facet labels at the side of your
+#' # choice. Setting it to `bottom` makes it act as a subtitle for the axis.
+#' # This is typically used with free scales and a theme without boxes around
+#' # strip labels.
#' ggplot(economics_long, aes(date, value)) +
#' geom_line() +
-#' facet_wrap(~variable, scales = "free_y", nrow = 2, switch = "x") +
-#' theme(strip.background = element_blank())
+#' facet_wrap(~variable, scales = "free_y", nrow = 2, strip.position = "bottom") +
+#' theme(strip.background = element_blank(), strip.placement = "outside")
#' }
facet_wrap <- function(facets, nrow = NULL, ncol = NULL, scales = "fixed",
shrink = TRUE, labeller = "label_value", as.table = TRUE,
- switch = NULL, drop = TRUE, dir = "h") {
+ switch = NULL, drop = TRUE, dir = "h", strip.position = 'top') {
scales <- match.arg(scales, c("fixed", "free_x", "free_y", "free"))
dir <- match.arg(dir, c("h", "v"))
free <- list(
x = any(scales %in% c("free_x", "free")),
y = any(scales %in% c("free_y", "free"))
)
-
+ if (!is.null(switch)) {
+ .Deprecated("strip.position", old = "switch")
+ strip.position <- if (switch == "x") "bottom" else "left"
+ }
+ strip.position <- match.arg(strip.position, c("top", "bottom", "left", "right"))
if (identical(dir, "v")) {
# swap
nrow_swap <- ncol
@@ -98,314 +105,244 @@ facet_wrap <- function(facets, nrow = NULL, ncol = NULL, scales = "fixed",
# Check for deprecated labellers
labeller <- check_labeller(labeller)
- facet(
- facets = as.quoted(facets), free = free, shrink = shrink,
- as.table = as.table, switch = switch,
+ ggproto(NULL, FacetWrap,
+ shrink = shrink,
+ params = list(facets = as.quoted(facets), free = free,
+ as.table = as.table, strip.position = strip.position,
drop = drop, ncol = ncol, nrow = nrow,
labeller = labeller,
- dir = dir,
- subclass = "wrap"
+ dir = dir)
)
}
+#' @rdname ggplot2-ggproto
+#' @format NULL
+#' @usage NULL
#' @export
-facet_train_layout.wrap <- function(facet, data) {
- panels <- layout_wrap(data, facet$facets, facet$nrow, facet$ncol,
- facet$as.table, facet$drop, facet$dir)
-
- n <- nrow(panels)
- nrow <- max(panels$ROW)
-
- # Add scale identification
- panels$SCALE_X <- if (facet$free$x) seq_len(n) else 1L
- panels$SCALE_Y <- if (facet$free$y) seq_len(n) else 1L
-
- # Figure out where axes should go
- panels$AXIS_X <- if (facet$free$x) TRUE else panels$ROW == nrow
- panels$AXIS_Y <- if (facet$free$y) TRUE else panels$COL == 1
-
- panels
-}
-
-#' @export
-facet_map_layout.wrap <- function(facet, data, layout) {
- locate_wrap(data, layout, facet$facets)
-}
-
-# How to think about facet wrap:
-# * vector of panels
-# * every panel has strips (strip_pos) and axes (axis_pos)
-# * if scales fixed, most axes empty
-# * combine panels, strips and axes, then wrap into 2d
-# * finally: add title, labels and legend
-#
-#' @export
-facet_render.wrap <- function(facet, panel, coord, theme, geom_grobs) {
-
- # If coord is (non-cartesian or flip) and (x is free or y is free)
- # then print a warning
- if ((!inherits(coord, "CoordCartesian") || inherits(coord, "CoordFlip")) &&
- (facet$free$x || facet$free$y)) {
- stop("ggplot2 does not currently support free scales with a non-cartesian coord or coord_flip.\n")
- }
-
- # If user hasn't set aspect ratio, and we have fixed scales, then
- # ask the coordinate system if it wants to specify one
- aspect_ratio <- theme$aspect.ratio
- if (is.null(aspect_ratio) && !facet$free$x && !facet$free$y) {
- aspect_ratio <- coord$aspect(panel$ranges[[1]])
- }
-
- if (is.null(aspect_ratio)) {
- aspect_ratio <- 1
- respect <- FALSE
- } else {
- respect <- TRUE
- }
-
- layout <- panel$layout
- ncol <- max(layout$COL)
- nrow <- max(layout$ROW)
- n <- nrow(layout)
-
- # Set switch to default value when misspecified
- switch_to_x <- FALSE
- switch_to_y <- FALSE
- if (!is.null(facet$switch) && facet$switch == "x") {
- switch_to_x <- TRUE
- } else if (!is.null(facet$switch) && facet$switch == "y") {
- switch_to_y <- TRUE
- } else if (!is.null(facet$switch)) {
- message("`switch` must be set to 'x', 'y' or NULL")
- facet$switch <- NULL
- }
+FacetWrap <- ggproto("FacetWrap", Facet,
+ shrink = TRUE,
- panels <- facet_panels(facet, panel, coord, theme, geom_grobs)
- axes <- facet_axes(facet, panel, coord, theme)
- strips <- facet_strips(facet, panel, theme)
+ compute_layout = function(data, params) {
+ vars <- as.quoted(params$facets)
+ if (length(vars) == 0) return(layout_null())
-
- # Should become facet_arrange_grobs
-
- # Locate each element in panel
- find_pos <- function(layout, loc, size) {
- n <- nrow(layout)
- l <- size[1] * (layout$COL - 1) + loc[1]
- t <- size[2] * (layout$ROW - 1) + loc[2]
- data.frame(t = t, r = l, b = t, l = l, id = seq_len(n))
- }
-
-
- if (switch_to_x) {
- locs <- list(
- panel = c(2, 1),
- strip_t = c(2, 3),
- axis_l = c(1, 1),
- axis_b = c(2, 2),
- hspace = c(2, 4),
- vspace = c(3, 1)
+ base <- plyr::unrowname(
+ combine_vars(data, params$plot_env, vars, drop = params$drop)
)
- } else if (switch_to_y) {
- locs <- list(
- panel = c(3, 1),
- strip_t = c(1, 1),
- axis_l = c(2, 1),
- axis_b = c(3, 2),
- hspace = c(3, 3),
- vspace = c(4, 1)
- )
- } else {
- locs <- list(
- panel = c(2, 2),
- strip_t = c(2, 1),
- axis_l = c(1, 2),
- axis_b = c(2, 3),
- hspace = c(2, 4),
- vspace = c(3, 2)
- )
- }
-
- grobs <- list(
- panel = panels,
- strip_t = strips$t,
- axis_l = axes$l,
- axis_b = axes$b
- )
- # If strips are switched, add padding
- if (switch_to_x) {
- padding <- convertUnit(theme$strip.switch.pad.wrap, "cm")
+ id <- plyr::id(base, drop = TRUE)
+ n <- attr(id, "n")
- add_padding <- function(strip) {
- gt_t <- gtable_row("strip_t", list(strip),
- height = unit(height_cm(strip), "cm"))
+ dims <- wrap_dims(n, params$nrow, params$ncol)
+ layout <- data.frame(PANEL = factor(id, levels = seq_len(n)))
- # One padding above and two below, so that the strip does not look
- # like a title for the panel just below.
- gt_t <- gtable_add_rows(gt_t, padding, pos = 0)
- gt_t <- gtable_add_rows(gt_t, 2 * padding, pos = 2)
- gt_t
+ if (params$as.table) {
+ layout$ROW <- as.integer((id - 1L) %/% dims[2] + 1L)
+ } else {
+ layout$ROW <- as.integer(dims[1] - (id - 1L) %/% dims[2])
}
- grobs$strip_t <- lapply(strips$t, add_padding)
-
- strip_height <- lapply(strips$t, function(x) {
- 3 * as.numeric(padding) + height_cm(x)
- })
- strip_width <- NULL
- size <- c(3, 4)
+ layout$COL <- as.integer((id - 1L) %% dims[2] + 1L)
- } else if (switch_to_y) {
- padding <- convertUnit(theme$strip.switch.pad.wrap, "cm")
-
- add_padding <- function(strip) {
- gt_t <- gtable_col("strip_t", list(strip),
- heights = unit(aspect_ratio, "null"))
-
- gt_t <- gtable_add_cols(gt_t, padding, pos = 0)
- gt_t <- gtable_add_cols(gt_t, padding, pos = 2)
- gt_t
+ # For vertical direction, flip row and col
+ if (identical(params$dir, "v")) {
+ layout[c("ROW", "COL")] <- layout[c("COL", "ROW")]
}
- grobs$strip_t <- lapply(strips$t, add_padding)
-
- strip_height <- NULL
- strip_width <- lapply(strips$t, function(x) {
- 3 * as.numeric(padding) + width_cm(x)
- })
- size <- c(4, 3)
-
- } else {
- strip_height <- height_cm(grobs$strip_t)
- strip_width <- NULL
- size <- c(3, 4)
- }
-
- info <- plyr::ldply(locs, find_pos, layout = layout, size = size)
- names(info)[1] <- "type"
- info$clip <- ifelse(info$type == "panel", "on", "off")
- info$name <- paste(info$type, info$id, sep = "-")
-
- # Bare numbers are taken as cm
- # If not listed, assume is unit(1, "null")
- widths <- list(
- axis_l = width_cm(grobs$axis_l),
- strip_t = strip_width,
- vspace = ifelse(layout$COL == ncol, 0, width_cm(theme$panel.margin.x %||% theme$panel.margin))
- )
- heights <- list(
- panel = unit(aspect_ratio, "null"),
- strip_t = strip_height,
- axis_b = height_cm(grobs$axis_b),
- hspace = ifelse(layout$ROW == nrow, 0, height_cm(theme$panel.margin.y %||% theme$panel.margin))
- )
-
- # Remove strip_t according to which strips are switched
- heights <- Filter(Negate(is.null), heights)
- widths <- Filter(Negate(is.null), widths)
- col_widths <- compute_grob_widths(info, widths)
- row_heights <- compute_grob_heights(info, heights)
+ panels <- cbind(layout, plyr::unrowname(base))
+ panels <- panels[order(panels$PANEL), , drop = FALSE]
+ rownames(panels) <- NULL
- # Create the gtable for the legend
- gt <- gtable(widths = col_widths, heights = row_heights, respect = respect)
+ # Add scale identification
+ panels$SCALE_X <- if (params$free$x) seq_len(n) else 1L
+ panels$SCALE_Y <- if (params$free$y) seq_len(n) else 1L
- # Keep only the rows in info that refer to grobs
- info <- info[info$type %in% names(grobs), ]
- grobs <- unlist(grobs, recursive = FALSE)
+ panels
+ },
+ map_data = function(data, layout, params) {
+ if (empty(data)) {
+ return(cbind(data, PANEL = integer(0)))
+ }
+ vars <- as.quoted(params$facets)
- # Add the grobs
- gt <- gtable_add_grob(gt, grobs, l = info$l, t = info$t, r = info$r,
- b = info$b, name = info$name, clip = info$clip)
+ facet_vals <- eval_facet_vars(vars, data, params$plot_env)
+ facet_vals[] <- lapply(facet_vals[], as.factor)
- gt
-}
+ missing_facets <- setdiff(names(vars), names(facet_vals))
+ if (length(missing_facets) > 0) {
-#' @export
-facet_panels.wrap <- function(facet, panel, coord, theme, geom_grobs) {
- panels <- panel$layout$PANEL
- lapply(panels, function(i) {
- fg <- coord$render_fg(panel$ranges[[i]], theme)
- bg <- coord$render_bg(panel$ranges[[i]], theme)
+ to_add <- unique(layout[missing_facets])
- geom_grobs <- lapply(geom_grobs, "[[", i)
+ data_rep <- rep.int(1:nrow(data), nrow(to_add))
+ facet_rep <- rep(1:nrow(to_add), each = nrow(data))
- if (theme$panel.ontop) {
- panel_grobs <- c(geom_grobs, list(bg), list(fg))
- } else {
- panel_grobs <- c(list(bg), geom_grobs, list(fg))
+ data <- plyr::unrowname(data[data_rep, , drop = FALSE])
+ facet_vals <- plyr::unrowname(cbind(
+ facet_vals[data_rep, , drop = FALSE],
+ to_add[facet_rep, , drop = FALSE]))
}
- ggname(paste("panel", i, sep = "-"),
- gTree(children = do.call("gList", panel_grobs)))
- })
-}
-
-#' @export
-facet_strips.wrap <- function(facet, panel, theme) {
- labels_df <- panel$layout[names(facet$facets)]
-
- # Adding labels metadata, useful for labellers
- attr(labels_df, "facet") <- "wrap"
- if (is.null(facet$switch) || facet$switch == "x") {
- dir <- "b"
- attr(labels_df, "type") <- "rows"
- } else {
- dir <- "l"
- attr(labels_df, "type") <- "cols"
- }
-
- strips_table <- build_strip(panel, labels_df, facet$labeller,
- theme, dir, switch = facet$switch)
-
- # While grid facetting works with a whole gtable, wrap processes the
- # strips separately. So we turn the gtable into a list
- if (dir == "b") {
- n_strips <- ncol(strips_table)
- } else {
- n_strips <- nrow(strips_table)
- }
+ keys <- plyr::join.keys(facet_vals, layout, by = names(vars))
- strips <- list(t = vector("list", n_strips))
- for (i in seq_along(strips$t)) {
- if (dir == "b") {
- strips$t[[i]] <- strips_table[, i]
- } else {
- strips$t[[i]] <- strips_table[i, ]
+ data$PANEL <- layout$PANEL[match(keys$x, keys$y)]
+ data[order(data$PANEL), ]
+ },
+ draw_panels = function(panels, layout, x_scales, y_scales, ranges, coord, data, theme, params) {
+ # If coord is non-cartesian and (x is free or y is free)
+ # then throw error
+ if ((!inherits(coord, "CoordCartesian")) && (params$free$x || params$free$y)) {
+ stop("ggplot2 does not currently support free scales with a non-cartesian coord", call. = FALSE)
+ }
+ if (inherits(coord, "CoordFlip")) {
+ if (params$free$x) {
+ layout$SCALE_X <- seq_len(nrow(layout))
+ } else {
+ layout$SCALE_X <- 1L
+ }
+ if (params$free$y) {
+ layout$SCALE_Y <- seq_len(nrow(layout))
+ } else {
+ layout$SCALE_Y <- 1L
+ }
}
- }
- strips
-}
-
-#' @export
-facet_axes.wrap <- function(facet, panel, coord, theme) {
- panels <- panel$layout$PANEL
+ ncol <- max(layout$COL)
+ nrow <- max(layout$ROW)
+ n <- nrow(layout)
+ panel_order <- order(layout$ROW, layout$COL)
+ layout <- layout[panel_order, ]
+ panels <- panels[panel_order]
+ panel_pos <- convertInd(layout$ROW, layout$COL, nrow)
+
+ axes <- render_axes(ranges, ranges, coord, theme, transpose = TRUE)
+
+ labels_df <- layout[names(params$facets)]
+ attr(labels_df, "facet") <- "wrap"
+ strips <- render_strips(
+ structure(labels_df, type = "rows"),
+ structure(labels_df, type = "cols"),
+ params$labeller, theme)
+
+ # If user hasn't set aspect ratio, and we have fixed scales, then
+ # ask the coordinate system if it wants to specify one
+ aspect_ratio <- theme$aspect.ratio
+ if (is.null(aspect_ratio) && !params$free$x && !params$free$y) {
+ aspect_ratio <- coord$aspect(ranges[[1]])
+ }
- axes <- list()
- axes$b <- lapply(panels, function(i) {
- if (panel$layout$AXIS_X[i]) {
- grob <- coord$render_axis_h(panel$ranges[[i]], theme)
+ if (is.null(aspect_ratio)) {
+ aspect_ratio <- 1
+ respect <- FALSE
} else {
- grob <- zeroGrob()
+ respect <- TRUE
}
- ggname(paste("axis-b-", i, sep = ""), grob)
- })
- axes$l <- lapply(panels, function(i) {
- if (panel$layout$AXIS_Y[i]) {
- grob <- coord$render_axis_v(panel$ranges[[i]], theme)
+ empty_table <- matrix(list(zeroGrob()), nrow = nrow, ncol = ncol)
+ panel_table <- empty_table
+ panel_table[panel_pos] <- panels
+ empties <- apply(panel_table, c(1,2), function(x) is.zero(x[[1]]))
+ panel_table <- gtable_matrix("layout", panel_table,
+ widths = unit(rep(1, ncol), "null"),
+ heights = unit(rep(aspect_ratio, nrow), "null"), respect = respect, clip = "on", z = matrix(1, ncol = ncol, nrow = nrow))
+ panel_table$layout$name <- paste0('panel-', rep(seq_len(ncol), nrow), '-', rep(seq_len(nrow), each = ncol))
+
+ panel_table <- gtable_add_col_space(panel_table,
+ theme$panel.spacing.x %||% theme$panel.spacing)
+ panel_table <- gtable_add_row_space(panel_table,
+ theme$panel.spacing.y %||% theme$panel.spacing)
+
+ # Add axes
+ axis_mat_x_top <- empty_table
+ axis_mat_x_top[panel_pos] <- axes$x$top[layout$SCALE_X]
+ axis_mat_x_bottom <- empty_table
+ axis_mat_x_bottom[panel_pos] <- axes$x$bottom[layout$SCALE_X]
+ axis_mat_y_left <- empty_table
+ axis_mat_y_left[panel_pos] <- axes$y$left[layout$SCALE_Y]
+ axis_mat_y_right <- empty_table
+ axis_mat_y_right[panel_pos] <- axes$y$right[layout$SCALE_Y]
+ if (!params$free$x) {
+ axis_mat_x_top[-1,]<- list(zeroGrob())
+ axis_mat_x_bottom[-nrow,]<- list(zeroGrob())
+ }
+ if (!params$free$y) {
+ axis_mat_y_left[, -1] <- list(zeroGrob())
+ axis_mat_y_right[, -ncol] <- list(zeroGrob())
+ }
+ axis_height_top <- unit(apply(axis_mat_x_top, 1, max_height), "cm")
+ axis_height_bottom <- unit(apply(axis_mat_x_bottom, 1, max_height), "cm")
+ axis_width_left <- unit(apply(axis_mat_y_left, 2, max_width), "cm")
+ axis_width_right <- unit(apply(axis_mat_y_right, 2, max_width), "cm")
+ # Add back missing axes
+ if (any(empties)) {
+ first_row <- which(apply(empties, 1, any))[1] - 1
+ first_col <- which(apply(empties, 2, any))[1] - 1
+ row_panels <- which(layout$ROW == first_row & layout$COL > first_col)
+ row_pos <- convertInd(layout$ROW[row_panels], layout$COL[row_panels], nrow)
+ row_axes <- axes$x$bottom[layout$SCALE_X[row_panels]]
+ col_panels <- which(layout$ROW > first_row & layout$COL == first_col)
+ col_pos <- convertInd(layout$ROW[col_panels], layout$COL[col_panels], nrow)
+ col_axes <- axes$y$right[layout$SCALE_Y[col_panels]]
+ if (params$strip.position == "bottom" &&
+ theme$strip.placement != "inside" &&
+ any(!vapply(row_axes, is.zero, logical(length(row_axes))))) {
+ warning("Suppressing axis rendering when strip.position = 'bottom' and strip.placement == 'outside'", call. = FALSE)
+ } else {
+ axis_mat_x_bottom[row_pos] <- row_axes
+ }
+ if (params$strip.position == "right" &&
+ theme$strip.placement != "inside" &&
+ any(!vapply(col_axes, is.zero, logical(length(col_axes))))) {
+ warning("Suppressing axis rendering when strip.position = 'right' and strip.placement == 'outside'", call. = FALSE)
+ } else {
+ axis_mat_y_right[col_pos] <- col_axes
+ }
+ }
+ panel_table <- weave_tables_row(panel_table, axis_mat_x_top, -1, axis_height_top, "axis-t", 3)
+ panel_table <- weave_tables_row(panel_table, axis_mat_x_bottom, 0, axis_height_bottom, "axis-b", 3)
+ panel_table <- weave_tables_col(panel_table, axis_mat_y_left, -1, axis_width_left, "axis-l", 3)
+ panel_table <- weave_tables_col(panel_table, axis_mat_y_right, 0, axis_width_right, "axis-r", 3)
+
+ strip_padding <- convertUnit(theme$strip.switch.pad.wrap, "cm")
+ strip_name <- paste0("strip-", substr(params$strip.position, 1, 1))
+ strip_mat <- empty_table
+ strip_mat[panel_pos] <- unlist(unname(strips), recursive = FALSE)[[params$strip.position]]
+ if (params$strip.position %in% c("top", "bottom")) {
+ inside <- (theme$strip.placement.x %||% theme$strip.placement %||% "inside") == "inside"
+ if (params$strip.position == "top") {
+ placement <- if (inside) -1 else -2
+ strip_pad <- axis_height_top
+ } else {
+ placement <- if (inside) 0 else 1
+ strip_pad <- axis_height_bottom
+ }
+ strip_height <- unit(apply(strip_mat, 1, max_height), "cm")
+ panel_table <- weave_tables_row(panel_table, strip_mat, placement, strip_height, strip_name, 2, "on")
+ if (!inside) {
+ strip_pad[unclass(strip_pad) != 0] <- strip_padding
+ panel_table <- weave_tables_row(panel_table, row_shift = placement, row_height = strip_pad)
+ }
} else {
- grob <- zeroGrob()
+ inside <- (theme$strip.placement.y %||% theme$strip.placement %||% "inside") == "inside"
+ if (params$strip.position == "left") {
+ placement <- if (inside) -1 else -2
+ strip_pad <- axis_width_left
+ } else {
+ placement <- if (inside) 0 else 1
+ strip_pad <- axis_width_right
+ }
+ strip_pad[unclass(strip_pad) != 0] <- strip_padding
+ strip_width <- unit(apply(strip_mat, 2, max_width), "cm")
+ panel_table <- weave_tables_col(panel_table, strip_mat, placement, strip_width, strip_name, 2, "on")
+ if (!inside) {
+ strip_pad[unclass(strip_pad) != 0] <- strip_padding
+ panel_table <- weave_tables_col(panel_table, col_shift = placement, col_width = strip_pad)
+ }
}
- ggname(paste("axis-l-", i, sep = ""), grob)
- })
- axes
+ panel_table
+ }
+)
-}
-#' @export
-facet_vars.wrap <- function(facet) {
- paste(lapply(facet$facets, paste, collapse = ", "), collapse = " ~ ")
-}
+# Helpers -----------------------------------------------------------------
#' Sanitise the number of rows or columns
#'
@@ -461,3 +398,54 @@ sanitise_dim <- function(n) {
n
}
+#' Arrange 1d structure into a grid
+#'
+#' @param n length of structure
+#' @param nrow,ncol desired dimensions for the grid
+#'
+#' @return the grid dimension as a vector with nrow and then ncol
+#'
+#' @keywords internal
+#' @export
+wrap_dims <- function(n, nrow = NULL, ncol = NULL) {
+ if (is.null(ncol) && is.null(nrow)) {
+ rc <- grDevices::n2mfrow(n)
+ nrow <- rc[2]
+ ncol <- rc[1]
+ } else if (is.null(ncol)) {
+ ncol <- ceiling(n / nrow)
+ } else if (is.null(nrow)) {
+ nrow <- ceiling(n / ncol)
+ }
+ stopifnot(nrow * ncol >= n)
+
+ c(nrow, ncol)
+}
+convertInd <- function(row, col, nrow) {
+ (col - 1) * nrow + row
+}
+
+weave_tables_col <- function(table, table2, col_shift, col_width, name, z = 1, clip = "off") {
+ panel_col <- panel_cols(table)$l
+ panel_row <- panel_rows(table)$t
+ for (i in rev(seq_along(panel_col))) {
+ col_ind <- panel_col[i] + col_shift
+ table <- gtable_add_cols(table, col_width[i], pos = col_ind)
+ if (!missing(table2)) {
+ table <- gtable_add_grob(table, table2[, i], t = panel_row, l = col_ind + 1, clip = clip, name = paste0(name, "-", seq_along(panel_row), "-", i), z = z)
+ }
+ }
+ table
+}
+weave_tables_row <- function(table, table2, row_shift, row_height, name, z = 1, clip = "off") {
+ panel_col <- panel_cols(table)$l
+ panel_row <- panel_rows(table)$t
+ for (i in rev(seq_along(panel_row))) {
+ row_ind <- panel_row[i] + row_shift
+ table <- gtable_add_rows(table, row_height[i], pos = row_ind)
+ if (!missing(table2)) {
+ table <- gtable_add_grob(table, table2[i, ], t = row_ind + 1, l = panel_col, clip = clip, name = paste0(name, "-", seq_along(panel_col), "-", i), z = z)
+ }
+ }
+ table
+}
diff --git a/R/fortify-lm.r b/R/fortify-lm.r
index 574a834..a0195a5 100644
--- a/R/fortify-lm.r
+++ b/R/fortify-lm.r
@@ -14,6 +14,7 @@
#' @param model linear model
#' @param data data set, defaults to data used to fit model
#' @param ... not used by this method
+#' @keywords internal
#' @export
#' @examples
#' mod <- lm(mpg ~ wt, data = mtcars)
@@ -50,7 +51,7 @@
#'
#' plot(mod, which = 4)
#' ggplot(mod, aes(seq_along(.cooksd), .cooksd)) +
-#' geom_bar(stat = "identity")
+#' geom_col()
#'
#' plot(mod, which = 5)
#' ggplot(mod, aes(.hat, .stdresid)) +
diff --git a/R/fortify-map.r b/R/fortify-map.r
index 858bedd..88ede63 100644
--- a/R/fortify-map.r
+++ b/R/fortify-map.r
@@ -1,4 +1,4 @@
-#' Fortify method for map objects.
+#' Fortify method for map objects
#'
#' This function turns a map into a data frame that can more easily be
#' plotted with ggplot2.
@@ -8,6 +8,7 @@
#' @param model map object
#' @param data not used by this method
#' @param ... not used by this method
+#' @keywords internal
#' @examples
#' if (require("maps")) {
#' ca <- map("county", "ca", plot = FALSE, fill = TRUE)
@@ -32,7 +33,10 @@ fortify.map <- function(model, data, ...) {
df[stats::complete.cases(df$lat, df$long), ]
}
-#' Create a data frame of map data.
+#' Create a data frame of map data
+#'
+#' Easily turn data from the \pkg{maps} package in to a data frame suitable
+#' for plotting with ggplot2.
#'
#' @param map name of map provided by the \pkg{maps} package. These
#' include \code{\link[maps]{county}}, \code{\link[maps]{france}},
@@ -45,6 +49,7 @@ fortify.map <- function(model, data, ...) {
#' @param exact should the \code{region} be treated as a regular expression
#' (\code{FALSE}) or as a fixed string (\code{TRUE}).
#' @param ... all other arguments passed on to \code{\link[maps]{map}}
+#' @keywords internal
#' @export
#' @examples
#' if (require("maps")) {
@@ -68,7 +73,12 @@ map_data <- function(map, region = ".", exact = FALSE, ...) {
fortify(map(map, region, exact = exact, plot = FALSE, fill = TRUE, ...))
}
-#' Create a layer of map borders.
+#' Create a layer of map borders
+#'
+#' This is a quick and dirty way to get map data (from the maps package)
+#' on to your plot. This is a good place to start if you need some crude
+#' reference lines, but you'll typically want something more sophisticated
+#' for communication graphics.
#'
#' @param database map data, see \code{\link[maps]{map}} for details
#' @param regions map region
diff --git a/R/fortify-multcomp.r b/R/fortify-multcomp.r
index b1c19b5..a0c8f19 100644
--- a/R/fortify-multcomp.r
+++ b/R/fortify-multcomp.r
@@ -4,6 +4,7 @@
#' \code{summary.glht} or \code{\link[multcomp]{cld}}
#' @param data,... other arguments to the generic ignored in this method.
#' @name fortify-multcomp
+#' @keywords internal
#' @examples
#' if (require("multcomp")) {
#' amod <- aov(breaks ~ wool + tension, data = warpbreaks)
diff --git a/R/fortify-spatial.r b/R/fortify-spatial.r
index 3b63966..59a3b27 100644
--- a/R/fortify-spatial.r
+++ b/R/fortify-spatial.r
@@ -7,6 +7,7 @@
#' @param data not used by this method
#' @param region name of variable used to split up regions
#' @param ... not used by this method
+#' @keywords internal
#' @name fortify.sp
#' @examples
#' if (require("maptools")) {
diff --git a/R/fortify.r b/R/fortify.r
index a35be2a..f63bd9d 100644
--- a/R/fortify.r
+++ b/R/fortify.r
@@ -1,6 +1,6 @@
#' Fortify a model with data.
#'
-#' Rather than using this function, I now recomend using the \pkg{broom}
+#' Rather than using this function, I now recommend using the \pkg{broom}
#' package, which implements a much wider range of methods. \code{fortify}
#' may be deprecated in the future.
#'
diff --git a/R/geom-.r b/R/geom-.r
index 610824e..b2d73e9 100644
--- a/R/geom-.r
+++ b/R/geom-.r
@@ -54,6 +54,7 @@ NULL
Geom <- ggproto("Geom",
required_aes = character(),
non_missing_aes = character(),
+ optional_aes = character(),
default_aes = aes(),
@@ -66,7 +67,7 @@ Geom <- ggproto("Geom",
)
},
- draw_layer = function(self, data, params, panel, coord) {
+ draw_layer = function(self, data, params, layout, coord) {
if (empty(data)) {
n <- if (is.factor(data$PANEL)) nlevels(data$PANEL) else 1L
return(rep(list(zeroGrob()), n))
@@ -79,7 +80,7 @@ Geom <- ggproto("Geom",
plyr::dlply(data, "PANEL", function(data) {
if (empty(data)) return(zeroGrob())
- panel_scales <- panel$ranges[[data$PANEL[1]]]
+ panel_scales <- layout$panel_ranges[[data$PANEL[1]]]
do.call(self$draw_panel, args)
}, .drop = FALSE)
},
@@ -141,7 +142,7 @@ Geom <- ggproto("Geom",
},
aesthetics = function(self) {
- c(union(self$required_aes, names(self$default_aes)), "group")
+ c(union(self$required_aes, names(self$default_aes)), self$optional_aes, "group")
}
)
@@ -153,6 +154,8 @@ Geom <- ggproto("Geom",
#' that grid uses internally for \code{lwd} and \code{fontsize}.
#'
#' @name graphical-units
+#' @keywords internal
+#' @aliases NULL
NULL
#' @export
diff --git a/R/geom-abline.r b/R/geom-abline.r
index 518c648..58b5e1e 100644
--- a/R/geom-abline.r
+++ b/R/geom-abline.r
@@ -1,11 +1,11 @@
#' @include stat-.r
NULL
-#' Lines: horizontal, vertical, and specified by slope and intercept.
+#' Reference lines: horizontal, vertical, and diagonal
#'
-#' These paired geoms and stats add straight lines to a plot, either
-#' horizontal, vertical or specified by slope and intercept. These are useful
-#' for annotating plots.
+#' These geoms add reference lines (sometimes called rules) to a plot, either
+#' horizontal, vertical, or diagonal (specified by slope and intercept).
+#' These are useful for annotating plots.
#'
#' These geoms act slightly different to other geoms. You can supply the
#' parameters in two ways: either as arguments to the layer function,
@@ -21,8 +21,9 @@ NULL
#'
#' @section Aesthetics:
#' These geoms are drawn using with \code{\link{geom_line}} so support the
-#' same aesthetics: alpha, colour, linetype and size. They also each have
-#' aesthetics that control the position of the line:
+#' same aesthetics: \code{alpha}, \code{colour}, \code{linetype} and
+#' \code{size}. They also each have aesthetics that control the position of
+#' the line:
#'
#' \itemize{
#' \item \code{geom_vline}: \code{xintercept}
diff --git a/R/geom-bar.r b/R/geom-bar.r
index ca264d4..7e68117 100644
--- a/R/geom-bar.r
+++ b/R/geom-bar.r
@@ -1,29 +1,31 @@
-#' Bars, rectangles with bases on x-axis
+#' Bars charts
#'
-#' There are two types of bar charts, determined by what is mapped to bar
-#' height. By default, \code{geom_bar} uses \code{stat="count"} which makes the
-#' height of the bar proportion to the number of cases in each group (or if the
+#' There are two types of bar charts: \code{geom_bar} makes the height of the
+#' bar proportional to the number of cases in each group (or if the
#' \code{weight} aethetic is supplied, the sum of the weights). If you want the
#' heights of the bars to represent values in the data, use
-#' \code{stat="identity"} and map a variable to the \code{y} aesthetic.
+#' \link{geom_col} instead. \code{geom_bar} uses \code{stat_count} by
+#' default: it counts the number of cases at each x position. \code{geom_col}
+#' uses \code{stat_identity}: it leaves the data as is.
#'
-#' A bar chart maps the height of the bar to a variable, and so the base of the
+#' A bar chart uses height to represent a value, and so the base of the
#' bar must always be shown to produce a valid visual comparison. Naomi Robbins
#' has a nice
#' \href{http://www.b-eye-network.com/view/index.php?cid=2468}{article on this
#' topic}. This is why it doesn't make sense to use a log-scaled y axis with a
#' bar chart.
#'
-#' By default, multiple x's occurring in the same place will be stacked atop one
-#' another by \code{\link{position_stack}}. If you want them to be dodged
-#' side-to-side, see \code{\link{position_dodge}}. Finally,
-#' \code{\link{position_fill}} shows relative proportions at each x by stacking
-#' the bars and then stretching or squashing to the same height.
+#' By default, multiple bar occupying the same \code{x} position will be
+#' stacked atop one another by \code{\link{position_stack}}. If you want them
+#' to be dodged side-to-side, use \code{\link{position_dodge}}. Finally,
+#' \code{\link{position_fill}} shows relative proportions at each \code{x} by
+#' stacking the bars and then standardising each bar to have the same height.
#'
#' @section Aesthetics:
-#' \Sexpr[results=rd,stage=build]{ggplot2:::rd_aesthetics("geom", "bar")}
+#' \aesthetics{geom}{bar}
#'
-#' @seealso \code{\link{geom_histogram}} for continuous data,
+#' @seealso
+#' \code{\link{geom_histogram}} for continuous data,
#' \code{\link{position_dodge}} for creating side-by-side barcharts.
#' @export
#' @inheritParams layer
@@ -43,11 +45,12 @@
#' # Total engine displacement of each class
#' g + geom_bar(aes(weight = displ))
#'
-#' # To show (e.g.) means, you need stat = "identity"
+#' # To show (e.g.) means, you need geom_col()
+#' # And, even more succinctly with geom_col()
#' df <- data.frame(trt = c("a", "b", "c"), outcome = c(2.3, 1.9, 3.2))
#' ggplot(df, aes(trt, outcome)) +
-#' geom_bar(stat = "identity")
-#' # But geom_point() display exactly the same information and doesn't
+#' geom_col()
+#' # But geom_point() displays exactly the same information and doesn't
#' # require the y-axis to touch zero.
#' ggplot(df, aes(trt, outcome)) +
#' geom_point()
@@ -113,7 +116,7 @@ geom_bar <- function(mapping = NULL, data = NULL,
#' @export
#' @include geom-rect.r
GeomBar <- ggproto("GeomBar", GeomRect,
- required_aes = "x",
+ required_aes = c("x", "y"),
setup_data = function(data, params) {
data$width <- data$width %||%
diff --git a/R/geom-bin2d.r b/R/geom-bin2d.r
index c372eed..d922774 100644
--- a/R/geom-bin2d.r
+++ b/R/geom-bin2d.r
@@ -1,7 +1,12 @@
-#' Add heatmap of 2d bin counts.
+#' Heatmap of 2d bin counts
+#'
+#' Divides the plane into rectangles, counts the number of cases in
+#' each rectangle, and then (by default) maps the number of cases to the
+#' rectangle's fill. This is a useful alternative to \code{\link{geom_point}}
+#' in the presence of overplotting.
#'
#' @section Aesthetics:
-#' \Sexpr[results=rd,stage=build]{ggplot2:::rd_aesthetics("stat", "bin2d")}
+#' \aesthetics{stat}{bin2d}
#'
#' @export
#' @inheritParams layer
diff --git a/R/geom-blank.r b/R/geom-blank.r
index 133a2fa..4602f18 100644
--- a/R/geom-blank.r
+++ b/R/geom-blank.r
@@ -1,7 +1,8 @@
-#' Blank, draws nothing.
+#' Draw nothing
#'
#' The blank geom draws nothing, but can be a useful way of ensuring common
-#' scales between different plots.
+#' scales between different plots. See \code{\link{expand_limits}} for
+#' more details.
#'
#' @export
#' @inheritParams layer
@@ -22,7 +23,8 @@ geom_blank <- function(mapping = NULL, data = NULL,
position = position,
show.legend = show.legend,
inherit.aes = inherit.aes,
- params = list(...)
+ params = list(...),
+ check.aes = FALSE
)
}
diff --git a/R/geom-boxplot.r b/R/geom-boxplot.r
index 8623b3b..06c4897 100644
--- a/R/geom-boxplot.r
+++ b/R/geom-boxplot.r
@@ -1,32 +1,38 @@
-#' Box and whiskers plot.
+#' A box and whiskers plot (in the style of Tukey)
#'
-#' The lower and upper "hinges" correspond to the first and third quartiles
+#' The boxplot compactly displays the distribution of a continuous variable.
+#' It visualises five summary statistics (the median, two hinges
+#' and two whiskers), and all "outlying" points individually.
+#'
+#' @section Summary statistics:
+#' The lower and upper hinges correspond to the first and third quartiles
#' (the 25th and 75th percentiles). This differs slightly from the method used
#' by the \code{boxplot} function, and may be apparent with small samples.
#' See \code{\link{boxplot.stats}} for for more information on how hinge
#' positions are calculated for \code{boxplot}.
#'
-#' The upper whisker extends from the hinge to the highest value that is within
-#' 1.5 * IQR of the hinge, where IQR is the inter-quartile range, or distance
-#' between the first and third quartiles. The lower whisker extends from the
-#' hinge to the lowest value within 1.5 * IQR of the hinge. Data beyond the
-#' end of the whiskers are outliers and plotted as points (as specified by Tukey).
+#' The upper whisker extends from the hinge to the largest value no further than
+#' 1.5 * IQR from the hinge (where IQR is the inter-quartile range, or distance
+#' between the first and third quartiles). The lower whisker extends from the
+#' hinge to the smallest value at most 1.5 * IQR of the hinge. Data beyond the
+#' end of the whiskers are called "outlying" points and are plotted
+#' individually.
#'
#' In a notched box plot, the notches extend \code{1.58 * IQR / sqrt(n)}.
-#' This gives a roughly 95% confidence interval for comparing medians.
+#' This gives a roughly 95\% confidence interval for comparing medians.
#' See McGill et al. (1978) for more details.
#'
#' @section Aesthetics:
-#' \Sexpr[results=rd,stage=build]{ggplot2:::rd_aesthetics("geom", "boxplot")}
+#' \aesthetics{geom}{boxplot}
#'
-#' @seealso \code{\link{stat_quantile}} to view quantiles conditioned on a
-#' continuous variable, \code{\link{geom_jitter}} for another way to look
-#' at conditional distributions.
+#' @seealso \code{\link{geom_quantile}} for continuous x,
+#' \code{\link{geom_violin}} for a richer display of the distribution, and
+#' \code{\link{geom_jitter}} for a useful technique for small data.
#' @inheritParams layer
#' @inheritParams geom_point
#' @param geom,stat Use to override the default connection between
#' \code{geom_boxplot} and \code{stat_boxplot}.
-#' @param outlier.colour,outlier.color,outlier.shape,outlier.size,outlier.stroke
+#' @param outlier.colour,outlier.color,outlier.fill,outlier.shape,outlier.size,outlier.stroke,outlier.alpha
#' Default aesthetics for outliers. Set to \code{NULL} to inherit from the
#' aesthetics used for the box.
#'
@@ -67,6 +73,8 @@
#' geom_boxplot()
#' ggplot(diamonds, aes(carat, price)) +
#' geom_boxplot(aes(group = cut_width(carat, 0.25)))
+#' ggplot(diamonds, aes(carat, price)) +
+#' geom_boxplot(aes(group = cut_width(carat, 0.25)), outlier.alpha = 0.1)
#'
#' \donttest{
#' # It's possible to draw a boxplot with your own computations if you
@@ -91,9 +99,11 @@ geom_boxplot <- function(mapping = NULL, data = NULL,
...,
outlier.colour = NULL,
outlier.color = NULL,
+ outlier.fill = NULL,
outlier.shape = 19,
outlier.size = 1.5,
outlier.stroke = 0.5,
+ outlier.alpha = NULL,
notch = FALSE,
notchwidth = 0.5,
varwidth = FALSE,
@@ -110,9 +120,11 @@ geom_boxplot <- function(mapping = NULL, data = NULL,
inherit.aes = inherit.aes,
params = list(
outlier.colour = outlier.color %||% outlier.colour,
+ outlier.fill = outlier.fill,
outlier.shape = outlier.shape,
outlier.size = outlier.size,
outlier.stroke = outlier.stroke,
+ outlier.alpha = outlier.alpha,
notch = notch,
notchwidth = notchwidth,
varwidth = varwidth,
@@ -158,8 +170,10 @@ GeomBoxplot <- ggproto("GeomBoxplot", Geom,
},
draw_group = function(data, panel_scales, coord, fatten = 2,
- outlier.colour = NULL, outlier.shape = 19,
+ outlier.colour = NULL, outlier.fill = NULL,
+ outlier.shape = 19,
outlier.size = 1.5, outlier.stroke = 0.5,
+ outlier.alpha = NULL,
notch = FALSE, notchwidth = 0.5, varwidth = FALSE) {
common <- data.frame(
@@ -200,11 +214,12 @@ GeomBoxplot <- ggproto("GeomBoxplot", Geom,
y = data$outliers[[1]],
x = data$x[1],
colour = outlier.colour %||% data$colour[1],
+ fill = outlier.fill %||% data$fill[1],
shape = outlier.shape %||% data$shape[1],
size = outlier.size %||% data$size[1],
stroke = outlier.stroke %||% data$stroke[1],
fill = NA,
- alpha = NA,
+ alpha = outlier.alpha %||% data$alpha[1],
stringsAsFactors = FALSE
)
outliers_grob <- GeomPoint$draw_panel(outliers, panel_scales, coord)
diff --git a/R/geom-col.r b/R/geom-col.r
new file mode 100644
index 0000000..e10ba7c
--- /dev/null
+++ b/R/geom-col.r
@@ -0,0 +1,48 @@
+#' @export
+#' @rdname geom_bar
+geom_col <- function(mapping = NULL, data = NULL,
+ position = "stack",
+ ...,
+ width = NULL,
+ na.rm = FALSE,
+ show.legend = NA,
+ inherit.aes = TRUE) {
+
+ layer(
+ data = data,
+ mapping = mapping,
+ stat = "identity",
+ geom = GeomCol,
+ position = position,
+ show.legend = show.legend,
+ inherit.aes = inherit.aes,
+ params = list(
+ width = width,
+ na.rm = na.rm,
+ ...
+ )
+ )
+}
+
+#' @rdname ggplot2-ggproto
+#' @format NULL
+#' @usage NULL
+#' @export
+#' @include geom-rect.r
+GeomCol <- ggproto("GeomCol", GeomRect,
+ required_aes = c("x", "y"),
+
+ setup_data = function(data, params) {
+ data$width <- data$width %||%
+ params$width %||% (resolution(data$x, FALSE) * 0.9)
+ transform(data,
+ ymin = pmin(y, 0), ymax = pmax(y, 0),
+ xmin = x - width / 2, xmax = x + width / 2, width = NULL
+ )
+ },
+
+ draw_panel = function(self, data, panel_scales, coord, width = NULL) {
+ # Hack to ensure that width is detected as a parameter
+ ggproto_parent(GeomRect, self)$draw_panel(data, panel_scales, coord)
+ }
+)
diff --git a/R/geom-contour.r b/R/geom-contour.r
index 31c1c07..bea90a9 100644
--- a/R/geom-contour.r
+++ b/R/geom-contour.r
@@ -1,7 +1,15 @@
-#' Display contours of a 3d surface in 2d.
+#' 2d contours of a 3d surface
+#'
+#' ggplot2 can not draw true 3d surfaces, but you can use \code{geom_contour}
+#' and \code{\link{geom_tile}} to visualise 3d surfaces in 2d. To be a valid
+#' surface, the data must contain only a single row for each unique combination
+#' of the variables mapped to the \code{x} and \code{y} aesthetics. Contouring
+#' tends to work best when \code{x} and \code{y} form a (roughly) evenly
+#' spaced grid. If you data is not evenly spaced, you may want to interpolate
+#' to a grid before visualising.
#'
#' @section Aesthetics:
-#' \Sexpr[results=rd,stage=build]{ggplot2:::rd_aesthetics("geom", "contour")}
+#' \aesthetics{geom}{contour}
#'
#' @inheritParams layer
#' @inheritParams geom_point
diff --git a/R/geom-count.r b/R/geom-count.r
index a04a6f0..c3ae056 100644
--- a/R/geom-count.r
+++ b/R/geom-count.r
@@ -1,13 +1,15 @@
-#' Count the number of observations at each location.
+#' Count overlapping points
#'
#' This is a variant \code{\link{geom_point}} that counts the number of
-#' observations at each location, then maps the count to point size. It
-#' useful when you have discrete data.
+#' observations at each location, then maps the count to point area. It
+#' useful when you have discrete data and overplotting.
#'
#' @section Aesthetics:
-#' \Sexpr[results=rd,stage=build]{ggplot2:::rd_aesthetics("geom", "point")}
+#' \aesthetics{geom}{point}
+#'
#' @param geom,stat Use to override the default connection between
#' \code{geom_count} and \code{stat_sum}.
+#' @seealso For continuous \code{x} and \code{x}, use \code{\link{geom_bin2d}}.
#' @inheritParams layer
#' @inheritParams geom_point
#' @export
@@ -22,7 +24,7 @@
#' # counts of zero would be given size 0. Doesn't make much different
#' # here because the smallest count is already close to 0.
#' ggplot(mpg, aes(cty, hwy)) +
-#' geom_count()
+#' geom_count() +
#' scale_size_area()
#'
#' # Display proportions instead of counts -------------------------------------
diff --git a/R/geom-curve.r b/R/geom-curve.r
index 20124d5..9440e23 100644
--- a/R/geom-curve.r
+++ b/R/geom-curve.r
@@ -45,6 +45,7 @@ GeomCurve <- ggproto("GeomCurve", GeomSegment,
call. = FALSE)
}
trans <- coord$transform(data, panel_scales)
+
curveGrob(
trans$x, trans$y, trans$xend, trans$yend,
default.units = "native",
@@ -54,7 +55,7 @@ GeomCurve <- ggproto("GeomCurve", GeomSegment,
col = alpha(trans$colour, trans$alpha),
lwd = trans$size * .pt,
lty = trans$linetype,
- lineend = trans$lineend),
+ lineend = lineend),
arrow = arrow
)
}
diff --git a/R/geom-defaults.r b/R/geom-defaults.r
index 44b11a3..bd1ba1c 100644
--- a/R/geom-defaults.r
+++ b/R/geom-defaults.r
@@ -4,6 +4,7 @@
#' \code{"bin"}), or a Geom/Stat object (like \code{GeomPoint} or
#' \code{StatBin}).
#' @param new Named list of aesthetics.
+#' @keywords internal
#' @export
#' @examples
#' update_geom_defaults("point", list(colour = "darkblue"))
@@ -12,7 +13,7 @@
#' @rdname update_defaults
update_geom_defaults <- function(geom, new) {
if (is.character(geom)) {
- g <- find_subclass("Geom", geom)
+ g <- find_subclass("Geom", geom, parent.frame())
} else if (inherits(geom, "Geom")) {
g <- geom
} else {
@@ -28,7 +29,7 @@ update_geom_defaults <- function(geom, new) {
#' @export
update_stat_defaults <- function(stat, new) {
if (is.character(stat)) {
- g <- find_subclass("Stat", stat)
+ g <- find_subclass("Stat", stat, parent.frame())
} else if (inherits(stat, "Stat")) {
g <- stat
} else {
diff --git a/R/geom-density.r b/R/geom-density.r
index 101dce6..6d9c7f2 100644
--- a/R/geom-density.r
+++ b/R/geom-density.r
@@ -1,10 +1,11 @@
-#' Display a smooth density estimate.
+#' Smoothed density estimates
#'
-#' A kernel density estimate, useful for display the distribution of variables
-#' with underlying smoothness.
+#' Computes and draws kernel density estimate, which is a smoothed version of
+#' the histogram. This is a useful alternative to the histogram if for continuous
+#' data that comes from an underlying smooth distribution.
#'
#' @section Aesthetics:
-#' \Sexpr[results=rd,stage=build]{ggplot2:::rd_aesthetics("geom", "density")}
+#' \aesthetics{geom}{density}
#'
#' @seealso See \code{\link{geom_histogram}}, \code{\link{geom_freqpoly}} for
#' other methods of displaying continuous distribution.
diff --git a/R/geom-density2d.r b/R/geom-density2d.r
index 66418dd..16e072e 100644
--- a/R/geom-density2d.r
+++ b/R/geom-density2d.r
@@ -1,13 +1,15 @@
-#' Contours from a 2d density estimate.
+#' Contours of a 2d density estimate
#'
-#' Perform a 2D kernel density estimation using kde2d and display the
-#' results with contours. This can be useful for dealing with overplotting.
+#' Perform a 2D kernel density estimation using \code{\link[MASS]{kde2d}} and
+#' display the results with contours. This can be useful for dealing with
+#' overplotting. This is a 2d version of \code{\link{geom_density}}.
#'
#' @section Aesthetics:
-#' \Sexpr[results=rd,stage=build]{ggplot2:::rd_aesthetics("geom", "density_2d")}
+#' \aesthetics{geom}{density_2d}
#'
-#' @seealso \code{\link{geom_contour}} for contour drawing geom,
-#' \code{\link{stat_sum}} for another way of dealing with overplotting
+#' @seealso \code{\link{geom_contour}} for information about how contours
+#' are drawn; \code{\link{geom_bin2d}} for another way of dealing with
+#' overplotting.
#' @param geom,stat Use to override the default connection between
#' \code{geom_density_2d} and \code{stat_density_2d}.
#' @inheritParams layer
diff --git a/R/geom-dotplot.r b/R/geom-dotplot.r
index 9cb0c1c..38933ce 100644
--- a/R/geom-dotplot.r
+++ b/R/geom-dotplot.r
@@ -4,12 +4,12 @@
#' (or maximum width, depending on the binning algorithm), and dots are
#' stacked, with each dot representing one observation.
#'
+#' There are two basic approaches: \emph{dot-density} and \emph{histodot}.
#' With dot-density binning, the bin positions are determined by the data and
#' \code{binwidth}, which is the maximum width of each bin. See Wilkinson
-#' (1999) for details on the dot-density binning algorithm.
-#'
-#' With histodot binning, the bins have fixed positions and fixed widths, much
-#' like a histogram.
+#' (1999) for details on the dot-density binning algorithm. With histodot
+#' binning, the bins have fixed positions and fixed widths, much like a
+#' histogram.
#'
#' When binning along the x axis and stacking along the y axis, the numbers on
#' y axis are not meaningful, due to technical limitations of ggplot2. You can
@@ -17,7 +17,20 @@
#' to match the number of dots.
#'
#' @section Aesthetics:
-#' \Sexpr[results=rd,stage=build]{ggplot2:::rd_aesthetics("geom", "dotplot")}
+#' \aesthetics{geom}{dotplot}
+#'
+#' @section Computed variables:
+#' \describe{
+#' \item{x}{center of each bin, if binaxis is "x"}
+#' \item{y}{center of each bin, if binaxis is "x"}
+#' \item{binwidth}{max width of each bin if method is "dotdensity";
+#' width of each bin if method is "histodot"}
+#' \item{count}{number of points in bin}
+#' \item{ncount}{count, scaled to maximum of 1}
+#' \item{density}{density of points in bin, scaled to integrate to 1,
+#' if method is "histodot"}
+#' \item{ndensity}{density, scaled to maximum of 1, if method is "histodot"}
+#' }
#'
#' @inheritParams layer
#' @inheritParams geom_point
@@ -45,18 +58,6 @@
#' @param width When \code{binaxis} is "y", the spacing of the dot stacks
#' for dodging.
#' @param drop If TRUE, remove all bins with zero counts
-#' @section Computed variables:
-#' \describe{
-#' \item{x}{center of each bin, if binaxis is "x"}
-#' \item{y}{center of each bin, if binaxis is "x"}
-#' \item{binwidth}{max width of each bin if method is "dotdensity";
-#' width of each bin if method is "histodot"}
-#' \item{count}{number of points in bin}
-#' \item{ncount}{count, scaled to maximum of 1}
-#' \item{density}{density of points in bin, scaled to integrate to 1,
-#' if method is "histodot"}
-#' \item{ndensity}{density, scaled to maximum of 1, if method is "histodot"}
-#' }
#' @export
#' @references Wilkinson, L. (1999) Dot plots. The American Statistician,
#' 53(3), 276-281.
@@ -238,7 +239,7 @@ GeomDotplot <- ggproto("GeomDotplot", Geom,
# works. They're just set to the standard x +- width/2 so that dot clusters
# can be dodged like other geoms.
# After position code is rewritten, each dot should have its own bounding box.
- data <- plyr::ddply(data, "group", transform,
+ data <- plyr::ddply(data, c("group", "PANEL"), transform,
ymin = min(y) - binwidth[1] / 2,
ymax = max(y) + binwidth[1] / 2)
diff --git a/R/geom-errorbarh.r b/R/geom-errorbarh.r
index 06d84eb..e9afc33 100644
--- a/R/geom-errorbarh.r
+++ b/R/geom-errorbarh.r
@@ -1,9 +1,10 @@
#' Horizontal error bars
#'
+#' A rotated version of \code{\link{geom_errorbar}}.
+#'
#' @section Aesthetics:
-#' \Sexpr[results=rd,stage=build]{ggplot2:::rd_aesthetics("geom", "errorbarh")}
+#' \aesthetics{geom}{errorbarh}
#'
-#' @seealso \code{\link{geom_errorbar}}: vertical error bars
#' @inheritParams layer
#' @inheritParams geom_point
#' @export
diff --git a/R/geom-hex.r b/R/geom-hex.r
index e8c904f..3fa9d5b 100644
--- a/R/geom-hex.r
+++ b/R/geom-hex.r
@@ -1,7 +1,12 @@
-#' Hexagon binning.
+#' Hexagonal heatmap of 2d bin counts
+#'
+#' Divides the plane into regular hexagons, counts the number of cases in
+#' each hexagon, and then (by default) maps the number of cases to the hexagon
+#' fill. Hexagon bins avoid the visual artefacts sometimes generated by
+#' the very regular alignment of \code{\link{geom_bin2d}}.
#'
#' @section Aesthetics:
-#' \Sexpr[results=rd,stage=build]{ggplot2:::rd_aesthetics("geom", "hex")}
+#' \aesthetics{geom}{hex}
#'
#' @seealso \code{\link{stat_bin2d}} for rectangular binning
#' @param geom,stat Override the default connection between \code{geom_hex} and
diff --git a/R/geom-histogram.r b/R/geom-histogram.r
index a6eeaf2..16f4aab 100644
--- a/R/geom-histogram.r
+++ b/R/geom-histogram.r
@@ -1,16 +1,20 @@
-#' Histograms and frequency polygons.
+#' Histograms and frequency polygons
#'
-#' Display a 1d distribution by dividing into bins and counting the number
-#' of observations in each bin. Histograms use bars; frequency polygons use
-#' lines.
+#' Visualise the distribution of a single continuous variable by dividing
+#' the x axis into bins and counting the number of observations in each bin.
+#' Histograms (\code{geom_histogram}) display the count with bars; frequency
+#' polygons (\code{geom_freqpoly}), display the counts with lines. Frequency
+#' polygons are more suitable when you want to compare the distribution
+#' across a the levels of a categorical variable.
#'
-#' By default, \code{stat_bin} uses 30 bins - this is not a good default,
-#' but the idea is to get you experimenting with different binwidths. You
-#' may need to look at a few to uncover the full story behind your data.
+#' By default, the underlying computation (\code{stat_bin}) uses 30 bins -
+#' this is not a good default, but the idea is to get you experimenting with
+#' different binwidths. You may need to look at a few to uncover the full
+#' story behind your data.
#'
#' @section Aesthetics:
-#' \code{geom_histogram} uses the same aesthetics as \code{geom_bar};
-#' \code{geom_freqpoly} uses the same aesthetics as \code{geom_line}.
+#' \code{geom_histogram} uses the same aesthetics as \code{\link{geom_bar}};
+#' \code{geom_freqpoly} uses the same aesthetics as \code{\link{geom_line}}.
#'
#' @export
#' @inheritParams layer
@@ -61,16 +65,15 @@
#' # bar is anchored at zero, and so when transformed becomes negative
#' # infinity. This is not a problem when transforming the scales, because
#' # no observations have 0 ratings.
-#' m + geom_histogram(origin = 0) + coord_trans(x = "log10")
-#' # Use origin = 0, to make sure we don't take sqrt of negative values
-#' m + geom_histogram(origin = 0) + coord_trans(x = "sqrt")
+#' m + geom_histogram(boundary = 0) + coord_trans(x = "log10")
+#' # Use boundary = 0, to make sure we don't take sqrt of negative values
+#' m + geom_histogram(boundary = 0) + coord_trans(x = "sqrt")
#'
#' # You can also transform the y axis. Remember that the base of the bars
#' # has value 0, so log transformations are not appropriate
#' m <- ggplot(movies, aes(x = rating))
#' m + geom_histogram(binwidth = 0.5) + scale_y_sqrt()
#' }
-#' rm(movies)
geom_histogram <- function(mapping = NULL, data = NULL,
stat = "bin", position = "stack",
...,
diff --git a/R/geom-jitter.r b/R/geom-jitter.r
index 6254b40..08d4aa1 100644
--- a/R/geom-jitter.r
+++ b/R/geom-jitter.r
@@ -1,11 +1,12 @@
-#' Points, jittered to reduce overplotting.
+#' Jittered points
#'
-#' The jitter geom is a convenient default for geom_point with position =
-#' 'jitter'. It's a useful way of handling overplotting caused by discreteness
-#' in smaller datasets.
+#' The jitter geom is a convenient shortcut for
+#' \code{geom_point(position = "jitter")}. It adds a small amount of random
+#' variation to the location of each point, and is a useful way of handling
+#' overplotting caused by discreteness in smaller datasets.
#'
#' @section Aesthetics:
-#' \Sexpr[results=rd,stage=build]{ggplot2:::rd_aesthetics("geom", "point")}
+#' \aesthetics{geom}{point}
#'
#' @inheritParams layer
#' @inheritParams geom_point
diff --git a/R/geom-linerange.r b/R/geom-linerange.r
index c1a96cf..afd8b7a 100644
--- a/R/geom-linerange.r
+++ b/R/geom-linerange.r
@@ -1,17 +1,18 @@
-#' Vertical intervals: lines, crossbars & errorbars.
+#' Vertical intervals: lines, crossbars & errorbars
#'
#' Various ways of representing a vertical interval defined by \code{x},
-#' \code{ymin} and \code{ymax}.
+#' \code{ymin} and \code{ymax}. Each case draws a single graphical object.
#'
#' @section Aesthetics:
-#' \Sexpr[results=rd,stage=build]{ggplot2:::rd_aesthetics("geom", "linerange")}
+#' \aesthetics{geom}{linerange}
#'
#' @param fatten A multiplicative factor used to increase the size of the
#' middle bar in \code{geom_crossbar()} and the middle point in
#' \code{geom_pointrange()}.
#' @seealso
#' \code{\link{stat_summary}} for examples of these guys in use,
-#' \code{\link{geom_smooth}} for continuous analog
+#' \code{\link{geom_smooth}} for continuous analog,
+#' \code{\link{geom_errorbarh}} for a horizontal error bar.
#' @export
#' @inheritParams layer
#' @inheritParams geom_point
@@ -40,14 +41,14 @@
#' # specify the dodge width
#' p <- ggplot(df, aes(trt, resp, fill = group))
#' p +
-#' geom_bar(position = "dodge", stat = "identity") +
+#' geom_col(position = "dodge") +
#' geom_errorbar(aes(ymin = lower, ymax = upper), position = "dodge", width = 0.25)
#'
#' # Because the bars and errorbars have different widths
#' # we need to specify how wide the objects we are dodging are
#' dodge <- position_dodge(width=0.9)
#' p +
-#' geom_bar(position = dodge, stat = "identity") +
+#' geom_col(position = dodge) +
#' geom_errorbar(aes(ymin = lower, ymax = upper), position = dodge, width = 0.25)
geom_linerange <- function(mapping = NULL, data = NULL,
stat = "identity", position = "identity",
diff --git a/R/geom-map.r b/R/geom-map.r
index 5256e64..7679969 100644
--- a/R/geom-map.r
+++ b/R/geom-map.r
@@ -1,12 +1,12 @@
#' @include geom-polygon.r
NULL
-#' Polygons from a reference map.
+#' Polygons from a reference map
#'
-#' Does not affect position scales.
+#' This is pure annotation, so does not affect position scales.
#'
#' @section Aesthetics:
-#' \Sexpr[results=rd,stage=build]{ggplot2:::rd_aesthetics("geom", "map")}
+#' \aesthetics{geom}{map}
#'
#' @export
#' @param map Data frame that contains the map coordinates. This will
@@ -36,7 +36,8 @@ NULL
#' 2.2, 2.1, 1.7, 2.1, 3.2, 2.8, 2.1, 2.2, 3.3, 3.2)
#' )
#'
-#' ggplot(values) + geom_map(aes(map_id = id), map = positions) +
+#' ggplot(values) +
+#' geom_map(aes(map_id = id), map = positions) +
#' expand_limits(positions)
#' ggplot(values, aes(fill = value)) +
#' geom_map(aes(map_id = id), map = positions) +
diff --git a/R/geom-path.r b/R/geom-path.r
index 79b4282..ef792b9 100644
--- a/R/geom-path.r
+++ b/R/geom-path.r
@@ -1,12 +1,16 @@
-#' Connect observations.
+#' Connect observations
#'
#' \code{geom_path()} connects the observations in the order in which they appear
#' in the data. \code{geom_line()} connects them in order of the variable on the
#' x axis. \code{geom_step()} creates a stairstep plot, highlighting exactly
-#' when changes occur.
+#' when changes occur. The \code{group} aesthetic determines which cases are
+#' connected together.
+#'
+#' An alternative parameterisation is \code{\link{geom_segment}}: each line
+#' corresponds to a single case which provides the start and end coordinates.
#'
#' @section Aesthetics:
-#' \Sexpr[results=rd,stage=build]{ggplot2:::rd_aesthetics("geom", "path")}
+#' \aesthetics{geom}{path}
#'
#' @inheritParams layer
#' @inheritParams geom_point
@@ -298,6 +302,11 @@ stairstep <- function(data, direction="hv") {
data <- as.data.frame(data)[order(data$x), ]
n <- nrow(data)
+ if (n <= 1) {
+ # Need at least one observation
+ return(data[0, , drop = FALSE])
+ }
+
if (direction == "vh") {
xs <- rep(1:n, each = 2)[-2*n]
ys <- c(1, rep(2:n, each = 2))
diff --git a/R/geom-point.r b/R/geom-point.r
index e1eacf6..2582ba5 100644
--- a/R/geom-point.r
+++ b/R/geom-point.r
@@ -1,37 +1,41 @@
-#' Points, as for a scatterplot
+#' Points
#'
-#' The point geom is used to create scatterplots.
-#'
-#' The scatterplot is useful for displaying the relationship between two
-#' continuous variables, although it can also be used with one continuous
-#' and one categorical variable, or two categorical variables. See
-#' \code{\link{geom_jitter}} for possibilities.
+#' The point geom is used to create scatterplots. The scatterplot is most
+#' useful for displaying the relationship between two continuous variables.
+#' It can be used to compare one continuous and one categorical variable, or
+#' two categorical variables, but a variation like \code{\link{geom_jitter}},
+#' \code{\link{geom_count}}, or \code{\link{geom_bin2d}} is usually more
+#' appropriate.
#'
#' The \emph{bubblechart} is a scatterplot with a third variable mapped to
-#' the size of points. There are no special names for scatterplots where
+#' the size of points. There are no special names for scatterplots where
#' another variable is mapped to point shape or colour, however.
#'
+#' @section Overplotting:
#' The biggest potential problem with a scatterplot is overplotting: whenever
#' you have more than a few points, points may be plotted on top of one
#' another. This can severely distort the visual appearance of the plot.
#' There is no one solution to this problem, but there are some techniques
-#' that can help. You can add additional information with
+#' that can help. You can add additional information with
#' \code{\link{geom_smooth}}, \code{\link{geom_quantile}} or
-#' \code{\link{geom_density_2d}}. If you have few unique x values,
-#' \code{\link{geom_boxplot}} may also be useful. Alternatively, you can
+#' \code{\link{geom_density_2d}}. If you have few unique x values,
+#' \code{\link{geom_boxplot}} may also be useful.
+#'
+#' Alternatively, you can
#' summarise the number of points at each location and display that in some
-#' way, using \code{\link{stat_sum}}. Another technique is to use transparent
-#' points, e.g. \code{geom_point(alpha = 0.05)}.
+#' way, using \code{\link{geom_count}}, \code{\link{geom_hex}}, or
+#' \code{\link{geom_density2d}}.
+#'
+#' Another technique is to make the points transparent (e.g.
+#' \code{geom_point(alpha = 0.05)}) or very small (e.g.
+#' \code{geom_point(shape = ".")}).
#'
#' @section Aesthetics:
-#' \Sexpr[results=rd,stage=build]{ggplot2:::rd_aesthetics("geom", "point")}
+#' \aesthetics{geom}{point}
#'
-#' @seealso \code{\link{scale_size}} to see scale area of points, instead of
-#' radius, \code{\link{geom_jitter}} to jitter points to reduce (mild)
-#' overplotting
#' @inheritParams layer
-#' @param na.rm If \code{FALSE} (the default), removes missing values with
-#' a warning. If \code{TRUE} silently removes missing values.
+#' @param na.rm If \code{FALSE}, the default, missing values are removed with
+#' a warning. If \code{TRUE}, missing values are silently removed.
#' @param ... other arguments passed on to \code{\link{layer}}. These are
#' often aesthetics, used to set an aesthetic to a fixed value, like
#' \code{color = "red"} or \code{size = 3}. They may also be parameters
@@ -117,7 +121,7 @@ geom_point <- function(mapping = NULL, data = NULL,
#' @export
GeomPoint <- ggproto("GeomPoint", Geom,
required_aes = c("x", "y"),
- non_missing_aes = c("size", "shape"),
+ non_missing_aes = c("size", "shape", "colour"),
default_aes = aes(
shape = 19, colour = "black", size = 1.5, fill = NA,
alpha = NA, stroke = 0.5
diff --git a/R/geom-polygon.r b/R/geom-polygon.r
index 794e3dc..5c494f8 100644
--- a/R/geom-polygon.r
+++ b/R/geom-polygon.r
@@ -1,7 +1,12 @@
-#' Polygon, a filled path.
+#' Polygons
+#'
+#' Polygons are very similar to paths (as drawn by \code{\link{geom_path}})
+#' except that the start and end points are connected and the inside is
+#' coloured by \code{fill}. The \code{group} aesthetic determines which cases
+#' are connected together into a polygon.
#'
#' @section Aesthetics:
-#' \Sexpr[results=rd,stage=build]{ggplot2:::rd_aesthetics("geom", "polygon")}
+#' \aesthetics{geom}{polygon}
#'
#' @seealso
#' \code{\link{geom_path}} for an unfilled polygon,
@@ -31,9 +36,11 @@
#' )
#'
#' # Currently we need to manually merge the two together
-#' datapoly <- merge(values, positions, by=c("id"))
+#' datapoly <- merge(values, positions, by = c("id"))
#'
-#' (p <- ggplot(datapoly, aes(x=x, y=y)) + geom_polygon(aes(fill=value, group=id)))
+#' p <- ggplot(datapoly, aes(x = x, y = y)) +
+#' geom_polygon(aes(fill = value, group = id))
+#' p
#'
#' # Which seems like a lot of work, but then it's easy to add on
#' # other features in this coordinate system, e.g.:
@@ -43,7 +50,7 @@
#' y = cumsum(runif(50,max = 0.1))
#' )
#'
-#' p + geom_line(data = stream, colour="grey30", size = 5)
+#' p + geom_line(data = stream, colour = "grey30", size = 5)
#'
#' # And if the positions are in longitude and latitude, you can use
#' # coord_map to produce different map projections.
diff --git a/R/geom-quantile.r b/R/geom-quantile.r
index cc18c69..b7256a9 100644
--- a/R/geom-quantile.r
+++ b/R/geom-quantile.r
@@ -1,9 +1,10 @@
-#' Add quantile lines from a quantile regression.
+#' Quantile regression
#'
-#' This can be used as a continuous analogue of a geom_boxplot.
+#' This fits a quantile regression to the data and draws the fitted quantiles
+#' with lines. This is as a continuous analogue to \code{\link{geom_boxplot}}.
#'
#' @section Aesthetics:
-#' \Sexpr[results=rd,stage=build]{ggplot2:::rd_aesthetics("geom", "quantile")}
+#' \aesthetics{geom}{quantile}
#'
#' @export
#' @inheritParams layer
diff --git a/R/geom-ribbon.r b/R/geom-ribbon.r
index 54a946f..0440c11 100644
--- a/R/geom-ribbon.r
+++ b/R/geom-ribbon.r
@@ -1,17 +1,18 @@
-#' Ribbons and area plots.
+#' Ribbons and area plots
#'
-#' For each continuous x value, \code{geom_interval} displays a y interval.
-#' \code{geom_area} is a special case of \code{geom_ribbon}, where the
-#' minimum of the range is fixed to 0.
+#' For each x value, \code{geom_ribbon} displays a y interval defined
+#' by \code{ymin} and \code{ymax}. \code{geom_area} is a special case of
+#' \code{geom_ribbon}, where the \code{ymin} is fixed to 0.
#'
#' An area plot is the continuous analog of a stacked bar chart (see
#' \code{\link{geom_bar}}), and can be used to show how composition of the
-#' whole varies over the range of x. Choosing the order in which different
+#' whole varies over the range of x. Choosing the order in which different
#' components is stacked is very important, as it becomes increasing hard to
-#' see the individual pattern as you move up the stack.
+#' see the individual pattern as you move up the stack. See
+#' \code{\link{position_stack}} for the details of stacking algorithm.
#'
#' @section Aesthetics:
-#' \Sexpr[results=rd,stage=build]{ggplot2:::rd_aesthetics("geom", "ribbon")}
+#' \aesthetics{geom}{ribbon}
#'
#' @seealso
#' \code{\link{geom_bar}} for discrete intervals (bars),
diff --git a/R/geom-rug.r b/R/geom-rug.r
index eebe6c7..19f8ba5 100644
--- a/R/geom-rug.r
+++ b/R/geom-rug.r
@@ -1,7 +1,15 @@
-#' Marginal rug plots.
+#' Rug plots in the margins
+#'
+#' A rug plot is a compact visualisation designed to supplement a 2d display
+#' with the two 1d marginal distributions. Rug plots display individual
+#' cases so are best used with smaller datasets.
+#'
+#' The rug lines are drawn with a fixed size (3% of the total plot size) so
+#' are dependent on the overall scale expansion in order not to overplot
+#' existing data.
#'
#' @section Aesthetics:
-#' \Sexpr[results=rd,stage=build]{ggplot2:::rd_aesthetics("geom", "rug")}
+#' \aesthetics{geom}{rug}
#'
#' @inheritParams layer
#' @inheritParams geom_point
@@ -10,12 +18,21 @@
#' bottom, and left.
#' @export
#' @examples
-#' p <- ggplot(mtcars, aes(wt, mpg))
-#' p + geom_point()
-#' p + geom_point() + geom_rug()
-#' p + geom_point() + geom_rug(sides="b") # Rug on bottom only
-#' p + geom_point() + geom_rug(sides="trbl") # All four sides
-#' p + geom_point() + geom_rug(position='jitter')
+#' p <- ggplot(mtcars, aes(wt, mpg)) +
+#' geom_point()
+#' p
+#' p + geom_rug()
+#' p + geom_rug(sides="b") # Rug on bottom only
+#' p + geom_rug(sides="trbl") # All four sides
+#'
+#' # Use jittering to avoid overplotting for smaller datasets
+#' ggplot(mpg, aes(displ, cty)) +
+#' geom_point() +
+#' geom_rug()
+#'
+#' ggplot(mpg, aes(displ, cty)) +
+#' geom_jitter() +
+#' geom_rug(alpha = 1/2, position = "jitter")
geom_rug <- function(mapping = NULL, data = NULL,
stat = "identity", position = "identity",
...,
@@ -45,6 +62,8 @@ geom_rug <- function(mapping = NULL, data = NULL,
#' @usage NULL
#' @export
GeomRug <- ggproto("GeomRug", Geom,
+ optional_aes = c("x", "y"),
+
draw_panel = function(data, panel_scales, coord, sides = "bl") {
rugs <- list()
data <- coord$transform(data, panel_scales)
diff --git a/R/geom-segment.r b/R/geom-segment.r
index a8e04b2..110be91 100644
--- a/R/geom-segment.r
+++ b/R/geom-segment.r
@@ -1,15 +1,20 @@
-#' Line segments and curves.
+#' Line segments and curves
#'
-#' \code{geom_segment} draws a straight line between points (x1, y1) and
-#' (x2, y2). \code{geom_curve} draws a curved line.
+#' \code{geom_segment} draws a straight line between points (x, y) and
+#' (xend, yend). \code{geom_curve} draws a curved line. See the underlying
+#' drawing function \code{\link[grid]{curveGrob}} for the parameters that
+#' control the curve.
+#'
+#' Both geoms draw a single segment/curve per case. See \code{geom_path} if you
+#' need to connect points across multiple cases.
#'
#' @section Aesthetics:
-#' \Sexpr[results=rd,stage=build]{ggplot2:::rd_aesthetics("geom", "segment")}
+#' \aesthetics{geom}{segment}
#'
#' @inheritParams layer
#' @inheritParams geom_point
-#' @param arrow specification for arrow heads, as created by arrow()
-#' @param lineend Line end style (round, butt, square)
+#' @param arrow specification for arrow heads, as created by arrow().
+#' @param lineend Line end style (round, butt, square).
#' @seealso \code{\link{geom_path}} and \code{\link{geom_line}} for multi-
#' segment lines and paths.
#' @seealso \code{\link{geom_spoke}} for a segment parameterised by a location
diff --git a/R/geom-smooth.r b/R/geom-smooth.r
index 04a4e65..b42ea57 100644
--- a/R/geom-smooth.r
+++ b/R/geom-smooth.r
@@ -1,4 +1,4 @@
-#' Add a smoothed conditional mean.
+#' Smoothed conditional means
#'
#' Aids the eye in seeing patterns in the presence of overplotting.
#' \code{geom_smooth} and \code{stat_smooth} are effectively aliases: they
@@ -13,7 +13,7 @@
#' scale, and then back-transformed to the response scale.
#'
#' @section Aesthetics:
-#' \Sexpr[results=rd,stage=build]{ggplot2:::rd_aesthetics("geom", "smooth")}
+#' \aesthetics{geom}{smooth}
#'
#' @inheritParams layer
#' @inheritParams geom_point
diff --git a/R/geom-spoke.r b/R/geom-spoke.r
index 6c17911..a98c599 100644
--- a/R/geom-spoke.r
+++ b/R/geom-spoke.r
@@ -1,7 +1,10 @@
-#' A line segment parameterised by location, direction and distance.
+#' Line segments parameterised by location, direction and distance
+#'
+#' This is a polar parameterisation of \code{\link{geom_segment}}. It is
+#' useful when you have variables that describe direction and distance.
#'
#' @section Aesthetics:
-#' \Sexpr[results=rd,stage=build]{ggplot2:::rd_aesthetics("geom", "spoke")}
+#' \aesthetics{geom}{spoke}
#'
#' @inheritParams layer
#' @inheritParams geom_segment
diff --git a/R/geom-text.r b/R/geom-text.r
index 84d1189..9746d0c 100644
--- a/R/geom-text.r
+++ b/R/geom-text.r
@@ -1,7 +1,7 @@
-#' Textual annotations.
+#' Text
#'
#' \code{geom_text} adds text directly to the plot. \code{geom_label} draws
-#' a rectangle underneath the text, making it easier to read.
+#' a rectangle behind the text, making it easier to read.
#'
#' Note the the "width" and "height" of a text element are 0, so stacking
#' and dodging text will not work by default, and axis limits are not
@@ -11,7 +11,7 @@
#' resize a plot, labels stay the same size, but the size of the axes changes.
#'
#' @section Aesthetics:
-#' \Sexpr[results=rd,stage=build]{ggplot2:::rd_aesthetics("geom", "text")}
+#' \aesthetics{geom}{text}
#'
#' @section \code{geom_label}:
#' Currently \code{geom_label} does not support the \code{rot} parameter and
@@ -33,7 +33,7 @@
#' @param nudge_x,nudge_y Horizontal and vertical adjustment to nudge labels by.
#' Useful for offsetting text from points, particularly on discrete scales.
#' @param check_overlap If \code{TRUE}, text that overlaps previous text in the
-#' same layer will not be plotted. A quick and dirty way
+#' same layer will not be plotted.
#' @export
#' @examples
#' p <- ggplot(mtcars, aes(wt, mpg, label = rownames(mtcars)))
@@ -86,25 +86,27 @@
#'
#' # ggplot2 doesn't know you want to give the labels the same virtual width
#' # as the bars:
-#' ggplot(data = df, aes(x, y, fill = grp, label = y)) +
-#' geom_bar(stat = "identity", position = "dodge") +
-#' geom_text(position = "dodge")
+#' ggplot(data = df, aes(x, y, group = grp)) +
+#' geom_col(aes(fill = grp), position = "dodge") +
+#' geom_text(aes(label = y), position = "dodge")
#' # So tell it:
-#' ggplot(data = df, aes(x, y, fill = grp, label = y)) +
-#' geom_bar(stat = "identity", position = "dodge") +
-#' geom_text(position = position_dodge(0.9))
+#' ggplot(data = df, aes(x, y, group = grp)) +
+#' geom_col(aes(fill = grp), position = "dodge") +
+#' geom_text(aes(label = y), position = position_dodge(0.9))
#' # Use you can't nudge and dodge text, so instead adjust the y postion
-#' ggplot(data = df, aes(x, y, fill = grp, label = y)) +
-#' geom_bar(stat = "identity", position = "dodge") +
-#' geom_text(aes(y = y + 0.05), position = position_dodge(0.9), vjust = 0)
+#' ggplot(data = df, aes(x, y, group = grp)) +
+#' geom_col(aes(fill = grp), position = "dodge") +
+#' geom_text(
+#' aes(label = y, y = y + 0.05),
+#' position = position_dodge(0.9),
+#' vjust = 0
+#' )
#'
#' # To place text in the middle of each bar in a stacked barplot, you
-#' # need to do the computation yourself
-#' df <- transform(df, mid_y = ave(df$y, df$x, FUN = function(val) cumsum(val) - (0.5 * val)))
-#'
-#' ggplot(data = df, aes(x, y, fill = grp, label = y)) +
-#' geom_bar(stat = "identity") +
-#' geom_text(aes(y = mid_y))
+#' # need to set the vjust parameter of position_stack()
+#' ggplot(data = df, aes(x, y, group = grp)) +
+#' geom_col(aes(fill = grp)) +
+#' geom_text(aes(label = y), position = position_stack(vjust = 0.5))
#'
#' # Justification -------------------------------------------------------------
#' df <- data.frame(
diff --git a/R/geom-tile.r b/R/geom-tile.r
index dc188ff..0e6f04c 100644
--- a/R/geom-tile.r
+++ b/R/geom-tile.r
@@ -1,14 +1,14 @@
-#' Draw rectangles.
+#' Rectangles
#'
#' \code{geom_rect} and \code{geom_tile} do the same thing, but are
-#' parameterised differently. \code{geom_rect} uses the locations of the four
-#' corners (\code{xmin}, \code{xmax}, \code{ymin} and \code{ymax}).
+#' parameterised differently: \code{geom_rect} uses the locations of the four
+#' corners (\code{xmin}, \code{xmax}, \code{ymin} and \code{ymax}), while
#' \code{geom_tile} uses the center of the tile and its size (\code{x},
#' \code{y}, \code{width}, \code{height}). \code{geom_raster} is a high
#' performance special case for when all the tiles are the same size.
#'
#' @section Aesthetics:
-#' \Sexpr[results=rd,stage=build]{ggplot2:::rd_aesthetics("geom", "tile")}
+#' \aesthetics{geom}{tile}
#'
#' @inheritParams layer
#' @inheritParams geom_point
diff --git a/R/geom-violin.r b/R/geom-violin.r
index e1446d6..e66b3f5 100644
--- a/R/geom-violin.r
+++ b/R/geom-violin.r
@@ -1,7 +1,12 @@
-#' Violin plot.
+#' Violin plot
+#'
+#' A violin plot is a compact display of a continuous distribution. It is a
+#' blend of \code{\link{geom_boxplot}} and \code{\link{geom_density}}: a
+#' violin plot is a mirrored density plot displayed in the same way as a
+#' boxplot.
#'
#' @section Aesthetics:
-#' \Sexpr[results=rd,stage=build]{ggplot2:::rd_aesthetics("geom", "violin")}
+#' \aesthetics{geom}{violin}
#'
#' @inheritParams layer
#' @inheritParams geom_point
@@ -19,8 +24,7 @@
#' p + geom_violin()
#'
#' \donttest{
-#' p + geom_violin() + geom_jitter(height = 0)
-#' p + geom_violin() + coord_flip()
+#' p + geom_violin() + geom_jitter(height = 0, width = 0.1)
#'
#' # Scale maximum width proportional to sample size:
#' p + geom_violin(scale = "count")
@@ -126,8 +130,8 @@ GeomViolin <- ggproto("GeomViolin", Geom,
# Needed for coord_polar and such
newdata <- rbind(newdata, newdata[1,])
- # Draw quantiles if requested
- if (length(draw_quantiles) > 0) {
+ # Draw quantiles if requested, so long as there is non-zero y range
+ if (length(draw_quantiles) > 0 & !scales::zero_range(range(data$y))) {
stopifnot(all(draw_quantiles >= 0), all(draw_quantiles <= 1))
# Compute the quantile segments and combine with existing aesthetics
@@ -137,6 +141,7 @@ GeomViolin <- ggproto("GeomViolin", Geom,
setdiff(names(data), c("x", "y")),
drop = FALSE
]
+ aesthetics$alpha <- rep(1, nrow(quantiles))
both <- cbind(quantiles, aesthetics)
quantile_grob <- GeomPath$draw_panel(both, ...)
@@ -163,11 +168,11 @@ create_quantile_segment_frame <- function(data, draw_quantiles) {
ecdf <- stats::approxfun(dens, data$y)
ys <- ecdf(draw_quantiles) # these are all the y-values for quantiles
- # Get the violin bounds for the requested quantiles
+ # Get the violin bounds for the requested quantiles.
violin.xminvs <- (stats::approxfun(data$y, data$xminv))(ys)
violin.xmaxvs <- (stats::approxfun(data$y, data$xmaxv))(ys)
- # We have two rows per segment drawn. Each segments gets its own group.
+ # We have two rows per segment drawn. Each segment gets its own group.
data.frame(
x = interleave(violin.xminvs, violin.xmaxvs),
y = rep(ys, each = 2),
diff --git a/R/ggplot2.r b/R/ggplot2.r
index 90f86f6..f05bb92 100644
--- a/R/ggplot2.r
+++ b/R/ggplot2.r
@@ -1,3 +1,6 @@
+#' @keywords internal
+"_PACKAGE"
+
#' @import scales grid gtable
#' @importFrom plyr defaults
#' @importFrom stats setNames
diff --git a/R/ggproto.r b/R/ggproto.r
index 4168907..c783d94 100644
--- a/R/ggproto.r
+++ b/R/ggproto.r
@@ -1,11 +1,18 @@
#' Create a new ggproto object
#'
-#' ggproto is inspired by the proto package, but it has some important
-#' differences. Notably, it cleanly supports cross-package inheritance, and has
-#' faster performance.
+#' Construct a new object with \code{ggproto}, test with \code{is.proto},
+#' and access parent methods/fields with \code{ggproto_parent}.
#'
-#' @section Calling ggproto methods:
+#' ggproto implements a protype based OO system which blurs the lines between
+#' classes and instances. It is inspired by the proto package, but it has some
+#' important differences. Notably, it cleanly supports cross-package
+#' inheritance, and has faster performance.
#'
+#' In most cases, creating a new OO system to be used by a single package is
+#' not a good idea. However, it was the least-bad solution for ggplot2 because
+#' it required the fewest changes to an already complex code base.
+#'
+#' @section Calling methods:
#' ggproto methods can take an optional \code{self} argument: if it is present,
#' it is a regular method; if it's absent, it's a "static" method (i.e. it
#' doesn't use any fields).
@@ -17,18 +24,36 @@
#' in the function signature, although customarily it comes first.
#'
#' @section Calling methods in a parent:
-#'
#' To explicitly call a methods in a parent, use
#' \code{ggproto_parent(Parent, self)}.
#'
#' @param _class Class name to assign to the object. This is stored as the class
-#' attribute of the object. If \code{NULL} (the default), no class name will
-#' be added to the object.
-#' @param _inherit ggproto object to inherit from. If \code{NULL}, don't inherit
-#' from any object.
-#' @param parent,self Access parent class \code{parent} of object \code{self}.
+#' attribute of the object. This is optional: if \code{NULL} (the default),
+#' no class name will be added to the object.
+#' @param _inherit ggproto object to inherit from. If \code{NULL}, don't
+#' inherit from any object.
#' @param ... A list of members in the ggproto object.
#' @export
+#' @examples
+#' Adder <- ggproto("Adder",
+#' x = 0,
+#' add = function(self, n) {
+#' self$x <- self$x + n
+#' self$x
+#' }
+#' )
+#' is.ggproto(Adder)
+#'
+#' Adder$add(10)
+#' Adder$add(10)
+#'
+#' Doubler <- ggproto("Doubler", Adder,
+#' add = function(self, n) {
+#' ggproto_parent(Adder, self)$add(n * 2)
+#' }
+#' )
+#' Doubler$x
+#' Doubler$add(10)
ggproto <- function(`_class` = NULL, `_inherit` = NULL, ...) {
e <- new.env(parent = emptyenv())
@@ -38,18 +63,26 @@ ggproto <- function(`_class` = NULL, `_inherit` = NULL, ...) {
}
# R <3.1.2 will error when list2env() is given an empty list, so we need to
- # check length. https://github.com/hadley/ggplot2/issues/1444
+ # check length. https://github.com/tidyverse/ggplot2/issues/1444
if (length(members) > 0) {
list2env(members, envir = e)
}
- if (!is.null(`_inherit`)) {
- if (!is.ggproto(`_inherit`)) {
+ # Dynamically capture parent: this is necessary in order to avoid
+ # capturing the parent at package build time.
+ `_inherit` <- substitute(`_inherit`)
+ env <- parent.frame()
+ find_super <- function() {
+ eval(`_inherit`, env, NULL)
+ }
+
+ super <- find_super()
+ if (!is.null(super)) {
+ if (!is.ggproto(super)) {
stop("`_inherit` must be a ggproto object.")
}
- e$super <- `_inherit`
- class(e) <- c(`_class`, class(`_inherit`))
-
+ e$super <- find_super
+ class(e) <- c(`_class`, class(super))
} else {
class(e) <- c(`_class`, "ggproto")
}
@@ -57,10 +90,17 @@ ggproto <- function(`_class` = NULL, `_inherit` = NULL, ...) {
e
}
-#' Is an object a ggproto object?
-#'
+
+#' @export
+#' @rdname ggproto
+#' @param parent,self Access parent class \code{parent} of object \code{self}.
+ggproto_parent <- function(parent, self) {
+ structure(list(parent = parent, self = self), class = "ggproto_parent")
+}
+
#' @param x An object to test.
#' @export
+#' @rdname ggproto
is.ggproto <- function(x) inherits(x, "ggproto")
fetch_ggproto <- function(x, name) {
@@ -74,20 +114,23 @@ fetch_ggproto <- function(x, name) {
} else {
# If not found here, recurse into super environments
super <- .subset2(x, "super")
- if (is.ggproto(super))
- res <- fetch_ggproto(super, name)
+ if (is.null(super)) {
+ # no super class
+ } else if (is.function(super)) {
+ res <- fetch_ggproto(super(), name)
+ } else {
+ stop(
+ class(x)[[1]], " was built with an incompatible version of ggproto.\n",
+ "Please reinstall the package that provides this extension.",
+ call. = FALSE
+ )
+ }
}
res
}
#' @export
-#' @rdname ggproto
-ggproto_parent <- function(parent, self) {
- structure(list(parent = parent, self = self), class = "ggproto_parent")
-}
-
-#' @export
`$.ggproto` <- function(x, name) {
res <- fetch_ggproto(x, name)
if (!is.function(res)) {
@@ -123,7 +166,6 @@ make_proto_method <- function(self, f) {
fun
}
-
#' @export
`[[.ggproto` <- `$.ggproto`
@@ -136,12 +178,13 @@ make_proto_method <- function(self, f) {
#' the returned list. If \code{FALSE}, do not include any inherited items.
#' @param ... Further arguments to pass to \code{as.list.environment}.
#' @export
+#' @keywords internal
as.list.ggproto <- function(x, inherit = TRUE, ...) {
res <- list()
if (inherit) {
- if (!is.null(x$super)) {
- res <- as.list(x$super)
+ if (is.function(x$super)) {
+ res <- as.list(x$super())
}
}
@@ -152,7 +195,7 @@ as.list.ggproto <- function(x, inherit = TRUE, ...) {
}
-#' Print a ggproto object
+#' Format or print a ggproto object
#'
#' If a ggproto object has a \code{$print} method, this will call that method.
#' Otherwise, it will print out the members of the object, and optionally, the
@@ -165,6 +208,14 @@ as.list.ggproto <- function(x, inherit = TRUE, ...) {
#' will be passed to it. Otherwise, these arguments are unused.
#'
#' @export
+#' @examples
+#' Dog <- ggproto(
+#' print = function(self, n) {
+#' cat("Woof!\n")
+#' }
+#' )
+#' Dog
+#' cat(format(Dog), "\n")
print.ggproto <- function(x, ..., flat = TRUE) {
if (is.function(x$print)) {
x$print(...)
@@ -176,10 +227,8 @@ print.ggproto <- function(x, ..., flat = TRUE) {
}
-#' Format a ggproto object
-#'
-#' @inheritParams print.ggproto
#' @export
+#' @rdname print.ggproto
format.ggproto <- function(x, ..., flat = TRUE) {
classes_str <- function(obj) {
classes <- setdiff(class(obj), "ggproto")
@@ -200,11 +249,11 @@ format.ggproto <- function(x, ..., flat = TRUE) {
indent(object_summaries(objs, flat = flat), 4)
)
- if (flat && !is.null(x$super)) {
+ if (flat && is.function(x$super)) {
str <- paste0(
str, "\n",
indent(
- paste0("super: ", " <ggproto object", classes_str(x$super), ">"),
+ paste0("super: ", " <ggproto object", classes_str(x$super()), ">"),
4
)
)
diff --git a/R/guide-colorbar.r b/R/guide-colorbar.r
index 9a52361..cebb06b 100644
--- a/R/guide-colorbar.r
+++ b/R/guide-colorbar.r
@@ -1,4 +1,4 @@
-#' Continuous colour bar guide.
+#' Continuous colour bar guide
#'
#' Colour bar guide shows continuous color scales mapped onto values.
#' Colour bar is available with \code{scale_fill} and \code{scale_colour}.
@@ -75,7 +75,7 @@
#' p1 + guides(fill = guide_colorbar(nbin = 100))
#'
#' # make top- and bottom-most ticks invisible
-#' p1 + scale_fill_continuous(limits = c(0,20), breaks=c(0, 5, 10, 15, 20),
+#' p1 + scale_fill_continuous(limits = c(0,20), breaks = c(0, 5, 10, 15, 20),
#' guide = guide_colorbar(nbin=100, draw.ulim = FALSE, draw.llim = FALSE))
#'
#' # guides can be controlled independently
@@ -211,7 +211,22 @@ guide_merge.colorbar <- function(guide, new_guide) {
# this guide is not geom-based.
#' @export
-guide_geom.colorbar <- function(guide, ...) {
+guide_geom.colorbar <- function(guide, layers, default_mapping) {
+ # Layers that use this guide
+ guide_layers <- plyr::llply(layers, function(layer) {
+ matched <- matched_aes(layer, guide, default_mapping)
+
+ if (length(matched) && ((is.na(layer$show.legend) || layer$show.legend))) {
+ layer
+ } else {
+ # This layer does not use this guide
+ NULL
+ }
+ })
+
+ # Remove this guide if no layer uses it
+ if (length(compact(guide_layers)) == 0) guide <- NULL
+
guide
}
@@ -412,9 +427,9 @@ guide_gengrob.colorbar <- function(guide, theme) {
grob.background <- element_render(theme, "legend.background")
# padding
- padding <- unit(1.5, "mm")
- widths <- c(padding, widths, padding)
- heights <- c(padding, heights, padding)
+ padding <- convertUnit(theme$legend.margin %||% margin(), "mm")
+ widths <- c(padding[4], widths, padding[2])
+ heights <- c(padding[1], heights, padding[3])
gt <- gtable(widths = unit(widths, "mm"), heights = unit(heights, "mm"))
gt <- gtable_add_grob(gt, grob.background, name = "background", clip = "off",
diff --git a/R/guide-legend.r b/R/guide-legend.r
index bee2208..385dd7d 100644
--- a/R/guide-legend.r
+++ b/R/guide-legend.r
@@ -1,4 +1,4 @@
-#' Legend guide.
+#' Legend guide
#'
#' Legend type guide shows key (i.e., geoms) mapped onto values.
#' Legend guides for various scales are integrated if possible.
@@ -216,16 +216,9 @@ guide_train.legend <- function(guide, scale) {
stringsAsFactors = FALSE)
key$.label <- scale$get_labels(breaks)
- # this is a quick fix for #118
- # some scales have NA as na.value (e.g., size)
- # some scales have non NA as na.value (e.g., "grey50" for colour)
- # drop rows if data (instead of the mapped value) is NA
- #
- # Also, drop out-of-range values for continuous scale
+ # Drop out-of-range values for continuous scale
# (should use scale$oob?)
- if (scale$is_discrete()) {
- key <- key[!is.na(breaks), , drop = FALSE]
- } else {
+ if (!scale$is_discrete()) {
limits <- scale$get_limits()
noob <- !is.na(breaks) & limits[1] <= breaks & breaks <= limits[2]
key <- key[noob, , drop = FALSE]
@@ -252,11 +245,7 @@ guide_merge.legend <- function(guide, new_guide) {
guide_geom.legend <- function(guide, layers, default_mapping) {
# arrange common data for vertical and horizontal guide
guide$geoms <- plyr::llply(layers, function(layer) {
- all <- names(c(layer$mapping, if (layer$inherit.aes) default_mapping, layer$stat$default_aes))
- geom <- c(layer$geom$required_aes, names(layer$geom$default_aes))
- matched <- intersect(intersect(all, geom), names(guide$key))
- matched <- setdiff(matched, names(layer$geom_params))
- matched <- setdiff(matched, names(layer$aes_params))
+ matched <- matched_aes(layer, guide, default_mapping)
if (length(matched) > 0) {
# This layer contributes to the legend
@@ -493,9 +482,9 @@ guide_gengrob.legend <- function(guide, theme) {
krows <- rep(vps$key.row, each = ngeom)
# padding
- padding <- 0.15
- widths <- c(padding, widths, padding)
- heights <- c(padding, heights, padding)
+ padding <- convertUnit(theme$legend.margin %||% margin(), "cm")
+ widths <- c(padding[4], widths, padding[2])
+ heights <- c(padding[1], heights, padding[3])
# Create the gtable for the legend
gt <- gtable(widths = unit(widths, "cm"), heights = unit(heights, "cm"))
diff --git a/R/guides-.r b/R/guides-.r
index 4f6ddf9..a395701 100644
--- a/R/guides-.r
+++ b/R/guides-.r
@@ -1,9 +1,12 @@
-#' Set guides for each scale.
+#' Set guides for each scale
#'
-#' Guides for each scale can be set in call of \code{scale_*} with argument
-#' \code{guide}, or in \code{guides}.
+#' Guides for each scale can be set scale-by-scale with the \code{guide}
+#' argument, or en masse with \code{guides()}.
#'
-#' @param ... List of scale guide pairs
+#' @param ... List of scale name-guide pairs. The guide can either
+#' be a string (i.e. "colorbar" or "legend"), or a call to a guide function
+#' (i.e. \code{\link{guide_colourbar}} or \code{\link{guide_legend}})
+#' specifying additional arguments.
#' @return A list containing the mapping between scale and guide.
#' @export
#' @family guides
@@ -45,8 +48,6 @@
#'
#' # position of guides
#'
-#' p + theme(legend.position = "bottom", legend.box = "horizontal")
-#'
#' # Set order for multiple guides
#' ggplot(mpg, aes(displ, cty)) +
#' geom_point(aes(size = hwy, colour = cyl, shape = drv)) +
@@ -93,32 +94,24 @@ update_guides <- function(p, guides) {
# arrange all ggrobs
build_guides <- function(scales, layers, default_mapping, position, theme, guides, labels) {
-
- # set themes w.r.t. guides
- # should these theme$legend.XXX be renamed to theme$guide.XXX ?
-
- # by default, guide boxes are vertically aligned
- theme$legend.box <- theme$legend.box %||% "vertical"
-
- # size of key (also used for bar in colorbar guide)
theme$legend.key.width <- theme$legend.key.width %||% theme$legend.key.size
theme$legend.key.height <- theme$legend.key.height %||% theme$legend.key.size
- # by default, direction of each guide depends on the position of the guide.
- theme$legend.direction <-
- theme$legend.direction %||%
- if (length(position) == 1 && position %in% c("top", "bottom", "left", "right"))
- switch(position[1], top = , bottom = "horizontal", left = , right = "vertical")
- else
- "vertical"
-
- # justification of legend boxes
- theme$legend.box.just <-
- theme$legend.box.just %||%
- if (length(position) == 1 && position %in% c("top", "bottom", "left", "right"))
- switch(position, bottom = , top = c("center", "top"), left = , right = c("left", "top"))
- else
- c("center", "center")
+ # Layout of legends depends on their overall location
+ position <- legend_position(position)
+ if (position == "inside") {
+ theme$legend.box <- theme$legend.box %||% "vertical"
+ theme$legend.direction <- theme$legend.direction %||% "vertical"
+ theme$legend.box.just <- theme$legend.box.just %||% c("center", "center")
+ } else if (position == "vertical") {
+ theme$legend.box <- theme$legend.box %||% "vertical"
+ theme$legend.direction <- theme$legend.direction %||% "vertical"
+ theme$legend.box.just <- theme$legend.box.just %||% c("left", "top")
+ } else if (position == "horizontal") {
+ theme$legend.box <- theme$legend.box %||% "horizontal"
+ theme$legend.direction <- theme$legend.direction %||% "horizontal"
+ theme$legend.box.just <- theme$legend.box.just %||% c("center", "top")
+ }
# scales -> data for guides
gdefs <- guides_train(scales = scales, theme = theme, guides = guides, labels = labels)
@@ -140,6 +133,19 @@ build_guides <- function(scales, layers, default_mapping, position, theme, guide
grobs
}
+# Simplify legend position to one of horizontal/vertical/inside
+legend_position <- function(position) {
+ if (length(position) == 1) {
+ if (position %in% c("top", "bottom")) {
+ "horizontal"
+ } else {
+ "vertical"
+ }
+ } else {
+ "inside"
+ }
+}
+
# validate guide object
validate_guide <- function(guide) {
# if guide is specified by character, then find the corresponding guide
@@ -177,7 +183,7 @@ guides_train <- function(scales, theme, guides, labels) {
if (guide$available_aes != "any" && !scale$aesthetics %in% guide$available_aes)
stop("Guide '", guide$name, "' cannot be used for '", scale$aesthetics, "'.")
- guide$title <- guide$title %|W|% scale$name %|W|% labels[[output]]
+ guide$title <- scale$make_title(guide$title %|W|% scale$name %|W|% labels[[output]])
# direction of this grob
guide$direction <- guide$direction %||% theme$legend.direction
@@ -227,9 +233,9 @@ guides_gengrob <- function(gdefs, theme) {
# build up all guide boxes into one guide-boxes.
guides_build <- function(ggrobs, theme) {
- theme$legend.margin <- theme$legend.margin %||% unit(0.5, "lines")
- theme$legend.vmargin <- theme$legend.vmargin %||% theme$legend.margin
- theme$legend.hmargin <- theme$legend.hmargin %||% theme$legend.margin
+ theme$legend.spacing <- theme$legend.spacing %||% unit(0.5, "lines")
+ theme$legend.spacing.y <- theme$legend.spacing.y %||% theme$legend.spacing
+ theme$legend.spacing.x <- theme$legend.spacing.x %||% theme$legend.spacing
widths <- do.call("unit.c", lapply(ggrobs, function(g)sum(g$widths)))
heights <- do.call("unit.c", lapply(ggrobs, function(g)sum(g$heights)))
@@ -254,7 +260,7 @@ guides_build <- function(ggrobs, theme) {
widths = widths, height = max(heights))
# add space between the guide-boxes
- guides <- gtable_add_col_space(guides, theme$legend.hmargin)
+ guides <- gtable_add_col_space(guides, theme$legend.spacing.x)
} else if (theme$legend.box == "vertical") {
# Set justification for each legend
@@ -269,15 +275,21 @@ guides_build <- function(ggrobs, theme) {
width = max(widths), heights = heights)
# add space between the guide-boxes
- guides <- gtable_add_row_space(guides, theme$legend.vmargin)
+ guides <- gtable_add_row_space(guides, theme$legend.spacing.y)
}
- # add margins around the guide-boxes.
- guides <- gtable_add_cols(guides, theme$legend.hmargin, pos = 0)
- guides <- gtable_add_cols(guides, theme$legend.hmargin, pos = ncol(guides))
- guides <- gtable_add_rows(guides, theme$legend.vmargin, pos = 0)
- guides <- gtable_add_rows(guides, theme$legend.vmargin, pos = nrow(guides))
+ # Add margins around the guide-boxes.
+ theme$legend.box.margin <- theme$legend.box.margin %||% margin()
+ guides <- gtable_add_cols(guides, theme$legend.box.margin[4], pos = 0)
+ guides <- gtable_add_cols(guides, theme$legend.box.margin[2], pos = ncol(guides))
+ guides <- gtable_add_rows(guides, theme$legend.box.margin[1], pos = 0)
+ guides <- gtable_add_rows(guides, theme$legend.box.margin[3], pos = nrow(guides))
+
+ # Add legend box background
+ background <- element_grob(theme$legend.box.background %||% element_blank())
+ guides <- gtable_add_grob(guides, background, t = 1, l = 1,
+ b = -1, r = -1, z = -Inf, clip = "off", name = "legend.box.background")
guides$name <- "guide-box"
guides
}
@@ -291,3 +303,12 @@ guide_merge <- function(...) UseMethod("guide_merge")
guide_geom <- function(...) UseMethod("guide_geom")
guide_gengrob <- function(...) UseMethod("guide_gengrob")
+
+# Helpers
+matched_aes <- function(layer, guide, defaults) {
+ all <- names(c(layer$mapping, if (layer$inherit.aes) defaults, layer$stat$default_aes))
+ geom <- c(layer$geom$required_aes, names(layer$geom$default_aes))
+ matched <- intersect(intersect(all, geom), names(guide$key))
+ matched <- setdiff(matched, names(layer$geom_params))
+ setdiff(matched, names(layer$aes_params))
+}
diff --git a/R/guides-axis.r b/R/guides-axis.r
index 715bb2a..1dfc05d 100644
--- a/R/guides-axis.r
+++ b/R/guides-axis.r
@@ -15,8 +15,8 @@ guide_axis <- function(at, labels, position = "right", theme) {
one <- unit(1, "npc")
label_render <- switch(position,
- top = , bottom = "axis.text.x",
- left = , right = "axis.text.y"
+ top = "axis.text.x.top", bottom = "axis.text.x",
+ left = "axis.text.y", right = "axis.text.y.right"
)
label_x <- switch(position,
diff --git a/R/hexbin.R b/R/hexbin.R
index 804c10e..d92b9b6 100644
--- a/R/hexbin.R
+++ b/R/hexbin.R
@@ -13,6 +13,10 @@ hex_bounds <- function(x, binwidth) {
}
hexBinSummarise <- function(x, y, z, binwidth, fun = mean, fun.args = list(), drop = TRUE) {
+ if (length(binwidth) == 1) {
+ binwidth <- rep(binwidth, 2)
+ }
+
# Convert binwidths into bounds + nbins
xbnds <- hex_bounds(x, binwidth[1])
xbins <- diff(xbnds) / binwidth[1]
diff --git a/R/facet-labels.r b/R/labeller.r
similarity index 92%
rename from R/facet-labels.r
rename to R/labeller.r
index b75e812..0a1c8a4 100644
--- a/R/facet-labels.r
+++ b/R/labeller.r
@@ -1,4 +1,4 @@
-#' Labeller functions
+#' Useful labeller functions
#'
#' Labeller functions are in charge of formatting the strip labels of
#' facet grids and wraps. Most of them accept a \code{multi_line}
@@ -65,7 +65,6 @@
#' @family facet
#' @seealso \code{\link{labeller}()}, \code{\link{as_labeller}()},
#' \code{\link{label_bquote}()}
-#' @name labellers
#' @examples
#' mtcars$cyl2 <- factor(mtcars$cyl, labels = c("alpha", "beta", "gamma"))
#' p <- ggplot(mtcars, aes(wt, mpg)) + geom_point()
@@ -87,6 +86,7 @@
#' p + facet_grid(. ~ cyl2, labeller = label_parsed)
#' p + facet_wrap(~vs + cyl2, labeller = label_parsed)
#' }
+#' @name labellers
NULL
collapse_labels_lines <- function(labels) {
@@ -178,11 +178,12 @@ find_names <- function(expr) {
}
}
-#' Backquoted labeller
+#' Label with mathematical expressions
#'
-#' \code{\link{label_bquote}()} offers a flexible way of labelling
+#' \code{label_bquote()} offers a flexible way of labelling
#' facet rows or columns with plotmath expressions. Backquoted
#' variables will be replaced with their value in the facet.
+#'
#' @param rows Backquoted labelling expression for rows.
#' @param cols Backquoted labelling expression for columns.
#' @param default Default labeller function for the rows or the
@@ -281,6 +282,7 @@ resolve_labeller <- function(rows, cols, labels) {
#' @param default Default labeller to process the labels produced by
#' lookup tables or modified by non-labeller functions.
#' @seealso \code{\link{labeller}()}, \link{labellers}
+#' @keywords internal
#' @export
#' @examples
#' p <- ggplot(mtcars, aes(disp, drat)) + geom_point()
@@ -322,7 +324,7 @@ as_labeller <- function(x, default = label_value, multi_line = TRUE) {
structure(fun, class = "labeller")
}
-#' Generic labeller function for facets
+#' Construct labelling specification
#'
#' This function makes it easy to assign different labellers to
#' different factors. The labeller can be a function or it can be a
@@ -472,50 +474,53 @@ labeller <- function(..., .rows = NULL, .cols = NULL,
}
-build_strip <- function(panel, label_df, labeller, theme, side = "right", switch = NULL) {
- side <- match.arg(side, c("top", "left", "bottom", "right"))
- horizontal <- side %in% c("top", "bottom")
+build_strip <- function(label_df, labeller, theme, horizontal) {
labeller <- match.fun(labeller)
# No labelling data, so return empty row/col
if (empty(label_df)) {
- if (horizontal) {
- widths <- unit(rep(0, max(panel$layout$COL)), "null")
- return(gtable_row_spacer(widths))
+ return(if (horizontal) {
+ list(top = NULL, bottom = NULL)
} else {
- heights <- unit(rep(0, max(panel$layout$ROW)), "null")
- return(gtable_col_spacer(heights))
- }
+ list(left = NULL, right = NULL)
+ })
}
# Create matrix of labels
labels <- lapply(labeller(label_df), cbind)
labels <- do.call("cbind", labels)
- # Display the mirror of the y strip labels if switched
- if (!is.null(switch) && switch %in% c("both", "y")) {
- theme$strip.text.y$angle <- adjust_angle(theme$strip.text.y$angle)
- }
-
- # Render as grobs
- grobs <- apply(labels, c(1, 2), ggstrip, theme = theme,
- horizontal = horizontal)
-
- # Create layout
- name <- paste("strip", side, sep = "-")
if (horizontal) {
- # Each row is as high as the highest and as a wide as the panel
- row_height <- function(row) max(plyr::laply(row, height_cm))
- grobs <- t(grobs)
- heights <- unit(apply(grobs, 1, row_height), "cm")
- widths <- unit(rep(1, ncol(grobs)), "null")
+ grobs <- apply(labels, c(1, 2), ggstrip, theme = theme,
+ horizontal = horizontal)
+ heights <- unit(apply(grobs, 2, max_height), "cm")
+ grobs <- apply(grobs, 1, function(strips) {
+ gtable_matrix("strip", matrix(strips, ncol = 1), unit(1, "null"), heights, clip = "on")
+ })
+ list(
+ top = grobs,
+ bottom = grobs
+ )
} else {
- # Each row is wide as the widest and as high as the panel
- col_width <- function(col) max(plyr::laply(col, width_cm))
- widths <- unit(apply(grobs, 2, col_width), "cm")
- heights <- unit(rep(1, nrow(grobs)), "null")
+ grobs_right <- apply(labels, c(1, 2), ggstrip, theme = theme,
+ horizontal = horizontal)
+ grobs_right <- grobs_right[, rev(seq_len(ncol(grobs_right))), drop = FALSE]
+ widths <- unit(apply(grobs_right, 2, max_width), "cm")
+ grobs_right <- apply(grobs_right, 1, function(strips) {
+ gtable_matrix("strip", matrix(strips, nrow = 1), widths, unit(1, "null"), clip = "on")
+ })
+ theme$strip.text.y$angle <- adjust_angle(theme$strip.text.y$angle)
+ grobs_left <- apply(labels, c(1, 2), ggstrip, theme = theme,
+ horizontal = horizontal)
+ widths <- unit(apply(grobs_left, 2, max_width), "cm")
+ grobs_left <- apply(grobs_left, 1, function(strips) {
+ gtable_matrix("strip", matrix(strips, nrow = 1), widths, unit(1, "null"), clip = "off")
+ })
+ list(
+ left = grobs_left,
+ right = grobs_right
+ )
}
- gtable_matrix(name, grobs, heights = heights, widths = widths)
}
# Grob for strip labels
diff --git a/R/labels.r b/R/labels.r
index 1808993..adadc15 100644
--- a/R/labels.r
+++ b/R/labels.r
@@ -2,6 +2,7 @@
#'
#' @param p plot to modify
#' @param labels named list of new labels
+#' @keywords internal
#' @export
#' @examples
#' p <- ggplot(mtcars, aes(mpg, wt)) + geom_point()
@@ -15,30 +16,37 @@ update_labels <- function(p, labels) {
p
}
-#' Change axis labels and legend titles
+#' Modify axis, legend, and plot labels
#'
-#' @param label The text for the axis or plot title.
-#' @param ... a list of new names in the form aesthetic = "new name"
-#' @export
-#' @examples
-#' p <- ggplot(mtcars, aes(mpg, wt)) + geom_point()
-#' p + labs(title = "New plot title")
-#' p + labs(x = "New x label")
-#' p + xlab("New x label")
-#' p + ylab("New y label")
-#' p + ggtitle("New plot title")
+#' Good labels are critical for making your plots accessible to a wider
+#' audience. Ensure the axis and legend labels display the full variable name.
+#' Use the plot \code{title} and \code{subtitle} to explain the main findings.
+#' It's common to use the \code{caption} to provide information about the
+#' data source.
#'
-#' # This should work independently of other functions that modify the
-#' # the scale names
-#' p + ylab("New y label") + ylim(2, 4)
-#' p + ylim(2, 4) + ylab("New y label")
+#' You can also set axis and legend labels in the individual scales (using
+#' the first argument, the \code{name}. I recommend doing that if you're
+#' changing other scale options.
#'
-#' # The labs function also modifies legend labels
+#' @param label The text for the axis, plot title or caption below the plot.
+#' @param subtitle the text for the subtitle for the plot which will be
+#' displayed below the title. Leave \code{NULL} for no subtitle.
+#' @param ... A list of new name-value pairs. The name should either be
+#' an aesthetic, or one of "title", "subtitle", or "caption".
+#' @export
+#' @examples
#' p <- ggplot(mtcars, aes(mpg, wt, colour = cyl)) + geom_point()
#' p + labs(colour = "Cylinders")
+#' p + labs(x = "New x label")
+#'
+#' # The plot title appears at the top-left, with the subtitle
+#' # display in smaller text underneath it
+#' p + labs(title = "New plot title")
+#' p + labs(title = "New plot title", subtitle = "A subtitle")
#'
-#' # Can also pass in a list, if that is more convenient
-#' p + labs(list(title = "Title", x = "X", y = "Y"))
+#' # The caption appears in the bottom-right, and is often used for
+#' # sources, notes or copyright
+#' p + labs(caption = "(based on data from ...)")
labs <- function(...) {
args <- list(...)
if (is.list(args[[1]])) args <- args[[1]]
@@ -51,15 +59,17 @@ labs <- function(...) {
xlab <- function(label) {
labs(x = label)
}
+
#' @rdname labs
#' @export
ylab <- function(label) {
labs(y = label)
}
+
#' @rdname labs
#' @export
-ggtitle <- function(label) {
- labs(title = label)
+ggtitle <- function(label, subtitle = NULL) {
+ labs(title = label, subtitle = subtitle)
}
# Convert aesthetic mapping into text labels
diff --git a/R/layer.r b/R/layer.r
index 125557c..b4690a9 100644
--- a/R/layer.r
+++ b/R/layer.r
@@ -35,9 +35,13 @@
#' rather than combining with them. This is most useful for helper functions
#' that define both data and aesthetics and shouldn't inherit behaviour from
#' the default plot specification, e.g. \code{\link{borders}}.
+#' @param check.aes,check.param If \code{TRUE}, the default, will check that
+#' supplied parameters and aesthetics are understood by the \code{geom} or
+#' \code{stat}. Use \code{FALSE} to suppress the checks.
#' @param params Additional parameters to the \code{geom} and \code{stat}.
#' @param subset DEPRECATED. An older way of subsetting the dataset used in a
#' layer.
+#' @keywords internal
#' @examples
#' # geom calls are just a short cut for layer
#' ggplot(mpg, aes(displ, hwy)) + geom_point()
@@ -56,7 +60,8 @@
layer <- function(geom = NULL, stat = NULL,
data = NULL, mapping = NULL,
position = NULL, params = list(),
- inherit.aes = TRUE, subset = NULL, show.legend = NA) {
+ inherit.aes = TRUE, check.aes = TRUE, check.param = TRUE,
+ subset = NULL, show.legend = NA) {
if (is.null(geom))
stop("Attempted to create layer with no geom.", call. = FALSE)
if (is.null(stat))
@@ -82,11 +87,11 @@ layer <- function(geom = NULL, stat = NULL,
}
if (is.character(geom))
- geom <- find_subclass("Geom", geom)
+ geom <- find_subclass("Geom", geom, parent.frame())
if (is.character(stat))
- stat <- find_subclass("Stat", stat)
+ stat <- find_subclass("Stat", stat, parent.frame())
if (is.character(position))
- position <- find_subclass("Position", position)
+ position <- find_subclass("Position", position, parent.frame())
# Special case for na.rm parameter needed by all layers
if (is.null(params$na.rm)) {
@@ -100,11 +105,28 @@ layer <- function(geom = NULL, stat = NULL,
stat_params <- params[intersect(names(params), stat$parameters(TRUE))]
all <- c(geom$parameters(TRUE), stat$parameters(TRUE), geom$aesthetics())
- extra <- setdiff(names(params), all)
- if (length(extra) > 0) {
- stop("Unknown parameters: ", paste(extra, collapse = ", "), call. = FALSE)
+
+ # Warn about extra params and aesthetics
+ extra_param <- setdiff(names(params), all)
+ if (check.param && length(extra_param) > 0) {
+ warning(
+ "Ignoring unknown parameters: ", paste(extra_param, collapse = ", "),
+ call. = FALSE,
+ immediate. = TRUE
+ )
+ }
+
+ extra_aes <- setdiff(names(mapping), c(geom$aesthetics(), stat$aesthetics()))
+ if (check.aes && length(extra_aes) > 0) {
+ warning(
+ "Ignoring unknown aesthetics: ", paste(extra_aes, collapse = ", "),
+ call. = FALSE,
+ immediate. = TRUE
+ )
}
+
+
ggproto("LayerInstance", Layer,
geom = geom,
geom_params = geom_params,
@@ -209,13 +231,13 @@ Layer <- ggproto("Layer", NULL,
evaled
},
- compute_statistic = function(self, data, panel) {
+ compute_statistic = function(self, data, layout) {
if (empty(data))
return(data.frame())
params <- self$stat$setup_params(data, self$stat_params)
data <- self$stat$setup_data(data, params)
- self$stat$compute_layer(data, params, panel)
+ self$stat$compute_layer(data, params, layout)
},
map_statistic = function(self, data, plot) {
@@ -260,13 +282,13 @@ Layer <- ggproto("Layer", NULL,
data
},
- compute_position = function(self, data, panel) {
+ compute_position = function(self, data, layout) {
if (empty(data)) return(data.frame())
params <- self$position$setup_params(data)
data <- self$position$setup_data(data, params)
- self$position$compute_layer(data, params, panel)
+ self$position$compute_layer(data, params, layout)
},
compute_geom_2 = function(self, data) {
@@ -276,28 +298,31 @@ Layer <- ggproto("Layer", NULL,
self$geom$use_defaults(data, self$aes_params)
},
- draw_geom = function(self, data, panel, coord) {
+ finish_statistics = function(self, data) {
+ self$stat$finish_layer(data, self$stat_params)
+ },
+
+ draw_geom = function(self, data, layout, coord) {
if (empty(data)) {
- n <- nrow(panel$layout)
+ n <- nrow(layout$panel_layout)
return(rep(list(zeroGrob()), n))
}
data <- self$geom$handle_na(data, self$geom_params)
- self$geom$draw_layer(data, self$geom_params, panel, coord)
+ self$geom$draw_layer(data, self$geom_params, layout, coord)
}
)
is.layer <- function(x) inherits(x, "Layer")
-find_subclass <- function(super, class) {
+find_subclass <- function(super, class, env) {
name <- paste0(super, camelize(class, first = TRUE))
- if (!exists(name)) {
- stop("No ", tolower(super), " called ", name, ".", call. = FALSE)
- }
+ obj <- find_global(name, env = env)
- obj <- get(name)
- if (!inherits(obj, super)) {
+ if (is.null(name)) {
+ stop("No ", tolower(super), " called ", name, ".", call. = FALSE)
+ } else if (!inherits(obj, super)) {
stop("Found object is not a ", tolower(super), ".", call. = FALSE)
}
diff --git a/R/layout.R b/R/layout.R
new file mode 100644
index 0000000..8c20a52
--- /dev/null
+++ b/R/layout.R
@@ -0,0 +1,214 @@
+create_layout <- function(facet) {
+ ggproto(NULL, Layout, facet = facet)
+}
+
+Layout <- ggproto("Layout", NULL,
+ facet = NULL,
+ panel_layout = NULL,
+ panel_scales = NULL,
+ panel_ranges = NULL,
+
+ setup = function(self, data, plot_data, plot_env, plot_coord) {
+ data <- c(list(plot_data), data)
+ self$facet$params <- utils::modifyList(
+ self$facet$setup_params(data, self$facet$params),
+ list(plot_env = plot_env)
+ )
+ data <- self$facet$setup_data(data, self$facet$params)
+ self$panel_layout <- self$facet$train(data)
+ if (!all(c("PANEL", "SCALE_X", "SCALE_Y") %in% names(self$panel_layout))) {
+ stop("Facet layout has bad format. It must contains the columns 'PANEL', 'SCALE_X', and 'SCALE_Y'", call. = FALSE)
+ }
+ # Special case of CoordFlip - switch the layout scales
+ if (inherits(plot_coord, "CoordFlip")) {
+ self$panel_layout[, c("SCALE_X", "SCALE_Y")] <- self$panel_layout[, c("SCALE_Y", "SCALE_X"), drop = FALSE]
+ }
+ data[-1]
+ },
+
+ map = function(self, data) {
+ lapply(data, function(data) {
+ self$facet$map(data, self$panel_layout)
+ })
+ },
+
+ render = function(self, panels, data, coord, theme, labels) {
+ below <- self$facet$render_back(data, self$panel_layout, self$panel_scales$x, self$panel_scales$y, theme)
+ above <- self$facet$render_front(data, self$panel_layout, self$panel_scales$x, self$panel_scales$y, theme)
+
+ panels <- lapply(seq_along(panels[[1]]), function(i) {
+ fg <- coord$render_fg(self$panel_ranges[[i]], theme)
+ bg <- coord$render_bg(self$panel_ranges[[i]], theme)
+
+ panel <- lapply(panels, `[[`, i)
+ panel <- c(below[i], panel, above[i])
+
+ if (theme$panel.ontop) {
+ panel <- c(panel, list(bg), list(fg))
+ } else {
+ panel <- c(list(bg), panel, list(fg))
+ }
+
+ ggname(paste("panel", i, sep = "-"),
+ gTree(children = do.call("gList", panel)))
+ })
+ labels <- coord$labels(list(
+ x = self$xlabel(labels),
+ y = self$ylabel(labels)
+ ))
+ labels <- self$render_labels(labels, theme)
+ self$facet$render_panels(panels, self$panel_layout, self$panel_scales$x,
+ self$panel_scales$y, self$panel_ranges, coord, data, theme, labels)
+ },
+
+ train_position = function(self, data, x_scale, y_scale) {
+ # Initialise scales if needed, and possible.
+ layout <- self$panel_layout
+ if (is.null(self$panel_scales$x)) {
+ self$panel_scales$x <- self$facet$init_scales(layout, x_scale = x_scale,
+ params = self$facet$params)$x
+ }
+ if (is.null(self$panel_scales$y)) {
+ self$panel_scales$y <- self$facet$init_scales(layout, y_scale = y_scale,
+ params = self$facet$params)$y
+ }
+
+ self$facet$train_positions(self$panel_scales$x, self$panel_scales$y, layout, data)
+ },
+
+ reset_scales = function(self) {
+ if (!self$facet$shrink) return()
+ lapply(self$panel_scales$x, function(s) s$reset())
+ lapply(self$panel_scales$y, function(s) s$reset())
+ invisible()
+ },
+
+ map_position = function(self, data) {
+ layout <- self$panel_layout
+
+ lapply(data, function(layer_data) {
+ match_id <- match(layer_data$PANEL, layout$PANEL)
+
+ # Loop through each variable, mapping across each scale, then joining
+ # back together
+ x_vars <- intersect(self$panel_scales$x[[1]]$aesthetics, names(layer_data))
+ names(x_vars) <- x_vars
+ SCALE_X <- layout$SCALE_X[match_id]
+ new_x <- scale_apply(layer_data, x_vars, "map", SCALE_X, self$panel_scales$x)
+ layer_data[, x_vars] <- new_x
+
+ y_vars <- intersect(self$panel_scales$y[[1]]$aesthetics, names(layer_data))
+ names(y_vars) <- y_vars
+ SCALE_Y <- layout$SCALE_Y[match_id]
+ new_y <- scale_apply(layer_data, y_vars, "map", SCALE_Y, self$panel_scales$y)
+ layer_data[, y_vars] <- new_y
+
+ layer_data
+ })
+ },
+
+ finish_data = function(self, data) {
+ lapply(data, function(layer_data) {
+ self$facet$finish_data(layer_data, self$panel_layout, self$panel_scales$x,
+ self$panel_scales$y, self$facet$params)
+ })
+ },
+
+ get_scales = function(self, i) {
+ this_panel <- self$panel_layout[self$panel_layout$PANEL == i, ]
+
+ list(
+ x = self$panel_scales$x[[this_panel$SCALE_X]],
+ y = self$panel_scales$y[[this_panel$SCALE_Y]]
+ )
+ },
+
+ train_ranges = function(self, coord) {
+ compute_range <- function(ix, iy) {
+ # TODO: change coord_train method to take individual x and y scales
+ coord$train(list(x = self$panel_scales$x[[ix]], y = self$panel_scales$y[[iy]]))
+ }
+ # Switch position of all scales if CoordFlip
+ if (inherits(coord, "CoordFlip") || (inherits(coord, "CoordPolar") && coord$theta == "y")) {
+ lapply(self$panel_scales$x, function(scale) {
+ scale$position <- if (scale$position == "top") "bottom" else "top"
+ })
+ lapply(self$panel_scales$y, function(scale) {
+ scale$position <- if (scale$position == "left") "right" else "left"
+ })
+ }
+ self$panel_ranges <- Map(compute_range, self$panel_layout$SCALE_X, self$panel_layout$SCALE_Y)
+ },
+
+ xlabel = function(self, labels) {
+ primary <- self$panel_scales$x[[1]]$name %|W|% labels$x
+ primary <- self$panel_scales$x[[1]]$make_title(primary)
+ secondary <- if (is.null(self$panel_scales$x[[1]]$secondary.axis)) {
+ waiver()
+ } else {
+ self$panel_scales$x[[1]]$sec_name()
+ } %|W|% labels$sec.x
+ if (is.derived(secondary)) secondary <- primary
+ secondary <- self$panel_scales$x[[1]]$make_sec_title(secondary)
+ list(primary = primary, secondary = secondary)[self$panel_scales$x[[1]]$axis_order()]
+ },
+
+ ylabel = function(self, labels) {
+ primary <- self$panel_scales$y[[1]]$name %|W|% labels$y
+ primary <- self$panel_scales$y[[1]]$make_title(primary)
+ secondary <- if (is.null(self$panel_scales$y[[1]]$secondary.axis)) {
+ waiver()
+ } else {
+ self$panel_scales$y[[1]]$sec_name()
+ } %|W|% labels$sec.y
+ if (is.derived(secondary)) secondary <- primary
+ secondary <- self$panel_scales$y[[1]]$make_sec_title(secondary)
+ list(primary = primary, secondary = secondary)[self$panel_scales$y[[1]]$axis_order()]
+ },
+
+ render_labels = function(self, labels, theme) {
+ label_grobs <- lapply(names(labels), function(label) {
+ lapply(c(1, 2), function(i) {
+ modify <- if (i == 2 && label == "y") ".right" else if (i == 1 && label == "x") ".top" else ""
+ if (is.null(labels[[label]][[i]]) || is.waive(labels[[label]][[i]]))
+ return(zeroGrob())
+
+ element_render(
+ theme = theme,
+ element = paste0("axis.title.", label, modify),
+ label = labels[[label]][[i]],
+ expand_x = label == "y",
+ expand_y = label == "x"
+ )
+ })
+ })
+ names(label_grobs) <- names(labels)
+ label_grobs
+ }
+)
+
+
+# Helpers -----------------------------------------------------------------
+
+# Function for applying scale method to multiple variables in a given
+# data set. Implement in such a way to minimize copying and hence maximise
+# speed
+scale_apply <- function(data, vars, method, scale_id, scales) {
+ if (length(vars) == 0) return()
+ if (nrow(data) == 0) return()
+
+ n <- length(scales)
+ if (any(is.na(scale_id))) stop()
+
+ scale_index <- plyr::split_indices(scale_id, n)
+
+ lapply(vars, function(var) {
+ pieces <- lapply(seq_along(scales), function(i) {
+ scales[[i]][[method]](data[[var]][scale_index[[i]]])
+ })
+ # Join pieces back together, if necessary
+ if (!is.null(pieces)) {
+ unlist(pieces)[order(unlist(scale_index))]
+ }
+ })
+}
diff --git a/R/limits.r b/R/limits.r
index 315f442..75812c3 100644
--- a/R/limits.r
+++ b/R/limits.r
@@ -1,34 +1,51 @@
-#' Convenience functions to set the axis limits.
+#' Set scale limits
#'
-#' Observations not in this range will be dropped completely and
-#' not passed to any other layers. If a NA value is substituted for one of the
-#' limits that limit is automatically calculated.
+#' This is a shortcut for supplying the \code{limits} argument to the
+#' individual scales. Note that, by default, any values outside the limits
+#' will be replaced with \code{NA}.
#'
-#' @param ... If numeric, will create a continuous scale, if factor or
-#' character, will create a discrete scale. For \code{lims}, every
-#' argument must be named.
+#' @param ... A name-value pair. The name must be an aesthetic, and the value
+#' must be either a length-2 numeric, a character, a factor, or a date/time.
+#'
+#' A numeric value will create a continuous scale. If the larger value
+#' comes first, the scale will be reversed. You can leave one value as
+#' \code{NA} to compute from the range of the data.
+#'
+#' A character or factor value will create a discrete scale.
+#'
+#' A date-time value will create a continuous date/time scale.
#' @seealso For changing x or y axis limits \strong{without} dropping data
-#' observations, see \code{\link{coord_cartesian}}.
+#' observations, see \code{\link{coord_cartesian}}. To expand the range of
+#' a plot to always include certain values, see \code{\link{expand_limits}}.
#' @export
#' @examples
-#' # xlim
-#' xlim(15, 20)
-#' xlim(20, 15)
-#' xlim(c(10, 20))
-#' xlim("a", "b", "c")
-#'
+#' # Zoom into a specified area
#' ggplot(mtcars, aes(mpg, wt)) +
#' geom_point() +
#' xlim(15, 20)
+#'
+#' # reverse scale
+#' ggplot(mtcars, aes(mpg, wt)) +
+#' geom_point() +
+#' xlim(20, 15)
+#'
#' # with automatic lower limit
#' ggplot(mtcars, aes(mpg, wt)) +
#' geom_point() +
#' xlim(NA, 20)
#'
-#' # Change both xlim and ylim
-#' ggplot(mtcars, aes(mpg, wt)) +
+#' # You can also supply limits that are larger than the data.
+#' # This is useful if you want to match scales across different plots
+#' small <- subset(mtcars, cyl == 4)
+#' big <- subset(mtcars, cyl > 4)
+#'
+#' ggplot(small, aes(mpg, wt, colour = factor(cyl))) +
+#' geom_point() +
+#' lims(colour = c("4", "6", "8"))
+#'
+#' ggplot(big, aes(mpg, wt, colour = factor(cyl))) +
#' geom_point() +
-#' lims(x = c(10, 20), y = c(3, 5))
+#' lims(colour = c("4", "6", "8"))
lims <- function(...) {
args <- list(...)
@@ -104,9 +121,9 @@ limits.POSIXlt <- function(lims, var) {
make_scale("datetime", var, limits = as.POSIXct(lims))
}
-#' Expand the plot limits with data.
+#' Expand the plot limits, using data
#'
-#. Sometimes you may want to ensure limits include a single value, for all
+#' Sometimes you may want to ensure limits include a single value, for all
#' panels or all plots. This function is a thin wrapper around
#' \code{\link{geom_blank}} that makes it easy to add such values.
#'
diff --git a/R/margins.R b/R/margins.R
index 837f964..b61c8de 100644
--- a/R/margins.R
+++ b/R/margins.R
@@ -1,21 +1,14 @@
-#' Define margins.
-#'
-#' This is a convenient function that creates a grid unit object of the
-#' correct length to use for setting margins.
-#'
-#' @export
#' @param t,r,b,l Dimensions of each margin. (To remember order, think trouble).
#' @param unit Default units of dimensions. Defaults to "pt" so it
#' can be most easily scaled with the text.
+#' @rdname element
#' @export
-#' @examples
-#' margin(4)
-#' margin(4, 2)
-#' margin(4, 3, 2, 1)
margin <- function(t = 0, r = 0, b = 0, l = 0, unit = "pt") {
structure(unit(c(t, r, b, l), unit), class = c("margin", "unit"))
}
-
+is.margin <- function(x) {
+ inherits(x, "margin")
+}
margin_height <- function(grob, margins) {
if (is.zero(grob)) return(unit(0, "cm"))
@@ -62,20 +55,26 @@ titleGrob <- function(label, x, y, hjust, vjust, angle = 0, gp = gpar(),
text_grob <- textGrob(label, x, y, hjust = hjust, vjust = vjust,
rot = angle, gp = gp)
+ # The grob dimensions don't include the text descenders, so add on using
+ # a little trigonometry. This is only exactly correct when vjust = 1.
+ descent <- descentDetails(text_grob)
+ text_height <- unit(1, "grobheight", text_grob) + cos(angle / 180 * pi) * descent
+ text_width <- unit(1, "grobwidth", text_grob) + sin(angle / 180 * pi) * descent
+
if (expand_x && expand_y) {
- widths <- unit.c(margin[4], unit(1, "grobwidth", text_grob), margin[2])
- heights <- unit.c(margin[1], unit(1, "grobheight", text_grob), margin[3])
+ widths <- unit.c(margin[4], text_width, margin[2])
+ heights <- unit.c(margin[1], text_height, margin[3])
vp <- viewport(layout = grid.layout(3, 3, heights = heights, widths = widths), gp = gp)
child_vp <- viewport(layout.pos.row = 2, layout.pos.col = 2)
} else if (expand_x) {
- widths <- unit.c(margin[4], unit(1, "grobwidth", text_grob), margin[2])
+ widths <- unit.c(margin[4], text_width, margin[2])
vp <- viewport(layout = grid.layout(1, 3, widths = widths), gp = gp)
child_vp <- viewport(layout.pos.col = 2)
heights <- unit(1, "null")
} else if (expand_y) {
- heights <- unit.c(margin[1], unit(1, "grobheight", text_grob), margin[3])
+ heights <- unit.c(margin[1], text_height, margin[3])
vp <- viewport(layout = grid.layout(3, 1, heights = heights), gp = gp)
child_vp <- viewport(layout.pos.row = 2)
diff --git a/R/panel.r b/R/panel.r
deleted file mode 100644
index 1cbf451..0000000
--- a/R/panel.r
+++ /dev/null
@@ -1,185 +0,0 @@
-# Panel object.
-#
-# A panel figures out how data is positioned within a panel of a plot,
-# coordinates information from scales, facets and coords. Eventually all
-# state will move out of facets and coords, and live only in panels and
-# stats, simplifying these data structures to become strategies.
-#
-# Information about a panel is built up progressively over time, which
-# is why the initial object is empty to start with.
-new_panel <- function() {
- structure(list(), class = "panel")
-}
-
-# Learn the layout of panels within a plot.
-#
-# This is determined by the facet, which returns a data frame, than
-# when joined to the data to be plotted tells us which panel it should
-# appear in, where that panel appears in the grid, and what scales it
-# uses.
-#
-# As well as the layout info, this function also adds empty lists in which
-# to house the x and y scales.
-#
-# @param the panel object to train
-# @param the facetting specification
-# @param data a list of data frames (one for each layer), and one for the plot
-# @param plot_data the default data frame
-# @return an updated panel object
-train_layout <- function(panel, facet, data, plot_data) {
- layout <- facet_train_layout(facet, c(list(plot_data), data))
- panel$layout <- layout
- panel$shrink <- facet$shrink
-
- panel
-}
-
-# Map data to find out where it belongs in the plot.
-#
-# Layout map ensures that all layer data has extra copies of data for margins
-# and missing facetting variables, and has a PANEL variable that tells that
-# so it know what panel it belongs to. This is a change from the previous
-# design which added facetting variables directly to the data frame and
-# caused problems when they had names of aesthetics (like colour or group).
-#
-# @param panel a trained panel object
-# @param the facetting specification
-# @param data list of data frames (one for each layer)
-map_layout <- function(panel, facet, data) {
- lapply(data, function(data) {
- facet_map_layout(facet, data, panel$layout)
- })
-}
-
-# Train position scales with data
-#
-# If panel-specific scales are not already present, will clone from
-# the scales provided in the parameter
-#
-# @param panel the panel object to train
-# @param data a list of data frames (one for each layer)
-# @param x_scale x scale for the plot
-# @param y_scale y scale for the plot
-train_position <- function(panel, data, x_scale, y_scale) {
- # Initialise scales if needed, and possible.
- layout <- panel$layout
- if (is.null(panel$x_scales) && !is.null(x_scale)) {
- panel$x_scales <- plyr::rlply(max(layout$SCALE_X), x_scale$clone())
- }
- if (is.null(panel$y_scales) && !is.null(y_scale)) {
- panel$y_scales <- plyr::rlply(max(layout$SCALE_Y), y_scale$clone())
- }
-
- # loop over each layer, training x and y scales in turn
- for (layer_data in data) {
-
- match_id <- match(layer_data$PANEL, layout$PANEL)
-
- if (!is.null(x_scale)) {
- x_vars <- intersect(x_scale$aesthetics, names(layer_data))
- SCALE_X <- layout$SCALE_X[match_id]
-
- scale_apply(layer_data, x_vars, "train", SCALE_X, panel$x_scales)
- }
-
- if (!is.null(y_scale)) {
- y_vars <- intersect(y_scale$aesthetics, names(layer_data))
- SCALE_Y <- layout$SCALE_Y[match_id]
-
- scale_apply(layer_data, y_vars, "train", SCALE_Y, panel$y_scales)
- }
- }
-
- panel
-}
-
-
-reset_scales <- function(panel) {
- if (!panel$shrink) return()
- lapply(panel$x_scales, function(s) s$reset())
- lapply(panel$y_scales, function(s) s$reset())
- invisible()
-}
-
-# Map data with scales.
-#
-# This operation must be idempotent because it is applied twice: both before
-# and after statistical transformation.
-#
-# @param data a list of data frames (one for each layer)
-map_position <- function(panel, data, x_scale, y_scale) {
- layout <- panel$layout
-
- lapply(data, function(layer_data) {
- match_id <- match(layer_data$PANEL, layout$PANEL)
-
- # Loop through each variable, mapping across each scale, then joining
- # back together
- x_vars <- intersect(x_scale$aesthetics, names(layer_data))
- names(x_vars) <- x_vars
- SCALE_X <- layout$SCALE_X[match_id]
- new_x <- scale_apply(layer_data, x_vars, "map", SCALE_X, panel$x_scales)
- layer_data[, x_vars] <- new_x
-
- y_vars <- intersect(y_scale$aesthetics, names(layer_data))
- names(y_vars) <- y_vars
- SCALE_Y <- layout$SCALE_Y[match_id]
- new_y <- scale_apply(layer_data, y_vars, "map", SCALE_Y, panel$y_scales)
-
- layer_data[, y_vars] <- new_y
- layer_data
- })
-}
-
-# Function for applying scale method to multiple variables in a given
-# data set. Implement in such a way to minimize copying and hence maximise
-# speed
-scale_apply <- function(data, vars, method, scale_id, scales) {
- if (length(vars) == 0) return()
- if (nrow(data) == 0) return()
-
- n <- length(scales)
- if (any(is.na(scale_id))) stop()
-
- scale_index <- plyr::split_indices(scale_id, n)
-
- lapply(vars, function(var) {
- pieces <- lapply(seq_along(scales), function(i) {
- scales[[i]][[method]](data[[var]][scale_index[[i]]])
- })
- # Join pieces back together, if necessary
- if (!is.null(pieces)) {
- unlist(pieces)[order(unlist(scale_index))]
- }
- })
-}
-
-
-panel_scales <- function(panel, i) {
- this_panel <- panel$layout[panel$layout$PANEL == i, ]
-
- list(
- x = panel$x_scales[[this_panel$SCALE_X]],
- y = panel$y_scales[[this_panel$SCALE_Y]]
- )
-}
-
-# Compute ranges and dimensions of each panel, using the coord.
-train_ranges <- function(panel, coord) {
- compute_range <- function(ix, iy) {
- # TODO: change coord_train method to take individual x and y scales
- coord$train(list(x = panel$x_scales[[ix]], y = panel$y_scales[[iy]]))
- }
-
- panel$ranges <- Map(compute_range,
- panel$layout$SCALE_X, panel$layout$SCALE_Y)
- panel
-}
-
-xlabel <- function(panel, labels) {
- panel$x_scales[[1]]$name %|W|% labels$x
-}
-
-ylabel <- function(panel, labels) {
- panel$y_scales[[1]]$name %|W|% labels$y
-}
diff --git a/R/plot-build.r b/R/plot-build.r
index 17185b0..7451a33 100644
--- a/R/plot-build.r
+++ b/R/plot-build.r
@@ -37,9 +37,9 @@ ggplot_build <- function(plot) {
# Initialise panels, add extra data for margins & missing facetting
# variables, and add on a PANEL variable to data
- panel <- new_panel()
- panel <- train_layout(panel, plot$facet, layer_data, plot$data)
- data <- map_layout(panel, plot$facet, layer_data)
+ layout <- create_layout(plot$facet)
+ data <- layout$setup(layer_data, plot$data, plot$plot_env, plot$coordinates)
+ data <- layout$map(data)
# Compute aesthetics to produce data with generalised variable names
data <- by_layer(function(l, d) l$compute_aesthetics(d, plot))
@@ -52,11 +52,11 @@ ggplot_build <- function(plot) {
scale_x <- function() scales$get_scales("x")
scale_y <- function() scales$get_scales("y")
- panel <- train_position(panel, data, scale_x(), scale_y())
- data <- map_position(panel, data, scale_x(), scale_y())
+ layout$train_position(data, scale_x(), scale_y())
+ data <- layout$map_position(data)
# Apply and map statistics
- data <- by_layer(function(l, d) l$compute_statistic(d, panel))
+ data <- by_layer(function(l, d) l$compute_statistic(d, layout))
data <- by_layer(function(l, d) l$map_statistic(d, plot))
# Make sure missing (but required) aesthetics are added
@@ -66,14 +66,14 @@ ggplot_build <- function(plot) {
data <- by_layer(function(l, d) l$compute_geom_1(d))
# Apply position adjustments
- data <- by_layer(function(l, d) l$compute_position(d, panel))
+ data <- by_layer(function(l, d) l$compute_position(d, layout))
# Reset position scales, then re-train and map. This ensures that facets
# have control over the range of a plot: is it generated from what's
# displayed, or does it include the range of underlying data
- reset_scales(panel)
- panel <- train_position(panel, data, scale_x(), scale_y())
- data <- map_position(panel, data, scale_x(), scale_y())
+ layout$reset_scales()
+ layout$train_position(data, scale_x(), scale_y())
+ data <- layout$map_position(data)
# Train and map non-position scales
npscales <- scales$non_position_scales()
@@ -83,12 +83,18 @@ ggplot_build <- function(plot) {
}
# Train coordinate system
- panel <- train_ranges(panel, plot$coordinates)
+ layout$train_ranges(plot$coordinates)
# Fill in defaults etc.
data <- by_layer(function(l, d) l$compute_geom_2(d))
- list(data = data, panel = panel, plot = plot)
+ # Let layer stat have a final say before rendering
+ data <- by_layer(function(l, d) l$finish_statistics(d))
+
+ # Let Layout modify data before rendering
+ data <- layout$finish_data(data)
+
+ list(data = data, layout = layout, plot = plot)
}
#' @export
@@ -102,12 +108,12 @@ layer_data <- function(plot, i = 1L) {
layer_scales <- function(plot, i = 1L, j = 1L) {
b <- ggplot_build(plot)
- layout <- b$panel$layout
+ layout <- b$layout$panel_layout
selected <- layout[layout$ROW == i & layout$COL == j, , drop = FALSE]
list(
- x = b$panel$x_scales[[selected$SCALE_X]],
- y = b$panel$y_scales[[selected$SCALE_Y]]
+ x = b$layout$panel_scales$x[[selected$SCALE_X]],
+ y = b$layout$panel_scales$y[[selected$SCALE_Y]]
)
}
@@ -116,7 +122,7 @@ layer_scales <- function(plot, i = 1L, j = 1L) {
layer_grob <- function(plot, i = 1L) {
b <- ggplot_build(plot)
- b$plot$layers[[i]]$draw_geom(b$data[[i]], b$panel, b$plot$coordinates)
+ b$plot$layers[[i]]$draw_geom(b$data[[i]], b$layout, b$plot$coordinates)
}
#' Build a plot with all the usual bits and pieces.
@@ -137,47 +143,14 @@ layer_grob <- function(plot, i = 1L) {
#' @export
ggplot_gtable <- function(data) {
plot <- data$plot
- panel <- data$panel
+ layout <- data$layout
data <- data$data
theme <- plot_theme(plot)
- geom_grobs <- Map(function(l, d) l$draw_geom(d, panel, plot$coordinates),
+ geom_grobs <- Map(function(l, d) l$draw_geom(d, layout, plot$coordinates),
plot$layers, data)
- plot_table <- facet_render(plot$facet, panel, plot$coordinates,
- theme, geom_grobs)
-
- # Axis labels
- labels <- plot$coordinates$labels(list(
- x = xlabel(panel, plot$labels),
- y = ylabel(panel, plot$labels)
- ))
- xlabel <- element_render(theme, "axis.title.x", labels$x, expand_y = TRUE)
- ylabel <- element_render(theme, "axis.title.y", labels$y, expand_x = TRUE)
-
- # helper function return the position of panels in plot_table
- find_panel <- function(table) {
- layout <- table$layout
- panels <- layout[grepl("^panel", layout$name), , drop = FALSE]
-
- data.frame(
- t = min(panels$t),
- r = max(panels$r),
- b = max(panels$b),
- l = min(panels$l)
- )
- }
- panel_dim <- find_panel(plot_table)
-
- xlab_height <- grobHeight(xlabel)
- plot_table <- gtable_add_rows(plot_table, xlab_height)
- plot_table <- gtable_add_grob(plot_table, xlabel, name = "xlab",
- l = panel_dim$l, r = panel_dim$r, t = -1, clip = "off")
-
- ylab_width <- grobWidth(ylabel)
- plot_table <- gtable_add_cols(plot_table, ylab_width, pos = 0)
- plot_table <- gtable_add_grob(plot_table, ylabel, name = "ylab",
- l = 1, b = panel_dim$b, t = panel_dim$t, clip = "off")
+ plot_table <- layout$render(geom_grobs, data, plot$coordinates, theme, plot$labels)
# Legends
position <- theme$legend.position
@@ -195,8 +168,8 @@ ggplot_gtable <- function(data) {
position <- "none"
} else {
# these are a bad hack, since it modifies the contents of viewpoint directly...
- legend_width <- gtable_width(legend_box) + theme$legend.margin
- legend_height <- gtable_height(legend_box) + theme$legend.margin
+ legend_width <- gtable_width(legend_box)
+ legend_height <- gtable_height(legend_box)
# Set the justification of the legend box
# First value is xjust, second value is yjust
@@ -216,6 +189,10 @@ ggplot_gtable <- function(data) {
# x and y are adjusted using justification of legend box (i.e., theme$legend.justification)
legend_box <- editGrob(legend_box,
vp = viewport(x = xjust, y = yjust, just = c(xjust, yjust)))
+ legend_box <- gtable_add_rows(legend_box, unit(yjust, 'null'))
+ legend_box <- gtable_add_rows(legend_box, unit(1 - yjust, 'null'), 0)
+ legend_box <- gtable_add_cols(legend_box, unit(xjust, 'null'), 0)
+ legend_box <- gtable_add_cols(legend_box, unit(1 - xjust, 'null'))
}
}
@@ -223,19 +200,24 @@ ggplot_gtable <- function(data) {
# for align-to-device, use this:
# panel_dim <- summarise(plot_table$layout, t = min(t), r = max(r), b = max(b), l = min(l))
+ theme$legend.box.spacing <- theme$legend.box.spacing %||% unit(0.2, 'cm')
if (position == "left") {
+ plot_table <- gtable_add_cols(plot_table, theme$legend.box.spacing, pos = 0)
plot_table <- gtable_add_cols(plot_table, legend_width, pos = 0)
plot_table <- gtable_add_grob(plot_table, legend_box, clip = "off",
t = panel_dim$t, b = panel_dim$b, l = 1, r = 1, name = "guide-box")
} else if (position == "right") {
+ plot_table <- gtable_add_cols(plot_table, theme$legend.box.spacing, pos = -1)
plot_table <- gtable_add_cols(plot_table, legend_width, pos = -1)
plot_table <- gtable_add_grob(plot_table, legend_box, clip = "off",
t = panel_dim$t, b = panel_dim$b, l = -1, r = -1, name = "guide-box")
} else if (position == "bottom") {
+ plot_table <- gtable_add_rows(plot_table, theme$legend.box.spacing, pos = -1)
plot_table <- gtable_add_rows(plot_table, legend_height, pos = -1)
plot_table <- gtable_add_grob(plot_table, legend_box, clip = "off",
t = -1, b = -1, l = panel_dim$l, r = panel_dim$r, name = "guide-box")
} else if (position == "top") {
+ plot_table <- gtable_add_rows(plot_table, theme$legend.box.spacing, pos = 0)
plot_table <- gtable_add_rows(plot_table, legend_height, pos = 0)
plot_table <- gtable_add_grob(plot_table, legend_box, clip = "off",
t = 1, b = 1, l = panel_dim$l, r = panel_dim$r, name = "guide-box")
@@ -250,13 +232,29 @@ ggplot_gtable <- function(data) {
title <- element_render(theme, "plot.title", plot$labels$title, expand_y = TRUE)
title_height <- grobHeight(title)
+ # Subtitle
+ subtitle <- element_render(theme, "plot.subtitle", plot$labels$subtitle, expand_y = TRUE)
+ subtitle_height <- grobHeight(subtitle)
+
+ # whole plot annotation
+ caption <- element_render(theme, "plot.caption", plot$labels$caption, expand_y = TRUE)
+ caption_height <- grobHeight(caption)
+
pans <- plot_table$layout[grepl("^panel", plot_table$layout$name), ,
drop = FALSE]
+ plot_table <- gtable_add_rows(plot_table, subtitle_height, pos = 0)
+ plot_table <- gtable_add_grob(plot_table, subtitle, name = "subtitle",
+ t = 1, b = 1, l = min(pans$l), r = max(pans$r), clip = "off")
+
plot_table <- gtable_add_rows(plot_table, title_height, pos = 0)
plot_table <- gtable_add_grob(plot_table, title, name = "title",
t = 1, b = 1, l = min(pans$l), r = max(pans$r), clip = "off")
+ plot_table <- gtable_add_rows(plot_table, caption_height, pos = -1)
+ plot_table <- gtable_add_grob(plot_table, caption, name = "caption",
+ t = -1, b = -1, l = min(pans$l), r = max(pans$r), clip = "off")
+
# Margins
plot_table <- gtable_add_rows(plot_table, theme$plot.margin[1], pos = 0)
plot_table <- gtable_add_cols(plot_table, theme$plot.margin[2])
diff --git a/R/plot-construction.r b/R/plot-construction.r
index dc10caf..2b87a0b 100644
--- a/R/plot-construction.r
+++ b/R/plot-construction.r
@@ -1,66 +1,44 @@
-#' Add a new component to a ggplot or theme object.
+#' Add components to a plot
#'
-#' This operator allows you to add objects to a ggplot or theme object.
+#' \code{+} is the key to constructing sophisticated ggplot2 graphics. It
+#' allows you to start simple, then get more and more complex, checking your
+#' work at each step.
#'
-#' If the first object is an object of class \code{ggplot}, you can add
-#' the following types of objects, and it will return a modified ggplot
-#' object.
+#' @section What can you add?:
+#' You can add any of the following types of objects:
#'
#' \itemize{
-#' \item \code{data.frame}: replace current data.frame
-#' (must use \code{\%+\%})
-#' \item \code{uneval}: replace current aesthetics
-#' \item \code{layer}: add new layer
-#' \item \code{theme}: update plot theme
-#' \item \code{scale}: replace current scale
-#' \item \code{coord}: override current coordinate system
-#' \item \code{facet}: override current coordinate faceting
+#' \item A \code{\link{aes}()} objects replaces the default aesthetics.
+#' \item A layer created by a \code{geom_} or \code{stat_} function adds
+#' new layer.
+#' \item A \code{scale} overrides the existing scale.
+#' \item A \code{\link{theme}} modifies the current theme.
+#' \item A \code{coord} overrides current coordinate system.
+#' \item A \code{facet} specificatio override current faceting.
#' }
#'
-#' If the first object is an object of class \code{theme}, you can add
-#' another theme object. This will return a modified theme object.
+#' To replace the current default data frame, you must use \code{\%+\%},
+#' due to S3 method precedence issues.
#'
-#' For theme objects, the \code{+} operator and the \code{\%+replace\%}
-#' can be used to modify elements in themes.
+#' You can also supply a list, in which case each element of the list will
+#' be added in turn.
#'
-#' The \code{+} operator updates the elements of e1 that differ from
-#' elements specified (not NULL) in e2.
-#' Thus this operator can be used to incrementally add or modify attributes
-#' of a ggplot theme.
-#'
-#' In contrast, the \code{\%+replace\%} operator replaces the
-#' entire element; any element of a theme not specified in e2 will not be
-#' present in the resulting theme (i.e. NULL).
-#' Thus this operator can be used to overwrite an entire theme.
-#'
-#' @examples
-#' ### Adding objects to a ggplot object
-#' p <- ggplot(mtcars, aes(wt, mpg, colour = disp)) +
-#' geom_point()
-#'
-#' p
-#' p + coord_cartesian(ylim = c(0, 40))
-#' p + scale_colour_continuous(breaks = c(100, 300))
-#' p + guides(colour = "colourbar")
-#'
-#' # Use a different data frame
-#' m <- mtcars[1:10, ]
-#' p %+% m
-#'
-#' ### Adding objects to a theme object
-#' # Compare these results of adding theme objects to other theme objects
-#' add_el <- theme_grey() + theme(text = element_text(family = "Times"))
-#' rep_el <- theme_grey() %+replace% theme(text = element_text(family = "Times"))
-#'
-#' add_el$text
-#' rep_el$text
-#'
-#' @param e1 An object of class \code{ggplot} or \code{theme}
-#' @param e2 A component to add to \code{e1}
-#' @export
+#' @param e1 An object of class \code{\link{ggplot}} or a \code{\link{theme}}.
+#' @param e2 A plot component, as described below.
#' @seealso \code{\link{theme}}
+#' @export
#' @method + gg
#' @rdname gg-add
+#' @examples
+#' base <- ggplot(mpg, aes(displ, hwy)) + geom_point()
+#' base + geom_smooth()
+#'
+#' # To override the data, you must use %+%
+#' base %+% subset(mpg, fl == "p")
+#'
+#' # Alternatively, you can add multiple components with a list.
+#' # This can be useful to return from a function.
+#' base + list(subset(mpg, fl == "p"), geom_smooth())
"+.gg" <- function(e1, e2) {
# Get the name of what was passed in as e2, and pass along so that it
# can be displayed in error messages
@@ -75,7 +53,6 @@
#' @export
"%+%" <- `+.gg`
-
add_ggplot <- function(p, object, objectname) {
if (is.null(object)) return(p)
@@ -92,6 +69,8 @@ add_ggplot <- function(p, object, objectname) {
p <- update_guides(p, object)
} else if (inherits(object, "uneval")) {
p$mapping <- defaults(object, p$mapping)
+ # defaults() doesn't copy class, so copy it.
+ class(p$mapping) <- class(object)
labels <- lapply(object, deparse)
names(labels) <- names(object)
@@ -104,7 +83,7 @@ add_ggplot <- function(p, object, objectname) {
p
} else if (is.list(object)) {
for (o in object) {
- p <- p + o
+ p <- p %+% o
}
} else if (is.layer(object)) {
p$layers <- append(p$layers, object)
diff --git a/R/plot-last.r b/R/plot-last.r
index 9e167ec..d7ef178 100644
--- a/R/plot-last.r
+++ b/R/plot-last.r
@@ -16,4 +16,5 @@ set_last_plot <- function(value) .store$set(value)
#'
#' @seealso \code{\link{ggsave}}
#' @export
+#' @keywords internal
last_plot <- function() .store$get()
diff --git a/R/plot.r b/R/plot.r
index e3ad241..baa43cf 100644
--- a/R/plot.r
+++ b/R/plot.r
@@ -1,23 +1,20 @@
-#' Create a new ggplot plot.
+#' Create a new ggplot
#'
#' \code{ggplot()} initializes a ggplot object. It can be used to
#' declare the input data frame for a graphic and to specify the
#' set of plot aesthetics intended to be common throughout all
#' subsequent layers unless specifically overridden.
#'
-#' \code{ggplot()} is typically used to construct a plot
-#' incrementally, using the + operator to add layers to the
-#' existing ggplot object. This is advantageous in that the
-#' code is explicit about which layers are added and the order
-#' in which they are added. For complex graphics with multiple
-#' layers, initialization with \code{ggplot} is recommended.
+#' \code{ggplot()} is used to construct the initial plot object,
+#' and is almost always followed by \code{+} to add component to the
+#' plot. There are three common ways to invoke \code{ggplot}:
+#'
+#' \enumerate{
+#' \item \code{ggplot(df, aes(x, y, <other aesthetics>))}
+#' \item \code{ggplot(df)}
+#' \item \code{ggplot()}
+#' }
#'
-#' There are three common ways to invoke \code{ggplot}:
-#' \itemize{
-#' \item \code{ggplot(df, aes(x, y, <other aesthetics>))}
-#' \item \code{ggplot(df)}
-#' \item \code{ggplot()}
-#' }
#' The first method is recommended if all layers use the same
#' data and the same set of aesthetics, although this method
#' can also be used to add a layer using data from another
@@ -42,51 +39,52 @@
#' to using the environment in which \code{ggplot()} is called.
#' @export
#' @examples
-#' df <- data.frame(gp = factor(rep(letters[1:3], each = 10)),
-#' y = rnorm(30))
-#' # Compute sample mean and standard deviation in each group
+#' # Generate some sample data, then compute mean and standard deviation
+#' # in each group
+#' df <- data.frame(
+#' gp = factor(rep(letters[1:3], each = 10)),
+#' y = rnorm(30)
+#' )
#' ds <- plyr::ddply(df, "gp", plyr::summarise, mean = mean(y), sd = sd(y))
#'
-#' # Declare the data frame and common aesthetics.
-#' # The summary data frame ds is used to plot
-#' # larger red points in a second geom_point() layer.
-#' # If the data = argument is not specified, it uses the
-#' # declared data frame from ggplot(); ditto for the aesthetics.
-#' ggplot(df, aes(x = gp, y = y)) +
-#' geom_point() +
-#' geom_point(data = ds, aes(y = mean),
-#' colour = 'red', size = 3)
+#' # The summary data frame ds is used to plot larger red points on top
+#' # of the raw data. Note that we don't need to supply `data` or `mapping`
+#' # in each layer because the defaults from ggplot() are used.
+#' ggplot(df, aes(gp, y)) +
+#' geom_point() +
+#' geom_point(data = ds, aes(y = mean), colour = 'red', size = 3)
+#'
#' # Same plot as above, declaring only the data frame in ggplot().
#' # Note how the x and y aesthetics must now be declared in
#' # each geom_point() layer.
#' ggplot(df) +
-#' geom_point(aes(x = gp, y = y)) +
-#' geom_point(data = ds, aes(x = gp, y = mean),
-#' colour = 'red', size = 3)
-#' # Set up a skeleton ggplot object and add layers:
+#' geom_point(aes(gp, y)) +
+#' geom_point(data = ds, aes(gp, mean), colour = 'red', size = 3)
+#'
+#' # Alternatively we can fully specify the plot in each layer. This
+#' # is not useful here, but can be more clear when working with complex
+#' # mult-dataset graphics
#' ggplot() +
-#' geom_point(data = df, aes(x = gp, y = y)) +
-#' geom_point(data = ds, aes(x = gp, y = mean),
-#' colour = 'red', size = 3) +
-#' geom_errorbar(data = ds, aes(x = gp, y = mean,
-#' ymin = mean - sd, ymax = mean + sd),
-#' colour = 'red', width = 0.4)
+#' geom_point(data = df, aes(gp, y)) +
+#' geom_point(data = ds, aes(gp, mean), colour = 'red', size = 3) +
+#' geom_errorbar(
+#' data = ds,
+#' aes(gp, mean, ymin = mean - sd, ymax = mean + sd),
+#' colour = 'red',
+#' width = 0.4
+#' )
ggplot <- function(data = NULL, mapping = aes(), ...,
environment = parent.frame()) {
UseMethod("ggplot")
}
#' @export
-#' @rdname ggplot
-#' @usage NULL
ggplot.default <- function(data = NULL, mapping = aes(), ...,
environment = parent.frame()) {
ggplot.data.frame(fortify(data, ...), mapping, environment = environment)
}
#' @export
-#' @rdname ggplot
-#' @usage NULL
ggplot.data.frame <- function(data, mapping = aes(), ...,
environment = parent.frame()) {
if (!missing(mapping) && !inherits(mapping, "uneval")) {
@@ -123,7 +121,12 @@ plot_clone <- function(plot) {
#' @export
is.ggplot <- function(x) inherits(x, "ggplot")
-#' Draw plot on current graphics device.
+#' Explicitly draw plot
+#'
+#' Generally, you do not need to print or plot a ggplot2 plot explicitly: the
+#' default top-level print method will do it for you. You will, however, need
+#' to call \code{print()} explicitly if you want to draw a plot inside a
+#' function or for loop.
#'
#' @param x plot to display
#' @param newpage draw new (empty) page first?
@@ -135,6 +138,20 @@ is.ggplot <- function(x) inherits(x, "ggplot")
#' information about the scales, panels etc.
#' @export
#' @method print ggplot
+#' @examples
+#' colours <- list(~class, ~drv, ~fl)
+#'
+#' # Doesn't seem to do anything!
+#' for (colour in colours) {
+#' ggplot(mpg, aes_(~ displ, ~ hwy, colour = colour)) +
+#' geom_point()
+#' }
+#'
+#' # Works when we explicitly print the plots
+#' for (colour in colours) {
+#' print(ggplot(mpg, aes_(~ displ, ~ hwy, colour = colour)) +
+#' geom_point())
+#' }
print.ggplot <- function(x, newpage = is.null(vp), vp = NULL, ...) {
set_last_plot(x)
if (newpage) grid.newpage()
diff --git a/R/position-.r b/R/position-.r
index 253b796..6aaad4f 100644
--- a/R/position-.r
+++ b/R/position-.r
@@ -57,11 +57,11 @@ Position <- ggproto("Position",
data
},
- compute_layer = function(self, data, params, panel) {
+ compute_layer = function(self, data, params, layout) {
plyr::ddply(data, "PANEL", function(data) {
if (empty(data)) return(data.frame())
- scales <- panel_scales(panel, data$PANEL[1])
+ scales <- layout$get_scales(data$PANEL[1])
self$compute_panel(data = data, params = params, scales = scales)
})
},
diff --git a/R/position-collide.r b/R/position-collide.r
index 5516886..6096db4 100644
--- a/R/position-collide.r
+++ b/R/position-collide.r
@@ -1,6 +1,6 @@
# Detect and prevent collisions.
# Powers dodging, stacking and filling.
-collide <- function(data, width = NULL, name, strategy, check.width = TRUE) {
+collide <- function(data, width = NULL, name, strategy, ..., check.width = TRUE, reverse = FALSE) {
# Determine width
if (!is.null(width)) {
# Width set manually
@@ -26,9 +26,14 @@ collide <- function(data, width = NULL, name, strategy, check.width = TRUE) {
width <- widths[1]
}
- # Reorder by x position, relying on stable sort to preserve existing
- # ordering, which may be by group or order.
- data <- data[order(data$xmin), ]
+ # Reorder by x position, then on group. The default stacking order reverses
+ # the group in order to match the legend order.
+ if (reverse) {
+ data <- data[order(data$xmin, data$group), ]
+ } else {
+ data <- data[order(data$xmin, -data$group), ]
+ }
+
# Check for overlap
intervals <- as.numeric(t(unique(data[c("xmin", "xmax")])))
@@ -41,10 +46,10 @@ collide <- function(data, width = NULL, name, strategy, check.width = TRUE) {
}
if (!is.null(data$ymax)) {
- plyr::ddply(data, "xmin", strategy, width = width)
+ plyr::ddply(data, "xmin", strategy, ..., width = width)
} else if (!is.null(data$y)) {
data$ymax <- data$y
- data <- plyr::ddply(data, "xmin", strategy, width = width)
+ data <- plyr::ddply(data, "xmin", strategy, ..., width = width)
data$y <- data$ymax
data
} else {
@@ -52,34 +57,6 @@ collide <- function(data, width = NULL, name, strategy, check.width = TRUE) {
}
}
-# Stack overlapping intervals.
-# Assumes that each set has the same horizontal position
-pos_stack <- function(df, width) {
- if (nrow(df) == 1) return(df)
-
- n <- nrow(df) + 1
- y <- ifelse(is.na(df$y), 0, df$y)
- if (all(is.na(df$x))) {
- heights <- rep(NA, n)
- } else {
- heights <- c(0, cumsum(y))
- }
-
- df$ymin <- heights[-n]
- df$ymax <- heights[-1]
- df$y <- df$ymax
- df
-}
-
-# Stack overlapping intervals and set height to 1.
-# Assumes that each set has the same horizontal position.
-pos_fill <- function(df, width) {
- stacked <- pos_stack(df, width)
- stacked$ymin <- stacked$ymin / max(stacked$ymax)
- stacked$ymax <- stacked$ymax / max(stacked$ymax)
- stacked$y <- stacked$ymax
- stacked
-}
# Dodge overlapping interval.
# Assumes that each set has the same horizontal position.
diff --git a/R/position-dodge.r b/R/position-dodge.r
index 9682f10..433ceae 100644
--- a/R/position-dodge.r
+++ b/R/position-dodge.r
@@ -1,9 +1,12 @@
-#' Adjust position by dodging overlaps to the side.
+#' Dodge overlapping objects side-to-side
+#'
+#' Dodging preserves the vertical position of an geom while adjusting the
+#' horizontal position.
#'
#' @inheritParams position_identity
#' @param width Dodging width, when different to the width of the individual
#' elements. This is useful when you want to align narrow geoms with wider
-#' geoms. See the examples for a use case.
+#' geoms. See the examples.
#' @family position adjustments
#' @export
#' @examples
@@ -14,27 +17,39 @@
#' geom_histogram(position="dodge")
#' # see ?geom_boxplot and ?geom_bar for more examples
#'
+#' # In this case a frequency polygon is probably a better choice
+#' ggplot(diamonds, aes(price, colour = cut)) +
+#' geom_freqpoly()
+#' }
+#'
+#' # Dodging with various widths -------------------------------------
#' # To dodge items with different widths, you need to be explicit
-#' df <- data.frame(x=c("a","a","b","b"), y=2:5, g = rep(1:2, 2))
+#' df <- data.frame(x = c("a","a","b","b"), y = 2:5, g = rep(1:2, 2))
#' p <- ggplot(df, aes(x, y, group = g)) +
-#' geom_bar(
-#' stat = "identity", position = "dodge",
-#' fill = "grey50", colour = "black"
-#' )
+#' geom_col(position = "dodge", fill = "grey50", colour = "black")
#' p
#'
#' # A line range has no width:
-#' p + geom_linerange(aes(ymin = y-1, ymax = y+1), position = "dodge")
-#' # You need to explicitly specify the width for dodging
-#' p + geom_linerange(aes(ymin = y-1, ymax = y+1),
-#' position = position_dodge(width = 0.9))
+#' p + geom_linerange(aes(ymin = y - 1, ymax = y + 1), position = "dodge")
#'
-#' # Similarly with error bars:
-#' p + geom_errorbar(aes(ymin = y-1, ymax = y+1), width = 0.2,
-#' position = "dodge")
-#' p + geom_errorbar(aes(ymin = y-1, ymax = y+1, width = 0.2),
-#' position = position_dodge(width = 0.90))
-#' }
+#' # So you must explicitly specify the width
+#' p + geom_linerange(
+#' aes(ymin = y - 1, ymax = y + 1),
+#' position = position_dodge(width = 0.9)
+#' )
+#'
+#' # The same principle applies to error bars, which are usually
+#' # narrower than the bars
+#' p + geom_errorbar(
+#' aes(ymin = y - 1, ymax = y + 1),
+#' width = 0.2,
+#' position = "dodge"
+#' )
+#' p + geom_errorbar(
+#' aes(ymin = y - 1, ymax = y + 1),
+#' width = 0.2,
+#' position = position_dodge(width = 0.9)
+#' )
position_dodge <- function(width = NULL) {
ggproto(NULL, PositionDodge, width = width)
}
diff --git a/R/position-fill.r b/R/position-fill.r
deleted file mode 100644
index 1d6b66a..0000000
--- a/R/position-fill.r
+++ /dev/null
@@ -1,24 +0,0 @@
-#' @export
-#' @rdname position_stack
-position_fill <- function() {
- PositionFill
-}
-
-#' @rdname ggplot2-ggproto
-#' @format NULL
-#' @usage NULL
-#' @export
-PositionFill <- ggproto("PositionFill", Position,
- required_aes = c("x", "ymax"),
-
- setup_data = function(self, data, params) {
- if (!is.null(data$ymin) && !all(data$ymin == 0))
- warning("Filling not well defined when ymin != 0", call. = FALSE)
-
- ggproto_parent(Position, self)$setup_data(data)
- },
-
- compute_panel = function(data, params, scales) {
- collide(data, NULL, "position_fill", pos_fill)
- }
-)
diff --git a/R/position-jitter.r b/R/position-jitter.r
index 1a5bc2c..8f65e7e 100644
--- a/R/position-jitter.r
+++ b/R/position-jitter.r
@@ -1,4 +1,8 @@
-#' Jitter points to avoid overplotting.
+#' Jitter points to avoid overplotting
+#'
+#' Couterintuitively adding random noise to a plot can sometimes make it
+#' easier to read. Jittering is particularly useful for small datasets with
+#' at least one discrete position.
#'
#' @family position adjustments
#' @param width,height Amount of vertical and horizontal jitter. The jitter
@@ -11,23 +15,22 @@
#' data so it's not possible to see the distinction between the categories.
#' @export
#' @examples
-#' ggplot(mtcars, aes(am, vs)) + geom_point()
+#' # Jittering is useful when you have a discrete position, and a relatively
+#' # small number of points
+#' # take up as much space as a boxplot or a bar
+#' ggplot(mpg, aes(class, hwy)) +
+#' geom_boxplot(colour = "grey50") +
+#' geom_jitter()
#'
-#' # Default amount of jittering will generally be too much for
-#' # small datasets:
-#' ggplot(mtcars, aes(am, vs)) + geom_jitter()
+#' # If the default jittering is too much, as in this plot:
+#' ggplot(mtcars, aes(am, vs)) +
+#' geom_jitter()
#'
-#' # Two ways to override
+#' # You can adjust it in two ways
#' ggplot(mtcars, aes(am, vs)) +
#' geom_jitter(width = 0.1, height = 0.1)
#' ggplot(mtcars, aes(am, vs)) +
#' geom_jitter(position = position_jitter(width = 0.1, height = 0.1))
-#'
-#' # The default works better for large datasets, where it will
-#' # take up as much space as a boxplot or a bar
-#' ggplot(mpg, aes(class, hwy)) +
-#' geom_jitter() +
-#' geom_boxplot()
position_jitter <- function(width = NULL, height = NULL) {
ggproto(NULL, PositionJitter,
width = width,
@@ -44,8 +47,8 @@ PositionJitter <- ggproto("PositionJitter", Position,
setup_params = function(self, data) {
list(
- width = self$width %||% resolution(data$x, zero = FALSE) * 0.4,
- height = self$height %||% resolution(data$y, zero = FALSE) * 0.4
+ width = self$width %||% (resolution(data$x, zero = FALSE) * 0.4),
+ height = self$height %||% (resolution(data$y, zero = FALSE) * 0.4)
)
},
diff --git a/R/position-jitterdodge.R b/R/position-jitterdodge.R
index f87ce4c..05c72d1 100644
--- a/R/position-jitterdodge.R
+++ b/R/position-jitterdodge.R
@@ -1,4 +1,4 @@
-#' Adjust position by simultaneously dodging and jittering
+#' Simultaneously dodge and jitter
#'
#' This is primarily used for aligning points generated through
#' \code{geom_point()} with dodged boxplots (e.g., a \code{geom_boxplot()} with
@@ -38,15 +38,15 @@ PositionJitterdodge <- ggproto("PositionJitterdodge", Position,
required_aes = c("x", "y"),
setup_params = function(self, data) {
- width <- self$jitter.width %||% resolution(data$x, zero = FALSE) * 0.4
+ width <- self$jitter.width %||% (resolution(data$x, zero = FALSE) * 0.4)
# Adjust the x transformation based on the number of 'dodge' variables
- dodgecols <- intersect(c("fill", "colour", "linetype", "shape", "size", "alpha"), colnames(data))
+ dodgecols <- intersect(c("fill", "colour", "linetype", "shape", "size", "alpha"), colnames(data))
if (length(dodgecols) == 0) {
stop("`position_jitterdodge()` requires at least one aesthetic to dodge by", call. = FALSE)
}
ndodge <- lapply(data[dodgecols], levels) # returns NULL for numeric, i.e. non-dodge layers
ndodge <- length(unique(unlist(ndodge)))
-
+
list(
dodge.width = self$dodge.width,
jitter.height = self$jitter.height,
diff --git a/R/position-nudge.R b/R/position-nudge.R
index 1ed8e9e..78d8717 100644
--- a/R/position-nudge.R
+++ b/R/position-nudge.R
@@ -1,7 +1,9 @@
-#' Nudge points.
+#' Nudge points a fixed distance
#'
-#' This is useful if you want to nudge labels a little ways from their
-#' points.
+#' \code{position_nudge} is generally useful for adjusting the position of
+#' items on discrete scales by a small amount. Nudging is built in to
+#' \code{\link{geom_text}} because it's so useful for moving labels a small
+#' distance from what they're labelling.
#'
#' @family position adjustments
#' @param x,y Amount of vertical and horizontal distance to move.
@@ -19,6 +21,11 @@
#' ggplot(df, aes(x, y)) +
#' geom_point() +
#' geom_text(aes(label = y), position = position_nudge(y = -0.1))
+#'
+#' # Or, in brief
+#' ggplot(df, aes(x, y)) +
+#' geom_point() +
+#' geom_text(aes(label = y), nudge_y = -0.1)
position_nudge <- function(x = 0, y = 0) {
ggproto(NULL, PositionNudge,
x = x,
diff --git a/R/position-stack.r b/R/position-stack.r
index 4267928..564c3cc 100644
--- a/R/position-stack.r
+++ b/R/position-stack.r
@@ -1,48 +1,129 @@
-#' Stack overlapping objects on top of one another.
+#' Stack overlapping objects on top of each another
#'
-#' \code{position_fill} additionally standardises each stack to have unit
-#' height.
+#' \code{position_stack()} stacks bars on top of each other;
+#' \code{position_fill()} stacks bars and standardises each stack to have
+#' constant height.
+#'
+#' \code{position_fill()} and \code{position_stack()} automatically stack
+#' values in reverse order of the group aesthetic, which for bar charts is
+#' usually defined by the fill aesthetic (the default group aesthetic is formed
+#' by the combination of all discrete aesthetics except for x and y). This
+#' default ensures that bar colours align with the default legend.
+#'
+#' There are three ways to override the defaults depending on what you want:
+#'
+#' \enumerate{
+#' \item Change the order of the levels in the underyling factor. This
+#' will change the stacking order, and the order of keys in the legend.
+#'
+#' \item Set the legend \code{breaks} to change the order of the keys
+#' without affecting the stacking.
+#'
+#' \item Manually set the group aesthetic to change the stacking order
+#' without affecting the legend.
+#' }
+#'
+#' Stacking of positive and negative values are performed separately so that
+#' positive values stack upwards from the x-axis and negative values stack
+#' downward.
#'
#' @family position adjustments
+#' @param vjust Vertical adjustment for geoms that have a position
+#' (like points or lines), not a dimension (like bars or areas). Set to
+#' \code{0} to align with the bottom, \code{0.5} for the middle,
+#' and \code{1} (the default) for the top.
+#' @param reverse If \code{TRUE}, will reverse the default stacking order.
+#' This is useful if you're rotating both the plot and legend.
#' @seealso See \code{\link{geom_bar}} and \code{\link{geom_area}} for
#' more examples.
#' @export
#' @examples
-#' # Stacking is the default behaviour for most area plots:
-#' ggplot(mtcars, aes(factor(cyl), fill = factor(vs))) + geom_bar()
+#' # Stacking and filling ------------------------------------------------------
+#'
+#' # Stacking is the default behaviour for most area plots.
#' # Fill makes it easier to compare proportions
#' ggplot(mtcars, aes(factor(cyl), fill = factor(vs))) +
+#' geom_bar()
+#' ggplot(mtcars, aes(factor(cyl), fill = factor(vs))) +
#' geom_bar(position = "fill")
#'
-#' # To change stacking order, use factor() to change order of levels
-#' mtcars$vs <- factor(mtcars$vs, levels = c(1,0))
-#' ggplot(mtcars, aes(factor(cyl), fill = factor(vs))) + geom_bar()
-#'
#' ggplot(diamonds, aes(price, fill = cut)) +
#' geom_histogram(binwidth = 500)
-#' # When used with a histogram, position_fill creates a conditional density
-#' # estimate
#' ggplot(diamonds, aes(price, fill = cut)) +
#' geom_histogram(binwidth = 500, position = "fill")
#'
#' # Stacking is also useful for time series
-#' data.set <- data.frame(
-#' Time = c(rep(1, 4),rep(2, 4), rep(3, 4), rep(4, 4)),
-#' Type = rep(c('a', 'b', 'c', 'd'), 4),
-#' Value = rpois(16, 10)
+#' series <- data.frame(
+#' time = c(rep(1, 4),rep(2, 4), rep(3, 4), rep(4, 4)),
+#' type = rep(c('a', 'b', 'c', 'd'), 4),
+#' value = rpois(16, 10)
#' )
+#' ggplot(series, aes(time, value)) +
+#' geom_area(aes(fill = type))
+#'
+#' # Stacking order ------------------------------------------------------------
+#'
+#' # You control the stacking order by setting the levels of the underlying
+#' # factor. See the forcats package for convenient helpers.
+#' series$type2 <- factor(series$type, levels = c('c', 'b', 'd', 'a'))
+#' ggplot(series, aes(time, value)) +
+#' geom_area(aes(fill = type2))
+#'
+#' # You can change the order of the levels in the legend using the scale
+#' ggplot(series, aes(time, value)) +
+#' geom_area(aes(fill = type)) +
+#' scale_fill_discrete(breaks = c('a', 'b', 'c', 'd'))
#'
-#' ggplot(data.set, aes(Time, Value)) + geom_area(aes(fill = Type))
+#' # Non-area plots ------------------------------------------------------------
#'
-#' # If you want to stack lines, you need to say so:
-#' ggplot(data.set, aes(Time, Value)) + geom_line(aes(colour = Type))
-#' ggplot(data.set, aes(Time, Value)) +
-#' geom_line(position = "stack", aes(colour = Type))
+#' # When stacking across multiple layers it's a good idea to always set
+#' # the `group` aethetic in the ggplot() call. This ensures that all layers
+#' # are stacked in the same way.
+#' ggplot(series, aes(time, value, group = type)) +
+#' geom_line(aes(colour = type), position = "stack") +
+#' geom_point(aes(colour = type), position = "stack")
#'
-#' # But realise that this makes it *much* harder to compare individual
-#' # trends
-position_stack <- function() {
- PositionStack
+#' ggplot(series, aes(time, value, group = type)) +
+#' geom_area(aes(fill = type)) +
+#' geom_line(aes(group = type), position = "stack")
+#'
+#' # You can also stack labels, but the default position is suboptimal.
+#' ggplot(series, aes(time, value, group = type)) +
+#' geom_area(aes(fill = type)) +
+#' geom_text(aes(label = type), position = "stack")
+#'
+#' # You can override this with the vjust parameter. A vjust of 0.5
+#' # will center the labels inside the corresponding area
+#' ggplot(series, aes(time, value, group = type)) +
+#' geom_area(aes(fill = type)) +
+#' geom_text(aes(label = type), position = position_stack(vjust = 0.5))
+#'
+#' # Negative values -----------------------------------------------------------
+#'
+#' df <- tibble::tribble(
+#' ~x, ~y, ~grp,
+#' "a", 1, "x",
+#' "a", 2, "y",
+#' "b", 1, "x",
+#' "b", 3, "y",
+#' "b", -1, "y"
+#' )
+#' ggplot(data = df, aes(x, y, group = grp)) +
+#' geom_col(aes(fill = grp), position = position_stack(reverse = TRUE)) +
+#' geom_hline(yintercept = 0)
+#'
+#' ggplot(data = df, aes(x, y, group = grp)) +
+#' geom_col(aes(fill = grp)) +
+#' geom_hline(yintercept = 0) +
+#' geom_text(aes(label = grp), position = position_stack(vjust = 0.5))
+position_stack <- function(vjust = 1, reverse = FALSE) {
+ ggproto(NULL, PositionStack, vjust = vjust, reverse = reverse)
+}
+
+#' @export
+#' @rdname position_stack
+position_fill <- function(vjust = 1, reverse = FALSE) {
+ ggproto(NULL, PositionFill, vjust = vjust, reverse = reverse)
}
#' @rdname ggplot2-ggproto
@@ -50,25 +131,103 @@ position_stack <- function() {
#' @usage NULL
#' @export
PositionStack <- ggproto("PositionStack", Position,
- # requires one of c("ymax", "y"),
+ type = NULL,
+ vjust = 1,
+ fill = FALSE,
+ reverse = FALSE,
- setup_data = function(self, data, params) {
- data = remove_missing(data, FALSE,
- c("x", "y", "ymin", "ymax", "xmin", "xmax"), name = "position_stack")
+ setup_params = function(self, data) {
+ list(
+ var = self$var %||% stack_var(data),
+ fill = self$fill,
+ vjust = self$vjust,
+ reverse = self$reverse
+ )
+ },
- if (is.null(data$ymax) && is.null(data$y)) {
- message("Missing y and ymax in position = 'stack'. ",
- "Maybe you want position = 'identity'?")
+ setup_data = function(self, data, params) {
+ if (is.null(params$var)) {
return(data)
}
- if (!is.null(data$ymin) && !all(data$ymin == 0))
- warning("Stacking not well defined when ymin != 0", call. = FALSE)
+ data$ymax <- switch(params$var,
+ y = data$y,
+ ymax = ifelse(data$ymax == 0, data$ymin, data$ymax)
+ )
- data
+ remove_missing(
+ data,
+ vars = c("x", "xmin", "xmax", "y"),
+ name = "position_stack"
+ )
},
compute_panel = function(data, params, scales) {
- collide(data, NULL, "position_stack", pos_stack)
+ if (is.null(params$var)) {
+ return(data)
+ }
+
+ negative <- data$ymax < 0
+ neg <- data[negative, , drop = FALSE]
+ pos <- data[!negative, , drop = FALSE]
+
+ if (any(negative)) {
+ neg <- collide(neg, NULL, "position_stack", pos_stack,
+ vjust = params$vjust,
+ fill = params$fill,
+ reverse = params$reverse
+ )
+ }
+ if (any(!negative)) {
+ pos <- collide(pos, NULL, "position_stack", pos_stack,
+ vjust = params$vjust,
+ fill = params$fill,
+ reverse = params$reverse
+ )
+ }
+
+ rbind(neg, pos)
}
)
+
+pos_stack <- function(df, width, vjust = 1, fill = FALSE) {
+ n <- nrow(df) + 1
+ y <- ifelse(is.na(df$y), 0, df$y)
+ heights <- c(0, cumsum(y))
+
+ if (fill) {
+ heights <- heights / abs(heights[length(heights)])
+ }
+
+ df$ymin <- pmin(heights[-n], heights[-1])
+ df$ymax <- pmax(heights[-n], heights[-1])
+ df$y <- (1 - vjust) * df$ymin + vjust * df$ymax
+ df
+}
+
+
+#' @rdname ggplot2-ggproto
+#' @format NULL
+#' @usage NULL
+#' @export
+PositionFill <- ggproto("PositionFill", PositionStack,
+ fill = TRUE
+)
+
+stack_var <- function(data) {
+ if (!is.null(data$ymax)) {
+ if (any(data$ymin != 0 && data$ymax != 0, na.rm = TRUE)) {
+ warning("Stacking not well defined when not anchored on the axis", call. = FALSE)
+ }
+ "ymax"
+ } else if (!is.null(data$y)) {
+ "y"
+ } else {
+ warning(
+ "Stacking requires either ymin & ymin or y aesthetics.\n",
+ "Maybe you want position = 'identity'?",
+ call. = FALSE
+ )
+ NULL
+ }
+}
diff --git a/R/quick-plot.r b/R/quick-plot.r
index 2714f86..cb7ea6d 100644
--- a/R/quick-plot.r
+++ b/R/quick-plot.r
@@ -1,9 +1,11 @@
#' Quick plot
#'
-#' \code{qplot} is the basic plotting function in the ggplot2 package,
-#' designed to be familiar if you're used to base \code{\link{plot}()}.
-#' It's a convenient wrapper for creating a number of different types of plots
-#' using a consistent calling scheme.
+#' \code{qplot} is a shortcut designed to be familiar if you're used to base
+#' \code{\link{plot}()}. It's a convenient wrapper for creating a number of
+#' different types of plots using a consistent calling scheme. It's great
+#' for allowing you to produce plots quickly, but I highly recommend
+#' learning \code{\link{ggplot}()} as it makes it easier to create
+#' complex graphics.
#'
#' @param x,y,... Aesthetics passed into each layer
#' @param data Data frame to use (optional). If not specified, will create
@@ -31,7 +33,7 @@
#' \donttest{
#' qplot(1:10, rnorm(10), colour = runif(10))
#' qplot(1:10, letters[1:10])
-#' mod <- lm(mpg ~ wt, data=mtcars)
+#' mod <- lm(mpg ~ wt, data = mtcars)
#' qplot(resid(mod), fitted(mod))
#'
#' f <- function() {
@@ -57,7 +59,7 @@
#' qplot(factor(cyl), wt, data = mtcars, geom = c("boxplot", "jitter"))
#' qplot(mpg, data = mtcars, geom = "dotplot")
#' }
-qplot <- function(x, y = NULL, ..., data, facets = NULL, margins=FALSE,
+qplot <- function(x, y = NULL, ..., data, facets = NULL, margins = FALSE,
geom = "auto", xlim = c(NA, NA),
ylim = c(NA, NA), log = "", main = NULL,
xlab = deparse(substitute(x)), ylab = deparse(substitute(y)),
diff --git a/R/range.r b/R/range.r
index 5d363ad..f2559ef 100644
--- a/R/range.r
+++ b/R/range.r
@@ -13,8 +13,8 @@ Range <- ggproto("Range", NULL,
)
RangeDiscrete <- ggproto("RangeDiscrete", Range,
- train = function(self, x, drop = FALSE) {
- self$range <- scales::train_discrete(x, self$range, drop)
+ train = function(self, x, drop = FALSE, na.rm = FALSE) {
+ self$range <- scales::train_discrete(x, self$range, drop = drop, na.rm = na.rm)
}
)
diff --git a/R/save.r b/R/save.r
index c7139b7..b851183 100644
--- a/R/save.r
+++ b/R/save.r
@@ -7,21 +7,18 @@
#'
#' @param filename File name to create on disk.
#' @param plot Plot to save, defaults to last plot displayed.
-#' @param device Device to use (function or any of the recognized extensions,
-#' e.g. \code{"pdf"}). By default, extracted from filename extension.
-#' \code{ggsave} currently recognises eps/ps, tex (pictex), pdf, jpeg, tiff,
-#' png, bmp, svg and wmf (windows only).
+#' @param device Device to use. Can be either be a device function
+#' (e.g. \code{\link{png}}), or one of "eps", "ps", "tex" (pictex),
+#' "pdf", "jpeg", "tiff", "png", "bmp", "svg" or "wmf" (windows only).
#' @param path Path to save plot to (combined with filename).
#' @param scale Multiplicative scaling factor.
-#' @param width,height Plot dimensions, defaults to size of current graphics
-#' device.
-#' @param units Units for width and height when specified explicitly (in, cm,
-#' or mm)
-#' @param dpi Resolution used for raster outputs.
+#' @param width,height,units Plot size in \code{units} ("in", "cm", or "mm").
+#' If not supplied, uses the size of current graphics device.
+#' @param dpi Plot resolution. Applies only to raster output types.
#' @param limitsize When \code{TRUE} (the default), \code{ggsave} will not
#' save images larger than 50x50 inches, to prevent the common error of
#' specifying dimensions in pixels.
-#' @param ... Other arguments passed on to graphics device
+#' @param ... Other arguments passed on to graphics \code{device}.
#' @export
#' @examples
#' \dontrun{
@@ -84,7 +81,7 @@ plot_dim <- function(dim = c(NA, NA), scale = 1, units = c("in", "cm", "mm"),
if (limitsize && any(dim >= 50)) {
stop("Dimensions exceed 50 inches (height and width are specified in '",
- units, "' not pixels). If you're sure you a plot that big, use ",
+ units, "' not pixels). If you're sure you want a plot that big, use ",
"`limitsize = FALSE`.", call. = FALSE)
}
diff --git a/R/scale-.r b/R/scale-.r
index fe0aac1..60f3970 100644
--- a/R/scale-.r
+++ b/R/scale-.r
@@ -30,6 +30,7 @@ Scale <- ggproto("Scale", NULL,
breaks = waiver(),
labels = waiver(),
guide = "legend",
+ position = "left",
is_discrete = function() {
@@ -151,6 +152,23 @@ Scale <- ggproto("Scale", NULL,
break_info = function(self, range = NULL) {
stop("Not implemented", call. = FALSE)
+ },
+
+ # Only relevant for positional scales
+ axis_order = function(self) {
+ ord <- c("primary", "secondary")
+ if (self$position %in% c("right", "bottom")) {
+ ord <- rev(ord)
+ }
+ ord
+ },
+
+ # Here to make it possible for scales to modify the default titles
+ make_title = function(title) {
+ title
+ },
+ make_sec_title = function(title) {
+ title
}
)
@@ -187,7 +205,13 @@ ScaleContinuous <- ggproto("ScaleContinuous", Scale,
},
transform = function(self, x) {
- self$trans$transform(x)
+ new_x <- self$trans$transform(x)
+ if (any(is.finite(x) != is.finite(new_x))) {
+ type <- if (self$scale_name == "position_c") "continuous" else "discrete"
+ axis <- if ("x" %in% self$aesthetics) "x" else "y"
+ warning("Transformation introduced infinite values in ", type, " ", axis, "-axis", call. = FALSE)
+ }
+ new_x
},
map = function(self, x, limits = self$get_limits()) {
@@ -232,10 +256,6 @@ ScaleContinuous <- ggproto("ScaleContinuous", Scale,
# guides cannot discriminate oob from missing value.
breaks <- censor(self$trans$transform(breaks), self$trans$transform(limits),
only.finite = FALSE)
- if (length(breaks) == 0) {
- stop("Zero breaks in scale for ", paste(self$aesthetics, collapse = "/"),
- call. = FALSE)
- }
breaks
},
@@ -345,12 +365,14 @@ ScaleContinuous <- ggproto("ScaleContinuous", Scale,
ScaleDiscrete <- ggproto("ScaleDiscrete", Scale,
drop = TRUE,
na.value = NA,
+ n.breaks.cache = NULL,
+ palette.cache = NULL,
is_discrete = function() TRUE,
train = function(self, x) {
if (length(x) == 0) return()
- self$range$train(x, drop = self$drop)
+ self$range$train(x, drop = self$drop, na.rm = !self$na.translate)
},
transform = function(x) {
@@ -359,7 +381,14 @@ ScaleDiscrete <- ggproto("ScaleDiscrete", Scale,
map = function(self, x, limits = self$get_limits()) {
n <- sum(!is.na(limits))
- pal <- self$palette(n)
+ if (!is.null(self$n.breaks.cache) && self$n.breaks.cache == n) {
+ pal <- self$palette.cache
+ } else {
+ if (!is.null(self$n.breaks.cache)) warning("Cached palette does not match requested", call. = FALSE)
+ pal <- self$palette(n)
+ self$palette.cache <- pal
+ self$n.breaks.cache <- n
+ }
if (is.null(names(pal))) {
pal_match <- pal[match(as.character(x), limits)]
@@ -368,7 +397,11 @@ ScaleDiscrete <- ggproto("ScaleDiscrete", Scale,
pal_match <- unname(pal_match)
}
- ifelse(is.na(x) | is.na(pal_match), self$na.value, pal_match)
+ if (self$na.translate) {
+ ifelse(is.na(x) | is.na(pal_match), self$na.value, pal_match)
+ } else {
+ pal_match
+ }
},
dimension = function(self, expand = c(0, 0)) {
@@ -406,8 +439,14 @@ ScaleDiscrete <- ggproto("ScaleDiscrete", Scale,
return(NULL)
} else if (identical(self$labels, NA)) {
stop("Invalid labels specification. Use NULL, not NA", call. = FALSE)
- }else if (is.waive(self$labels)) {
- format(self$get_breaks(), justify = "none", trim = TRUE)
+ } else if (is.waive(self$labels)) {
+ breaks <- self$get_breaks()
+ if (is.numeric(breaks)) {
+ # Only format numbers, because on Windows, format messes up encoding
+ format(breaks, justify = "none")
+ } else {
+ as.character(breaks)
+ }
} else if (is.function(self$labels)) {
self$labels(breaks)
} else {
@@ -516,16 +555,19 @@ ScaleDiscrete <- ggproto("ScaleDiscrete", Scale,
#' \code{c(0.05, 0)} for continuous variables, and \code{c(0, 0.6)} for
#' discrete variables.
#' @param guide Name of guide object, or object itself.
+#' @param position The position of the axis. "left" or "right" for vertical
+#' scales, "top" or "bottom" for horizontal scales
+#' @param super The super class to use for the constructed scale
#' @keywords internal
continuous_scale <- function(aesthetics, scale_name, palette, name = waiver(),
- breaks = waiver(), minor_breaks = waiver(),
- labels = waiver(), limits = NULL,
- rescaler = rescale, oob = censor,
- expand = waiver(), na.value = NA_real_,
- trans = "identity", guide = "legend") {
+ breaks = waiver(), minor_breaks = waiver(), labels = waiver(), limits = NULL,
+ rescaler = rescale, oob = censor, expand = waiver(), na.value = NA_real_,
+ trans = "identity", guide = "legend", position = "left", super = ScaleContinuous) {
check_breaks_labels(breaks, labels)
+ position <- match.arg(position, c("left", "right", "top", "bottom"))
+
if (is.null(breaks) && !is_position_aes(aesthetics) && guide != "none") {
guide <- "none"
}
@@ -535,7 +577,7 @@ continuous_scale <- function(aesthetics, scale_name, palette, name = waiver(),
limits <- trans$transform(limits)
}
- ggproto(NULL, ScaleContinuous,
+ ggproto(NULL, super,
call = match.call(),
aesthetics = aesthetics,
@@ -555,7 +597,8 @@ continuous_scale <- function(aesthetics, scale_name, palette, name = waiver(),
minor_breaks = minor_breaks,
labels = labels,
- guide = guide
+ guide = guide,
+ position = position
)
}
@@ -596,21 +639,32 @@ continuous_scale <- function(aesthetics, scale_name, palette, name = waiver(),
#' additive constant used to expand the range of the scales so that there
#' is a small gap between the data and the axes. The defaults are (0,0.6)
#' for discrete scales and (0.05,0) for continuous scales.
-#' @param na.value how should missing values be displayed?
+#' @param na.translate Unlike continuous scales, discrete scales can easily show
+#' missing values, and do so by default. If you want to remove missing values
+#' from a discrete scale, specify \code{na.translate = FALSE}.
+#' @param na.value If \code{na.translate = TRUE}, what value aesthetic
+#' value should missing be displayed as? Does not apply to position scales
+#' where \code{NA} is always placed at the far right.
#' @param guide the name of, or actual function, used to create the
#' guide. See \code{\link{guides}} for more info.
+#' @param position The position of the axis. "left" or "right" for vertical
+#' scales, "top" or "bottom" for horizontal scales
+#' @param super The super class to use for the constructed scale
#' @keywords internal
-discrete_scale <- function(aesthetics, scale_name, palette, name = waiver(), breaks = waiver(),
- labels = waiver(), limits = NULL, expand = waiver(), na.value = NA, drop = TRUE,
- guide = "legend") {
+discrete_scale <- function(aesthetics, scale_name, palette, name = waiver(),
+ breaks = waiver(), labels = waiver(), limits = NULL, expand = waiver(),
+ na.translate = TRUE, na.value = NA, drop = TRUE,
+ guide = "legend", position = "left", super = ScaleDiscrete) {
check_breaks_labels(breaks, labels)
+ position <- match.arg(position, c("left", "right", "top", "bottom"))
+
if (is.null(breaks) && !is_position_aes(aesthetics) && guide != "none") {
guide <- "none"
}
- ggproto(NULL, ScaleDiscrete,
+ ggproto(NULL, super,
call = match.call(),
aesthetics = aesthetics,
@@ -620,12 +674,14 @@ discrete_scale <- function(aesthetics, scale_name, palette, name = waiver(), bre
range = discrete_range(),
limits = limits,
na.value = na.value,
+ na.translate = na.translate,
expand = expand,
name = name,
breaks = breaks,
labels = labels,
drop = drop,
- guide = guide
+ guide = guide,
+ position = position
)
}
diff --git a/R/scale-alpha.r b/R/scale-alpha.r
index 06026e4..a497538 100644
--- a/R/scale-alpha.r
+++ b/R/scale-alpha.r
@@ -1,24 +1,23 @@
-#' Alpha scales.
+#' Alpha transparency scales
#'
+#' Alpha-transparency scales are not tremendously useful, but can be a
+#' convenient way to visually down-weight less important observations.
#' \code{scale_alpha} is an alias for \code{scale_alpha_continuous} since
#' that is the most common use of alpha, and it saves a bit of typing.
#'
#' @param ... Other arguments passed on to \code{\link{continuous_scale}}
#' or \code{\link{discrete_scale}} as appropriate, to control name, limits,
#' breaks, labels and so forth.
-#' @param range range of output alpha values. Should lie between 0 and 1.
+#' @param range Output range of alpha values. Must lie between 0 and 1.
+#' @family colour scales
#' @export
#' @examples
-#' (p <- ggplot(mtcars, aes(mpg, cyl)) +
-#' geom_point(aes(alpha = cyl)))
-#' p + scale_alpha("cylinders")
-#' p + scale_alpha("number\nof\ncylinders")
+#' p <- ggplot(mpg, aes(displ, hwy)) +
+#' geom_point(aes(alpha = year))
#'
+#' p
+#' p + scale_alpha("cylinders")
#' p + scale_alpha(range = c(0.4, 0.8))
-#'
-#' (p <- ggplot(mtcars, aes(mpg, cyl)) +
-#' geom_point(aes(alpha = factor(cyl))))
-#' p + scale_alpha_discrete(range = c(0.4, 0.8))
scale_alpha <- function(..., range = c(0.1, 1)) {
continuous_scale("alpha", "alpha_c", rescale_pal(range), ...)
}
diff --git a/R/scale-brewer.r b/R/scale-brewer.r
index 70d5b90..1487603 100644
--- a/R/scale-brewer.r
+++ b/R/scale-brewer.r
@@ -1,14 +1,19 @@
#' Sequential, diverging and qualitative colour scales from colorbrewer.org
#'
-#' ColorBrewer provides sequential, diverging and qualitative colour schemes
-#' which are particularly suited and tested to display discrete values (levels
-#' of a factor) on a map. ggplot2 can use those colours in discrete scales. It
-#' also allows to smoothly interpolate 6 colours from any palette to a
-#' continuous scale (6 colours per palette gives nice gradients; more results in
-#' more saturated colours which do not look as good). However, the original
-#' colour schemes (particularly the qualitative ones) were not intended for this
-#' and the perceptual result is left to the appreciation of the user.
-#' See \url{http://colorbrewer2.org} for more information.
+#' @description
+#' The \code{brewer} scales provides sequential, diverging and qualitative
+#' colour schemes from ColorBrewer. These are particularly well suited to
+#' display discrete values on a map. See \url{http://colorbrewer2.org} for
+#' more information.
+#'
+#' @note
+#' The \code{distiller} scales extends brewer to continuous scales by smoothly
+#' interpolate 6 colours from any palette to a continuous scale.
+#'
+#' @details
+#' The \code{brewer} scales were carefully designed and tested on discrete data.
+#' They were not designed to be extended to continuous data, but results often
+#' look good. Your mileage may vary.
#'
#' @section Palettes:
#' The following palettes are available for use with these scales:
@@ -23,19 +28,16 @@
#' @inheritParams scale_colour_hue
#' @inheritParams scale_colour_gradient
#' @inheritParams scales::gradient_n_pal
-#' @seealso Other colour scales:
-#' \code{\link{scale_colour_gradient}},
-#' \code{\link{scale_colour_grey}},
-#' \code{\link{scale_colour_hue}}
+#' @family colour scales
#' @rdname scale_brewer
#' @export
#' @examples
#' dsamp <- diamonds[sample(nrow(diamonds), 1000), ]
#' (d <- ggplot(dsamp, aes(carat, price)) +
#' geom_point(aes(colour = clarity)))
+#' d + scale_colour_brewer()
#'
#' # Change scale label
-#' d + scale_colour_brewer()
#' d + scale_colour_brewer("Diamond\nclarity")
#'
#' # Select brewer palette to use, see ?scales::brewer_pal for more details
diff --git a/R/scale-continuous.r b/R/scale-continuous.r
index c6f3c22..8755cf7 100644
--- a/R/scale-continuous.r
+++ b/R/scale-continuous.r
@@ -1,99 +1,95 @@
-#' Continuous position scales (x & y).
+#' Position scales for continuous data (x & y)
#'
-#' \code{scale_x_continuous} and \code{scale_y_continuous} are the key functions.
-#' The others, \code{scale_x_log10}, \code{scale_y_sqrt} etc, are aliases
-#' that set the \code{trans} argument to commonly used transformations.
+#' \code{scale_x_continuous} and \code{scale_y_continuous} are the default
+#' scales for continuous x and y aesthetics. There are three variants
+#' that set the \code{trans} argument for commonly used transformations:
+#' \code{scale_*_log10}, \code{scale_*_sqrt} and \code{scale_*_reverse}.
+#'
+#' For simple manipulation of labels and limits, you may wish to use
+#' \code{\link{labs}()} and \code{\link{lims}()} instead.
#'
#' @inheritParams continuous_scale
-#' @seealso \code{\link{scale_date}} for date/time position scales.
+#' @family position scales
#' @param ... Other arguments passed on to \code{scale_(x|y)_continuous}
#' @examples
-#' \donttest{
-#' if (require(ggplot2movies)) {
-#' m <- ggplot(subset(movies, votes > 1000), aes(rating, votes)) +
-#' geom_point(na.rm = TRUE)
-#' m
+#' p1 <- ggplot(mpg, aes(displ, hwy)) +
+#' geom_point()
+#' p1
#'
#' # Manipulating the default position scales lets you:
-#'
#' # * change the axis labels
-#' m + scale_y_continuous("number of votes")
-#' m + scale_y_continuous(quote(votes ^ alpha))
+#' p1 +
+#' scale_x_continuous("Engine displacement (L)") +
+#' scale_y_continuous("Highway MPG")
+#'
+#' # You can also use the short-cut labs().
+#' # Use NULL to suppress axis labels
+#' p1 + labs(x = NULL, y = NULL)
#'
#' # * modify the axis limits
-#' m + scale_y_continuous(limits = c(0, 5000))
-#' m + scale_y_continuous(limits = c(1000, 10000))
-#' m + scale_x_continuous(limits = c(7, 8))
+#' p1 + scale_x_continuous(limits = c(2, 6))
+#' p1 + scale_x_continuous(limits = c(0, 10))
#'
-#' # you can also use the short hand functions xlim and ylim
-#' m + ylim(0, 5000)
-#' m + ylim(1000, 10000)
-#' m + xlim(7, 8)
+#' # you can also use the short hand functions `xlim()` and `ylim()`
+#' p1 + xlim(2, 6)
#'
#' # * choose where the ticks appear
-#' m + scale_x_continuous(breaks = 1:10)
-#' m + scale_x_continuous(breaks = c(1,3,7,9))
-#'
-#' # * manually label the ticks
-#' m + scale_x_continuous(breaks = c(2,5,8), labels = c("two", "five", "eight"))
-#' m + scale_x_continuous(breaks = c(2,5,8), labels = c("horrible", "ok", "awesome"))
-#' m + scale_x_continuous(breaks = c(2,5,8), labels = expression(Alpha, Beta, Omega))
+#' p1 + scale_x_continuous(breaks = c(2, 4, 6))
#'
-#' # There are a few built in transformation that you can use:
-#' m + scale_y_log10()
-#' m + scale_y_sqrt()
-#' m + scale_y_reverse()
-#' # You can also create your own and supply them to the trans argument.
-#' # See ?scales::trans_new
+#' # * add what labels they have
+#' p1 + scale_x_continuous(
+#' breaks = c(2, 4, 6),
+#' label = c("two", "four", "six")
+#' )
#'
-#' # You can control the formatting of the labels with the formatter
-#' # argument. Some common formats are built into the scales package:
+#' # Typically you'll pass a function to the `labels` argument.
+#' # Some common formats are built into the scales package:
#' df <- data.frame(
#' x = rnorm(10) * 100000,
#' y = seq(0, 1, length.out = 10)
#' )
-#' p <- ggplot(df, aes(x, y)) + geom_point()
-#' p + scale_y_continuous(labels = scales::percent)
-#' p + scale_y_continuous(labels = scales::dollar)
-#' p + scale_x_continuous(labels = scales::comma)
+#' p2 <- ggplot(df, aes(x, y)) + geom_point()
+#' p2 + scale_y_continuous(labels = scales::percent)
+#' p2 + scale_y_continuous(labels = scales::dollar)
+#' p2 + scale_x_continuous(labels = scales::comma)
+#'
+#' # You can also override the default linear mapping by using a
+#' # transformation. There are three shortcuts:
+#' p1 + scale_y_log10()
+#' p1 + scale_y_sqrt()
+#' p1 + scale_y_reverse()
#'
-#' # Other shortcut functions
-#' ggplot(movies, aes(rating, votes)) +
-#' geom_point() +
-#' ylim(1e4, 5e4)
-#' # * axis labels
-#' ggplot(movies, aes(rating, votes)) +
-#' geom_point() +
-#' labs(x = "My x axis", y = "My y axis")
-#' # * log scaling
-#' ggplot(movies, aes(rating, votes)) +
-#' geom_point() +
-#' scale_x_log10() +
-#' scale_y_log10()
-#' }
-#' }
+#' # Or you can supply a transformation in the `trans` argument:
+#' p1 + scale_y_continuous(trans = scales::reciprocal_trans())
+#'
+#' # You can also create your own. See ?scales::trans_new
#' @name scale_continuous
+#' @aliases NULL
NULL
#' @rdname scale_continuous
+#'
+#' @param sec.axis specifify a secondary axis
+#'
+#' @seealso \code{\link{sec_axis}} for how to specify secondary axes
#' @export
scale_x_continuous <- function(name = waiver(), breaks = waiver(),
minor_breaks = waiver(), labels = waiver(),
limits = NULL, expand = waiver(), oob = censor,
- na.value = NA_real_, trans = "identity") {
+ na.value = NA_real_, trans = "identity",
+ position = "bottom", sec.axis = waiver()) {
sc <- continuous_scale(
- c("x", "xmin", "xmax", "xend", "xintercept"),
+ c("x", "xmin", "xmax", "xend", "xintercept", "xmin_final", "xmax_final", "xlower", "xmiddle", "xupper"),
"position_c", identity, name = name, breaks = breaks,
minor_breaks = minor_breaks, labels = labels, limits = limits,
expand = expand, oob = oob, na.value = na.value, trans = trans,
- guide = "none"
+ guide = "none", position = position, super = ScaleContinuousPosition
)
-
- # TODO: Fix this hack. We're reassigning the parent ggproto object, but this
- # object should in the first place be created with the correct parent.
- sc$super <- ScaleContinuousPosition
- class(sc) <- class(ScaleContinuousPosition)
-
+ if (!is.waive(sec.axis)) {
+ if (is.formula(sec.axis)) sec.axis <- sec_axis(sec.axis)
+ if (!is.sec_axis(sec.axis)) stop("Secondary axes must be specified using 'sec_axis()'")
+ sc$secondary.axis <- sec.axis
+ }
sc
}
@@ -102,20 +98,20 @@ scale_x_continuous <- function(name = waiver(), breaks = waiver(),
scale_y_continuous <- function(name = waiver(), breaks = waiver(),
minor_breaks = waiver(), labels = waiver(),
limits = NULL, expand = waiver(), oob = censor,
- na.value = NA_real_, trans = "identity") {
+ na.value = NA_real_, trans = "identity",
+ position = "left", sec.axis = waiver()) {
sc <- continuous_scale(
c("y", "ymin", "ymax", "yend", "yintercept", "ymin_final", "ymax_final", "lower", "middle", "upper"),
"position_c", identity, name = name, breaks = breaks,
minor_breaks = minor_breaks, labels = labels, limits = limits,
expand = expand, oob = oob, na.value = na.value, trans = trans,
- guide = "none"
+ guide = "none", position = position, super = ScaleContinuousPosition
)
-
- # TODO: Fix this hack. We're reassigning the parent ggproto object, but this
- # object should in the first place be created with the correct parent.
- sc$super <- ScaleContinuousPosition
- class(sc) <- class(ScaleContinuousPosition)
-
+ if (!is.waive(sec.axis)) {
+ if (is.formula(sec.axis)) sec.axis <- sec_axis(sec.axis)
+ if (!is.sec_axis(sec.axis)) stop("Secondary axes must be specified using 'sec_axis()'")
+ sc$secondary.axis <- sec.axis
+ }
sc
}
@@ -125,12 +121,35 @@ scale_y_continuous <- function(name = waiver(), breaks = waiver(),
#' @usage NULL
#' @export
ScaleContinuousPosition <- ggproto("ScaleContinuousPosition", ScaleContinuous,
+ secondary.axis = waiver(),
# Position aesthetics don't map, because the coordinate system takes
# care of it. But they do need to be made in to doubles, so stat methods
# can tell the difference between continuous and discrete data.
map = function(self, x, limits = self$get_limits()) {
scaled <- as.numeric(self$oob(x, limits))
ifelse(!is.na(scaled), scaled, self$na.value)
+ },
+ break_info = function(self, range = NULL) {
+ breaks <- ggproto_parent(ScaleContinuous, self)$break_info(range)
+ if (!(is.waive(self$secondary.axis) || self$secondary.axis$empty())) {
+ self$secondary.axis$init(self)
+ breaks <- c(breaks, self$secondary.axis$break_info(breaks$range, self))
+ }
+ breaks
+ },
+ sec_name = function(self) {
+ if (is.waive(self$secondary.axis)) {
+ waiver()
+ } else {
+ self$secondary.axis$name
+ }
+ },
+ make_sec_title = function(self, title) {
+ if (!is.waive(self$secondary.axis)) {
+ self$secondary.axis$make_title(title)
+ } else {
+ ggproto_parent(ScaleContinuous, self)$make_sec_title(title)
+ }
}
)
diff --git a/R/scale-date.r b/R/scale-date.r
index a437adf..94e67f5 100644
--- a/R/scale-date.r
+++ b/R/scale-date.r
@@ -1,10 +1,13 @@
-#' Position scale, date & date times
+#' Position scales for date/time data
#'
-#' Use \code{scale_*_date} with \code{Date} variables, and
-#' \code{scale_*_datetime} with \code{POSIXct} variables.
+#' These are the default scales for the three date/time class. These will
+#' usually be added automatically. To override manually, use
+#' \code{scale_*_date} for dates (class \code{Date}),
+#' \code{scale_*_datetime} for datetimes (class \code{POSIXct}), and
+#' \code{scale_*_time} for times (class \code{hms}).
#'
-#' @name scale_date
#' @inheritParams continuous_scale
+#' @inheritParams scale_x_continuous
#' @param date_breaks A string giving the distance between breaks like "2
#' weeks", or "10 years". If both \code{breaks} and \code{date_breaks} are
#' specified, \code{date_breaks} wins.
@@ -14,7 +17,9 @@
#' @param date_labels A string giving the formatting specification for the
#' labels. Codes are defined in \code{\link{strftime}}. If both \code{labels}
#' and \code{date_labels} are specified, \code{date_labels} wins.
-#' @seealso \code{\link{scale_continuous}} for continuous position scales.
+#' @param timezone The timezone to use for display on the axes. The default
+#' (\code{NULL}) uses the timezone encoded in the data.
+#' @family position scales
#' @examples
#' last_month <- Sys.Date() - 0:29
#' df <- data.frame(
@@ -33,6 +38,8 @@
#'
#' # Set limits
#' base + scale_x_date(limits = c(Sys.Date() - 7, NA))
+#' @name scale_date
+#' @aliases NULL
NULL
#' @rdname scale_date
@@ -41,14 +48,14 @@ scale_x_date <- function(name = waiver(),
breaks = waiver(), date_breaks = waiver(),
labels = waiver(), date_labels = waiver(),
minor_breaks = waiver(), date_minor_breaks = waiver(),
- limits = NULL, expand = waiver()) {
+ limits = NULL, expand = waiver(), position = "bottom") {
scale_datetime(c("x", "xmin", "xmax", "xend"), "date",
name = name,
breaks = breaks, date_breaks = date_breaks,
labels = labels, date_labels = date_labels,
minor_breaks = minor_breaks, date_minor_breaks = date_minor_breaks,
- limits = limits, expand = expand
+ limits = limits, expand = expand, position = position
)
}
@@ -58,32 +65,32 @@ scale_y_date <- function(name = waiver(),
breaks = waiver(), date_breaks = waiver(),
labels = waiver(), date_labels = waiver(),
minor_breaks = waiver(), date_minor_breaks = waiver(),
- limits = NULL, expand = waiver()) {
+ limits = NULL, expand = waiver(), position = "left") {
scale_datetime(c("y", "ymin", "ymax", "yend"), "date",
name = name,
breaks = breaks, date_breaks = date_breaks,
labels = labels, date_labels = date_labels,
minor_breaks = minor_breaks, date_minor_breaks = date_minor_breaks,
- limits = limits, expand = expand
+ limits = limits, expand = expand, position = position
)
}
-
#' @export
#' @rdname scale_date
scale_x_datetime <- function(name = waiver(),
breaks = waiver(), date_breaks = waiver(),
labels = waiver(), date_labels = waiver(),
minor_breaks = waiver(), date_minor_breaks = waiver(),
- limits = NULL, expand = waiver()) {
+ timezone = NULL, limits = NULL, expand = waiver(),
+ position = "bottom") {
scale_datetime(c("x", "xmin", "xmax", "xend"), "time",
name = name,
breaks = breaks, date_breaks = date_breaks,
labels = labels, date_labels = date_labels,
minor_breaks = minor_breaks, date_minor_breaks = date_minor_breaks,
- limits = limits, expand = expand
+ timezone = timezone, limits = limits, expand = expand, position = position
)
}
@@ -94,14 +101,70 @@ scale_y_datetime <- function(name = waiver(),
breaks = waiver(), date_breaks = waiver(),
labels = waiver(), date_labels = waiver(),
minor_breaks = waiver(), date_minor_breaks = waiver(),
- limits = NULL, expand = waiver()) {
+ timezone = NULL, limits = NULL, expand = waiver(),
+ position = "left") {
scale_datetime(c("y", "ymin", "ymax", "yend"), "time",
name = name,
breaks = breaks, date_breaks = date_breaks,
labels = labels, date_labels = date_labels,
minor_breaks = minor_breaks, date_minor_breaks = date_minor_breaks,
- limits = limits, expand = expand
+ timezone = timezone, limits = limits, expand = expand, position = position
+ )
+}
+
+
+
+#' @export
+#' @rdname scale_date
+scale_x_time <- function(name = waiver(),
+ breaks = waiver(),
+ minor_breaks = waiver(),
+ labels = waiver(),
+ limits = NULL,
+ expand = waiver(),
+ oob = censor,
+ na.value = NA_real_,
+ position = "bottom") {
+
+ scale_x_continuous(
+ name = name,
+ breaks = breaks,
+ labels = labels,
+ minor_breaks = minor_breaks,
+ limits = limits,
+ expand = expand,
+ oob = oob,
+ na.value = na.value,
+ position = position,
+ trans = scales::hms_trans()
+ )
+}
+
+
+#' @rdname scale_date
+#' @export
+scale_y_time <- function(name = waiver(),
+ breaks = waiver(),
+ minor_breaks = waiver(),
+ labels = waiver(),
+ limits = NULL,
+ expand = waiver(),
+ oob = censor,
+ na.value = NA_real_,
+ position = "left") {
+
+ scale_y_continuous(
+ name = name,
+ breaks = breaks,
+ labels = labels,
+ minor_breaks = minor_breaks,
+ limits = limits,
+ expand = expand,
+ oob = oob,
+ na.value = na.value,
+ position = position,
+ trans = scales::hms_trans()
)
}
@@ -109,10 +172,9 @@ scale_datetime <- function(aesthetics, trans,
breaks = pretty_breaks(), minor_breaks = waiver(),
labels = waiver(), date_breaks = waiver(),
date_labels = waiver(),
- date_minor_breaks = waiver(),
+ date_minor_breaks = waiver(), timezone = NULL,
...) {
- name <- switch(trans, date = "date", time = "datetime")
# Backward compatibility
if (is.character(breaks)) breaks <- date_breaks(breaks)
@@ -125,18 +187,26 @@ scale_datetime <- function(aesthetics, trans,
minor_breaks <- date_breaks(date_minor_breaks)
}
if (!is.waive(date_labels)) {
- labels <- date_format(date_labels)
+ labels <- function(self, x) {
+ tz <- if (is.null(self$timezone)) "UTC" else self$timezone
+ date_format(date_labels, tz)(x)
+ }
}
- sc <- continuous_scale(aesthetics, name, identity,
+ name <- switch(trans,
+ date = "date",
+ time = "datetime"
+ )
+ scale_class <- switch(trans,
+ date = ScaleContinuousDate,
+ time = ScaleContinuousDatetime
+ )
+ sc <- continuous_scale(
+ aesthetics, name, identity,
breaks = breaks, minor_breaks = minor_breaks, labels = labels,
- guide = "none", trans = trans, ...)
-
- # TODO: Fix this hack. We're reassigning the parent ggproto object, but this
- # object should in the first place be created with the correct parent.
- scale_class <- switch(trans, date = ScaleContinuousDate, time = ScaleContinuousDatetime)
- sc$super <- scale_class
- class(sc) <- class(scale_class)
+ guide = "none", trans = trans, ..., super = scale_class
+ )
+ sc$timezone <- timezone
sc
}
@@ -146,6 +216,15 @@ scale_datetime <- function(aesthetics, trans,
#' @usage NULL
#' @export
ScaleContinuousDatetime <- ggproto("ScaleContinuousDatetime", ScaleContinuous,
+ timezone = NULL,
+ transform = function(self, x) {
+ tz <- attr(x, "tzone")
+ if (is.null(self$timezone) && !is.null(tz)) {
+ self$timezone <- tz
+ self$trans <- time_trans(self$timezone)
+ }
+ ggproto_parent(ScaleContinuous, self)$transform(x)
+ },
map = function(self, x, limits = self$get_limits()) {
self$oob(x, limits)
}
diff --git a/R/scale-discrete-.r b/R/scale-discrete-.r
index 304d5c8..3c29c0b 100644
--- a/R/scale-discrete-.r
+++ b/R/scale-discrete-.r
@@ -1,4 +1,4 @@
-#' Discrete position.
+#' Position scales for discrete data
#'
#' You can use continuous positions even with a discrete position scale -
#' this allows you (e.g.) to place labels between bars in a bar chart.
@@ -12,7 +12,10 @@
#' @param expand a numeric vector of length two giving multiplicative and
#' additive expansion constants. These constants ensure that the data is
#' placed some distance away from the axes.
+#' @param position The position of the axis. \code{left} or \code{right} for y
+#' axes, \code{top} or \code{bottom} for x axes
#' @rdname scale_discrete
+#' @family position scales
#' @export
#' @examples
#' ggplot(diamonds, aes(cut)) + geom_bar()
@@ -30,7 +33,7 @@
#'
#' # Use limits to adjust the which levels (and in what order)
#' # are displayed
-#' d + scale_x_discrete(limits=c("Fair","Ideal"))
+#' d + scale_x_discrete(limits = c("Fair","Ideal"))
#'
#' # you can also use the short hand functions xlim and ylim
#' d + xlim("Fair","Ideal", "Good")
@@ -46,28 +49,18 @@
#' geom_point() +
#' scale_x_discrete(labels = abbreviate)
#' }
-scale_x_discrete <- function(..., expand = waiver()) {
+scale_x_discrete <- function(..., expand = waiver(), position = "bottom") {
sc <- discrete_scale(c("x", "xmin", "xmax", "xend"), "position_d", identity, ...,
- expand = expand, guide = "none")
-
- # TODO: Fix this hack. We're reassigning the parent ggproto object, but this
- # object should in the first place be created with the correct parent.
- sc$super <- ScaleDiscretePosition
- class(sc) <- class(ScaleDiscretePosition)
+ expand = expand, guide = "none", position = position, super = ScaleDiscretePosition)
sc$range_c <- continuous_range()
sc
}
#' @rdname scale_discrete
#' @export
-scale_y_discrete <- function(..., expand = waiver()) {
+scale_y_discrete <- function(..., expand = waiver(), position = "left") {
sc <- discrete_scale(c("y", "ymin", "ymax", "yend"), "position_d", identity, ...,
- expand = expand, guide = "none")
-
- # TODO: Fix this hack. We're reassigning the parent ggproto object, but this
- # object should in the first place be created with the correct parent.
- sc$super <- ScaleDiscretePosition
- class(sc) <- class(ScaleDiscretePosition)
+ expand = expand, guide = "none", position = position, super = ScaleDiscretePosition)
sc$range_c <- continuous_range()
sc
@@ -83,10 +76,9 @@ scale_y_discrete <- function(..., expand = waiver()) {
#' @usage NULL
#' @export
ScaleDiscretePosition <- ggproto("ScaleDiscretePosition", ScaleDiscrete,
-
train = function(self, x) {
if (is.discrete(x)) {
- self$range$train(x, drop = self$drop)
+ self$range$train(x, drop = self$drop, na.rm = !self$na.translate)
} else {
self$range_c$train(x)
}
@@ -94,6 +86,7 @@ ScaleDiscretePosition <- ggproto("ScaleDiscretePosition", ScaleDiscrete,
get_limits = function(self) {
if (self$is_empty()) return(c(0, 1))
+
self$limits %||% self$range$range %||% integer()
},
@@ -116,14 +109,14 @@ ScaleDiscretePosition <- ggproto("ScaleDiscretePosition", ScaleDiscrete,
dimension = function(self, expand = c(0, 0)) {
c_range <- self$range_c$range
- d_range <- self$range$range
+ d_range <- self$get_limits()
if (self$is_empty()) {
c(0, 1)
- } else if (is.null(d_range)) { # only continuous
- expand_range(c_range, expand[1], 0 , 1)
+ } else if (is.null(self$range$range)) { # only continuous
+ expand_range(c_range, expand[1], expand[2] , 1)
} else if (is.null(c_range)) { # only discrete
- expand_range(c(1, length(d_range)), 0, expand[2], 1)
+ expand_range(c(1, length(d_range)), expand[1], expand[2], 1)
} else { # both
range(
expand_range(c_range, expand[1], 0 , 1),
@@ -132,6 +125,10 @@ ScaleDiscretePosition <- ggproto("ScaleDiscretePosition", ScaleDiscrete,
}
},
+ get_breaks = function(self, limits = self$get_limits()) {
+ ggproto_parent(ScaleDiscrete, self)$get_breaks(limits)
+ },
+
clone = function(self) {
new <- ggproto(NULL, self)
new$range <- discrete_range()
diff --git a/R/scale-gradient.r b/R/scale-gradient.r
index ad57695..8424ccb 100644
--- a/R/scale-gradient.r
+++ b/R/scale-gradient.r
@@ -1,11 +1,11 @@
-#' Smooth gradient between two colours
+#' Gradient colour scales
#'
#' \code{scale_*_gradient} creates a two colour gradient (low-high),
#' \code{scale_*_gradient2} creates a diverging colour gradient (low-mid-high),
#' \code{scale_*_gradientn} creats a n-colour gradient.
#'
#' Default colours are generated with \pkg{munsell} and
-#' \code{mnsl(c("2.5PB 2/4", "2.5PB 7/10")}. Generally, for continuous
+#' \code{mnsl(c("2.5PB 2/4", "2.5PB 7/10"))}. Generally, for continuous
#' colour scales you want to keep hue constant, but vary chroma and
#' luminance. The \pkg{munsell} package makes this easy to do using the
#' Munsell colour system.
@@ -15,12 +15,11 @@
#' @param low,high Colours for low and high ends of the gradient.
#' @param guide Type of legend. Use \code{"colourbar"} for continuous
#' colour bar, or \code{"legend"} for discrete colour legend.
+#' @param ... Other arguments passed on to \code{\link{continuous_scale}}
+#' to control name, limits, breaks, labels and so forth.
#' @seealso \code{\link[scales]{seq_gradient_pal}} for details on underlying
#' palette
-#' @seealso Other colour scales:
-#' \code{\link{scale_colour_brewer}},
-#' \code{\link{scale_colour_grey}},
-#' \code{\link{scale_colour_hue}}
+#' @family colour scales
#' @rdname scale_gradient
#' @export
#' @examples
diff --git a/R/scale-grey.r b/R/scale-grey.r
index fc941f5..e333aac 100644
--- a/R/scale-grey.r
+++ b/R/scale-grey.r
@@ -1,13 +1,11 @@
-#' Sequential grey colour scale.
+#' Sequential grey colour scales
#'
-#' Based on \code{\link{gray.colors}}
+#' Based on \code{\link{gray.colors}}. This is black and white equivalent
+#' of \code{\link{scale_colour_gradient}}.
#'
#' @inheritParams scales::grey_pal
#' @inheritParams scale_colour_hue
-#' @seealso Other colour scales:
-#' \code{\link{scale_colour_brewer}},
-#' \code{\link{scale_colour_gradient}},
-#' \code{\link{scale_colour_hue}}
+#' @family colour scales
#' @rdname scale_grey
#' @export
#' @examples
diff --git a/R/scale-hue.r b/R/scale-hue.r
index 39c578f..0a93b36 100644
--- a/R/scale-hue.r
+++ b/R/scale-hue.r
@@ -1,4 +1,8 @@
-#' Qualitative colour scale with evenly spaced hues.
+#' Evenly spaced colours for discrete data
+#'
+#' This is the default colour scale for categorical variables. It maps each
+#' level to an evenly spaced hue on the colour wheel. It does not generate
+#' colour-blind safe palettes.
#'
#' @param na.value Colour to use for missing values
#' @param ... Other arguments passed on to \code{\link{discrete_scale}}
@@ -6,10 +10,7 @@
#' @inheritParams scales::hue_pal
#' @rdname scale_hue
#' @export
-#' @seealso Other colour scales:
-#' \code{\link{scale_colour_brewer}},
-#' \code{\link{scale_colour_gradient}},
-#' \code{\link{scale_colour_grey}}
+#' @family colour scales
#' @examples
#' \donttest{
#' dsamp <- diamonds[sample(nrow(diamonds), 1000), ]
@@ -21,16 +22,16 @@
#' d + scale_colour_hue(expression(clarity[beta]))
#'
#' # Adjust luminosity and chroma
-#' d + scale_colour_hue(l=40, c=30)
-#' d + scale_colour_hue(l=70, c=30)
-#' d + scale_colour_hue(l=70, c=150)
-#' d + scale_colour_hue(l=80, c=150)
+#' d + scale_colour_hue(l = 40, c = 30)
+#' d + scale_colour_hue(l = 70, c = 30)
+#' d + scale_colour_hue(l = 70, c = 150)
+#' d + scale_colour_hue(l = 80, c = 150)
#'
#' # Change range of hues used
-#' d + scale_colour_hue(h=c(0, 90))
-#' d + scale_colour_hue(h=c(90, 180))
-#' d + scale_colour_hue(h=c(180, 270))
-#' d + scale_colour_hue(h=c(270, 360))
+#' d + scale_colour_hue(h = c(0, 90))
+#' d + scale_colour_hue(h = c(90, 180))
+#' d + scale_colour_hue(h = c(180, 270))
+#' d + scale_colour_hue(h = c(270, 360))
#'
#' # Vary opacity
#' # (only works with pdf, quartz and cairo devices)
diff --git a/R/scale-identity.r b/R/scale-identity.r
index 4c771cc..5c25b24 100644
--- a/R/scale-identity.r
+++ b/R/scale-identity.r
@@ -1,9 +1,13 @@
-#' Use values without scaling.
+#' Use values without scaling
+#'
+#' Use this set of scales when your data has already been scaled, i.e. it
+#' already represents aesthetic values that ggplot2 can handle directly
+#' This will not produce a legend unless you also supply the \code{breaks}
+#' and \code{labels}.
#'
-#' @name scale_identity
#' @param ... Other arguments passed on to \code{\link{discrete_scale}} or
#' \code{\link{continuous_scale}}
-#' @param guide Guide to use for this scale - defaults to \code{"none"}.
+#' @param guide Guide to use for this scale. Defaults to \code{"none"}.
#' @examples
#' ggplot(luv_colours, aes(u, v)) +
#' geom_point(aes(colour = col), size = 3) +
@@ -31,83 +35,68 @@
#' guide = "legend")
#'
#' # cyl scaled to appropriate size
-#' ggplot(mtcars, aes(mpg, wt)) + geom_point(aes(size = cyl))
+#' ggplot(mtcars, aes(mpg, wt)) +
+#' geom_point(aes(size = cyl))
#'
#' # cyl used as point size
#' ggplot(mtcars, aes(mpg, wt)) +
#' geom_point(aes(size = cyl)) +
#' scale_size_identity()
+#' @name scale_identity
+#' @aliases NULL
NULL
#' @rdname scale_identity
#' @export
scale_colour_identity <- function(..., guide = "none") {
- sc <- discrete_scale("colour", "identity", identity_pal(), ..., guide = guide)
+ sc <- discrete_scale("colour", "identity", identity_pal(), ..., guide = guide,
+ super = ScaleDiscreteIdentity)
- # TODO: Fix this hack. We're reassigning the parent ggproto object, but this
- # object should in the first place be created with the correct parent.
- sc$super <- ScaleDiscreteIdentity
- class(sc) <- class(ScaleDiscreteIdentity)
sc
}
#' @rdname scale_identity
#' @export
scale_fill_identity <- function(..., guide = "none") {
- sc <- discrete_scale("fill", "identity", identity_pal(), ..., guide = guide)
+ sc <- discrete_scale("fill", "identity", identity_pal(), ..., guide = guide,
+ super = ScaleDiscreteIdentity)
- # TODO: Fix this hack. We're reassigning the parent ggproto object, but this
- # object should in the first place be created with the correct parent.
- sc$super <- ScaleDiscreteIdentity
- class(sc) <- class(ScaleDiscreteIdentity)
sc
}
#' @rdname scale_identity
#' @export
scale_shape_identity <- function(..., guide = "none") {
- sc <- continuous_scale("shape", "identity", identity_pal(), ..., guide = guide)
+ sc <- continuous_scale("shape", "identity", identity_pal(), ..., guide = guide,
+ super = ScaleDiscreteIdentity)
- # TODO: Fix this hack. We're reassigning the parent ggproto object, but this
- # object should in the first place be created with the correct parent.
- sc$super <- ScaleContinuousIdentity
- class(sc) <- class(ScaleContinuousIdentity)
sc
}
#' @rdname scale_identity
#' @export
scale_linetype_identity <- function(..., guide = "none") {
- sc <- discrete_scale("linetype", "identity", identity_pal(), ..., guide = guide)
+ sc <- discrete_scale("linetype", "identity", identity_pal(), ..., guide = guide,
+ super = ScaleDiscreteIdentity)
- # TODO: Fix this hack. We're reassigning the parent ggproto object, but this
- # object should in the first place be created with the correct parent.
- sc$super <- ScaleDiscreteIdentity
- class(sc) <- class(ScaleDiscreteIdentity)
sc
}
#' @rdname scale_identity
#' @export
scale_alpha_identity <- function(..., guide = "none") {
- sc <- continuous_scale("alpha", "identity", identity_pal(), ..., guide = guide)
+ sc <- continuous_scale("alpha", "identity", identity_pal(), ..., guide = guide,
+ super = ScaleContinuousIdentity)
- # TODO: Fix this hack. We're reassigning the parent ggproto object, but this
- # object should in the first place be created with the correct parent.
- sc$super <- ScaleContinuousIdentity
- class(sc) <- class(ScaleContinuousIdentity)
sc
}
#' @rdname scale_identity
#' @export
scale_size_identity <- function(..., guide = "none") {
- sc <- continuous_scale("size", "identity", identity_pal(), ..., guide = guide)
+ sc <- continuous_scale("size", "identity", identity_pal(), ..., guide = guide,
+ super = ScaleContinuousIdentity)
- # TODO: Fix this hack. We're reassigning the parent ggproto object, but this
- # object should in the first place be created with the correct parent.
- sc$super <- ScaleContinuousIdentity
- class(sc) <- class(ScaleContinuousIdentity)
sc
}
diff --git a/R/scale-linetype.r b/R/scale-linetype.r
index f505b60..1050ee5 100644
--- a/R/scale-linetype.r
+++ b/R/scale-linetype.r
@@ -1,8 +1,8 @@
-#' Scale for line patterns.
+#' Scale for line patterns
#'
#' Default line types based on a set supplied by Richard Pearson,
-#' University of Manchester. Line types can not be mapped to continuous
-#' values.
+#' University of Manchester. Continuous values can not be mapped to
+#' line types.
#'
#' @inheritParams scale_x_discrete
#' @param na.value The linetype to use for \code{NA} values.
@@ -14,6 +14,19 @@
#' base + geom_line(aes(linetype = variable))
#'
#' # See scale_manual for more flexibility
+#'
+#' # Common line types ----------------------------
+#' df_lines <- data.frame(
+#' linetype = factor(
+#' 1:4,
+#' labels = c("solid", "longdash", "dashed", "dotted")
+#' )
+#' )
+#' ggplot(df_lines) +
+#' geom_hline(aes(linetype = linetype, yintercept = 0), size = 2) +
+#' scale_linetype_identity() +
+#' facet_grid(linetype ~ .) +
+#' theme_void(20)
scale_linetype <- function(..., na.value = "blank") {
discrete_scale("linetype", "linetype_d", linetype_pal(),
na.value = na.value, ...)
diff --git a/R/scale-manual.r b/R/scale-manual.r
index 4ff4037..8e6b10d 100644
--- a/R/scale-manual.r
+++ b/R/scale-manual.r
@@ -1,40 +1,37 @@
-#' Create your own discrete scale.
+#' Create your own discrete scale
+#'
+#' This allows you to specify you own set of mappings from levels in the
+#' data to aesthetic values.
#'
-#' @name scale_manual
#' @inheritParams scale_x_discrete
-#' @param values a set of aesthetic values to map data values to. If this
+#' @param values a set of aesthetic values to map data values to. If this
#' is a named vector, then the values will be matched based on the names.
#' If unnamed, values will be matched in order (usually alphabetical) with
-#' the limits of the scale. Any data values that don't match will be
+#' the limits of the scale. Any data values that don't match will be
#' given \code{na.value}.
#' @examples
-#' \donttest{
#' p <- ggplot(mtcars, aes(mpg, wt)) +
#' geom_point(aes(colour = factor(cyl)))
+#' p + scale_colour_manual(values = c("red", "blue", "green"))
#'
-#' p + scale_colour_manual(values = c("red","blue", "green"))
-#' p + scale_colour_manual(
-#' values = c("8" = "red","4" = "blue","6" = "green"))
-#' # With rgb hex values
-#' p + scale_colour_manual(values = c("#FF0000", "#0000FF", "#00FF00"))
+#' # It's recommended to use a named vector
+#' cols <- c("8" = "red", "4" = "blue", "6" = "darkgreen", "10" = "orange")
+#' p + scale_colour_manual(values = cols)
#'
#' # As with other scales you can use breaks to control the appearance
-#' # of the legend
-#' cols <- c("8" = "red","4" = "blue","6" = "darkgreen", "10" = "orange")
+#' # of the legend.
#' p + scale_colour_manual(values = cols)
-#' p + scale_colour_manual(values = cols, breaks = c("4", "6", "8"))
-#' p + scale_colour_manual(values = cols, breaks = c("8", "6", "4"))
-#' p + scale_colour_manual(values = cols, breaks = c("4", "6", "8"),
-#' labels = c("four", "six", "eight"))
+#' p + scale_colour_manual(
+#' values = cols,
+#' breaks = c("4", "6", "8"),
+#' labels = c("four", "six", "eight")
+#' )
#'
#' # And limits to control the possible values of the scale
#' p + scale_colour_manual(values = cols, limits = c("4", "8"))
#' p + scale_colour_manual(values = cols, limits = c("4", "6", "8", "10"))
-#'
-#' # Notice that the values are matched with limits, and not breaks
-#' p + scale_colour_manual(limits = c(6, 8, 4), breaks = c(8, 4, 6),
-#' values = c("grey50", "grey80", "black"))
-#' }
+#' @name scale_manual
+#' @aliases NULL
NULL
#' @rdname scale_manual
@@ -73,7 +70,6 @@ scale_alpha_manual <- function(..., values) {
manual_scale("alpha", values, ...)
}
-
manual_scale <- function(aesthetic, values, ...) {
pal <- function(n) {
if (n > length(values)) {
diff --git a/R/scale-shape.r b/R/scale-shape.r
index 4b602a9..9cc29e5 100644
--- a/R/scale-shape.r
+++ b/R/scale-shape.r
@@ -1,8 +1,13 @@
-#' Scale for shapes, aka glyphs.
+#' Scales for shapes, aka glyphs
#'
-#' A continuous variable can not be mapped to shape.
+#' \code{scale_shape} maps discrete variables to six easily discernible shapes.
+#' If you have more than six levels, you will get a warning message, and the
+#' seventh and subsequence levels will not appear on the plot. Use
+#' \code{\link{scale_shape_manual}} to supply your own values. You can not map
+#' a continuous variable to shape.
#'
-#' @param solid Are the shapes solid, \code{TRUE}, or hollow \code{FALSE}?
+#' @param solid Should the shapes be solid, \code{TRUE}, or hollow,
+#' \code{FALSE}?
#' @inheritParams scale_x_discrete
#' @rdname scale_shape
#' @export
@@ -13,7 +18,6 @@
#' d + scale_shape(solid = TRUE) # the default
#' d + scale_shape(solid = FALSE)
#' d + scale_shape(name = "Cut of diamond")
-#' d + scale_shape(name = "Cut of\ndiamond")
#'
#' # To change order of levels, change order of
#' # underlying factor
@@ -22,8 +26,13 @@
#' # Need to recreate plot to pick up new data
#' ggplot(dsmall, aes(price, carat)) + geom_point(aes(shape = cut))
#'
-#' # Or for short:
-#' d %+% dsmall
+#' # Show a list of available shapes
+#' df_shapes <- data.frame(shape = 0:24)
+#' ggplot(df_shapes, aes(0, 0, shape = shape)) +
+#' geom_point(aes(shape = shape), size = 5, fill = 'red') +
+#' scale_shape_identity() +
+#' facet_wrap(~shape) +
+#' theme_void()
scale_shape <- function(..., solid = TRUE) {
discrete_scale("shape", "shape_d", shape_pal(solid), ...)
}
diff --git a/R/scale-size.r b/R/scale-size.r
index a8da394..b7ca196 100644
--- a/R/scale-size.r
+++ b/R/scale-size.r
@@ -1,4 +1,4 @@
-#' Scale size (area or radius).
+#' Scales for area or radius
#'
#' \code{scale_size} scales area, \code{scale_radius} scales radius. The size
#' aesthetic is most commonly used for points and text, and humans perceive
diff --git a/R/scale-type.R b/R/scale-type.R
index b08b1f9..f6583f7 100644
--- a/R/scale-type.R
+++ b/R/scale-type.R
@@ -64,3 +64,6 @@ scale_type.Date <- function(x) c("date", "continuous")
#' @export
scale_type.numeric <- function(x) "continuous"
+
+#' @export
+scale_type.hms <- function(x) "time"
diff --git a/R/stat-.r b/R/stat-.r
index 25879f5..4923adc 100644
--- a/R/stat-.r
+++ b/R/stat-.r
@@ -30,13 +30,18 @@
#' before the facets are trained, so they are global scales, not local
#' to the individual panels.\code{...} contains the parameters returned by
#' \code{setup_params()}.
+#' \item \code{finish_layer(data, params)}: called once for each layer. Used
+#' to modify the data after scales has been applied, but before the data is
+#' handed of to the geom for rendering. The default is to not modify the
+#' data. Use this hook if the stat needs access to the actual aesthetic
+#' values rather than the values that are mapped to the aesthetic.
#' \item \code{setup_params(data, params)}: called once for each layer.
-#' Used to setup defaults that need to complete dataset, and to inform
-#' the user of important choices. Should return list of parameters.
+#' Used to setup defaults that need to complete dataset, and to inform
+#' the user of important choices. Should return list of parameters.
#' \item \code{setup_data(data, params)}: called once for each layer,
-#' after \code{setp_params()}. Should return modified \code{data}.
-#' Default methods removes all rows containing a missing value in
-#' required aesthetics (with a warning if \code{!na.rm}).
+#' after \code{setp_params()}. Should return modified \code{data}.
+#' Default methods removes all rows containing a missing value in
+#' required aesthetics (with a warning if \code{!na.rm}).
#' \item \code{required_aes}: A character vector of aesthetics needed to
#' render the geom.
#' \item \code{default_aes}: A list (generated by \code{\link{aes}()} of
@@ -66,11 +71,11 @@ Stat <- ggproto("Stat",
data
},
- compute_layer = function(self, data, params, panels) {
+ compute_layer = function(self, data, params, layout) {
check_required_aesthetics(
- self$stat$required_aes,
+ self$required_aes,
c(names(data), names(params)),
- snake_class(self$stat)
+ snake_class(self)
)
data <- remove_missing(data, params$na.rm,
@@ -84,7 +89,7 @@ Stat <- ggproto("Stat",
args <- c(list(data = quote(data), scales = quote(scales)), params)
plyr::ddply(data, "PANEL", function(data) {
- scales <- panel_scales(panels, data$PANEL[1])
+ scales <- layout$get_scales(data$PANEL[1])
tryCatch(do.call(self$compute_panel, args), error = function(e) {
warning("Computation failed in `", snake_class(self), "()`:\n",
e$message, call. = FALSE)
@@ -118,6 +123,10 @@ Stat <- ggproto("Stat",
stop("Not implemented", call. = FALSE)
},
+ finish_layer = function(self, data, params) {
+ data
+ },
+
# See discussion at Geom$parameters()
extra_params = "na.rm",
@@ -134,5 +143,10 @@ Stat <- ggproto("Stat",
args <- union(args, self$extra_params)
}
args
+ },
+
+ aesthetics = function(self) {
+ c(union(self$required_aes, names(self$default_aes)), "group")
}
+
)
diff --git a/R/stat-bin.r b/R/stat-bin.r
index b6a9f3d..d7e5e3e 100644
--- a/R/stat-bin.r
+++ b/R/stat-bin.r
@@ -12,14 +12,17 @@
#' @param center The center of one of the bins. Note that if center is above or
#' below the range of the data, things will be shifted by an appropriate
#' number of \code{width}s. To center on integers, for example, use
-#' \code{width=1} and \code{center=0}, even if \code{0} is outside the range
+#' \code{width = 1} and \code{center = 0}, even if \code{0} is outside the range
#' of the data. At most one of \code{center} and \code{boundary} may be
#' specified.
#' @param boundary A boundary between two bins. As with \code{center}, things
#' are shifted when \code{boundary} is outside the range of the data. For
#' example, to center on integers, use \code{width = 1} and \code{boundary =
-#' 0.5}, even if \code{1} is outside the range of the data. At most one of
+#' 0.5}, even if \code{0.5} is outside the range of the data. At most one of
#' \code{center} and \code{boundary} may be specified.
+#' @param breaks Alternatively, you can supply a numeric vector giving
+#' the bin boundaries. Overrides \code{binwidth}, \code{bins}, \code{center},
+#' and \code{boundary}.
#' @param closed One of \code{"right"} or \code{"left"} indicating whether right
#' or left edges of bins are included in the bin.
#' @param pad If \code{TRUE}, adds empty bins at either end of x. This ensures
@@ -44,6 +47,7 @@ stat_bin <- function(mapping = NULL, data = NULL,
bins = NULL,
center = NULL,
boundary = NULL,
+ breaks = NULL,
closed = c("right", "left"),
pad = FALSE,
na.rm = FALSE,
@@ -63,6 +67,7 @@ stat_bin <- function(mapping = NULL, data = NULL,
bins = bins,
center = center,
boundary = boundary,
+ breaks = breaks,
closed = closed,
pad = pad,
na.rm = na.rm,
diff --git a/R/stat-binhex.r b/R/stat-binhex.r
index d64761c..d960f49 100644
--- a/R/stat-binhex.r
+++ b/R/stat-binhex.r
@@ -36,7 +36,7 @@ stat_binhex <- stat_bin_hex
#' @usage NULL
#' @export
StatBinhex <- ggproto("StatBinhex", Stat,
- default_aes = aes(fill = ..value..),
+ default_aes = aes(weight = 1, fill = ..count..),
required_aes = c("x", "y"),
@@ -46,7 +46,12 @@ StatBinhex <- ggproto("StatBinhex", Stat,
binwidth <- binwidth %||% hex_binwidth(bins, scales)
wt <- data$weight %||% rep(1L, nrow(data))
- hexBinSummarise(data$x, data$y, wt, binwidth, sum)
+ out <- hexBinSummarise(data$x, data$y, wt, binwidth, sum)
+ out$density <- as.vector(out$value / sum(out$value, na.rm = TRUE))
+ out$count <- out$value
+ out$value <- NULL
+
+ out
}
)
diff --git a/R/stat-boxplot.r b/R/stat-boxplot.r
index 8909ae1..96ca854 100644
--- a/R/stat-boxplot.r
+++ b/R/stat-boxplot.r
@@ -46,7 +46,7 @@ StatBoxplot <- ggproto("StatBoxplot", Stat,
non_missing_aes = "weight",
setup_params = function(data, params) {
- params$width <- params$width %||% resolution(data$x) * 0.75
+ params$width <- params$width %||% (resolution(data$x) * 0.75)
if (is.double(data$x) && !has_groups(data) && any(data$x != data$x[1L])) {
warning(
diff --git a/R/stat-contour.r b/R/stat-contour.r
index 7eb6ac9..a421973 100644
--- a/R/stat-contour.r
+++ b/R/stat-contour.r
@@ -66,6 +66,11 @@ StatContour <- ggproto("StatContour", Stat,
contour_lines <- function(data, breaks, complete = FALSE) {
z <- tapply(data$z, data[c("x", "y")], identity)
+ if (is.list(z)) {
+ stop("Contour requires single `z` at each combination of `x` and `y`.",
+ call. = FALSE)
+ }
+
cl <- grDevices::contourLines(
x = sort(unique(data$x)), y = sort(unique(data$y)), z = z,
levels = breaks)
diff --git a/R/stat-count.r b/R/stat-count.r
index c0b7285..ac700a8 100644
--- a/R/stat-count.r
+++ b/R/stat-count.r
@@ -1,6 +1,3 @@
-#' \code{stat_count} counts the number of cases at each x position. If you want
-#' to bin the data in ranges, you should use \code{\link{stat_bin}} instead.
-#'
#' @section Computed variables:
#' \describe{
#' \item{count}{number of points in bin}
@@ -21,6 +18,16 @@ stat_count <- function(mapping = NULL, data = NULL,
na.rm = FALSE,
show.legend = NA,
inherit.aes = TRUE) {
+
+ params <- list(
+ na.rm = na.rm,
+ width = width,
+ ...
+ )
+ if (!is.null(params$y)) {
+ stop("stat_count() must not be used with a y aesthetic.", call. = FALSE)
+ }
+
layer(
data = data,
mapping = mapping,
@@ -29,11 +36,7 @@ stat_count <- function(mapping = NULL, data = NULL,
position = position,
show.legend = show.legend,
inherit.aes = inherit.aes,
- params = list(
- na.rm = na.rm,
- width = width,
- ...
- )
+ params = params
)
}
@@ -44,10 +47,10 @@ stat_count <- function(mapping = NULL, data = NULL,
#' @include stat-.r
StatCount <- ggproto("StatCount", Stat,
required_aes = "x",
- default_aes = aes(y = ..count..),
+ default_aes = aes(y = ..count.., weight = 1),
setup_params = function(data, params) {
- if (!is.null(data$y) || !is.null(params$y)) {
+ if (!is.null(data$y)) {
stop("stat_count() must not be used with a y aesthetic.", call. = FALSE)
}
params
diff --git a/R/stat-density.r b/R/stat-density.r
index c1707ed..cb7c7b8 100644
--- a/R/stat-density.r
+++ b/R/stat-density.r
@@ -1,9 +1,14 @@
-#' @param bw the smoothing bandwidth to be used, see
-#' \code{\link{density}} for details
-#' @param adjust adjustment of the bandwidth, see
-#' \code{\link{density}} for details
-#' @param kernel kernel used for density estimation, see
-#' \code{\link{density}} for details
+#' @param bw The smoothing bandwidth to be used.
+#' If numeric, the standard deviation of the smoothing kernel.
+#' If character, a rule to choose the bandwidth, as listed in
+#' \code{\link[stats]{bw.nrd}}.
+#' @param adjust A multiplicate bandwidth adjustment. This makes it possible
+#' to adjust the bandwidth while still using the a bandwidth estimator.
+#' For exampe, \code{adjust = 1/2} means use half of the default bandwidth.
+#' @param kernel Kernel. See list of available kernels in \code{\link{density}}.
+#' @param n number of equally spaced points at which the density is to be
+#' estimated, should be a power of two, see \code{\link{density}} for
+#' details
#' @param trim This parameter only matters if you are displaying multiple
#' densities in one plot. If \code{FALSE}, the default, each density is
#' computed on the full range of the data. If \code{TRUE}, each density
@@ -25,6 +30,7 @@ stat_density <- function(mapping = NULL, data = NULL,
bw = "nrd0",
adjust = 1,
kernel = "gaussian",
+ n = 512,
trim = FALSE,
na.rm = FALSE,
show.legend = NA,
@@ -42,6 +48,7 @@ stat_density <- function(mapping = NULL, data = NULL,
bw = bw,
adjust = adjust,
kernel = kernel,
+ n = n,
trim = trim,
na.rm = na.rm,
...
@@ -58,7 +65,7 @@ StatDensity <- ggproto("StatDensity", Stat,
default_aes = aes(y = ..density.., fill = NA),
compute_group = function(data, scales, bw = "nrd0", adjust = 1, kernel = "gaussian",
- trim = FALSE, na.rm = FALSE) {
+ n = 512, trim = FALSE, na.rm = FALSE) {
if (trim) {
range <- range(data$x, na.rm = TRUE)
} else {
@@ -66,37 +73,37 @@ StatDensity <- ggproto("StatDensity", Stat,
}
compute_density(data$x, data$weight, from = range[1], to = range[2],
- bw = bw, adjust = adjust, kernel = kernel)
+ bw = bw, adjust = adjust, kernel = kernel, n = n)
}
)
compute_density <- function(x, w, from, to, bw = "nrd0", adjust = 1,
- kernel = "gaussian") {
- n <- length(x)
+ kernel = "gaussian", n = 512) {
+ nx <- length(x)
if (is.null(w)) {
- w <- rep(1 / n, n)
+ w <- rep(1 / nx, nx)
}
# if less than 3 points, spread density evenly over points
- if (n < 3) {
+ if (nx < 3) {
return(data.frame(
x = x,
density = w / sum(w),
scaled = w / max(w),
count = 1,
- n = n
+ n = nx
))
}
dens <- stats::density(x, weights = w, bw = bw, adjust = adjust,
- kernel = kernel, from = from, to = to)
+ kernel = kernel, n = n, from = from, to = to)
data.frame(
x = dens$x,
density = dens$y,
scaled = dens$y / max(dens$y, na.rm = TRUE),
- count = dens$y * n,
- n = n
+ count = dens$y * nx,
+ n = nx
)
}
diff --git a/R/stat-ecdf.r b/R/stat-ecdf.r
index 6663da7..17fa617 100644
--- a/R/stat-ecdf.r
+++ b/R/stat-ecdf.r
@@ -1,4 +1,11 @@
-#' Empirical Cumulative Density Function
+#' Compute empirical cumulative distribution
+#'
+#' The empirical cumulative distribution function (ECDF) provides an alternative
+#' visualisation of distribution. Compared to other visualisations that rely on
+#' density (like \code{\link{geom_histogram}}), the ECDF doesn't require any
+#' tuning parameters and handles both continuous and categorical variables.
+#' The downside is that it requires more training to accurately interpret,
+#' and the underlying visual tasks are somewhat more challenging.
#'
#' @inheritParams layer
#' @inheritParams geom_point
@@ -15,15 +22,17 @@
#' }
#' @export
#' @examples
-#' \donttest{
-#' df <- data.frame(x = rnorm(1000))
+#' df <- data.frame(
+#' x = c(rnorm(100, 0, 3), rnorm(100, 0, 10)),
+#' g = gl(2, 100)
+#' )
#' ggplot(df, aes(x)) + stat_ecdf(geom = "step")
#'
-#' df <- data.frame(x = c(rnorm(100, 0, 3), rnorm(100, 0, 10)),
-#' g = gl(2, 100))
+#' # Don't go to positive/negative infinity
+#' ggplot(df, aes(x)) + stat_ecdf(geom = "step", pad = FALSE)
#'
+#' # Multiple ECDFs
#' ggplot(df, aes(x, colour = g)) + stat_ecdf()
-#' }
stat_ecdf <- function(mapping = NULL, data = NULL,
geom = "step", position = "identity",
...,
@@ -42,6 +51,7 @@ stat_ecdf <- function(mapping = NULL, data = NULL,
inherit.aes = inherit.aes,
params = list(
n = n,
+ pad = pad,
na.rm = na.rm,
...
)
diff --git a/R/stat-ellipse.R b/R/stat-ellipse.R
index 97c01e3..d63413b 100644
--- a/R/stat-ellipse.R
+++ b/R/stat-ellipse.R
@@ -1,4 +1,4 @@
-#' Plot data ellipses.
+#' Compute normal confidence ellipses
#'
#' The method for calculating the ellipses has been modified from
#' \code{car::ellipse} (Fox and Weisberg, 2011)
diff --git a/R/stat-function.r b/R/stat-function.r
index ed499b8..c05103d 100644
--- a/R/stat-function.r
+++ b/R/stat-function.r
@@ -1,9 +1,13 @@
-#' Superimpose a function.
+#' Compute function for each x value
+#'
+#' This stat makes it easy to superimpose a function on top of an existing
+#' plot. The function is called with a grid of evenly spaced values along
+#' the x axis, and the results are drawn (by default) with a line.
#'
#' @section Aesthetics:
-#' \Sexpr[results=rd,stage=build]{ggplot2:::rd_aesthetics("stat", "function")}
+#' \aesthetics{stat}{function}
#'
-#' @param fun function to use
+#' @param fun function to use. Must be vectorised.
#' @param n number of points to interpolate along
#' @param args list of additional arguments to pass to \code{fun}
#' @param xlim Optionally, restrict the range of the function to this range.
diff --git a/R/stat-identity.r b/R/stat-identity.r
index 71bfb89..6df16d1 100644
--- a/R/stat-identity.r
+++ b/R/stat-identity.r
@@ -1,4 +1,4 @@
-#' Identity statistic.
+#' Leave data as is
#'
#' The identity statistic leaves the data unchanged.
#'
diff --git a/R/stat-qq.r b/R/stat-qq.r
index 23e8e61..8d91160 100644
--- a/R/stat-qq.r
+++ b/R/stat-qq.r
@@ -1,7 +1,7 @@
-#' Calculation for quantile-quantile plot.
+#' A quantile-quantile plot
#'
#' @section Aesthetics:
-#' \Sexpr[results=rd,stage=build]{ggplot2:::rd_aesthetics("stat", "qq")}
+#' \aesthetics{stat}{qq}
#'
#' @param distribution Distribution function to use, if x not specified
#' @param dparams Additional parameters passed on to \code{distribution}
@@ -32,7 +32,7 @@
#' ggplot(mtcars) +
#' stat_qq(aes(sample = mpg, colour = factor(cyl)))
#' }
-stat_qq <- function(mapping = NULL, data = NULL,
+geom_qq <- function(mapping = NULL, data = NULL,
geom = "point", position = "identity",
...,
distribution = stats::qnorm,
@@ -58,8 +58,8 @@ stat_qq <- function(mapping = NULL, data = NULL,
}
#' @export
-#' @rdname stat_qq
-geom_qq <- stat_qq
+#' @rdname geom_qq
+stat_qq <- geom_qq
#' @rdname ggplot2-ggproto
#' @format NULL
diff --git a/R/stat-smooth.r b/R/stat-smooth.r
index f275fb6..772b1e6 100644
--- a/R/stat-smooth.r
+++ b/R/stat-smooth.r
@@ -1,7 +1,12 @@
-#' @param method smoothing method (function) to use, eg. lm, glm, gam, loess,
-#' rlm. For datasets with n < 1000 default is \code{\link{loess}}. For datasets
-#' with 1000 or more observations defaults to gam, see \code{\link[mgcv]{gam}}
-#' for more details.
+#' @param method smoothing method (function) to use, eg. "lm", "glm",
+#' "gam", "loess", "rlm".
+#'
+#' For \code{method = "auto"} the smoothing method is chosen based on the
+#' size of the largest group (across all panels). \code{\link{loess}} is
+#' used for than 1,000 observations; otherwise \code{\link[mgcv]{gam}} is
+#' used with \code{formula = y ~ s(x, bs = "cs")}. Somewhat anecdotally,
+#' \code{loess} gives a better appearance, but is O(n^2) in memory, so does
+#' not work for larger datasets.
#' @param formula formula to use in smoothing function, eg. \code{y ~ x},
#' \code{y ~ poly(x, 2)}, \code{y ~ log(x)}
#' @param se display confidence interval around smooth? (TRUE by default, see
@@ -68,11 +73,11 @@ stat_smooth <- function(mapping = NULL, data = NULL,
StatSmooth <- ggproto("StatSmooth", Stat,
setup_params = function(data, params) {
- # Figure out what type of smoothing to do: loess for small datasets,
- # gam with a cubic regression basis for large data
- # This is based on the size of the _largest_ group.
if (identical(params$method, "auto")) {
- max_group <- max(table(data$group))
+ # Use loess for small datasets, gam with a cubic regression basis for
+ # larger. Based on size of the _largest_ group to avoid bad memory
+ # behaviour of loess
+ max_group <- max(table(interaction(data$group, data$PANEL, drop = TRUE)))
if (max_group < 1000) {
params$method <- "loess"
@@ -80,6 +85,7 @@ StatSmooth <- ggproto("StatSmooth", Stat,
params$method <- "gam"
params$formula <- y ~ s(x, bs = "cs")
}
+ message("`geom_smooth()` using method = '", params$method, "'")
}
if (identical(params$method, "gam")) {
params$method <- mgcv::gam
diff --git a/R/stat-sum.r b/R/stat-sum.r
index 62cf8ec..83e069e 100644
--- a/R/stat-sum.r
+++ b/R/stat-sum.r
@@ -33,7 +33,7 @@ stat_sum <- function(mapping = NULL, data = NULL,
#' @usage NULL
#' @export
StatSum <- ggproto("StatSum", Stat,
- default_aes = aes(size = ..n..),
+ default_aes = aes(size = ..n.., weight = 1),
required_aes = c("x", "y"),
diff --git a/R/stat-summary.r b/R/stat-summary.r
index a163fce..52e1257 100644
--- a/R/stat-summary.r
+++ b/R/stat-summary.r
@@ -1,4 +1,4 @@
-#' Summarise y values at unique/binned x x.
+#' Summarise y values at unique/binned x
#'
#' \code{stat_summary} operates on unique \code{x}; \code{stat_summary_bin}
#' operators on binned \code{x}. They are more flexible versions of
@@ -6,7 +6,7 @@
#' aggregate.
#'
#' @section Aesthetics:
-#' \Sexpr[results=rd,stage=build]{ggplot2:::rd_aesthetics("stat", "summary")}
+#' \aesthetics{stat}{summary}
#'
#' @seealso \code{\link{geom_errorbar}}, \code{\link{geom_pointrange}},
#' \code{\link{geom_linerange}}, \code{\link{geom_crossbar}} for geoms to
@@ -70,7 +70,7 @@
#'
#' # A set of useful summary functions is provided from the Hmisc package:
#' stat_sum_df <- function(fun, geom="crossbar", ...) {
-#' stat_summary(fun.data=fun, colour="red", geom=geom, width=0.2, ...)
+#' stat_summary(fun.data = fun, colour = "red", geom = geom, width = 0.2, ...)
#' }
#' d <- ggplot(mtcars, aes(cyl, mpg)) + geom_point()
#' # The crossbar geom needs grouping to be specified when used with
@@ -84,7 +84,7 @@
#' if (require("ggplot2movies")) {
#' set.seed(596)
#' mov <- movies[sample(nrow(movies), 1000), ]
-#' m2 <- ggplot(mov, aes(x= factor(round(rating)), y=votes)) + geom_point()
+#' m2 <- ggplot(mov, aes(x = factor(round(rating)), y = votes)) + geom_point()
#' m2 <- m2 + stat_summary(fun.data = "mean_cl_boot", geom = "crossbar",
#' colour = "red", width = 0.3) + xlab("rating")
#' m2
@@ -170,17 +170,29 @@ summarise_by_x <- function(data, summary, ...) {
merge(summary, unique, by = c("x", "group"), sort = FALSE)
}
-#' Wrap up a selection of summary functions from Hmisc to make it easy to use
-#' with \code{\link{stat_summary}}.
-#'
-#' See the Hmisc documentation for details of their options.
-#'
+#' A selection of summary functions from Hmisc
+#'
+#' @description
+#' These are wrappers around functions from \pkg{Hmsic} designed to make them
+#' easier to use with \code{\link{stat_summary}}. See the Hmisc documentation
+#' for more details:
+#'
+#' \itemize{
+#' \item \code{\link[Hmisc]{smean.cl.boot}}
+#' \item \code{\link[Hmisc]{smean.cl.normal}}
+#' \item \code{\link[Hmisc]{smean.sdl}}
+#' \item \code{\link[Hmisc]{smedian.hilow}}
+#' }
#' @param x a numeric vector
#' @param ... other arguments passed on to the respective Hmisc function.
-#' @seealso \code{\link[Hmisc]{smean.cl.boot}},
-#' \code{\link[Hmisc]{smean.cl.normal}}, \code{\link[Hmisc]{smean.sdl}},
-#' \code{\link[Hmisc]{smedian.hilow}}
+#' @return A data frame with columns \code{y}, \code{ymin}, and \code{ymax}.
#' @name hmisc
+#' @examples
+#' x <- rnorm(100)
+#' mean_cl_boot(x)
+#' mean_cl_normal(x)
+#' mean_sdl(x)
+#' median_hilow(x)
NULL
wrap_hmisc <- function(fun) {
@@ -212,12 +224,17 @@ mean_sdl <- wrap_hmisc("smean.sdl")
#' @rdname hmisc
median_hilow <- wrap_hmisc("smedian.hilow")
-#' Calculate mean and standard errors on either side.
+#' Calculate mean and standard error
+#'
+#' For use with \code{\link{stat_summary}}
#'
#' @param x numeric vector
#' @param mult number of multiples of standard error
-#' @seealso for use with \code{\link{stat_summary}}
+#' @return A data frame with columns \code{y}, \code{ymin}, and \code{ymax}.
#' @export
+#' @examples
+#' x <- rnorm(100)
+#' mean_se(x)
mean_se <- function(x, mult = 1) {
x <- stats::na.omit(x)
se <- mult * sqrt(stats::var(x) / length(x))
diff --git a/R/stat-unique.r b/R/stat-unique.r
index 73a1a0d..f3ca829 100644
--- a/R/stat-unique.r
+++ b/R/stat-unique.r
@@ -1,14 +1,16 @@
-#' Remove duplicates.
+#' Remove duplicates
#'
#' @section Aesthetics:
-#' \Sexpr[results=rd,stage=build]{ggplot2:::rd_aesthetics("stat", "unique")}
+#' \aesthetics{stat}{unique}
#'
#' @export
#' @inheritParams layer
#' @inheritParams geom_point
#' @examples
-#' ggplot(mtcars, aes(vs, am)) + geom_point(alpha = 0.1)
-#' ggplot(mtcars, aes(vs, am)) + geom_point(alpha = 0.1, stat="unique")
+#' ggplot(mtcars, aes(vs, am)) +
+#' geom_point(alpha = 0.1)
+#' ggplot(mtcars, aes(vs, am)) +
+#' geom_point(alpha = 0.1, stat = "unique")
stat_unique <- function(mapping = NULL, data = NULL,
geom = "point", position = "identity",
...,
diff --git a/R/stat-ydensity.r b/R/stat-ydensity.r
index bda4fe3..2dbb71b 100644
--- a/R/stat-ydensity.r
+++ b/R/stat-ydensity.r
@@ -63,13 +63,10 @@ StatYdensity <- ggproto("StatYdensity", Stat,
compute_group = function(data, scales, width = NULL, bw = "nrd0", adjust = 1,
kernel = "gaussian", trim = TRUE, na.rm = FALSE) {
if (nrow(data) < 3) return(data.frame())
-
- if (trim) {
- range <- range(data$y, na.rm = TRUE)
- } else {
- range <- scales$y$dimension()
- }
- dens <- compute_density(data$y, data$w, from = range[1], to = range[2],
+ range <- range(data$y, na.rm = TRUE)
+ modifier <- if (trim) 0 else 3
+ bw <- calc_bw(data$y, bw)
+ dens <- compute_density(data$y, data$w, from = range[1] - modifier*bw, to = range[2] + modifier*bw,
bw = bw, adjust = adjust, kernel = kernel)
dens$y <- dens$x
@@ -107,3 +104,22 @@ StatYdensity <- ggproto("StatYdensity", Stat,
}
)
+
+calc_bw <- function(x, bw) {
+ if (is.character(bw)) {
+ if (length(x) < 2)
+ stop("need at least 2 points to select a bandwidth automatically", call. = FALSE)
+ bw <- switch(
+ tolower(bw),
+ nrd0 = stats::bw.nrd0(x),
+ nrd = stats::bw.nrd(x),
+ ucv = stats::bw.ucv(x),
+ bcv = stats::bw.bcv(x),
+ sj = ,
+ `sj-ste` = stats::bw.SJ(x, method = "ste"),
+ `sj-dpi` = stats::bw.SJ(x, method = "dpi"),
+ stop("unknown bandwidth rule")
+ )
+ }
+ bw
+}
diff --git a/R/theme-current.R b/R/theme-current.R
new file mode 100644
index 0000000..c851156
--- /dev/null
+++ b/R/theme-current.R
@@ -0,0 +1,109 @@
+#' @include theme-defaults.r
+#' @include theme-elements.r
+theme_env <- new.env(parent = emptyenv())
+theme_env$current <- theme_gray()
+
+#' Get, set, and modify the active theme
+#'
+#' The current/active theme is automatically applied to every plot you draw.
+#' Use \code{theme_get} to get the current theme, and \code{theme_set} to
+#' completely override it. \code{theme_update} and \code{theme_replace} are
+#' shorthands for changing individual elements.
+#'
+#' @section Adding on to a theme:
+#'
+#' \code{+} and \code{\%+replace\%} can be used to modify elements in themes.
+#'
+#' \code{+} updates the elements of e1 that differ from elements specified (not
+#' NULL) in e2. Thus this operator can be used to incrementally add or modify
+#' attributes of a ggplot theme.
+#'
+#' In contrast, \code{\%+replace\%} replaces the entire element; any element of
+#' a theme not specified in e2 will not be present in the resulting theme (i.e.
+#' NULL). Thus this operator can be used to overwrite an entire theme.
+#'
+#' \code{theme_update} uses the \code{+} operator, so that any unspecified
+#' values in the theme element will default to the values they are set in the
+#' theme. \code{theme_replace} uses \code{\%+replace\%} tocompletely replace
+#' the element, so any unspecified values will overwrite the current value in
+#' the theme with \code{NULL}s.
+#'
+#' @param ... named list of theme settings
+#' @param e1,e2 Theme and element to combine
+#' @return \code{theme_set}, \code{theme_update}, and \code{theme_replace}
+#' invisibly return the previous theme so you can easily save it, then
+#' later restore it.
+#' @seealso \code{\link{+.gg}}
+#' @export
+#' @examples
+#' p <- ggplot(mtcars, aes(mpg, wt)) +
+#' geom_point()
+#' p
+#'
+#' # Use theme_set() to completely override the current theme.
+#' # Here we have the old theme so we can later restore it.
+#' # Note that the theme is applied when the plot is drawn, not
+#' # when it is created.
+#' old <- theme_set(theme_bw())
+#' p
+#' theme_set(old)
+#' p
+#'
+#'
+#' # Modifying theme objects -----------------------------------------
+#' # You can use + and %+replace% to modify a theme object.
+#' # They differ in how they deal with missing arguments in
+#' # the theme elements.
+#'
+#' add_el <- theme_grey() +
+#' theme(text = element_text(family = "Times"))
+#' add_el$text
+#'
+#' rep_el <- theme_grey() %+replace%
+#' theme(text = element_text(family = "Times"))
+#' rep_el$text
+#'
+#' # theme_update() and theme_replace() are similar except they
+#' # apply directly to the current/active theme.
+theme_get <- function() {
+ theme_env$current
+}
+
+#' @rdname theme_get
+#' @param new new theme (a list of theme elements)
+#' @export
+theme_set <- function(new) {
+ missing <- setdiff(names(theme_gray()), names(new))
+ if (length(missing) > 0) {
+ warning("New theme missing the following elements: ",
+ paste(missing, collapse = ", "), call. = FALSE)
+ }
+
+ old <- theme_env$current
+ theme_env$current <- new
+ invisible(old)
+}
+
+#' @rdname theme_get
+#' @export
+theme_update <- function(...) {
+ theme_set(theme_get() + theme(...))
+}
+
+#' @rdname theme_get
+#' @export
+theme_replace <- function(...) {
+ theme_set(theme_get() %+replace% theme(...))
+}
+
+#' @rdname theme_get
+#' @export
+"%+replace%" <- function(e1, e2) {
+ if (!is.theme(e1) || !is.theme(e2)) {
+ stop("%+replace% requires two theme objects", call. = FALSE)
+ }
+
+ # Can't use modifyList here since it works recursively and drops NULLs
+ e1[names(e2)] <- e2
+ e1
+}
diff --git a/R/theme-defaults.r b/R/theme-defaults.r
index 41e1ebc..579197e 100644
--- a/R/theme-defaults.r
+++ b/R/theme-defaults.r
@@ -1,12 +1,14 @@
-#' ggplot2 themes
+#' Complete themes
#'
-#' Themes set the general aspect of the plot such as the colour of the
-#' background, gridlines, the size and colour of fonts.
+#' These are complete themes which control all non-data display. Use
+#' \code{\link{theme}} if you just need to tweak the display of an existing
+#' theme.
#'
#' @param base_size base font size
#' @param base_family base font family
#'
-#' @details \describe{
+#' @details
+#' \describe{
#'
#' \item{\code{theme_gray}}{
#' The signature ggplot2 theme with a grey background and white gridlines,
@@ -42,10 +44,8 @@
#'
#' @examples
#' p <- ggplot(mtcars) + geom_point(aes(x = wt, y = mpg,
-#' colour=factor(gear))) + facet_wrap(~am)
-#'
-#' p
-#' p + theme_gray()
+#' colour = factor(gear))) + facet_wrap(~am)
+#' p + theme_gray() # the default
#' p + theme_bw()
#' p + theme_linedraw()
#' p + theme_light()
@@ -53,10 +53,11 @@
#' p + theme_minimal()
#' p + theme_classic()
#' p + theme_void()
-#'
#' @name ggtheme
+#' @aliases NULL
NULL
+#' @include theme.r
#' @export
#' @rdname ggtheme
theme_grey <- function(base_size = 11, base_family = "") {
@@ -76,24 +77,40 @@ theme_grey <- function(base_size = 11, base_family = "") {
margin = margin(), debug = FALSE
),
- axis.line = element_line(),
- axis.line.x = element_blank(),
- axis.line.y = element_blank(),
+ axis.line = element_blank(),
+ axis.line.x = NULL,
+ axis.line.y = NULL,
axis.text = element_text(size = rel(0.8), colour = "grey30"),
axis.text.x = element_text(margin = margin(t = 0.8 * half_line / 2), vjust = 1),
+ axis.text.x.top = element_text(margin = margin(b = 0.8 * half_line / 2), vjust = 0),
axis.text.y = element_text(margin = margin(r = 0.8 * half_line / 2), hjust = 1),
+ axis.text.y.right = element_text(margin = margin(l = 0.8 * half_line / 2), hjust = 0),
axis.ticks = element_line(colour = "grey20"),
axis.ticks.length = unit(half_line / 2, "pt"),
axis.title.x = element_text(
- margin = margin(t = 0.8 * half_line, b = 0.8 * half_line / 2)
+ margin = margin(t = half_line),
+ vjust = 1
+ ),
+ axis.title.x.top = element_text(
+ margin = margin(b = half_line),
+ vjust = 0
),
axis.title.y = element_text(
angle = 90,
- margin = margin(r = 0.8 * half_line, l = 0.8 * half_line / 2)
+ margin = margin(r = half_line),
+ vjust = 1
+ ),
+ axis.title.y.right = element_text(
+ angle = -90,
+ margin = margin(l = half_line),
+ vjust = 0
),
legend.background = element_rect(colour = NA),
- legend.margin = unit(0.2, "cm"),
+ legend.spacing = unit(0.4, "cm"),
+ legend.spacing.x = NULL,
+ legend.spacing.y = NULL,
+ legend.margin = margin(0.2, 0.2, 0.2, 0.2, "cm"),
legend.key = element_rect(fill = "grey95", colour = "white"),
legend.key.size = unit(1.2, "lines"),
legend.key.height = NULL,
@@ -106,28 +123,45 @@ theme_grey <- function(base_size = 11, base_family = "") {
legend.direction = NULL,
legend.justification = "center",
legend.box = NULL,
+ legend.box.margin = margin(0, 0, 0, 0, "cm"),
+ legend.box.background = element_blank(),
+ legend.box.spacing = unit(0.4, "cm"),
panel.background = element_rect(fill = "grey92", colour = NA),
panel.border = element_blank(),
panel.grid.major = element_line(colour = "white"),
panel.grid.minor = element_line(colour = "white", size = 0.25),
- panel.margin = unit(half_line, "pt"),
- panel.margin.x = NULL,
- panel.margin.y = NULL,
+ panel.spacing = unit(half_line, "pt"),
+ panel.spacing.x = NULL,
+ panel.spacing.y = NULL,
panel.ontop = FALSE,
strip.background = element_rect(fill = "grey85", colour = NA),
strip.text = element_text(colour = "grey10", size = rel(0.8)),
strip.text.x = element_text(margin = margin(t = half_line, b = half_line)),
strip.text.y = element_text(angle = -90, margin = margin(l = half_line, r = half_line)),
+ strip.placement = "inside",
+ strip.placement.x = NULL,
+ strip.placement.y = NULL,
strip.switch.pad.grid = unit(0.1, "cm"),
strip.switch.pad.wrap = unit(0.1, "cm"),
plot.background = element_rect(colour = "white"),
plot.title = element_text(
size = rel(1.2),
+ hjust = 0, vjust = 1,
margin = margin(b = half_line * 1.2)
),
+ plot.subtitle = element_text(
+ size = rel(0.9),
+ hjust = 0, vjust = 1,
+ margin = margin(b = half_line * 0.9)
+ ),
+ plot.caption = element_text(
+ size = rel(0.9),
+ hjust = 1, vjust = 1,
+ margin = margin(t = half_line * 0.9)
+ ),
plot.margin = margin(half_line, half_line, half_line, half_line),
complete = TRUE
@@ -139,142 +173,155 @@ theme_gray <- theme_grey
#' @export
#' @rdname ggtheme
-theme_bw <- function(base_size = 12, base_family = "") {
+theme_bw <- function(base_size = 11, base_family = "") {
# Starts with theme_grey and then modify some parts
theme_grey(base_size = base_size, base_family = base_family) %+replace%
theme(
- axis.text = element_text(size = rel(0.8)),
- axis.ticks = element_line(colour = "black"),
- legend.key = element_rect(colour = "grey80"),
- panel.background = element_rect(fill = "white", colour = NA),
- panel.border = element_rect(fill = NA, colour = "grey50"),
- panel.grid.major = element_line(colour = "grey90", size = 0.2),
- panel.grid.minor = element_line(colour = "grey98", size = 0.5),
- strip.background = element_rect(fill = "grey80", colour = "grey50", size = 0.2)
+ # white background and dark border
+ panel.background = element_rect(fill = "white", colour = NA),
+ panel.border = element_rect(fill = NA, colour = "grey20"),
+ # make gridlines dark, same contrast with white as in theme_grey
+ panel.grid.major = element_line(colour = "grey92"),
+ panel.grid.minor = element_line(colour = "grey92", size = 0.25),
+ # contour strips to match panel contour
+ strip.background = element_rect(fill = "grey85", colour = "grey20"),
+ # match legend key to background
+ legend.key = element_rect(fill = "white", colour=NA),
+
+ complete = TRUE
)
}
#' @export
#' @rdname ggtheme
-theme_linedraw <- function(base_size = 12, base_family = "") {
- half_line <- base_size / 2
+theme_linedraw <- function(base_size = 11, base_family = "") {
+ # Starts with theme_bw and then modify some parts
+ # = replace all greys with pure black or white
+ theme_bw(base_size = base_size, base_family = base_family) %+replace%
+ theme(
+ # black text and ticks on the axes
+ axis.text = element_text(colour = "black", size = rel(0.8)),
+ axis.ticks = element_line(colour = "black", size = 0.25),
+ # NB: match the *visual* thickness of axis ticks to the panel border
+ # 0.5 clipped looks like 0.25
+
+ # pure black panel border and grid lines, but thinner
+ panel.border = element_rect(fill = NA, colour = "black", size = 0.5),
+ panel.grid.major = element_line(colour = "black", size = 0.05),
+ panel.grid.minor = element_line(colour = "black", size = 0.025),
+
+ # strips with black background and white text
+ strip.background = element_rect(fill = "black"),
+ strip.text = element_text(colour = "white", size = rel(0.8)),
+
+ complete = TRUE
+ )
+}
+
+#' @export
+#' @rdname ggtheme
+theme_light <- function(base_size = 11, base_family = "") {
# Starts with theme_grey and then modify some parts
theme_grey(base_size = base_size, base_family = base_family) %+replace%
theme(
- axis.text = element_text(colour = "black", size = rel(0.8)),
- axis.ticks = element_line(colour = "black", size = 0.25),
- legend.key = element_rect(colour = "black", size = 0.25),
- panel.background = element_rect(fill = "white", colour = NA),
- panel.border = element_rect(fill = NA, colour = "black", size = 0.5),
- panel.grid.major = element_line(colour = "black", size = 0.05),
- panel.grid.minor = element_line(colour = "black", size = 0.01),
- strip.background = element_rect(fill = "black", colour = NA),
- strip.text.x = element_text(
- colour = "white",
- margin = margin(t = half_line, b = half_line)
- ),
- strip.text.y = element_text(
- colour = "white",
- angle = 90,
- margin = margin(l = half_line, r = half_line)
- )
+ # white panel with light grey border
+ panel.background = element_rect(fill = "white", colour = NA),
+ panel.border = element_rect(fill = NA, colour = "grey70", size = 0.5),
+ # light grey, thinner gridlines
+ # => make them slightly darker to keep acceptable contrast
+ panel.grid.major = element_line(colour = "grey87", size = 0.25),
+ panel.grid.minor = element_line(colour = "grey87", size = 0.125),
+
+ # match axes ticks thickness to gridlines and colour to panel border
+ axis.ticks = element_line(colour = "grey70", size = 0.25),
+
+ # match legend key to panel.background
+ legend.key = element_rect(fill = "white", colour = NA),
+
+ # dark strips with light text (inverse contrast compared to theme_grey)
+ strip.background = element_rect(fill = "grey70", colour = NA),
+ strip.text = element_text(colour = "white", size = rel(0.8)),
+
+ complete = TRUE
)
+
}
#' @export
#' @rdname ggtheme
-theme_light <- function(base_size = 12, base_family = "") {
- half_line <- base_size / 2
+theme_dark <- function(base_size = 11, base_family = "") {
# Starts with theme_grey and then modify some parts
theme_grey(base_size = base_size, base_family = base_family) %+replace%
theme(
- axis.ticks = element_line(colour = "grey70", size = 0.25),
- legend.key = element_rect(fill = "white", colour = "grey50", size = 0.25),
- panel.background = element_rect(fill = "white", colour = NA),
- panel.border = element_rect(fill = NA, colour = "grey70", size = 0.5),
- panel.grid.major = element_line(colour = "grey85", size = 0.25),
- panel.grid.minor = element_line(colour = "grey93", size = 0.125),
- strip.background = element_rect(fill = "grey70", colour = NA),
- strip.text.x = element_text(
- colour = "white",
- margin = margin(t = half_line, b = half_line)
- ),
- strip.text.y = element_text(
- colour = "white",
- angle = -90,
- margin = margin(l = half_line, r = half_line)
- )
- )
+ # dark panel
+ panel.background = element_rect(fill = "grey50", colour = NA),
+ # inverse grid lines contrast compared to theme_grey
+ # make them thinner and try to keep the same visual contrast as in theme_light
+ panel.grid.major = element_line(colour = "grey42", size = 0.25),
+ panel.grid.minor = element_line(colour = "grey42", size = 0.125),
+
+ # match axes ticks thickness to gridlines
+ axis.ticks = element_line(colour = "grey20", size = 0.25),
+
+ # match legend key to panel.background
+ legend.key = element_rect(fill = "grey50", colour = NA),
+
+ # dark strips with light text (inverse contrast compared to theme_grey)
+ strip.background = element_rect(fill = "grey15", colour = NA),
+ strip.text = element_text(colour = "grey90", size = rel(0.8)),
+ complete = TRUE
+ )
}
#' @export
#' @rdname ggtheme
-theme_minimal <- function(base_size = 12, base_family = "") {
- # Starts with theme_bw and then modify some parts
+theme_minimal <- function(base_size = 11, base_family = "") {
+ # Starts with theme_bw and remove most parts
theme_bw(base_size = base_size, base_family = base_family) %+replace%
theme(
+ axis.ticks = element_blank(),
legend.background = element_blank(),
legend.key = element_blank(),
panel.background = element_blank(),
panel.border = element_blank(),
strip.background = element_blank(),
plot.background = element_blank(),
- axis.ticks = element_line(),
- axis.ticks.x = element_blank(),
- axis.ticks.y = element_blank(),
- axis.ticks.length = unit(1, "lines")
+
+ complete = TRUE
)
}
#' @export
#' @rdname ggtheme
-theme_classic <- function(base_size = 12, base_family = ""){
+theme_classic <- function(base_size = 11, base_family = ""){
theme_bw(base_size = base_size, base_family = base_family) %+replace%
theme(
+ # no background and no grid
panel.border = element_blank(),
- axis.line = element_line(colour = "black"),
- panel.grid.major = element_line(),
- panel.grid.major.x = element_blank(),
- panel.grid.major.y = element_blank(),
- panel.grid.minor = element_line(),
- panel.grid.minor.x = element_blank(),
- panel.grid.minor.y = element_blank(),
- strip.background = element_rect(colour = "black", size = 0.5),
- legend.key = element_blank()
- )
-}
+ panel.grid.major = element_blank(),
+ panel.grid.minor = element_blank(),
-#' @export
-#' @rdname ggtheme
-theme_dark <- function(base_size = 12, base_family = "") {
- half_line <- base_size / 2
- # Starts with theme_grey and then modify some parts
- theme_grey(base_size = base_size, base_family = base_family) %+replace%
- theme(
- axis.ticks = element_line(colour = "grey40", size = 0.25),
- legend.key = element_rect(fill = "grey50", colour = "grey40", size = 0.25),
- panel.background = element_rect(fill = "grey50", colour = NA),
- panel.grid.major = element_line(colour = "grey40", size = 0.25),
- panel.grid.minor = element_line(colour = "grey45", size = 0.125),
- strip.background = element_rect(fill = "grey20", colour = NA),
- strip.text.x = element_text(
- colour = "white",
- margin = margin(t = half_line, b = half_line)
- ),
- strip.text.y = element_text(
- colour = "white",
- angle = -90,
- margin = margin(l = half_line, r = half_line)
- )
+ # show axes
+ axis.line = element_line(colour = "black", size = 0.5),
+
+ # match legend key to panel.background
+ legend.key = element_blank(),
+
+ # simple, black and white strips
+ strip.background = element_rect(fill = "white", colour = "black", size = 1),
+ # NB: size is 1 but clipped, it looks like the 0.5 of the axes
+
+ complete = TRUE
)
}
#' @export
#' @rdname ggtheme
-theme_void <- function(base_size = 12, base_family = "") {
+theme_void <- function(base_size = 11, base_family = "") {
theme(
- # Use only inherited elements and make everything blank
+ # Use only inherited elements and make almost everything blank
+ # Only keep indispensable text
line = element_blank(),
rect = element_blank(),
text = element_text(
@@ -283,14 +330,12 @@ theme_void <- function(base_size = 12, base_family = "") {
lineheight = 0.9, hjust = 0.5, vjust = 0.5, angle = 0,
margin = margin(), debug = FALSE
),
- plot.margin = unit(c(0, 0, 0, 0), "lines"),
- axis.text.x = element_blank(),
- axis.text.y = element_blank(),
- axis.title.x = element_blank(),
- axis.title.y = element_blank(),
+ axis.text = element_blank(),
+ axis.title = element_blank(),
legend.text = element_text(size = rel(0.8)),
- legend.title = element_blank(),
+ legend.title = element_text(hjust = 0),
strip.text = element_text(size = rel(0.8)),
+ plot.margin = unit(c(0, 0, 0, 0), "lines"),
complete = TRUE
)
diff --git a/R/theme-elements.r b/R/theme-elements.r
index 13f1a28..4e00f66 100644
--- a/R/theme-elements.r
+++ b/R/theme-elements.r
@@ -1,7 +1,59 @@
-#' Theme element: blank.
-#' This theme element draws nothing, and assigns no space
+#' Theme elements
#'
+#' @description
+#' In conjunction with the \link{theme} system, the \code{element_} functions
+#' specify the display of how non-data components of the plot are a drawn.
+#'
+#' \itemize{
+#' \item \code{element_blank}: draws nothing, and assigns no space.
+#' \item \code{element_rect}: borders and backgrounds.
+#' \item \code{element_line}: lines.
+#' \item \code{element_text}: text.
+#' }
+#'
+#' \code{rel()} is used to specify sizes relative to the parent,
+#' \code{margins()} is used to specify the margins of elements.
+#'
+#' @param fill Fill colour.
+#' @param colour,color Line/border colour. Color is an alias for colour.
+#' @param size Line/border size in mm; text size in pts.
+#' @param inherit.blank Should this element inherit the existence of an
+#' \code{element_blank} among its parents? If \code{TRUE} the existence of
+#' a blank element among its parents will cause this element to be blank as
+#' well. If \code{FALSE} any blank parent element will be ignored when
+#' calculating final element state.
+#' @return An S3 object of class \code{element}, \code{rel}, or \code{margin}.
+#' @examples
+#' plot <- ggplot(mpg, aes(displ, hwy)) + geom_point()
+#'
+#' plot + theme(
+#' panel.background = element_blank(),
+#' axis.text = element_blank()
+#' )
+#'
+#' plot + theme(
+#' axis.text = element_text(colour = "red", size = rel(1.5))
+#' )
+#'
+#' plot + theme(
+#' axis.line = element_line(arrow = arrow())
+#' )
+#'
+#' plot + theme(
+#' panel.background = element_rect(fill = "white"),
+#' plot.margin = margin(2, 2, 2, 2, "cm"),
+#' plot.background = element_rect(
+#' fill = "grey90",
+#' colour = "black",
+#' size = 1
+#' )
+#' )
+#' @name element
+#' @aliases NULL
+NULL
+
#' @export
+#' @rdname element
element_blank <- function() {
structure(
list(),
@@ -9,72 +61,63 @@ element_blank <- function() {
)
}
-#' Theme element: rectangle.
-#'
-#' Most often used for backgrounds and borders.
-#'
-#' @param fill fill colour
-#' @param colour border colour
-#' @param size border size
-#' @param linetype border linetype
-#' @param color an alias for \code{colour}
#' @export
+#' @rdname element
element_rect <- function(fill = NULL, colour = NULL, size = NULL,
- linetype = NULL, color = NULL) {
+ linetype = NULL, color = NULL, inherit.blank = FALSE) {
if (!is.null(color)) colour <- color
structure(
- list(fill = fill, colour = colour, size = size, linetype = linetype),
+ list(fill = fill, colour = colour, size = size, linetype = linetype,
+ inherit.blank = inherit.blank),
class = c("element_rect", "element")
)
}
-#' Theme element: line.
-#'
-#' @param colour line colour
-#' @param size line size
-#' @param linetype line type
-#' @param lineend line end
-#' @param color an alias for \code{colour}
#' @export
+#' @rdname element
+#' @param linetype Line type. An integer (0:8), a name (blank, solid,
+#' dashed, dotted, dotdash, longdash, twodash), or a string with
+#' an even number (up to eight) of hexadecimal digits which give the
+#' lengths in consecutive positions in the string.
+#' @param lineend Line end Line end style (round, butt, square)
+#' @param arrow Arrow specification, as created by \code{\link[grid]{arrow}}
element_line <- function(colour = NULL, size = NULL, linetype = NULL,
- lineend = NULL, color = NULL) {
+ lineend = NULL, color = NULL, arrow = NULL, inherit.blank = FALSE) {
if (!is.null(color)) colour <- color
+ if (is.null(arrow)) arrow <- FALSE
structure(
- list(colour = colour, size = size, linetype = linetype, lineend = lineend),
+ list(colour = colour, size = size, linetype = linetype, lineend = lineend,
+ arrow = arrow, inherit.blank = inherit.blank),
class = c("element_line", "element")
)
}
-#' Theme element: text.
-#'
-#' @param family font family
-#' @param face font face ("plain", "italic", "bold", "bold.italic")
-#' @param colour text colour
-#' @param size text size (in pts)
-#' @param hjust horizontal justification (in [0, 1])
-#' @param vjust vertical justification (in [0, 1])
-#' @param angle angle (in [0, 360])
-#' @param lineheight line height
-#' @param color an alias for \code{colour}
-#' @param margin margins around the text. See \code{\link{margin}} for more
+#' @param family Font family
+#' @param face Font face ("plain", "italic", "bold", "bold.italic")
+#' @param hjust Horizontal justification (in [0, 1])
+#' @param vjust Vertical justification (in [0, 1])
+#' @param angle Angle (in [0, 360])
+#' @param lineheight Line height
+#' @param margin Margins around the text. See \code{\link{margin}} for more
#' details. When creating a theme, the margins should be placed on the
#' side of the text facing towards the center of the plot.
#' @param debug If \code{TRUE}, aids visual debugging by drawing a solid
#' rectangle behind the complete text area, and a point where each label
#' is anchored.
#' @export
+#' @rdname element
element_text <- function(family = NULL, face = NULL, colour = NULL,
size = NULL, hjust = NULL, vjust = NULL, angle = NULL, lineheight = NULL,
- color = NULL, margin = NULL, debug = NULL) {
+ color = NULL, margin = NULL, debug = NULL, inherit.blank = FALSE) {
if (!is.null(color)) colour <- color
structure(
list(family = family, face = face, colour = colour, size = size,
hjust = hjust, vjust = vjust, angle = angle, lineheight = lineheight,
- margin = margin, debug = debug),
+ margin = margin, debug = debug, inherit.blank = inherit.blank),
class = c("element_text", "element")
)
}
@@ -84,14 +127,8 @@ element_text <- function(family = NULL, face = NULL, colour = NULL,
print.element <- function(x, ...) utils::str(x)
-#' Relative sizing for theme elements
-#'
-#' @param x A number representing the relative size
-#' @examples
-#' df <- data.frame(x = 1:3, y = 1:3)
-#' ggplot(df, aes(x, y)) +
-#' geom_point() +
-#' theme(axis.title.x = element_text(size = rel(2.5)))
+#' @param x A single number specifying size relative to parent element.
+#' @rdname element
#' @export
rel <- function(x) {
structure(x, class = "rel")
@@ -102,6 +139,7 @@ print.rel <- function(x, ...) print(noquote(paste(x, " *", sep = "")))
#' Reports whether x is a rel object
#' @param x An object to test
+#' @keywords internal
is.rel <- function(x) inherits(x, "rel")
# Given a theme object and element name, return a grob for the element
@@ -114,7 +152,8 @@ element_render <- function(theme, element, ..., name = NULL) {
return(zeroGrob())
}
- ggname(paste(element, name, sep = "."), element_grob(el, ...))
+ grob <- element_grob(el, ...)
+ ggname(paste(element, name, sep = "."), grob)
}
@@ -195,11 +234,15 @@ element_grob.element_line <- function(element, x = 0:1, y = 0:1,
gp <- gpar(lwd = len0_null(size * .pt), col = colour, lty = linetype, lineend = lineend)
element_gp <- gpar(lwd = len0_null(element$size * .pt), col = element$colour,
lty = element$linetype, lineend = element$lineend)
-
+ arrow <- if (is.logical(element$arrow) && !element$arrow) {
+ NULL
+ } else {
+ element$arrow
+ }
polylineGrob(
x, y, default.units = default.units,
gp = utils::modifyList(element_gp, gp),
- id.lengths = id.lengths, ...
+ id.lengths = id.lengths, arrow = arrow, ...
)
}
@@ -237,15 +280,22 @@ el_def <- function(class = NULL, inherit = NULL, description = NULL) {
axis.line.x = el_def("element_line", "axis.line"),
axis.line.y = el_def("element_line", "axis.line"),
axis.text.x = el_def("element_text", "axis.text"),
+ axis.text.x.top = el_def("element_text", "axis.text.x"),
axis.text.y = el_def("element_text", "axis.text"),
+ axis.text.y.right = el_def("element_text", "axis.text.y"),
axis.ticks.length = el_def("unit"),
axis.ticks.x = el_def("element_line", "axis.ticks"),
axis.ticks.y = el_def("element_line", "axis.ticks"),
axis.title.x = el_def("element_text", "axis.title"),
+ axis.title.x.top = el_def("element_text", "axis.title.x"),
axis.title.y = el_def("element_text", "axis.title"),
+ axis.title.y.right = el_def("element_text", "axis.title.y"),
legend.background = el_def("element_rect", "rect"),
- legend.margin = el_def("unit"),
+ legend.margin = el_def("margin"),
+ legend.spacing = el_def("unit"),
+ legend.spacing.x = el_def("unit", "legend.spacing"),
+ legend.spacing.y = el_def("unit", "legend.spacing"),
legend.key = el_def("element_rect", "rect"),
legend.key.height = el_def("unit", "legend.key.size"),
legend.key.width = el_def("unit", "legend.key.size"),
@@ -258,12 +308,15 @@ el_def <- function(class = NULL, inherit = NULL, description = NULL) {
legend.justification = el_def("character"),
legend.box = el_def("character"),
legend.box.just = el_def("character"),
+ legend.box.margin = el_def("margin"),
+ legend.box.background = el_def("element_rect", "rect"),
+ legend.box.spacing = el_def("unit"),
panel.background = el_def("element_rect", "rect"),
panel.border = el_def("element_rect", "rect"),
- panel.margin = el_def("unit"),
- panel.margin.x = el_def("unit", "panel.margin"),
- panel.margin.y = el_def("unit", "panel.margin"),
+ panel.spacing = el_def("unit"),
+ panel.spacing.x = el_def("unit", "panel.spacing"),
+ panel.spacing.y = el_def("unit", "panel.spacing"),
panel.grid.major.x = el_def("element_line", "panel.grid.major"),
panel.grid.major.y = el_def("element_line", "panel.grid.major"),
panel.grid.minor.x = el_def("element_line", "panel.grid.minor"),
@@ -273,11 +326,16 @@ el_def <- function(class = NULL, inherit = NULL, description = NULL) {
strip.background = el_def("element_rect", "rect"),
strip.text.x = el_def("element_text", "strip.text"),
strip.text.y = el_def("element_text", "strip.text"),
+ strip.placement = el_def("character"),
+ strip.placement.x = el_def("character", "strip.placement"),
+ strip.placement.y = el_def("character", "strip.placement"),
strip.switch.pad.grid = el_def("unit"),
strip.switch.pad.wrap = el_def("unit"),
plot.background = el_def("element_rect", "rect"),
plot.title = el_def("element_text", "title"),
+ plot.subtitle = el_def("element_text", "title"),
+ plot.caption = el_def("element_text", "title"),
plot.margin = el_def("margin"),
aspect.ratio = el_def("character")
diff --git a/R/theme.r b/R/theme.r
index 5934f89..edacf97 100644
--- a/R/theme.r
+++ b/R/theme.r
@@ -1,361 +1,380 @@
-#' Get, set and update themes.
+#' Modify components of a theme
#'
-#' Use \code{theme_get} to get the current theme, and \code{theme_set} to
-#' completely override it. \code{theme_update} and \code{theme_replace} are
-#' shorthands for changing individual elements in the current theme.
-#' \code{theme_update} uses the \code{+} operator, so that any unspecified
-#' values in the theme element will default to the values they are set in the
-#' theme. \code{theme_replace} will completely replace the element, so any
-#' unspecified values will overwrite the current value in the theme with \code{NULL}s.
+#' Use \code{theme()} to modify individual components of a theme, allowing
+#' you to control the appearance of all non-data components of the plot.
+#' \code{theme()} only affects a single plot: see \code{\link{theme_update}} if
+#' you want modify the active theme, to affect all subsequent plots.
#'
-#'
-#' @param ... named list of theme settings
-#' @seealso \code{\link{\%+replace\%}} and \code{\link{+.gg}}
-#' @export
-#' @examples
-#' p <- ggplot(mtcars, aes(mpg, wt)) +
-#' geom_point()
-#' p
-#' old <- theme_set(theme_bw())
-#' p
-#' theme_set(old)
-#' p
-#'
-#' #theme_replace NULLs out the fill attribute of panel.background,
-#' #resulting in a white background:
-#' theme_get()$panel.background
-#' old <- theme_replace(panel.background = element_rect(colour = "pink"))
-#' theme_get()$panel.background
-#' p
-#' theme_set(old)
-#'
-#' #theme_update only changes the colour attribute, leaving the others intact:
-#' old <- theme_update(panel.background = element_rect(colour = "pink"))
-#' theme_get()$panel.background
-#' p
-#' theme_set(old)
-#'
-#' theme_get()
-#'
-#'
-#' ggplot(mtcars, aes(mpg, wt)) +
-#' geom_point(aes(color = mpg)) +
-#' theme(legend.position = c(0.95, 0.95),
-#' legend.justification = c(1, 1))
-#' last_plot() +
-#' theme(legend.background = element_rect(fill = "white", colour = "white", size = 3))
-#'
-theme_update <- function(...) {
- theme_set(theme_get() + theme(...))
-}
-
-#' @rdname theme_update
-#' @export
-theme_replace <- function(...) {
- theme_set(theme_get() %+replace% theme(...))
-}
-
-#' Reports whether x is a theme object
-#' @param x An object to test
-#' @export
-is.theme <- function(x) inherits(x, "theme")
-
-#' @export
-print.theme <- function(x, ...) utils::str(x)
-
-#' Set theme elements
-#'
-#'
-#' Use this function to modify theme settings.
-#'
-#' Theme elements can inherit properties from other theme elements.
+#' @section Theme inheritance:
+#' Theme elements inherit properties from other theme elements.
#' For example, \code{axis.title.x} inherits from \code{axis.title},
#' which in turn inherits from \code{text}. All text elements inherit
#' directly or indirectly from \code{text}; all lines inherit from
#' \code{line}, and all rectangular objects inherit from \code{rect}.
-#'
-#' For more examples of modifying properties using inheritance, see
-#' \code{\link{+.gg}} and \code{\link{\%+replace\%}}.
-#'
-#' To see a graphical representation of the inheritance tree, see the
-#' last example below.
-#'
-#' @section Theme elements:
-#' The individual theme elements are:
-#'
-#' \tabular{ll}{
-#' line \tab all line elements
-#' (\code{element_line}) \cr
-#' rect \tab all rectangular elements
-#' (\code{element_rect}) \cr
-#' text \tab all text elements
-#' (\code{element_text}) \cr
-#' title \tab all title elements: plot, axes, legends
-#' (\code{element_text}; inherits from \code{text}) \cr
-#' aspect.ratio \tab aspect ratio of the panel \cr
-#'
-#' axis.title \tab label of axes
-#' (\code{element_text}; inherits from \code{text}) \cr
-#' axis.title.x \tab x axis label
-#' (\code{element_text}; inherits from \code{axis.title}) \cr
-#' axis.title.y \tab y axis label
-#' (\code{element_text}; inherits from \code{axis.title}) \cr
-#' axis.text \tab tick labels along axes
-#' (\code{element_text}; inherits from \code{text}) \cr
-#' axis.text.x \tab x axis tick labels
-#' (\code{element_text}; inherits from \code{axis.text}) \cr
-#' axis.text.y \tab y axis tick labels
-#' (\code{element_text}; inherits from \code{axis.text}) \cr
-#' axis.ticks \tab tick marks along axes
-#' (\code{element_line}; inherits from \code{line}) \cr
-#' axis.ticks.x \tab x axis tick marks
-#' (\code{element_line}; inherits from \code{axis.ticks}) \cr
-#' axis.ticks.y \tab y axis tick marks
-#' (\code{element_line}; inherits from \code{axis.ticks}) \cr
-#' axis.ticks.length \tab length of tick marks
-#' (\code{unit}) \cr
-#' axis.line \tab lines along axes
-#' (\code{element_line}; inherits from \code{line}) \cr
-#' axis.line.x \tab line along x axis
-#' (\code{element_line}; inherits from \code{axis.line}) \cr
-#' axis.line.y \tab line along y axis
-#' (\code{element_line}; inherits from \code{axis.line}) \cr
-#'
-#' legend.background \tab background of legend
-#' (\code{element_rect}; inherits from \code{rect}) \cr
-#' legend.margin \tab extra space added around legend
-#' (\code{unit}) \cr
-#' legend.key \tab background underneath legend keys
-#' (\code{element_rect}; inherits from \code{rect}) \cr
-#' legend.key.size \tab size of legend keys
-#' (\code{unit}; inherits from \code{legend.key.size}) \cr
-#' legend.key.height \tab key background height
-#' (\code{unit}; inherits from \code{legend.key.size}) \cr
-#' legend.key.width \tab key background width
-#' (\code{unit}; inherits from \code{legend.key.size}) \cr
-#' legend.text \tab legend item labels
-#' (\code{element_text}; inherits from \code{text}) \cr
-#' legend.text.align \tab alignment of legend labels
-#' (number from 0 (left) to 1 (right)) \cr
-#' legend.title \tab title of legend
-#' (\code{element_text}; inherits from \code{title}) \cr
-#' legend.title.align \tab alignment of legend title
-#' (number from 0 (left) to 1 (right)) \cr
-#' legend.position \tab the position of legends
-#' ("none", "left", "right", "bottom", "top", or two-element
-#' numeric vector) \cr
-#' legend.direction \tab layout of items in legends
-#' ("horizontal" or "vertical") \cr
-#' legend.justification \tab anchor point for positioning legend inside plot
-#' ("center" or two-element numeric vector) \cr
-#' legend.box \tab arrangement of multiple legends
-#' ("horizontal" or "vertical") \cr
-#' legend.box.just \tab justification of each legend within the overall
-#' bounding box, when there are multiple legends
-#' ("top", "bottom", "left", or "right")\cr
-#'
-#' panel.background \tab background of plotting area, drawn underneath plot
-#' (\code{element_rect}; inherits from \code{rect}) \cr
-#' panel.border \tab border around plotting area, drawn on top of plot
-#' so that it covers tick marks and grid lines. This should
-#' be used with \code{fill=NA}
-#' (\code{element_rect}; inherits from \code{rect}) \cr
-#' panel.margin \tab margin around facet panels
-#' (\code{unit}) \cr
-#' panel.margin.x \tab horizontal margin around facet panels
-#' (\code{unit}; inherits from \code{panel.margin}) \cr
-#' panel.margin.y \tab vertical margin around facet panels
-#' (\code{unit}; inherits from \code{panel.margin}) \cr
-#' panel.grid \tab grid lines
-#' (\code{element_line}; inherits from \code{line}) \cr
-#' panel.grid.major \tab major grid lines
-#' (\code{element_line}; inherits from \code{panel.grid}) \cr
-#' panel.grid.minor \tab minor grid lines
-#' (\code{element_line}; inherits from \code{panel.grid}) \cr
-#' panel.grid.major.x \tab vertical major grid lines
-#' (\code{element_line}; inherits from \code{panel.grid.major}) \cr
-#' panel.grid.major.y \tab horizontal major grid lines
-#' (\code{element_line}; inherits from \code{panel.grid.major}) \cr
-#' panel.grid.minor.x \tab vertical minor grid lines
-#' (\code{element_line}; inherits from \code{panel.grid.minor}) \cr
-#' panel.grid.minor.y \tab horizontal minor grid lines
-#' (\code{element_line}; inherits from \code{panel.grid.minor}) \cr
-#' panel.ontop \tab option to place the panel (background, gridlines)
-#' over the data layers. Usually used with a transparent
-#' or blank \code{panel.background}. (\code{logical}) \cr
-#'
-#' plot.background \tab background of the entire plot
-#' (\code{element_rect}; inherits from \code{rect}) \cr
-#' plot.title \tab plot title (text appearance)
-#' (\code{element_text}; inherits from \code{title}) \cr
-#' plot.margin \tab margin around entire plot
-#' (\code{unit} with the sizes of the top, right, bottom, and
-#' left margins) \cr
-#'
-#' strip.background \tab background of facet labels
-#' (\code{element_rect}; inherits from \code{rect}) \cr
-#' strip.text \tab facet labels
-#' (\code{element_text}; inherits from \code{text}) \cr
-#' strip.text.x \tab facet labels along horizontal direction
-#' (\code{element_text}; inherits from \code{strip.text}) \cr
-#' strip.text.y \tab facet labels along vertical direction
-#' (\code{element_text}; inherits from \code{strip.text}) \cr
-#' strip.switch.pad.grid \tab space between strips and axes when strips are switched
-#' (\code{unit}) \cr
-#' strip.switch.pad.wrap \tab space between strips and axes when strips are switched
-#' (\code{unit}) \cr
-#' }
-#'
-#' @param ... a list of element name, element pairings that modify the
-#' existing theme.
+#' This means that you can modify the appearance of multiple elements by
+#' setting a single high-level component.
+#'
+#' @param line all line elements (\code{element_line})
+#' @param rect all rectangular elements (\code{element_rect})
+#' @param text all text elements (\code{element_text})
+#' @param title all title elements: plot, axes, legends (\code{element_text};
+#' inherits from \code{text})
+#' @param aspect.ratio aspect ratio of the panel
+#'
+#' @param axis.title label of axes (\code{element_text}; inherits from
+#' \code{text})
+#' @param axis.title.x x axis label (\code{element_text}; inherits from
+#' \code{axis.title})
+#' @param axis.title.x.top x axis label on top axis (\code{element_text};
+#' inherits from \code{axis.title.x})
+#' @param axis.title.y y axis label (\code{element_text}; inherits from
+#' \code{axis.title})
+#' @param axis.title.y.right y axis label on right axis (\code{element_text};
+#' inherits from \code{axis.title.y})
+#' @param axis.text tick labels along axes (\code{element_text}; inherits from
+#' \code{text})
+#' @param axis.text.x x axis tick labels (\code{element_text}; inherits from
+#' \code{axis.text})
+#' @param axis.text.x.top x axis tick labels on top axis (\code{element_text};
+#' inherits from \code{axis.text.x})
+#' @param axis.text.y y axis tick labels (\code{element_text}; inherits from
+#' \code{axis.text})
+#' @param axis.text.y.right y axis tick labels on right axis
+#' (\code{element_text}; inherits from \code{axis.text.y})
+#' @param axis.ticks tick marks along axes (\code{element_line}; inherits from
+#' \code{line})
+#' @param axis.ticks.x x axis tick marks (\code{element_line}; inherits from
+#' \code{axis.ticks})
+#' @param axis.ticks.y y axis tick marks (\code{element_line}; inherits from
+#' \code{axis.ticks})
+#' @param axis.ticks.length length of tick marks (\code{unit})
+#' @param axis.line lines along axes (\code{element_line}; inherits from
+#' \code{line})
+#' @param axis.line.x line along x axis (\code{element_line}; inherits from
+#' \code{axis.line})
+#' @param axis.line.y line along y axis (\code{element_line}; inherits from
+#' \code{axis.line})
+#'
+#' @param legend.background background of legend (\code{element_rect}; inherits
+#' from \code{rect})
+#' @param legend.margin the margin around each legend (\code{margin})
+#' @param legend.spacing the spacing between legends (\code{unit})
+#' @param legend.spacing.x the horizontal spacing between legends (\code{unit});
+#' inherits from \code{legend.spacing}
+#' @param legend.spacing.y the horizontal spacing between legends (\code{unit});
+#' inherits from \code{legend.spacing}
+#' @param legend.key background underneath legend keys (\code{element_rect};
+#' inherits from \code{rect})
+#' @param legend.key.size size of legend keys (\code{unit})
+#' @param legend.key.height key background height (\code{unit}; inherits from
+#' \code{legend.key.size})
+#' @param legend.key.width key background width (\code{unit}; inherits from
+#' \code{legend.key.size})
+#' @param legend.text legend item labels (\code{element_text}; inherits from
+#' \code{text})
+#' @param legend.text.align alignment of legend labels (number from 0 (left) to
+#' 1 (right))
+#' @param legend.title title of legend (\code{element_text}; inherits from
+#' \code{title})
+#' @param legend.title.align alignment of legend title (number from 0 (left) to
+#' 1 (right))
+#' @param legend.position the position of legends ("none", "left", "right",
+#' "bottom", "top", or two-element numeric vector)
+#' @param legend.direction layout of items in legends ("horizontal" or
+#' "vertical")
+#' @param legend.justification anchor point for positioning legend inside plot
+#' ("center" or two-element numeric vector) or the justification according to
+#' the plot area when positioned outside the plot
+#' @param legend.box arrangement of multiple legends ("horizontal" or
+#' "vertical")
+#' @param legend.box.just justification of each legend within the overall
+#' bounding box, when there are multiple legends ("top", "bottom", "left", or
+#' "right")
+#' @param legend.box.margin margins around the full legend area, as specified
+#' using \code{\link{margin}}
+#' @param legend.box.background background of legend area (\code{element_rect};
+#' inherits from \code{rect})
+#' @param legend.box.spacing The spacing between the plotting area and the
+#' legend box (\code{unit})
+#'
+#' @param panel.background background of plotting area, drawn underneath plot
+#' (\code{element_rect}; inherits from \code{rect})
+#' @param panel.border border around plotting area, drawn on top of plot so that
+#' it covers tick marks and grid lines. This should be used with
+#' \code{fill=NA}
+#' (\code{element_rect}; inherits from \code{rect})
+#' @param panel.spacing spacing between facet panels (\code{unit})
+#' @param panel.spacing.x horizontal spacing between facet panels (\code{unit};
+#' inherits from \code{panel.spacing})
+#' @param panel.spacing.y vertical spacing between facet panels (\code{unit};
+#' inherits from \code{panel.spacing})
+#' @param panel.grid grid lines (\code{element_line}; inherits from \code{line})
+#' @param panel.grid.major major grid lines (\code{element_line}; inherits from
+#' \code{panel.grid})
+#' @param panel.grid.minor minor grid lines (\code{element_line}; inherits from
+#' \code{panel.grid})
+#' @param panel.grid.major.x vertical major grid lines (\code{element_line};
+#' inherits from \code{panel.grid.major})
+#' @param panel.grid.major.y horizontal major grid lines (\code{element_line};
+#' inherits from \code{panel.grid.major})
+#' @param panel.grid.minor.x vertical minor grid lines (\code{element_line};
+#' inherits from \code{panel.grid.minor})
+#' @param panel.grid.minor.y horizontal minor grid lines (\code{element_line};
+#' inherits from \code{panel.grid.minor})
+#' @param panel.ontop option to place the panel (background, gridlines) over
+#' the data layers. Usually used with a transparent or blank
+#' \code{panel.background}. (\code{logical})
+#'
+#' @param plot.background background of the entire plot (\code{element_rect};
+#' inherits from \code{rect})
+#' @param plot.title plot title (text appearance) (\code{element_text}; inherits
+#' from \code{title}) left-aligned by default
+#' @param plot.subtitle plot subtitle (text appearance) (\code{element_text};
+#' inherits from \code{title}) left-aligned by default
+#' @param plot.caption caption below the plot (text appearance)
+#' (\code{element_text}; inherits from \code{title}) right-aligned by default
+#' @param plot.margin margin around entire plot (\code{unit} with the sizes of
+#' the top, right, bottom, and left margins)
+#'
+#' @param strip.background background of facet labels (\code{element_rect};
+#' inherits from \code{rect})
+#' @param strip.placement placement of strip with respect to axes,
+#' either "inside" or "outside". Only important when axes and strips are
+#' on the same side of the plot.
+#' @param strip.text facet labels (\code{element_text}; inherits from
+#' \code{text})
+#' @param strip.text.x facet labels along horizontal direction
+#' (\code{element_text}; inherits from \code{strip.text})
+#' @param strip.text.y facet labels along vertical direction
+#' (\code{element_text}; inherits from \code{strip.text})
+#' @param strip.switch.pad.grid space between strips and axes when strips are
+#' switched (\code{unit})
+#' @param strip.switch.pad.wrap space between strips and axes when strips are
+#' switched (\code{unit})
+#'
+#' @param ... additional element specifications not part of base ggplot2. If
+#' supplied \code{validate} needs to be set to \code{FALSE}.
#' @param complete set this to TRUE if this is a complete theme, such as
#' the one returned \code{by theme_grey()}. Complete themes behave
-#' differently when added to a ggplot object.
-#' @param validate TRUE to run validate_element, FALSE to bypass checks.
-#'
-#' @seealso \code{\link{+.gg}}
-#' @seealso \code{\link{\%+replace\%}}
-#' @seealso \code{\link{rel}}
-#' @seealso \code{\link{element_blank}}
-#' @seealso \code{\link{element_line}}
-#' @seealso \code{\link{element_rect}}
-#' @seealso \code{\link{element_text}}
+#' differently when added to a ggplot object. Also, when setting
+#' \code{complete = TRUE} all elements will be set to inherit from blank
+#' elements.
+#' @param validate \code{TRUE} to run validate_element, \code{FALSE} to bypass checks.
+#'
+#' @seealso
+#' \code{\link{+.gg}} and \code{\link{\%+replace\%}},
+#' \code{\link{element_blank}}, \code{\link{element_line}},
+#' \code{\link{element_rect}}, and \code{\link{element_text}} for
+#' details of the specific theme elements.
#' @export
#' @examples
-#' \donttest{
-#' p <- ggplot(mtcars, aes(mpg, wt)) +
-#' geom_point()
-#' p
-#' p + theme(panel.background = element_rect(colour = "pink"))
-#' p + theme_bw()
-#'
-#' # Scatter plot of gas mileage by vehicle weight
-#' p <- ggplot(mtcars, aes(wt, mpg)) +
-#' geom_point()
-#' # Calculate slope and intercept of line of best fit
-#' coef(lm(mpg ~ wt, data = mtcars))
-#' p + geom_abline(intercept = 37, slope = -5)
-#' # Calculate correlation coefficient
-#' with(mtcars, cor(wt, mpg, use = "everything", method = "pearson"))
-#' #annotate the plot
-#' p + geom_abline(intercept = 37, slope = -5) +
-#' geom_text(data = data.frame(), aes(4.5, 30, label = "Pearson-R = -.87"))
-#'
-#' # Change the axis labels
-#' # Original plot
-#' p
-#' p + labs(x = "Vehicle Weight", y = "Miles per Gallon")
-#' # Or
-#' p + labs(x = "Vehicle Weight", y = "Miles per Gallon")
-#'
-#' # Change title appearance
-#' p <- p + labs(title = "Vehicle Weight-Gas Mileage Relationship")
-#' # Set title to twice the base font size
-#' p + theme(plot.title = element_text(size = rel(2)))
-#' p + theme(plot.title = element_text(size = rel(2), colour = "blue"))
-#'
-#' # Changing plot look with themes
-#' DF <- data.frame(x = rnorm(400))
-#' m <- ggplot(DF, aes(x = x)) +
-#' geom_histogram()
-#' # Default is theme_grey()
-#' m
-#' # Compare with
-#' m + theme_bw()
-#'
-#' # Manipulate Axis Attributes
-#' m + theme(axis.line = element_line(size = 3, colour = "red", linetype = "dotted"))
-#' m + theme(axis.text = element_text(colour = "blue"))
-#' m + theme(axis.text.y = element_blank())
-#' m + theme(axis.ticks = element_line(size = 2))
-#' m + theme(axis.title.y = element_text(size = rel(1.5), angle = 90))
-#' m + theme(axis.title.x = element_blank())
-#' m + theme(axis.ticks.length = unit(.85, "cm"))
-#'
-#' # Legend Attributes
-#' z <- ggplot(mtcars, aes(wt, mpg)) +
-#' geom_point(aes(colour = factor(cyl)))
-#' z
-#' z + theme(legend.position = "none")
-#' z + theme(legend.position = "bottom")
-#' # Or use relative coordinates between 0 and 1
-#' z + theme(legend.position = c(.5, .5))
-#' # Add a border to the whole legend
-#' z + theme(legend.background = element_rect(colour = "black"))
-#' # Legend margin controls extra space around outside of legend:
-#' z + theme(legend.background = element_rect(),
-#' legend.margin = unit(1, "cm"))
-#' z + theme(legend.background = element_rect(),
-#' legend.margin = unit(0, "cm"))
-#' # Or to just the keys
-#' z + theme(legend.key = element_rect(colour = "black"))
-#' z + theme(legend.key = element_rect(fill = "yellow"))
-#' z + theme(legend.key.size = unit(2.5, "cm"))
-#' z + theme(legend.text = element_text(size = 20, colour = "red", angle = 45))
-#' z + theme(legend.title = element_text(face = "italic"))
-#'
-#' # To change the title of the legend use the name argument
-#' # in one of the scale options
-#' z + scale_colour_brewer(name = "My Legend")
-#' z + scale_colour_grey(name = "Number of \nCylinders")
-#'
-#' # Panel and Plot Attributes
-#' z + theme(panel.background = element_rect(fill = "black"))
-#' z + theme(panel.border = element_rect(linetype = "dashed", colour = "black"))
-#' z + theme(panel.grid.major = element_line(colour = "blue"))
-#' z + theme(panel.grid.minor = element_line(colour = "red", linetype = "dotted"))
-#' z + theme(panel.grid.major = element_line(size = 2))
-#' z + theme(panel.grid.major.y = element_blank(),
-#' panel.grid.minor.y = element_blank())
-#' z + theme(plot.background = element_rect())
-#' z + theme(plot.background = element_rect(fill = "green"))
-#'
-#' # Faceting Attributes
-#' set.seed(4940)
-#' dsmall <- diamonds[sample(nrow(diamonds), 1000), ]
-#' k <- ggplot(dsmall, aes(carat, ..density..)) +
-#' geom_histogram(binwidth = 0.2) +
-#' facet_grid(. ~ cut)
-#' k + theme(strip.background = element_rect(colour = "purple", fill = "pink",
-#' size = 3, linetype = "dashed"))
-#' k + theme(strip.text.x = element_text(colour = "red", angle = 45, size = 10,
-#' hjust = 0.5, vjust = 0.5))
-#' k + theme(panel.margin = unit(5, "lines"))
-#' k + theme(panel.margin.y = unit(0, "lines"))
-#'
-#' # Put gridlines on top
-#' meanprice <- tapply(diamonds$price, diamonds$cut, mean)
-#' cut <- factor(levels(diamonds$cut), levels = levels(diamonds$cut))
-#' df <- data.frame(meanprice, cut)
-#' g <- ggplot(df, aes(cut, meanprice)) + geom_bar(stat = "identity")
-#' g + geom_bar(stat = "identity") +
-#' theme(panel.background = element_blank(),
-#' panel.grid.major.x = element_blank(),
-#' panel.grid.minor.x = element_blank(),
-#' panel.grid.minor.y = element_blank(),
-#' panel.ontop = TRUE)
-#'
-#' # Modify a theme and save it
-#' mytheme <- theme_grey() + theme(plot.title = element_text(colour = "red"))
-#' p + mytheme
+#' p1 <- ggplot(mtcars, aes(wt, mpg)) +
+#' geom_point() +
+#' labs(title = "Fuel economy declines as weight increases")
+#' p1
+#'
+#' # Plot ---------------------------------------------------------------------
+#' p1 + theme(plot.title = element_text(size = rel(2)))
+#' p1 + theme(plot.background = element_rect(fill = "green"))
+#'
+#' # Panels --------------------------------------------------------------------
+#'
+#' p1 + theme(panel.background = element_rect(fill = "white", colour = "grey50"))
+#' p1 + theme(panel.border = element_rect(linetype = "dashed", fill = NA))
+#' p1 + theme(panel.grid.major = element_line(colour = "black"))
+#' p1 + theme(
+#' panel.grid.major.y = element_blank(),
+#' panel.grid.minor.y = element_blank()
+#' )
+#'
+#' # Put gridlines on top of data
+#' p1 + theme(
+#' panel.background = element_rect(fill = NA),
+#' panel.grid.major = element_line(colour = "grey50"),
+#' panel.ontop = TRUE
+#' )
+#'
+#' # Axes ----------------------------------------------------------------------
+#' p1 + theme(axis.line = element_line(size = 3, colour = "grey80"))
+#' p1 + theme(axis.text = element_text(colour = "blue"))
+#' p1 + theme(axis.ticks = element_line(size = 2))
+#' p1 + theme(axis.ticks.length = unit(.25, "cm"))
+#' p1 + theme(axis.title.y = element_text(size = rel(1.5), angle = 90))
#'
+#' \donttest{
+#' # Legend --------------------------------------------------------------------
+#' p2 <- ggplot(mtcars, aes(wt, mpg)) +
+#' geom_point(aes(colour = factor(cyl), shape = factor(vs))) +
+#' labs(
+#' x = "Weight (1000 lbs)",
+#' y = "Fuel economy (mpg)",
+#' colour = "Cylinders",
+#' shape = "Transmission"
+#' )
+#' p2
+#'
+#' # Position
+#' p2 + theme(legend.position = "none")
+#' p2 + theme(legend.justification = "top")
+#' p2 + theme(legend.position = "bottom")
+#'
+#' # Or place inside the plot using relative coordinates between 0 and 1
+#' # legend.justification sets the corner that the position refers to
+#' p2 + theme(
+#' legend.position = c(.95, .95),
+#' legend.justification = c("right", "top"),
+#' legend.box.just = "right",
+#' legend.margin = margin(6, 6, 6, 6)
+#' )
+#'
+#' # The legend.box properties work similarly for the space around
+#' # all the legends
+#' p2 + theme(
+#' legend.box.background = element_rect(),
+#' legend.box.margin = margin(6, 6, 6, 6)
+#' )
+#'
+#' # You can also control the display of the keys
+#' # and the justifaction related to the plot area can be set
+#' p2 + theme(legend.key = element_rect(fill = "white", colour = "black"))
+#' p2 + theme(legend.text = element_text(size = 8, colour = "red"))
+#' p2 + theme(legend.title = element_text(face = "bold"))
+#'
+#' # Strips --------------------------------------------------------------------
+#'
+#' p3 <- ggplot(mtcars, aes(wt, mpg)) +
+#' geom_point() +
+#' facet_wrap(~ cyl)
+#' p3
+#'
+#' p3 + theme(strip.background = element_rect(colour = "black", fill = "white"))
+#' p3 + theme(strip.text.x = element_text(colour = "white", face = "bold"))
+#' p3 + theme(panel.spacing = unit(1, "lines"))
#' }
-theme <- function(..., complete = FALSE, validate = TRUE) {
- elements <- list(...)
+theme <- function(line,
+ rect,
+ text,
+ title,
+ aspect.ratio,
+ axis.title,
+ axis.title.x,
+ axis.title.x.top,
+ axis.title.y,
+ axis.title.y.right,
+ axis.text,
+ axis.text.x,
+ axis.text.x.top,
+ axis.text.y,
+ axis.text.y.right,
+ axis.ticks,
+ axis.ticks.x,
+ axis.ticks.y,
+ axis.ticks.length,
+ axis.line,
+ axis.line.x,
+ axis.line.y,
+ legend.background,
+ legend.margin,
+ legend.spacing,
+ legend.spacing.x,
+ legend.spacing.y,
+ legend.key,
+ legend.key.size,
+ legend.key.height,
+ legend.key.width,
+ legend.text,
+ legend.text.align,
+ legend.title,
+ legend.title.align,
+ legend.position,
+ legend.direction,
+ legend.justification,
+ legend.box,
+ legend.box.just,
+ legend.box.margin,
+ legend.box.background,
+ legend.box.spacing,
+ panel.background,
+ panel.border,
+ panel.spacing,
+ panel.spacing.x,
+ panel.spacing.y,
+ panel.grid,
+ panel.grid.major,
+ panel.grid.minor,
+ panel.grid.major.x,
+ panel.grid.major.y,
+ panel.grid.minor.x,
+ panel.grid.minor.y,
+ panel.ontop,
+ plot.background,
+ plot.title,
+ plot.subtitle,
+ plot.caption,
+ plot.margin,
+ strip.background,
+ strip.placement,
+ strip.text,
+ strip.text.x,
+ strip.text.y,
+ strip.switch.pad.grid,
+ strip.switch.pad.wrap,
+ ...,
+ complete = FALSE,
+ validate = TRUE
+ ) {
+ elements <- find_args(..., complete = NULL, validate = NULL)
if (!is.null(elements$axis.ticks.margin)) {
warning("`axis.ticks.margin` is deprecated. Please set `margin` property ",
" of `axis.text` instead", call. = FALSE)
elements$axis.ticks.margin <- NULL
}
+ if (!is.null(elements$panel.margin)) {
+ warning("`panel.margin` is deprecated. Please use `panel.spacing` property ",
+ "instead", call. = FALSE)
+ elements$panel.spacing <- elements$panel.margin
+ elements$panel.margin <- NULL
+ }
+ if (!is.null(elements$panel.margin.x)) {
+ warning("`panel.margin.x` is deprecated. Please use `panel.spacing.x` property ",
+ "instead", call. = FALSE)
+ elements$panel.spacing.x <- elements$panel.margin.x
+ elements$panel.margin.x <- NULL
+ }
+ if (!is.null(elements$panel.margin.y)) {
+ warning("`panel.margin` is deprecated. Please use `panel.spacing` property ",
+ "instead", call. = FALSE)
+ elements$panel.spacing.y <- elements$panel.margin.y
+ elements$panel.margin.y <- NULL
+ }
+ if (is.unit(elements$legend.margin) && !is.margin(elements$legend.margin)) {
+ warning("`legend.margin` must be specified using `margin()`. For the old ",
+ "behavior use legend.spacing", call. = FALSE)
+ elements$legend.spacing <- elements$legend.margin
+ elements$legend.margin <- margin()
+ }
# Check that all elements have the correct class (element_text, unit, etc)
if (validate) {
mapply(validate_element, elements, names(elements))
}
- structure(elements, class = c("theme", "gg"),
- complete = complete, validate = validate)
+ # If complete theme set all non-blank elements to inherit from blanks
+ if (complete) {
+ elements <- lapply(elements, function(el) {
+ if (inherits(el, "element") && !inherits(el, "element_blank")) {
+ el$inherit.blank <- TRUE
+ }
+ el
+ })
+ }
+ structure(
+ elements,
+ class = c("theme", "gg"),
+ complete = complete,
+ validate = validate
+ )
}
@@ -364,61 +383,16 @@ plot_theme <- function(x) {
defaults(x$theme, theme_get())
}
-
-.theme <- (function() {
- theme <- theme_gray()
-
- list(
- get = function() theme,
- set = function(new) {
- missing <- setdiff(names(theme_gray()), names(new))
- if (length(missing) > 0) {
- warning("New theme missing the following elements: ",
- paste(missing, collapse = ", "), call. = FALSE)
- }
-
- old <- theme
- theme <<- new
- invisible(old)
- }
- )
-})()
-
-
-#' @rdname theme_update
-#' @export
-theme_get <- .theme$get
-#' @rdname theme_update
-#' @param new new theme (a list of theme elements)
-#' @export
-theme_set <- .theme$set
-
-
-#' @rdname gg-add
-#' @export
-"%+replace%" <- function(e1, e2) {
- if (!is.theme(e1) || !is.theme(e2)) {
- stop("%+replace% requires two theme objects", call. = FALSE)
- }
-
- # Can't use modifyList here since it works recursively and drops NULLs
- e1[names(e2)] <- e2
- e1
-}
-
-
#' Modify properties of an element in a theme object
#'
#' @param t1 A theme object
#' @param t2 A theme object that is to be added to \code{t1}
#' @param t2name A name of the t2 object. This is used for printing
#' informative error messages.
-#'
-#' @seealso +.gg
-#'
+#' @keywords internal
add_theme <- function(t1, t2, t2name) {
if (!is.theme(t2)) {
- stop("Don't know how to add ", t2name, " to a theme object",
+ stop("Don't know how to add RHS to a theme object",
call. = FALSE)
}
@@ -509,6 +483,8 @@ update_theme <- function(oldtheme, newtheme) {
#' @param element The name of the theme element to calculate
#' @param theme A theme object (like theme_grey())
#' @param verbose If TRUE, print out which elements this one inherits from
+#' @keywords internal
+#' @export
#' @examples
#' t <- theme_grey()
#' calc_element('text', t)
@@ -522,8 +498,6 @@ update_theme <- function(oldtheme, newtheme) {
#' t$axis.text.x
#' t$axis.text
#' t$text
-#'
-#' @export
calc_element <- function(element, theme, verbose = FALSE) {
if (verbose) message(element, " --> ", appendLF = FALSE)
@@ -572,10 +546,15 @@ calc_element <- function(element, theme, verbose = FALSE) {
combine_elements <- function(e1, e2) {
# If e2 is NULL, nothing to inherit
- if (is.null(e2)) return(e1)
-
- # If e1 is NULL, or if e2 is element_blank, inherit everything from e2
- if (is.null(e1) || inherits(e2, "element_blank")) return(e2)
+ if (is.null(e2) || inherits(e1, "element_blank")) return(e1)
+ # If e1 is NULL inherit everything from e2
+ if (is.null(e1)) return(e2)
+ # If e2 is element_blank, and e1 inherits blank inherit everything from e2,
+ # otherwise ignore e2
+ if (inherits(e2, "element_blank")) {
+ if (e1$inherit.blank) return(e2)
+ else return(e1)
+ }
# If e1 has any NULL properties, inherit them from e2
n <- vapply(e1[names(e2)], is.null, logical(1))
@@ -588,3 +567,12 @@ combine_elements <- function(e1, e2) {
e1
}
+
+#' Reports whether x is a theme object
+#' @param x An object to test
+#' @export
+#' @keywords internal
+is.theme <- function(x) inherits(x, "theme")
+
+#' @export
+print.theme <- function(x, ...) utils::str(x)
diff --git a/R/translate-qplot-ggplot.r b/R/translate-qplot-ggplot.r
index 86de942..03fd1bf 100644
--- a/R/translate-qplot-ggplot.r
+++ b/R/translate-qplot-ggplot.r
@@ -7,6 +7,7 @@
#' describes what those defaults are, and how they map to the fuller ggplot()
#' syntax.
#'
+#' @keywords internal
#' @name translate_qplot_ggplot
#' @examples
#'
diff --git a/R/translate-qplot-lattice.r b/R/translate-qplot-lattice.r
index 1a97b6e..53db66c 100644
--- a/R/translate-qplot-lattice.r
+++ b/R/translate-qplot-lattice.r
@@ -4,6 +4,7 @@
#' formula based interface. ggplot2 does not because the formula does not
#' generalise well to more complicated situations.
#'
+#' @keywords internal
#' @name translate_qplot_lattice
#' @examples
#' \donttest{
diff --git a/R/utilities-break.r b/R/utilities-break.r
index aa18194..f96fee6 100644
--- a/R/utilities-break.r
+++ b/R/utilities-break.r
@@ -1,4 +1,4 @@
-#' Cut up numeric vector into useful groups.
+#' Discretise numeric data into categorical
#'
#' \code{cut_interval} makes \code{n} groups with equal range, \code{cut_number}
#' makes \code{n} groups with (approximately) equal numbers of observations;
diff --git a/R/utilities-help.r b/R/utilities-help.r
index 66d16a6..52eeaeb 100644
--- a/R/utilities-help.r
+++ b/R/utilities-help.r
@@ -1,29 +1,10 @@
-aesthetics <- function(x) {
- req_aes <- x$required_aes
- def_aes <- names(x$default_aes)
- def_aes <- setdiff(def_aes, req_aes)
- if (length(req_aes) == 0) {
- # Suppress warnings which occur when sorting NULL
- return(suppressWarnings(sort(names(x$default_aes))))
- }
- if (length(def_aes) == 0) {
- return(paste("\\strong{", sort(x$required_aes), "}",sep = ""))
- }
- return(c(paste("\\strong{", sort(x$required_aes), "}", sep = ""), sort(def_aes)))
-}
-geom_aesthetics <- function(x) {
- aesthetics(find_subclass("Geom", x))
-}
-stat_aesthetics <- function(x) {
- aesthetics(find_subclass("Stat", x))
-}
rd_aesthetics <- function(type, name) {
obj <- switch(type,
- geom = find_subclass("Geom", name),
- stat = find_subclass("Stat", name)
+ geom = find_subclass("Geom", name, globalenv()),
+ stat = find_subclass("Stat", name, globalenv())
)
- aes <- aesthetics(obj)
+ aes <- rd_aesthetics_item(obj)
paste("\\code{", type, "_", name, "} ",
"understands the following aesthetics (required aesthetics are in bold):\n\n",
@@ -31,3 +12,13 @@ rd_aesthetics <- function(type, name) {
paste(" \\item \\code{", aes, "}", collapse = "\n", sep = ""),
"\n}\n", sep = "")
}
+
+rd_aesthetics_item <- function(x) {
+ req <- x$required_aes
+ all <- union(req, sort(x$aesthetics()))
+
+ ifelse(all %in% req,
+ paste0("\\strong{", all, "}"),
+ all
+ )
+}
diff --git a/R/utilities-resolution.r b/R/utilities-resolution.r
index 11a0f0f..85d6997 100644
--- a/R/utilities-resolution.r
+++ b/R/utilities-resolution.r
@@ -1,11 +1,9 @@
-#' Compute the "resolution" of a data vector.
+#' Compute the "resolution" of a numeric vector
#'
-#' The resolution is is the smallest non-zero distance between adjacent
+#' The resolution is the smallest non-zero distance between adjacent
#' values. If there is only one unique value, then the resolution is defined
-#' to be one.
-#'
-#' If x is an integer vector, then it is assumed to represent a discrete
-#' variable, and the resolution is 1.
+#' to be one. If x is an integer vector, then it is assumed to represent a
+#' discrete variable, and the resolution is 1.
#'
#' @param x numeric vector
#' @param zero should a zero value be automatically included in the
@@ -15,8 +13,10 @@
#' resolution(1:10)
#' resolution((1:10) - 0.5)
#' resolution((1:10) - 0.5, FALSE)
-#' resolution(c(1,2, 10, 20, 50))
-#' resolution(as.integer(c(1, 10, 20, 50))) # Returns 1
+#'
+#' # Note the difference between numeric and integer vectors
+#' resolution(c(2, 10, 20, 50))
+#' resolution(c(2L, 10L, 20L, 50L))
resolution <- function(x, zero = TRUE) {
if (is.integer(x) || zero_range(range(x, na.rm = TRUE)))
return(1)
diff --git a/R/utilities.r b/R/utilities.r
index 7a60f8e..a1233f0 100644
--- a/R/utilities.r
+++ b/R/utilities.r
@@ -77,7 +77,8 @@ uniquecols <- function(df) {
#' @param finite If \code{TRUE}, will also remove non-finite values.
#' @keywords internal
#' @export
-remove_missing <- function(df, na.rm=FALSE, vars = names(df), name="", finite = FALSE) {
+remove_missing <- function(df, na.rm = FALSE, vars = names(df), name = "",
+ finite = FALSE) {
stopifnot(is.logical(na.rm))
vars <- intersect(vars, names(df))
@@ -279,3 +280,25 @@ dispatch_args <- function(f, ...) {
formals(f) <- formals
f
}
+
+is_missing_arg <- function(x) identical(x, quote(expr = ))
+# Get all arguments in a function as a list. Will fail if an ellipsis argument
+# named .ignore
+# @param ... passed on in case enclosing function uses ellipsis in argument list
+find_args <- function(...) {
+ env <- parent.frame()
+ args <- names(formals(sys.function(sys.parent(1))))
+
+ vals <- mget(args, envir = env)
+ vals <- vals[!vapply(vals, is_missing_arg, logical(1))]
+
+ utils::modifyList(vals, list(..., `...` = NULL))
+}
+
+# Used in annotations to ensure printed even when no
+# global data
+dummy_data <- function() data.frame(x = NA)
+
+# Needed to trigger package loading
+#' @importFrom tibble tibble
+NULL
diff --git a/R/zzz.r b/R/zzz.r
index 5c50a87..98925da 100644
--- a/R/zzz.r
+++ b/R/zzz.r
@@ -3,7 +3,7 @@
tips <- c(
"Need help? Try the ggplot2 mailing list: http://groups.google.com/group/ggplot2.",
- "Find out what's changed in ggplot2 at http://github.com/hadley/ggplot2/releases.",
+ "Find out what's changed in ggplot2 at http://github.com/tidyverse/ggplot2/releases.",
"Use suppressPackageStartupMessages() to eliminate package startup messages.",
"Stackoverflow is a great place to get help: http://stackoverflow.com/tags/ggplot2.",
"Need help getting started? Try the cookbook for R: http://www.cookbook-r.com/Graphs/",
@@ -13,3 +13,9 @@
tip <- sample(tips, 1)
packageStartupMessage(paste(strwrap(tip), collapse = "\n"))
}
+
+release_questions <- function() {
+ c(
+ "Have you built the book?"
+ )
+}
diff --git a/README.md b/README.md
index a078edf..b9e05d1 100644
--- a/README.md
+++ b/README.md
@@ -1,26 +1,62 @@
-# ggplot2
-[![Build Status](https://travis-ci.org/hadley/ggplot2.png?branch=master)](https://travis-ci.org/hadley/ggplot2)
-[![CRAN_Status_Badge](http://www.r-pkg.org/badges/version/ggplot2)](http://cran.r-project.org/package=ggplot2)
+<!-- README.md is generated from README.Rmd. Please edit that file -->
+ggplot2 <img src="logo.png" align="right" />
+============================================
-ggplot2 is a plotting system for R, based on the grammar of graphics, which tries to take the good parts of base and lattice graphics and avoid bad parts. It takes care of many of the fiddly details
-that make plotting a hassle (like drawing legends) as well as providing a powerful model of graphics that makes it easy to produce complex multi-layered graphics.
+[![Build Status](https://travis-ci.org/tidyverse/ggplot2.svg?branch=master)](https://travis-ci.org/tidyverse/ggplot2) [![Coverage Status](https://img.shields.io/codecov/c/github/tidyverse/ggplot2/master.svg)](https://codecov.io/github/tidyverse/ggplot2?branch=master) [![CRAN\_Status\_Badge](http://www.r-pkg.org/badges/version/ggplot2)](https://cran.r-project.org/package=ggplot2)
-Find out more at <http://ggplot2.org>, and check out the nearly 500
-examples of ggplot in use. If you're interested, you can also sign up to
-the [ggplot2 mailing list at](http://groups.google.com/group/ggplot2).
+Overview
+--------
-## Installation
+ggplot2 is a system for declaratively creating graphics, based on [The Grammar of Graphics](http://amzn.to/2ef1eWp). You provide the data, tell ggplot2 how to map variables to aesthetics, what graphical primitives to use, and it takes care of the details.
-Get the released version from CRAN:
+Installation
+------------
-```R
+``` r
+# The easiest way to get ggplot2 is to install the whole tidyverse:
+install.packages("tidyverse")
+
+# Alternatively, install just ggplot2:
install.packages("ggplot2")
+
+# Or the the development version from GitHub:
+# install.packages("devtools")
+devtools::install_github("tidyverse/ggplot2")
```
-Or the development version from github:
+Usage
+-----
-```R
-# install.packages("devtools")
-devtools::install_github("hadley/ggplot2")
+It's hard to succintly describe how ggplot2 works because it embodies a deep philosophy of visualisation. However, in most cases you start with `ggplot()`, supply a dataset and aesthetic mapping (with `aes()`). You then add on layers (like `geom_point()` or `geom_histogram()`), scales (like `scale_colour_brewer()`), faceting specifications (like `facet_wrap()`) and coordinate systems (like `coord_flip()`).
+
+``` r
+library(ggplot2)
+
+ggplot(mpg, aes(displ, hwy, colour = class)) +
+ geom_point()
```
+
+![](README-example-1.png)
+
+Learning ggplot2
+----------------
+
+If you are new to ggplot2 you are better off starting with a systematic introduction, rather than trying to learn from reading individual documentation pages. Currently, there are three good places to start:
+
+1. The [data visualisation](http://r4ds.had.co.nz/data-visualisation.html) and [graphics forcommunication](http://r4ds.had.co.nz/graphics-for-communication.html) chapters in [R for data science](http://r4ds.had.co.nz). R for data science is designed to give you a comprehensive introduction to the [tidyverse](http://tidyverse.org), and these two chapters will you get up to speed with the essentials of ggplot2 as quickly as possible.
+
+2. If you'd like to take an interactive online course, try [Data visualisation with ggplot2](https://www.datacamp.com/courses/data-visualization-with-ggplot2-1) by Rick Scavetta on datacamp.
+
+3. If you want to dive into making common graphics as quickly as possible, I recommend [The R Graphics Cookbook](http://amzn.to/2dVfMfn) by Winston Chang. It provides a set of recipes to solve common graphics problems. A 2nd edition will is due out in 2017.
+
+If you've mastered the basics and want to learn more, read [ggplot2: Elegant Graphics for Data Analysis](http://amzn.to/2fncG50). It describes the theoretical underpinnings of ggplot2 and shows you how all the pieces fit together. This book helps you understand the theory that underpins ggplot2, and will help you create new types of graphic specifically tailored to your needs. The book is not available for free, but you can find the complete source for the book at <https://github.com/had [...]
+
+Getting help
+------------
+
+There are two main places to get help with ggplot2:
+
+1. The [ggplot2 mailing list](https://groups.google.com/forum/?fromgroups#!forum/ggplot2) is a friendly place to ask any questions about ggplot2. You must be a member to post messages, but anyone can read the archived discussions.
+
+2. [stackoverflow](so) is a great source of answers to common ggplot2 questions. It is also a great place to get help, once you have created a reproducible example that illustrates your problem.
diff --git a/build/partial.rdb b/build/partial.rdb
index 4c7a641..58e39b9 100644
Binary files a/build/partial.rdb and b/build/partial.rdb differ
diff --git a/build/vignette.rds b/build/vignette.rds
index df6db96..fe4d89f 100644
Binary files a/build/vignette.rds and b/build/vignette.rds differ
diff --git a/inst/doc/extending-ggplot2.R b/inst/doc/extending-ggplot2.R
index af347e6..c4ff695 100644
--- a/inst/doc/extending-ggplot2.R
+++ b/inst/doc/extending-ggplot2.R
@@ -1,5 +1,5 @@
## ---- include = FALSE----------------------------------------------------
-knitr::opts_chunk$set(collapse = TRUE, comment = "#>")
+knitr::opts_chunk$set(collapse = TRUE, comment = "#>", fig.width = 7, fig.height = 7, fig.align = "center")
library(ggplot2)
## ----ggproto-intro-------------------------------------------------------
@@ -326,3 +326,353 @@ base <- ggplot(df, aes(x, y)) +
base
base + theme(text = element_text(colour = "red"))
+## ------------------------------------------------------------------------
+layout <- function(data, params) {
+ data.frame(PANEL = c(1L, 2L), SCALE_X = 1L, SCALE_Y = 1L)
+}
+
+## ------------------------------------------------------------------------
+mapping <- function(data, layout, params) {
+ if (plyr::empty(data)) {
+ return(cbind(data, PANEL = integer(0)))
+ }
+ rbind(
+ cbind(data, PANEL = 1L),
+ cbind(data, PANEL = 2L)
+ )
+}
+
+## ------------------------------------------------------------------------
+render <- function(panels, layout, x_scales, y_scales, ranges, coord, data,
+ theme, params) {
+ # Place panels according to settings
+ if (params$horizontal) {
+ # Put panels in matrix and convert to a gtable
+ panels <- matrix(panels, ncol = 2)
+ panel_table <- gtable::gtable_matrix("layout", panels,
+ widths = unit(c(1, 1), "null"), heights = unit(1, "null"), clip = "on")
+ # Add spacing according to theme
+ panel_spacing <- if (is.null(theme$panel.spacing.x)) {
+ theme$panel.spacing
+ } else {
+ theme$panel.spacing.x
+ }
+ panel_table <- gtable::gtable_add_col_space(panel_table, panel_spacing)
+ } else {
+ panels <- matrix(panels, ncol = 1)
+ panel_table <- gtable::gtable_matrix("layout", panels,
+ widths = unit(1, "null"), heights = unit(c(1, 1), "null"), clip = "on")
+ panel_spacing <- if (is.null(theme$panel.spacing.y)) {
+ theme$panel.spacing
+ } else {
+ theme$panel.spacing.y
+ }
+ panel_table <- gtable::gtable_add_row_space(panel_table, panel_spacing)
+ }
+ # Name panel grobs so they can be found later
+ panel_table$layout$name <- paste0("panel-", c(1, 2))
+
+ # Construct the axes
+ axes <- render_axes(ranges[1], ranges[1], coord, theme,
+ transpose = TRUE)
+
+ # Add axes around each panel
+ panel_pos_h <- panel_cols(panel_table)$l
+ panel_pos_v <- panel_rows(panel_table)$t
+ axis_width_l <- unit(grid::convertWidth(
+ grid::grobWidth(axes$y$left[[1]]), "cm", TRUE), "cm")
+ axis_width_r <- unit(grid::convertWidth(
+ grid::grobWidth(axes$y$right[[1]]), "cm", TRUE), "cm")
+ ## We do it reverse so we don't change the position of panels when we add axes
+ for (i in rev(panel_pos_h)) {
+ panel_table <- gtable::gtable_add_cols(panel_table, axis_width_r, i)
+ panel_table <- gtable::gtable_add_grob(panel_table,
+ rep(axes$y$right, length(panel_pos_v)), t = panel_pos_v, l = i + 1,
+ clip = "off")
+ panel_table <- gtable::gtable_add_cols(panel_table, axis_width_l, i - 1)
+ panel_table <- gtable::gtable_add_grob(panel_table,
+ rep(axes$y$left, length(panel_pos_v)), t = panel_pos_v, l = i,
+ clip = "off")
+ }
+ ## Recalculate as gtable has changed
+ panel_pos_h <- panel_cols(panel_table)$l
+ panel_pos_v <- panel_rows(panel_table)$t
+ axis_height_t <- unit(grid::convertHeight(
+ grid::grobHeight(axes$x$top[[1]]), "cm", TRUE), "cm")
+ axis_height_b <- unit(grid::convertHeight(
+ grid::grobHeight(axes$x$bottom[[1]]), "cm", TRUE), "cm")
+ for (i in rev(panel_pos_v)) {
+ panel_table <- gtable::gtable_add_rows(panel_table, axis_height_b, i)
+ panel_table <- gtable::gtable_add_grob(panel_table,
+ rep(axes$x$bottom, length(panel_pos_h)), t = i + 1, l = panel_pos_h,
+ clip = "off")
+ panel_table <- gtable::gtable_add_rows(panel_table, axis_height_t, i - 1)
+ panel_table <- gtable::gtable_add_grob(panel_table,
+ rep(axes$x$top, length(panel_pos_h)), t = i, l = panel_pos_h,
+ clip = "off")
+ }
+ panel_table
+}
+
+## ------------------------------------------------------------------------
+# Constructor: shrink is required to govern whether scales are trained on
+# Stat-transformed data or not.
+facet_duplicate <- function(horizontal = TRUE, shrink = TRUE) {
+ ggproto(NULL, FacetDuplicate,
+ shrink = shrink,
+ params = list(
+ horizontal = horizontal
+ )
+ )
+}
+
+FacetDuplicate <- ggproto("FacetDuplicate", Facet,
+ compute_layout = layout,
+ map_data = mapping,
+ draw_panels = render
+)
+
+## ------------------------------------------------------------------------
+p <- ggplot(mtcars, aes(x = hp, y = mpg)) + geom_point()
+p
+p + facet_duplicate()
+
+## ------------------------------------------------------------------------
+library(scales)
+
+facet_trans <- function(trans, horizontal = TRUE, shrink = TRUE) {
+ ggproto(NULL, FacetTrans,
+ shrink = shrink,
+ params = list(
+ trans = scales::as.trans(trans),
+ horizontal = horizontal
+ )
+ )
+}
+
+FacetTrans <- ggproto("FacetTrans", Facet,
+ # Almost as before but we want different y-scales for each panel
+ compute_layout = function(data, params) {
+ data.frame(PANEL = c(1L, 2L), SCALE_X = 1L, SCALE_Y = c(1L, 2L))
+ },
+ # Same as before
+ map_data = function(data, layout, params) {
+ if (plyr::empty(data)) {
+ return(cbind(data, PANEL = integer(0)))
+ }
+ rbind(
+ cbind(data, PANEL = 1L),
+ cbind(data, PANEL = 2L)
+ )
+ },
+ # This is new. We create a new scale with the defined transformation
+ init_scales = function(layout, x_scale = NULL, y_scale = NULL, params) {
+ scales <- list()
+ if (!is.null(x_scale)) {
+ scales$x <- plyr::rlply(max(layout$SCALE_X), x_scale$clone())
+ }
+ if (!is.null(y_scale)) {
+ y_scale_orig <- y_scale$clone()
+ y_scale_new <- y_scale$clone()
+ y_scale_new$trans <- params$trans
+ # Make sure that oob values are kept
+ y_scale_new$oob <- function(x, ...) x
+ scales$y <- list(y_scale_orig, y_scale_new)
+ }
+ scales
+ },
+ # We must make sure that the second scale is trained on transformed data
+ train_scales = function(x_scales, y_scales, layout, data, params) {
+ # Transform data for second panel prior to scale training
+ if (!is.null(y_scales)) {
+ data <- lapply(data, function(layer_data) {
+ match_id <- match(layer_data$PANEL, layout$PANEL)
+ y_vars <- intersect(y_scales[[1]]$aesthetics, names(layer_data))
+ trans_scale <- layer_data$PANEL == 2L
+ for (i in y_vars) {
+ layer_data[trans_scale, i] <- y_scales[[2]]$transform(layer_data[trans_scale, i])
+ }
+ layer_data
+ })
+ }
+ Facet$train_scales(x_scales, y_scales, layout, data, params)
+ },
+ # this is where we actually modify the data. It cannot be done in $map_data as that function
+ # doesn't have access to the scales
+ finish_data = function(data, layout, x_scales, y_scales, params) {
+ match_id <- match(data$PANEL, layout$PANEL)
+ y_vars <- intersect(y_scales[[1]]$aesthetics, names(data))
+ trans_scale <- data$PANEL == 2L
+ for (i in y_vars) {
+ data[trans_scale, i] <- y_scales[[2]]$transform(data[trans_scale, i])
+ }
+ data
+ },
+ # A few changes from before to accomodate that axes are now not duplicate of each other
+ # We also add a panel strip to annotate the different panels
+ draw_panels = function(panels, layout, x_scales, y_scales, ranges, coord,
+ data, theme, params) {
+ # Place panels according to settings
+ if (params$horizontal) {
+ # Put panels in matrix and convert to a gtable
+ panels <- matrix(panels, ncol = 2)
+ panel_table <- gtable::gtable_matrix("layout", panels,
+ widths = unit(c(1, 1), "null"), heights = unit(1, "null"), clip = "on")
+ # Add spacing according to theme
+ panel_spacing <- if (is.null(theme$panel.spacing.x)) {
+ theme$panel.spacing
+ } else {
+ theme$panel.spacing.x
+ }
+ panel_table <- gtable::gtable_add_col_space(panel_table, panel_spacing)
+ } else {
+ panels <- matrix(panels, ncol = 1)
+ panel_table <- gtable::gtable_matrix("layout", panels,
+ widths = unit(1, "null"), heights = unit(c(1, 1), "null"), clip = "on")
+ panel_spacing <- if (is.null(theme$panel.spacing.y)) {
+ theme$panel.spacing
+ } else {
+ theme$panel.spacing.y
+ }
+ panel_table <- gtable::gtable_add_row_space(panel_table, panel_spacing)
+ }
+ # Name panel grobs so they can be found later
+ panel_table$layout$name <- paste0("panel-", c(1, 2))
+
+ # Construct the axes
+ axes <- render_axes(ranges[1], ranges, coord, theme,
+ transpose = TRUE)
+
+ # Add axes around each panel
+ grobWidths <- function(x) {
+ unit(vapply(x, function(x) {
+ grid::convertWidth(
+ grid::grobWidth(x), "cm", TRUE)
+ }, numeric(1)), "cm")
+ }
+ panel_pos_h <- panel_cols(panel_table)$l
+ panel_pos_v <- panel_rows(panel_table)$t
+ axis_width_l <- grobWidths(axes$y$left)
+ axis_width_r <- grobWidths(axes$y$right)
+ ## We do it reverse so we don't change the position of panels when we add axes
+ for (i in rev(seq_along(panel_pos_h))) {
+ panel_table <- gtable::gtable_add_cols(panel_table, axis_width_r[i], panel_pos_h[i])
+ if (params$horizontal) {
+ panel_table <- gtable::gtable_add_grob(panel_table,
+ rep(axes$y$right[i], length(panel_pos_v)), t = panel_pos_v, l = panel_pos_h[i] + 1,
+ clip = "off")
+ } else {
+ panel_table <- gtable::gtable_add_grob(panel_table,
+ rep(axes$y$right, length(panel_pos_v)), t = panel_pos_v, l = panel_pos_h[i] + 1,
+ clip = "off")
+ }
+ panel_table <- gtable::gtable_add_cols(panel_table, axis_width_l[i], panel_pos_h[i] - 1)
+ if (params$horizontal) {
+ panel_table <- gtable::gtable_add_grob(panel_table,
+ rep(axes$y$left[i], length(panel_pos_v)), t = panel_pos_v, l = panel_pos_h[i],
+ clip = "off")
+ } else {
+ panel_table <- gtable::gtable_add_grob(panel_table,
+ rep(axes$y$left, length(panel_pos_v)), t = panel_pos_v, l = panel_pos_h[i],
+ clip = "off")
+ }
+ }
+ ## Recalculate as gtable has changed
+ panel_pos_h <- panel_cols(panel_table)$l
+ panel_pos_v <- panel_rows(panel_table)$t
+ axis_height_t <- unit(grid::convertHeight(
+ grid::grobHeight(axes$x$top[[1]]), "cm", TRUE), "cm")
+ axis_height_b <- unit(grid::convertHeight(
+ grid::grobHeight(axes$x$bottom[[1]]), "cm", TRUE), "cm")
+ for (i in rev(panel_pos_v)) {
+ panel_table <- gtable::gtable_add_rows(panel_table, axis_height_b, i)
+ panel_table <- gtable::gtable_add_grob(panel_table,
+ rep(axes$x$bottom, length(panel_pos_h)), t = i + 1, l = panel_pos_h,
+ clip = "off")
+ panel_table <- gtable::gtable_add_rows(panel_table, axis_height_t, i - 1)
+ panel_table <- gtable::gtable_add_grob(panel_table,
+ rep(axes$x$top, length(panel_pos_h)), t = i, l = panel_pos_h,
+ clip = "off")
+ }
+
+ # Add strips
+ strips <- render_strips(
+ x = data.frame(name = c("Original", paste0("Transformed (", params$trans$name, ")"))),
+ labeller = label_value, theme = theme)
+
+ panel_pos_h <- panel_cols(panel_table)$l
+ panel_pos_v <- panel_rows(panel_table)$t
+ strip_height <- unit(grid::convertHeight(
+ grid::grobHeight(strips$x$top[[1]]), "cm", TRUE), "cm")
+ for (i in rev(seq_along(panel_pos_v))) {
+ panel_table <- gtable::gtable_add_rows(panel_table, strip_height, panel_pos_v[i] - 1)
+ if (params$horizontal) {
+ panel_table <- gtable::gtable_add_grob(panel_table, strips$x$top,
+ t = panel_pos_v[i], l = panel_pos_h, clip = "off")
+ } else {
+ panel_table <- gtable::gtable_add_grob(panel_table, strips$x$top[i],
+ t = panel_pos_v[i], l = panel_pos_h, clip = "off")
+ }
+ }
+
+
+ panel_table
+ }
+)
+
+## ------------------------------------------------------------------------
+ggplot(mtcars, aes(x = hp, y = mpg)) + geom_point() + facet_trans('sqrt')
+
+## ------------------------------------------------------------------------
+facet_bootstrap <- function(n = 9, prop = 0.2, nrow = NULL, ncol = NULL,
+ scales = "fixed", shrink = TRUE, strip.position = "top") {
+
+ facet <- facet_wrap(~.bootstrap, nrow = nrow, ncol = ncol, scales = scales,
+ shrink = shrink, strip.position = strip.position)
+ facet$params$n <- n
+ facet$params$prop <- prop
+ ggproto(NULL, FacetBootstrap,
+ shrink = shrink,
+ params = facet$params
+ )
+}
+
+FacetBootstrap <- ggproto("FacetBootstrap", FacetWrap,
+ compute_layout = function(data, params) {
+ id <- seq_len(params$n)
+
+ dims <- wrap_dims(params$n, params$nrow, params$ncol)
+ layout <- data.frame(PANEL = factor(id))
+
+ if (params$as.table) {
+ layout$ROW <- as.integer((id - 1L) %/% dims[2] + 1L)
+ } else {
+ layout$ROW <- as.integer(dims[1] - (id - 1L) %/% dims[2])
+ }
+ layout$COL <- as.integer((id - 1L) %% dims[2] + 1L)
+
+ layout <- layout[order(layout$PANEL), , drop = FALSE]
+ rownames(layout) <- NULL
+
+ # Add scale identification
+ layout$SCALE_X <- if (params$free$x) id else 1L
+ layout$SCALE_Y <- if (params$free$y) id else 1L
+
+ cbind(layout, .bootstrap = id)
+ },
+ map_data = function(data, layout, params) {
+ if (is.null(data) || nrow(data) == 0) {
+ return(cbind(data, PANEL = integer(0)))
+ }
+ n_samples <- round(nrow(data) * params$prop)
+ new_data <- lapply(seq_len(params$n), function(i) {
+ cbind(data[sample(nrow(data), n_samples), , drop = FALSE], PANEL = i)
+ })
+ do.call(rbind, new_data)
+ }
+)
+
+ggplot(diamonds, aes(carat, price)) +
+ geom_point(alpha = 0.1) +
+ facet_bootstrap(n = 9, prop = 0.05)
+
diff --git a/inst/doc/extending-ggplot2.Rmd b/inst/doc/extending-ggplot2.Rmd
index 921c095..fa740f9 100644
--- a/inst/doc/extending-ggplot2.Rmd
+++ b/inst/doc/extending-ggplot2.Rmd
@@ -1,7 +1,5 @@
---
title: "Extending ggplot2"
-author: "Hadley Wickham"
-date: "`r Sys.Date()`"
output: rmarkdown::html_vignette
vignette: >
%\VignetteIndexEntry{Extending ggplot2}
@@ -10,7 +8,7 @@ vignette: >
---
```{r, include = FALSE}
-knitr::opts_chunk$set(collapse = TRUE, comment = "#>")
+knitr::opts_chunk$set(collapse = TRUE, comment = "#>", fig.width = 7, fig.height = 7, fig.align = "center")
library(ggplot2)
```
@@ -364,11 +362,11 @@ This is very similar to defining a new stat. You always need to provide fields/m
* `draw_key` provides the function used to draw the key in the legend.
You can see a list of all the build in key functions in `?draw_key`
-* `draw_group()` is where the magic happens. This function takes three
+* `draw_panel()` is where the magic happens. This function takes three
arguments and returns a grid grob. It is called once for each panel.
It's the most complicated part and is described in more detail below.
-`draw_group()` has three arguments:
+`draw_panel()` has three arguments:
* `data`: a data frame with one column for each aesthetic.
@@ -377,11 +375,11 @@ This is very similar to defining a new stat. You always need to provide fields/m
* `coord`: an object describing the coordinate system.
-Generally you won't use `panel_scales` and `coord` directly, but you will always use them to transform the data: `coords <- coord$transform(data, panel_scales)`. This creates a data frame where position variables are scaled to the range 0--1. You then take this data and call a grid grob function. (Transforming for non-Cartesian coordinate systems is quite complex - you're best of transforming your data to the form accepted by an existing ggplot2 geom and passing it.)
+Generally you won't use `panel_scales` and `coord` directly, but you will always use them to transform the data: `coords <- coord$transform(data, panel_scales)`. This creates a data frame where position variables are scaled to the range 0--1. You then take this data and call a grid grob function. (Transforming for non-Cartesian coordinate systems is quite complex - you're best off transforming your data to the form accepted by an existing ggplot2 geom and passing it.)
### Collective geoms
-Overriding `draw_panel()` is most appropriate if there is one graphic element per row. In other cases, you want graphic element per group. For example, take polygons: each row gives one vertex of a polygon. In this case, you should instead override `draw_group()`:
+Overriding `draw_panel()` is most appropriate if there is one graphic element per row. In other cases, you want graphic element per group. For example, take polygons: each row gives one vertex of a polygon. In this case, you should instead override `draw_group()`.
The following code makes a simplified version of `GeomPolygon`:
@@ -433,12 +431,8 @@ ggplot(mpg, aes(displ, hwy)) +
There are a few things to note here:
-* We override `draw_group()` instead of `draw_layer()` because we want
- one polygon per group, not one polygon per row. If you look at the source
- code for the original `GeomPolygon` you'll see it actually overrides
- `geom_layer()` because it uses some tricks to make `polygonGrob()` produce
- multiple polygons in one call. This is considerably more complicated, but
- gives better performance.
+* We override `draw_group()` instead of `draw_panel()` because we want
+ one polygon per group, not one polygon per row.
* If the data contains two or fewer points, there's no point trying to draw
a polygon, so we return a `nullGrob()`. This is the graphical equivalent
@@ -450,6 +444,8 @@ There are a few things to note here:
change it there). `lwd` is measured in points, but ggplot2 uses mm,
so we need to multiply it by the adjustment factor `.pt`.
+You might want to compare this to the real `GeomPolygon`. You'll see it overrides `draw_panel()` because it uses some tricks to make `polygonGrob()` produce multiple polygons in one call. This is considerably more complicated, but gives better performance.
+
### Inheriting from an existing Geom
Sometimes you just want to make a small modification to an existing geom. In this case, rather than inheriting from `Geom` you can inherit from an existing subclass. For example, we might want to change the defaults for `GeomPolygon` to work better with `StatChull`:
@@ -545,3 +541,426 @@ Complete and incomplete themes behave somewhat differently when added to a ggplo
those properties of elements defined in the call to `theme()`.
* Adding a complete theme wipes away the existing theme and applies the new theme.
+
+## Creating a new facetting
+
+One of the more daunting exercises in ggplot2 extensions is to create a new facetting system. The reason for this is that when creating new facettings you take on the responsibility of how (almost) everything is drawn on the screen, and many do not have experience with directly using [gtable](https://cran.r-project.org/package=gtable) and [grid](https://cran.r-project.org/package=grid) upon which the ggplot2 rendering is build. If you decide to venture into facetting extensions it is hig [...]
+
+The `Facet` class in ggplot2 is very powerfull as it takes on responsibility of a wide range of tasks. The main tasks of a `Facet` object are:
+
+* Define a layout; that is, a partitioning of the data into different plot areas (panels) as well as which panels share position scales.
+
+* Map plot data into the correct panels, potentially duplicating data if it should exist in multiple panels (e.g. margins in `facet_grid()`).
+
+* Assemble all panels into a final gtable, adding axes, strips and decorations in the process.
+
+Apart from these three tasks, for which functionality must be implemented, there are a couple of additional extension points where sensible defaults have been provided. These can generally be ignored, but adventurous developers can override them for even more control:
+
+* Initialization and training of positional scales for each panel.
+
+* Decoration in front of and behind each panel.
+
+* Drawing of axis labels
+
+To show how a new facetting class is created we will start simple and go through each of the required methods in turn to build up `facet_duplicate()` that simply duplicate our plot into two panels. After this we will tinker with it a bit to show some of the more powerful possibilities.
+
+### Creating a layout specification
+
+A layout in the context of facets is a `data.frame` that defines a mapping between data and the panels it should reside in as well as which positional scales should be used. The output should at least contain the columns `PANEL`, `SCALE_X`, and `SCALE_Y`, but will often contain more to help assign data to the correct panel (`facet_grid()` will e.g. also return the facetting variables associated with each panel). Let's make a function that defines a duplicate layout:
+
+```{r}
+layout <- function(data, params) {
+ data.frame(PANEL = c(1L, 2L), SCALE_X = 1L, SCALE_Y = 1L)
+}
+```
+
+This is quite simple as the facetting should just define two panels irrespectively of the input data and parameters.
+
+### Mapping data into panels
+
+In order for ggplot2 to know which data should go where it needs the data to be assigned to a panel. The purpose of the mapping step is to assign a `PANEL` column to the layer data identifying which panel it belongs to.
+
+```{r}
+mapping <- function(data, layout, params) {
+ if (plyr::empty(data)) {
+ return(cbind(data, PANEL = integer(0)))
+ }
+ rbind(
+ cbind(data, PANEL = 1L),
+ cbind(data, PANEL = 2L)
+ )
+}
+```
+
+here we first investigate whether we have gotten an empty `data.frame` and if not we duplicate the data and assign the original data to the first panel and the new data to the second panel.
+
+### Laying out the panels
+
+While the two functions above has been decievingly simple, this last one is going to take some more work. Our goal is to draw two panels beside (or above) each other with axes etc.
+
+```{r}
+render <- function(panels, layout, x_scales, y_scales, ranges, coord, data,
+ theme, params) {
+ # Place panels according to settings
+ if (params$horizontal) {
+ # Put panels in matrix and convert to a gtable
+ panels <- matrix(panels, ncol = 2)
+ panel_table <- gtable::gtable_matrix("layout", panels,
+ widths = unit(c(1, 1), "null"), heights = unit(1, "null"), clip = "on")
+ # Add spacing according to theme
+ panel_spacing <- if (is.null(theme$panel.spacing.x)) {
+ theme$panel.spacing
+ } else {
+ theme$panel.spacing.x
+ }
+ panel_table <- gtable::gtable_add_col_space(panel_table, panel_spacing)
+ } else {
+ panels <- matrix(panels, ncol = 1)
+ panel_table <- gtable::gtable_matrix("layout", panels,
+ widths = unit(1, "null"), heights = unit(c(1, 1), "null"), clip = "on")
+ panel_spacing <- if (is.null(theme$panel.spacing.y)) {
+ theme$panel.spacing
+ } else {
+ theme$panel.spacing.y
+ }
+ panel_table <- gtable::gtable_add_row_space(panel_table, panel_spacing)
+ }
+ # Name panel grobs so they can be found later
+ panel_table$layout$name <- paste0("panel-", c(1, 2))
+
+ # Construct the axes
+ axes <- render_axes(ranges[1], ranges[1], coord, theme,
+ transpose = TRUE)
+
+ # Add axes around each panel
+ panel_pos_h <- panel_cols(panel_table)$l
+ panel_pos_v <- panel_rows(panel_table)$t
+ axis_width_l <- unit(grid::convertWidth(
+ grid::grobWidth(axes$y$left[[1]]), "cm", TRUE), "cm")
+ axis_width_r <- unit(grid::convertWidth(
+ grid::grobWidth(axes$y$right[[1]]), "cm", TRUE), "cm")
+ ## We do it reverse so we don't change the position of panels when we add axes
+ for (i in rev(panel_pos_h)) {
+ panel_table <- gtable::gtable_add_cols(panel_table, axis_width_r, i)
+ panel_table <- gtable::gtable_add_grob(panel_table,
+ rep(axes$y$right, length(panel_pos_v)), t = panel_pos_v, l = i + 1,
+ clip = "off")
+ panel_table <- gtable::gtable_add_cols(panel_table, axis_width_l, i - 1)
+ panel_table <- gtable::gtable_add_grob(panel_table,
+ rep(axes$y$left, length(panel_pos_v)), t = panel_pos_v, l = i,
+ clip = "off")
+ }
+ ## Recalculate as gtable has changed
+ panel_pos_h <- panel_cols(panel_table)$l
+ panel_pos_v <- panel_rows(panel_table)$t
+ axis_height_t <- unit(grid::convertHeight(
+ grid::grobHeight(axes$x$top[[1]]), "cm", TRUE), "cm")
+ axis_height_b <- unit(grid::convertHeight(
+ grid::grobHeight(axes$x$bottom[[1]]), "cm", TRUE), "cm")
+ for (i in rev(panel_pos_v)) {
+ panel_table <- gtable::gtable_add_rows(panel_table, axis_height_b, i)
+ panel_table <- gtable::gtable_add_grob(panel_table,
+ rep(axes$x$bottom, length(panel_pos_h)), t = i + 1, l = panel_pos_h,
+ clip = "off")
+ panel_table <- gtable::gtable_add_rows(panel_table, axis_height_t, i - 1)
+ panel_table <- gtable::gtable_add_grob(panel_table,
+ rep(axes$x$top, length(panel_pos_h)), t = i, l = panel_pos_h,
+ clip = "off")
+ }
+ panel_table
+}
+```
+
+### Assembling the Facet class
+
+Usually all methods are defined within the class definition in the same way as is done for `Geom` and `Stat`. Here we have split it out so we could go through each in turn. All that remains is to assign our functions to the correct methods as well as making a constructor
+
+```{r}
+# Constructor: shrink is required to govern whether scales are trained on
+# Stat-transformed data or not.
+facet_duplicate <- function(horizontal = TRUE, shrink = TRUE) {
+ ggproto(NULL, FacetDuplicate,
+ shrink = shrink,
+ params = list(
+ horizontal = horizontal
+ )
+ )
+}
+
+FacetDuplicate <- ggproto("FacetDuplicate", Facet,
+ compute_layout = layout,
+ map_data = mapping,
+ draw_panels = render
+)
+```
+
+Now with everything assembled, lets test it out:
+
+```{r}
+p <- ggplot(mtcars, aes(x = hp, y = mpg)) + geom_point()
+p
+p + facet_duplicate()
+```
+
+### Doing more with facets
+
+The example above was pretty useless and we'll now try to expand on it to add some actual usability. We are going to make a facetting that adds panels with y-transformed axes:
+
+```{r}
+library(scales)
+
+facet_trans <- function(trans, horizontal = TRUE, shrink = TRUE) {
+ ggproto(NULL, FacetTrans,
+ shrink = shrink,
+ params = list(
+ trans = scales::as.trans(trans),
+ horizontal = horizontal
+ )
+ )
+}
+
+FacetTrans <- ggproto("FacetTrans", Facet,
+ # Almost as before but we want different y-scales for each panel
+ compute_layout = function(data, params) {
+ data.frame(PANEL = c(1L, 2L), SCALE_X = 1L, SCALE_Y = c(1L, 2L))
+ },
+ # Same as before
+ map_data = function(data, layout, params) {
+ if (plyr::empty(data)) {
+ return(cbind(data, PANEL = integer(0)))
+ }
+ rbind(
+ cbind(data, PANEL = 1L),
+ cbind(data, PANEL = 2L)
+ )
+ },
+ # This is new. We create a new scale with the defined transformation
+ init_scales = function(layout, x_scale = NULL, y_scale = NULL, params) {
+ scales <- list()
+ if (!is.null(x_scale)) {
+ scales$x <- plyr::rlply(max(layout$SCALE_X), x_scale$clone())
+ }
+ if (!is.null(y_scale)) {
+ y_scale_orig <- y_scale$clone()
+ y_scale_new <- y_scale$clone()
+ y_scale_new$trans <- params$trans
+ # Make sure that oob values are kept
+ y_scale_new$oob <- function(x, ...) x
+ scales$y <- list(y_scale_orig, y_scale_new)
+ }
+ scales
+ },
+ # We must make sure that the second scale is trained on transformed data
+ train_scales = function(x_scales, y_scales, layout, data, params) {
+ # Transform data for second panel prior to scale training
+ if (!is.null(y_scales)) {
+ data <- lapply(data, function(layer_data) {
+ match_id <- match(layer_data$PANEL, layout$PANEL)
+ y_vars <- intersect(y_scales[[1]]$aesthetics, names(layer_data))
+ trans_scale <- layer_data$PANEL == 2L
+ for (i in y_vars) {
+ layer_data[trans_scale, i] <- y_scales[[2]]$transform(layer_data[trans_scale, i])
+ }
+ layer_data
+ })
+ }
+ Facet$train_scales(x_scales, y_scales, layout, data, params)
+ },
+ # this is where we actually modify the data. It cannot be done in $map_data as that function
+ # doesn't have access to the scales
+ finish_data = function(data, layout, x_scales, y_scales, params) {
+ match_id <- match(data$PANEL, layout$PANEL)
+ y_vars <- intersect(y_scales[[1]]$aesthetics, names(data))
+ trans_scale <- data$PANEL == 2L
+ for (i in y_vars) {
+ data[trans_scale, i] <- y_scales[[2]]$transform(data[trans_scale, i])
+ }
+ data
+ },
+ # A few changes from before to accomodate that axes are now not duplicate of each other
+ # We also add a panel strip to annotate the different panels
+ draw_panels = function(panels, layout, x_scales, y_scales, ranges, coord,
+ data, theme, params) {
+ # Place panels according to settings
+ if (params$horizontal) {
+ # Put panels in matrix and convert to a gtable
+ panels <- matrix(panels, ncol = 2)
+ panel_table <- gtable::gtable_matrix("layout", panels,
+ widths = unit(c(1, 1), "null"), heights = unit(1, "null"), clip = "on")
+ # Add spacing according to theme
+ panel_spacing <- if (is.null(theme$panel.spacing.x)) {
+ theme$panel.spacing
+ } else {
+ theme$panel.spacing.x
+ }
+ panel_table <- gtable::gtable_add_col_space(panel_table, panel_spacing)
+ } else {
+ panels <- matrix(panels, ncol = 1)
+ panel_table <- gtable::gtable_matrix("layout", panels,
+ widths = unit(1, "null"), heights = unit(c(1, 1), "null"), clip = "on")
+ panel_spacing <- if (is.null(theme$panel.spacing.y)) {
+ theme$panel.spacing
+ } else {
+ theme$panel.spacing.y
+ }
+ panel_table <- gtable::gtable_add_row_space(panel_table, panel_spacing)
+ }
+ # Name panel grobs so they can be found later
+ panel_table$layout$name <- paste0("panel-", c(1, 2))
+
+ # Construct the axes
+ axes <- render_axes(ranges[1], ranges, coord, theme,
+ transpose = TRUE)
+
+ # Add axes around each panel
+ grobWidths <- function(x) {
+ unit(vapply(x, function(x) {
+ grid::convertWidth(
+ grid::grobWidth(x), "cm", TRUE)
+ }, numeric(1)), "cm")
+ }
+ panel_pos_h <- panel_cols(panel_table)$l
+ panel_pos_v <- panel_rows(panel_table)$t
+ axis_width_l <- grobWidths(axes$y$left)
+ axis_width_r <- grobWidths(axes$y$right)
+ ## We do it reverse so we don't change the position of panels when we add axes
+ for (i in rev(seq_along(panel_pos_h))) {
+ panel_table <- gtable::gtable_add_cols(panel_table, axis_width_r[i], panel_pos_h[i])
+ if (params$horizontal) {
+ panel_table <- gtable::gtable_add_grob(panel_table,
+ rep(axes$y$right[i], length(panel_pos_v)), t = panel_pos_v, l = panel_pos_h[i] + 1,
+ clip = "off")
+ } else {
+ panel_table <- gtable::gtable_add_grob(panel_table,
+ rep(axes$y$right, length(panel_pos_v)), t = panel_pos_v, l = panel_pos_h[i] + 1,
+ clip = "off")
+ }
+ panel_table <- gtable::gtable_add_cols(panel_table, axis_width_l[i], panel_pos_h[i] - 1)
+ if (params$horizontal) {
+ panel_table <- gtable::gtable_add_grob(panel_table,
+ rep(axes$y$left[i], length(panel_pos_v)), t = panel_pos_v, l = panel_pos_h[i],
+ clip = "off")
+ } else {
+ panel_table <- gtable::gtable_add_grob(panel_table,
+ rep(axes$y$left, length(panel_pos_v)), t = panel_pos_v, l = panel_pos_h[i],
+ clip = "off")
+ }
+ }
+ ## Recalculate as gtable has changed
+ panel_pos_h <- panel_cols(panel_table)$l
+ panel_pos_v <- panel_rows(panel_table)$t
+ axis_height_t <- unit(grid::convertHeight(
+ grid::grobHeight(axes$x$top[[1]]), "cm", TRUE), "cm")
+ axis_height_b <- unit(grid::convertHeight(
+ grid::grobHeight(axes$x$bottom[[1]]), "cm", TRUE), "cm")
+ for (i in rev(panel_pos_v)) {
+ panel_table <- gtable::gtable_add_rows(panel_table, axis_height_b, i)
+ panel_table <- gtable::gtable_add_grob(panel_table,
+ rep(axes$x$bottom, length(panel_pos_h)), t = i + 1, l = panel_pos_h,
+ clip = "off")
+ panel_table <- gtable::gtable_add_rows(panel_table, axis_height_t, i - 1)
+ panel_table <- gtable::gtable_add_grob(panel_table,
+ rep(axes$x$top, length(panel_pos_h)), t = i, l = panel_pos_h,
+ clip = "off")
+ }
+
+ # Add strips
+ strips <- render_strips(
+ x = data.frame(name = c("Original", paste0("Transformed (", params$trans$name, ")"))),
+ labeller = label_value, theme = theme)
+
+ panel_pos_h <- panel_cols(panel_table)$l
+ panel_pos_v <- panel_rows(panel_table)$t
+ strip_height <- unit(grid::convertHeight(
+ grid::grobHeight(strips$x$top[[1]]), "cm", TRUE), "cm")
+ for (i in rev(seq_along(panel_pos_v))) {
+ panel_table <- gtable::gtable_add_rows(panel_table, strip_height, panel_pos_v[i] - 1)
+ if (params$horizontal) {
+ panel_table <- gtable::gtable_add_grob(panel_table, strips$x$top,
+ t = panel_pos_v[i], l = panel_pos_h, clip = "off")
+ } else {
+ panel_table <- gtable::gtable_add_grob(panel_table, strips$x$top[i],
+ t = panel_pos_v[i], l = panel_pos_h, clip = "off")
+ }
+ }
+
+
+ panel_table
+ }
+)
+```
+
+As is very apparent, the `draw_panel` method can become very unwieldy once it begins to take multiple possibilities into account. The fact that we want to support both horizontal and vertical layout leads to a lot of if/else blocks in the above code. In general this is the big challenge when writing facet extensions so be prepared to be very meticulous when writing these methods.
+
+Enough talk - lets see if our new and powerful facetting extension works:
+
+```{r}
+ggplot(mtcars, aes(x = hp, y = mpg)) + geom_point() + facet_trans('sqrt')
+```
+
+## Extending existing facet function
+
+As the rendering part of a facet class is often the difficult development step, it is possible to piggyback on the existing facetting classes to achieve a range of new facettings. Below we will subclass `facet_wrap()` to make a `facet_bootstrap()` class that splits the input data into a number of panels at random.
+
+```{r}
+facet_bootstrap <- function(n = 9, prop = 0.2, nrow = NULL, ncol = NULL,
+ scales = "fixed", shrink = TRUE, strip.position = "top") {
+
+ facet <- facet_wrap(~.bootstrap, nrow = nrow, ncol = ncol, scales = scales,
+ shrink = shrink, strip.position = strip.position)
+ facet$params$n <- n
+ facet$params$prop <- prop
+ ggproto(NULL, FacetBootstrap,
+ shrink = shrink,
+ params = facet$params
+ )
+}
+
+FacetBootstrap <- ggproto("FacetBootstrap", FacetWrap,
+ compute_layout = function(data, params) {
+ id <- seq_len(params$n)
+
+ dims <- wrap_dims(params$n, params$nrow, params$ncol)
+ layout <- data.frame(PANEL = factor(id))
+
+ if (params$as.table) {
+ layout$ROW <- as.integer((id - 1L) %/% dims[2] + 1L)
+ } else {
+ layout$ROW <- as.integer(dims[1] - (id - 1L) %/% dims[2])
+ }
+ layout$COL <- as.integer((id - 1L) %% dims[2] + 1L)
+
+ layout <- layout[order(layout$PANEL), , drop = FALSE]
+ rownames(layout) <- NULL
+
+ # Add scale identification
+ layout$SCALE_X <- if (params$free$x) id else 1L
+ layout$SCALE_Y <- if (params$free$y) id else 1L
+
+ cbind(layout, .bootstrap = id)
+ },
+ map_data = function(data, layout, params) {
+ if (is.null(data) || nrow(data) == 0) {
+ return(cbind(data, PANEL = integer(0)))
+ }
+ n_samples <- round(nrow(data) * params$prop)
+ new_data <- lapply(seq_len(params$n), function(i) {
+ cbind(data[sample(nrow(data), n_samples), , drop = FALSE], PANEL = i)
+ })
+ do.call(rbind, new_data)
+ }
+)
+
+ggplot(diamonds, aes(carat, price)) +
+ geom_point(alpha = 0.1) +
+ facet_bootstrap(n = 9, prop = 0.05)
+```
+
+What we are doing above is to intercept the `compute_layout` and `map_data` methods and instead of dividing the data by a variable we randomly assigns rows to a panel based on the sampling parameters (`n` determines the number of panels, `prop` determines the proportion of data in each panel). It is important here that the layout returned by `compute_layout` is a valid layout for `FacetWrap` as we are counting on the `draw_panel` method from `FacetWrap` to do all the work for us. Thus if [...]
+
+### Exercises
+
+1. Rewrite FacetTrans to take a vector of transformations and create an additional panel for each transformation.
+2. Based on the FacetWrap implementation rewrite FacetTrans to take the strip.placement theme setting into account.
+3. Think about which caveats there are in FacetBootstrap specifically related to adding multiple layers with the same data.
+
diff --git a/inst/doc/extending-ggplot2.html b/inst/doc/extending-ggplot2.html
index baa2e91..a2639fd 100644
--- a/inst/doc/extending-ggplot2.html
+++ b/inst/doc/extending-ggplot2.html
@@ -7,11 +7,10 @@
<meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="pandoc" />
+
<meta name="viewport" content="width=device-width, initial-scale=1">
-<meta name="author" content="Hadley Wickham" />
-<meta name="date" content="2016-02-29" />
<title>Extending ggplot2</title>
@@ -57,6 +56,7 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
</style>
+
<link href="data:text/css;charset=utf-8,body%20%7B%0Abackground%2Dcolor%3A%20%23fff%3B%0Amargin%3A%201em%20auto%3B%0Amax%2Dwidth%3A%20700px%3B%0Aoverflow%3A%20visible%3B%0Apadding%2Dleft%3A%202em%3B%0Apadding%2Dright%3A%202em%3B%0Afont%2Dfamily%3A%20%22Open%20Sans%22%2C%20%22Helvetica%20Neue%22%2C%20Helvetica%2C%20Arial%2C%20sans%2Dserif%3B%0Afont%2Dsize%3A%2014px%3B%0Aline%2Dheight%3A%201%2E35%3B%0A%7D%0A%23header%20%7B%0Atext%2Dalign%3A%20center%3B%0A%7D%0A%23TOC%20%7B%0Aclear%3A%20bot [...]
</head>
@@ -65,14 +65,9 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
-<div class="fluid-row" id="header">
+<h1 class="title toc-ignore">Extending ggplot2</h1>
-<h1 class="title">Extending ggplot2</h1>
-<h4 class="author"><em>Hadley Wickham</em></h4>
-<h4 class="date"><em>2016-02-29</em></h4>
-
-</div>
<p>This vignette documents the official extension mechanism provided in ggplot2 2.0.0. This vignette is a high-level adjunct to the low-level details found in <code>?Stat</code>, <code>?Geom</code> and <code>?theme</code>. You’ll learn how to extend ggplot2 by creating a new stat, geom, or theme.</p>
@@ -130,18 +125,18 @@ A$x
<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r"><span class="kw">ggplot</span>(mpg, <span class="kw">aes</span>(displ, hwy)) +<span class="st"> </span>
<span class="st"> </span><span class="kw">geom_point</span>() +<span class="st"> </span>
<span class="st"> </span><span class="kw">stat_chull</span>(<span class="dt">fill =</span> <span class="ot">NA</span>, <span class="dt">colour =</span> <span class="st">"black"</span>)</code></pre></div>
-<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASAAAAEgCAYAAAAUg66AAAAEDWlDQ1BJQ0MgUHJvZmlsZQAAOI2NVV1oHFUUPrtzZyMkzlNsNIV0qD8NJQ2TVjShtLp/3d02bpZJNtoi6GT27s6Yyc44M7v9oU9FUHwx6psUxL+3gCAo9Q/bPrQvlQol2tQgKD60+INQ6Ium65k7M5lpurHeZe58853vnnvuuWfvBei5qliWkRQBFpquLRcy4nOHj4g9K5CEh6AXBqFXUR0rXalMAjZPC3e1W99Dwntf2dXd/p+tt0YdFSBxH2Kz5qgLiI8B8KdVy3YBevqRHz/qWh72Yui3MUDEL3q44WPXw3M+fo1pZuQs4tOIBVVTaoiXEI/MxfhGDPsxsNZfoE1q66ro5aJim3XdoLFw72H+n23BaIXzbcOnz5mfPoTvYVz7KzUl5+FRxEuqkp9G/Ajia [...]
+<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAqAAAAKgCAYAAABEPM/FAAAEDWlDQ1BJQ0MgUHJvZmlsZQAAOI2NVV1oHFUUPrtzZyMkzlNsNIV0qD8NJQ2TVjShtLp/3d02bpZJNtoi6GT27s6Yyc44M7v9oU9FUHwx6psUxL+3gCAo9Q/bPrQvlQol2tQgKD60+INQ6Ium65k7M5lpurHeZe58853vnnvuuWfvBei5qliWkRQBFpquLRcy4nOHj4g9K5CEh6AXBqFXUR0rXalMAjZPC3e1W99Dwntf2dXd/p+tt0YdFSBxH2Kz5qgLiI8B8KdVy3YBevqRHz/qWh72Yui3MUDEL3q44WPXw3M+fo1pZuQs4tOIBVVTaoiXEI/MxfhGDPsxsNZfoE1q66ro5aJim3XdoLFw72H+n23BaIXzbcOnz5mfPoTvYVz7KzUl5+FRxEuqkp9G/Ajia [...]
<p>(We’ll see later how to change the defaults of the geom so that you don’t need to specify <code>fill = NA</code> every time.)</p>
<p>Once we’ve written this basic object, ggplot2 gives a lot for free. For example, ggplot2 automatically preserves aesthetics that are constant within each group:</p>
<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r"><span class="kw">ggplot</span>(mpg, <span class="kw">aes</span>(displ, hwy, <span class="dt">colour =</span> drv)) +<span class="st"> </span>
<span class="st"> </span><span class="kw">geom_point</span>() +<span class="st"> </span>
<span class="st"> </span><span class="kw">stat_chull</span>(<span class="dt">fill =</span> <span class="ot">NA</span>)</code></pre></div>
-<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASAAAAEgCAYAAAAUg66AAAAEDWlDQ1BJQ0MgUHJvZmlsZQAAOI2NVV1oHFUUPrtzZyMkzlNsNIV0qD8NJQ2TVjShtLp/3d02bpZJNtoi6GT27s6Yyc44M7v9oU9FUHwx6psUxL+3gCAo9Q/bPrQvlQol2tQgKD60+INQ6Ium65k7M5lpurHeZe58853vnnvuuWfvBei5qliWkRQBFpquLRcy4nOHj4g9K5CEh6AXBqFXUR0rXalMAjZPC3e1W99Dwntf2dXd/p+tt0YdFSBxH2Kz5qgLiI8B8KdVy3YBevqRHz/qWh72Yui3MUDEL3q44WPXw3M+fo1pZuQs4tOIBVVTaoiXEI/MxfhGDPsxsNZfoE1q66ro5aJim3XdoLFw72H+n23BaIXzbcOnz5mfPoTvYVz7KzUl5+FRxEuqkp9G/Ajia [...]
+<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAqAAAAKgCAYAAABEPM/FAAAEDWlDQ1BJQ0MgUHJvZmlsZQAAOI2NVV1oHFUUPrtzZyMkzlNsNIV0qD8NJQ2TVjShtLp/3d02bpZJNtoi6GT27s6Yyc44M7v9oU9FUHwx6psUxL+3gCAo9Q/bPrQvlQol2tQgKD60+INQ6Ium65k7M5lpurHeZe58853vnnvuuWfvBei5qliWkRQBFpquLRcy4nOHj4g9K5CEh6AXBqFXUR0rXalMAjZPC3e1W99Dwntf2dXd/p+tt0YdFSBxH2Kz5qgLiI8B8KdVy3YBevqRHz/qWh72Yui3MUDEL3q44WPXw3M+fo1pZuQs4tOIBVVTaoiXEI/MxfhGDPsxsNZfoE1q66ro5aJim3XdoLFw72H+n23BaIXzbcOnz5mfPoTvYVz7KzUl5+FRxEuqkp9G/Ajia [...]
<p>We can also override the default geom to display the convex hull in a different way:</p>
<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r"><span class="kw">ggplot</span>(mpg, <span class="kw">aes</span>(displ, hwy)) +<span class="st"> </span>
<span class="st"> </span><span class="kw">stat_chull</span>(<span class="dt">geom =</span> <span class="st">"point"</span>, <span class="dt">size =</span> <span class="dv">4</span>, <span class="dt">colour =</span> <span class="st">"red"</span>) +
<span class="st"> </span><span class="kw">geom_point</span>()</code></pre></div>
-<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASAAAAEgCAYAAAAUg66AAAAEDWlDQ1BJQ0MgUHJvZmlsZQAAOI2NVV1oHFUUPrtzZyMkzlNsNIV0qD8NJQ2TVjShtLp/3d02bpZJNtoi6GT27s6Yyc44M7v9oU9FUHwx6psUxL+3gCAo9Q/bPrQvlQol2tQgKD60+INQ6Ium65k7M5lpurHeZe58853vnnvuuWfvBei5qliWkRQBFpquLRcy4nOHj4g9K5CEh6AXBqFXUR0rXalMAjZPC3e1W99Dwntf2dXd/p+tt0YdFSBxH2Kz5qgLiI8B8KdVy3YBevqRHz/qWh72Yui3MUDEL3q44WPXw3M+fo1pZuQs4tOIBVVTaoiXEI/MxfhGDPsxsNZfoE1q66ro5aJim3XdoLFw72H+n23BaIXzbcOnz5mfPoTvYVz7KzUl5+FRxEuqkp9G/Ajia [...]
+<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAqAAAAKgCAYAAABEPM/FAAAEDWlDQ1BJQ0MgUHJvZmlsZQAAOI2NVV1oHFUUPrtzZyMkzlNsNIV0qD8NJQ2TVjShtLp/3d02bpZJNtoi6GT27s6Yyc44M7v9oU9FUHwx6psUxL+3gCAo9Q/bPrQvlQol2tQgKD60+INQ6Ium65k7M5lpurHeZe58853vnnvuuWfvBei5qliWkRQBFpquLRcy4nOHj4g9K5CEh6AXBqFXUR0rXalMAjZPC3e1W99Dwntf2dXd/p+tt0YdFSBxH2Kz5qgLiI8B8KdVy3YBevqRHz/qWh72Yui3MUDEL3q44WPXw3M+fo1pZuQs4tOIBVVTaoiXEI/MxfhGDPsxsNZfoE1q66ro5aJim3XdoLFw72H+n23BaIXzbcOnz5mfPoTvYVz7KzUl5+FRxEuqkp9G/Ajia [...]
</div>
<div id="stat-parameters" class="section level3">
<h3>Stat parameters</h3>
@@ -173,7 +168,7 @@ stat_lm <-<span class="st"> </span>function(<span class="dt">mapping =</span>
<span class="kw">ggplot</span>(mpg, <span class="kw">aes</span>(displ, hwy)) +<span class="st"> </span>
<span class="st"> </span><span class="kw">geom_point</span>() +<span class="st"> </span>
<span class="st"> </span><span class="kw">stat_lm</span>()</code></pre></div>
-<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASAAAAEgCAYAAAAUg66AAAAEDWlDQ1BJQ0MgUHJvZmlsZQAAOI2NVV1oHFUUPrtzZyMkzlNsNIV0qD8NJQ2TVjShtLp/3d02bpZJNtoi6GT27s6Yyc44M7v9oU9FUHwx6psUxL+3gCAo9Q/bPrQvlQol2tQgKD60+INQ6Ium65k7M5lpurHeZe58853vnnvuuWfvBei5qliWkRQBFpquLRcy4nOHj4g9K5CEh6AXBqFXUR0rXalMAjZPC3e1W99Dwntf2dXd/p+tt0YdFSBxH2Kz5qgLiI8B8KdVy3YBevqRHz/qWh72Yui3MUDEL3q44WPXw3M+fo1pZuQs4tOIBVVTaoiXEI/MxfhGDPsxsNZfoE1q66ro5aJim3XdoLFw72H+n23BaIXzbcOnz5mfPoTvYVz7KzUl5+FRxEuqkp9G/Ajia [...]
+<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAqAAAAKgCAYAAABEPM/FAAAEDWlDQ1BJQ0MgUHJvZmlsZQAAOI2NVV1oHFUUPrtzZyMkzlNsNIV0qD8NJQ2TVjShtLp/3d02bpZJNtoi6GT27s6Yyc44M7v9oU9FUHwx6psUxL+3gCAo9Q/bPrQvlQol2tQgKD60+INQ6Ium65k7M5lpurHeZe58853vnnvuuWfvBei5qliWkRQBFpquLRcy4nOHj4g9K5CEh6AXBqFXUR0rXalMAjZPC3e1W99Dwntf2dXd/p+tt0YdFSBxH2Kz5qgLiI8B8KdVy3YBevqRHz/qWh72Yui3MUDEL3q44WPXw3M+fo1pZuQs4tOIBVVTaoiXEI/MxfhGDPsxsNZfoE1q66ro5aJim3XdoLFw72H+n23BaIXzbcOnz5mfPoTvYVz7KzUl5+FRxEuqkp9G/Ajia [...]
<p><code>StatLm</code> is inflexible because it has no parameters. We might want to allow the user to control the model formula and the number of points used to generate the grid. To do so, we add arguments to the <code>compute_group()</code> method and our wrapper function:</p>
<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">StatLm <-<span class="st"> </span><span class="kw">ggproto</span>(<span class="st">"StatLm"</span>, Stat,
<span class="dt">required_aes =</span> <span class="kw">c</span>(<span class="st">"x"</span>, <span class="st">"y"</span>),
@@ -204,7 +199,7 @@ stat_lm <-<span class="st"> </span>function(<span class="dt">mapping =</span>
<span class="st"> </span><span class="kw">geom_point</span>() +<span class="st"> </span>
<span class="st"> </span><span class="kw">stat_lm</span>(<span class="dt">formula =</span> y ~<span class="st"> </span><span class="kw">poly</span>(x, <span class="dv">10</span>)) +<span class="st"> </span>
<span class="st"> </span><span class="kw">stat_lm</span>(<span class="dt">formula =</span> y ~<span class="st"> </span><span class="kw">poly</span>(x, <span class="dv">10</span>), <span class="dt">geom =</span> <span class="st">"point"</span>, <span class="dt">colour =</span> <span class="st">"red"</span>, <span class="dt">n =</span> <span class="dv">20</span>)</code></pre></div>
-<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASAAAAEgCAYAAAAUg66AAAAEDWlDQ1BJQ0MgUHJvZmlsZQAAOI2NVV1oHFUUPrtzZyMkzlNsNIV0qD8NJQ2TVjShtLp/3d02bpZJNtoi6GT27s6Yyc44M7v9oU9FUHwx6psUxL+3gCAo9Q/bPrQvlQol2tQgKD60+INQ6Ium65k7M5lpurHeZe58853vnnvuuWfvBei5qliWkRQBFpquLRcy4nOHj4g9K5CEh6AXBqFXUR0rXalMAjZPC3e1W99Dwntf2dXd/p+tt0YdFSBxH2Kz5qgLiI8B8KdVy3YBevqRHz/qWh72Yui3MUDEL3q44WPXw3M+fo1pZuQs4tOIBVVTaoiXEI/MxfhGDPsxsNZfoE1q66ro5aJim3XdoLFw72H+n23BaIXzbcOnz5mfPoTvYVz7KzUl5+FRxEuqkp9G/Ajia [...]
+<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAqAAAAKgCAYAAABEPM/FAAAEDWlDQ1BJQ0MgUHJvZmlsZQAAOI2NVV1oHFUUPrtzZyMkzlNsNIV0qD8NJQ2TVjShtLp/3d02bpZJNtoi6GT27s6Yyc44M7v9oU9FUHwx6psUxL+3gCAo9Q/bPrQvlQol2tQgKD60+INQ6Ium65k7M5lpurHeZe58853vnnvuuWfvBei5qliWkRQBFpquLRcy4nOHj4g9K5CEh6AXBqFXUR0rXalMAjZPC3e1W99Dwntf2dXd/p+tt0YdFSBxH2Kz5qgLiI8B8KdVy3YBevqRHz/qWh72Yui3MUDEL3q44WPXw3M+fo1pZuQs4tOIBVVTaoiXEI/MxfhGDPsxsNZfoE1q66ro5aJim3XdoLFw72H+n23BaIXzbcOnz5mfPoTvYVz7KzUl5+FRxEuqkp9G/Ajia [...]
<p>Note that we don’t <em>have</em> to explicitly include the new parameters in the arguments for the layer, <code>...</code> will get passed to the right place anyway. But you’ll need to document them somewhere so the user knows about them. Here’s a brief example. Note <code>@inheritParams ggplot2::stat_identity</code>: that will automatically inherit documentation for all the parameters also defined for <code>stat_identity()</code>.</p>
<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r"><span class="co">#' @inheritParams ggplot2::stat_identity</span>
<span class="co">#' @param formula The modelling formula passed to \code{lm}. Should only </span>
@@ -261,11 +256,11 @@ stat_density_common <-<span class="st"> </span>function(<span class="dt">mapp
<span class="kw">ggplot</span>(mpg, <span class="kw">aes</span>(displ, <span class="dt">colour =</span> drv)) +<span class="st"> </span>
<span class="st"> </span><span class="kw">stat_density_common</span>()
<span class="co">#> Picking bandwidth of 0.345</span></code></pre></div>
-<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASAAAAEgCAYAAAAUg66AAAAEDWlDQ1BJQ0MgUHJvZmlsZQAAOI2NVV1oHFUUPrtzZyMkzlNsNIV0qD8NJQ2TVjShtLp/3d02bpZJNtoi6GT27s6Yyc44M7v9oU9FUHwx6psUxL+3gCAo9Q/bPrQvlQol2tQgKD60+INQ6Ium65k7M5lpurHeZe58853vnnvuuWfvBei5qliWkRQBFpquLRcy4nOHj4g9K5CEh6AXBqFXUR0rXalMAjZPC3e1W99Dwntf2dXd/p+tt0YdFSBxH2Kz5qgLiI8B8KdVy3YBevqRHz/qWh72Yui3MUDEL3q44WPXw3M+fo1pZuQs4tOIBVVTaoiXEI/MxfhGDPsxsNZfoE1q66ro5aJim3XdoLFw72H+n23BaIXzbcOnz5mfPoTvYVz7KzUl5+FRxEuqkp9G/Ajia [...]
+<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAqAAAAKgCAYAAABEPM/FAAAEDWlDQ1BJQ0MgUHJvZmlsZQAAOI2NVV1oHFUUPrtzZyMkzlNsNIV0qD8NJQ2TVjShtLp/3d02bpZJNtoi6GT27s6Yyc44M7v9oU9FUHwx6psUxL+3gCAo9Q/bPrQvlQol2tQgKD60+INQ6Ium65k7M5lpurHeZe58853vnnvuuWfvBei5qliWkRQBFpquLRcy4nOHj4g9K5CEh6AXBqFXUR0rXalMAjZPC3e1W99Dwntf2dXd/p+tt0YdFSBxH2Kz5qgLiI8B8KdVy3YBevqRHz/qWh72Yui3MUDEL3q44WPXw3M+fo1pZuQs4tOIBVVTaoiXEI/MxfhGDPsxsNZfoE1q66ro5aJim3XdoLFw72H+n23BaIXzbcOnz5mfPoTvYVz7KzUl5+FRxEuqkp9G/Ajia [...]
<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">
<span class="kw">ggplot</span>(mpg, <span class="kw">aes</span>(displ, <span class="dt">colour =</span> drv)) +<span class="st"> </span>
<span class="st"> </span><span class="kw">stat_density_common</span>(<span class="dt">bandwidth =</span> <span class="fl">0.5</span>)</code></pre></div>
-<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASAAAAEgCAYAAAAUg66AAAAEDWlDQ1BJQ0MgUHJvZmlsZQAAOI2NVV1oHFUUPrtzZyMkzlNsNIV0qD8NJQ2TVjShtLp/3d02bpZJNtoi6GT27s6Yyc44M7v9oU9FUHwx6psUxL+3gCAo9Q/bPrQvlQol2tQgKD60+INQ6Ium65k7M5lpurHeZe58853vnnvuuWfvBei5qliWkRQBFpquLRcy4nOHj4g9K5CEh6AXBqFXUR0rXalMAjZPC3e1W99Dwntf2dXd/p+tt0YdFSBxH2Kz5qgLiI8B8KdVy3YBevqRHz/qWh72Yui3MUDEL3q44WPXw3M+fo1pZuQs4tOIBVVTaoiXEI/MxfhGDPsxsNZfoE1q66ro5aJim3XdoLFw72H+n23BaIXzbcOnz5mfPoTvYVz7KzUl5+FRxEuqkp9G/Ajia [...]
+<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAqAAAAKgCAYAAABEPM/FAAAEDWlDQ1BJQ0MgUHJvZmlsZQAAOI2NVV1oHFUUPrtzZyMkzlNsNIV0qD8NJQ2TVjShtLp/3d02bpZJNtoi6GT27s6Yyc44M7v9oU9FUHwx6psUxL+3gCAo9Q/bPrQvlQol2tQgKD60+INQ6Ium65k7M5lpurHeZe58853vnnvuuWfvBei5qliWkRQBFpquLRcy4nOHj4g9K5CEh6AXBqFXUR0rXalMAjZPC3e1W99Dwntf2dXd/p+tt0YdFSBxH2Kz5qgLiI8B8KdVy3YBevqRHz/qWh72Yui3MUDEL3q44WPXw3M+fo1pZuQs4tOIBVVTaoiXEI/MxfhGDPsxsNZfoE1q66ro5aJim3XdoLFw72H+n23BaIXzbcOnz5mfPoTvYVz7KzUl5+FRxEuqkp9G/Ajia [...]
<p>I recommend using <code>NULL</code> as a default value. If you pick important parameters automatically, it’s a good idea to <code>message()</code> to the user (and when printing a floating point parameter, using <code>signif()</code> to show only a few significant digits).</p>
</div>
<div id="variable-names-and-default-aesthetics" class="section level3">
@@ -283,11 +278,11 @@ stat_density_common <-<span class="st"> </span>function(<span class="dt">mapp
<span class="kw">ggplot</span>(mpg, <span class="kw">aes</span>(displ, drv, <span class="dt">colour =</span> ..density..)) +<span class="st"> </span>
<span class="st"> </span><span class="kw">stat_density_common</span>(<span class="dt">bandwidth =</span> <span class="dv">1</span>, <span class="dt">geom =</span> <span class="st">"point"</span>)</code></pre></div>
-<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASAAAAEgCAYAAAAUg66AAAAEDWlDQ1BJQ0MgUHJvZmlsZQAAOI2NVV1oHFUUPrtzZyMkzlNsNIV0qD8NJQ2TVjShtLp/3d02bpZJNtoi6GT27s6Yyc44M7v9oU9FUHwx6psUxL+3gCAo9Q/bPrQvlQol2tQgKD60+INQ6Ium65k7M5lpurHeZe58853vnnvuuWfvBei5qliWkRQBFpquLRcy4nOHj4g9K5CEh6AXBqFXUR0rXalMAjZPC3e1W99Dwntf2dXd/p+tt0YdFSBxH2Kz5qgLiI8B8KdVy3YBevqRHz/qWh72Yui3MUDEL3q44WPXw3M+fo1pZuQs4tOIBVVTaoiXEI/MxfhGDPsxsNZfoE1q66ro5aJim3XdoLFw72H+n23BaIXzbcOnz5mfPoTvYVz7KzUl5+FRxEuqkp9G/Ajia [...]
+<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAqAAAAKgCAYAAABEPM/FAAAEDWlDQ1BJQ0MgUHJvZmlsZQAAOI2NVV1oHFUUPrtzZyMkzlNsNIV0qD8NJQ2TVjShtLp/3d02bpZJNtoi6GT27s6Yyc44M7v9oU9FUHwx6psUxL+3gCAo9Q/bPrQvlQol2tQgKD60+INQ6Ium65k7M5lpurHeZe58853vnnvuuWfvBei5qliWkRQBFpquLRcy4nOHj4g9K5CEh6AXBqFXUR0rXalMAjZPC3e1W99Dwntf2dXd/p+tt0YdFSBxH2Kz5qgLiI8B8KdVy3YBevqRHz/qWh72Yui3MUDEL3q44WPXw3M+fo1pZuQs4tOIBVVTaoiXEI/MxfhGDPsxsNZfoE1q66ro5aJim3XdoLFw72H+n23BaIXzbcOnz5mfPoTvYVz7KzUl5+FRxEuqkp9G/Ajia [...]
<p>However, using this stat with the area geom doesn’t work quite right. The areas don’t stack on top of each other:</p>
<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r"><span class="kw">ggplot</span>(mpg, <span class="kw">aes</span>(displ, <span class="dt">fill =</span> drv)) +<span class="st"> </span>
<span class="st"> </span><span class="kw">stat_density_common</span>(<span class="dt">bandwidth =</span> <span class="dv">1</span>, <span class="dt">geom =</span> <span class="st">"area"</span>, <span class="dt">position =</span> <span class="st">"stack"</span>)</code></pre></div>
-<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASAAAAEgCAYAAAAUg66AAAAEDWlDQ1BJQ0MgUHJvZmlsZQAAOI2NVV1oHFUUPrtzZyMkzlNsNIV0qD8NJQ2TVjShtLp/3d02bpZJNtoi6GT27s6Yyc44M7v9oU9FUHwx6psUxL+3gCAo9Q/bPrQvlQol2tQgKD60+INQ6Ium65k7M5lpurHeZe58853vnnvuuWfvBei5qliWkRQBFpquLRcy4nOHj4g9K5CEh6AXBqFXUR0rXalMAjZPC3e1W99Dwntf2dXd/p+tt0YdFSBxH2Kz5qgLiI8B8KdVy3YBevqRHz/qWh72Yui3MUDEL3q44WPXw3M+fo1pZuQs4tOIBVVTaoiXEI/MxfhGDPsxsNZfoE1q66ro5aJim3XdoLFw72H+n23BaIXzbcOnz5mfPoTvYVz7KzUl5+FRxEuqkp9G/Ajia [...]
+<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAqAAAAKgCAYAAABEPM/FAAAEDWlDQ1BJQ0MgUHJvZmlsZQAAOI2NVV1oHFUUPrtzZyMkzlNsNIV0qD8NJQ2TVjShtLp/3d02bpZJNtoi6GT27s6Yyc44M7v9oU9FUHwx6psUxL+3gCAo9Q/bPrQvlQol2tQgKD60+INQ6Ium65k7M5lpurHeZe58853vnnvuuWfvBei5qliWkRQBFpquLRcy4nOHj4g9K5CEh6AXBqFXUR0rXalMAjZPC3e1W99Dwntf2dXd/p+tt0YdFSBxH2Kz5qgLiI8B8KdVy3YBevqRHz/qWh72Yui3MUDEL3q44WPXw3M+fo1pZuQs4tOIBVVTaoiXEI/MxfhGDPsxsNZfoE1q66ro5aJim3XdoLFw72H+n23BaIXzbcOnz5mfPoTvYVz7KzUl5+FRxEuqkp9G/Ajia [...]
<p>This is because each density is computed independently, and the estimated <code>x</code>s don’t line up. We can resolve that issue by computing the range of the data once in <code>setup_params()</code>.</p>
<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">StatDensityCommon <-<span class="st"> </span><span class="kw">ggproto</span>(<span class="st">"StatDensityCommon"</span>, Stat,
<span class="dt">required_aes =</span> <span class="st">"x"</span>,
@@ -313,10 +308,10 @@ stat_density_common <-<span class="st"> </span>function(<span class="dt">mapp
<span class="kw">ggplot</span>(mpg, <span class="kw">aes</span>(displ, <span class="dt">fill =</span> drv)) +<span class="st"> </span>
<span class="st"> </span><span class="kw">stat_density_common</span>(<span class="dt">bandwidth =</span> <span class="dv">1</span>, <span class="dt">geom =</span> <span class="st">"area"</span>, <span class="dt">position =</span> <span class="st">"stack"</span>)</code></pre></div>
-<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASAAAAEgCAYAAAAUg66AAAAEDWlDQ1BJQ0MgUHJvZmlsZQAAOI2NVV1oHFUUPrtzZyMkzlNsNIV0qD8NJQ2TVjShtLp/3d02bpZJNtoi6GT27s6Yyc44M7v9oU9FUHwx6psUxL+3gCAo9Q/bPrQvlQol2tQgKD60+INQ6Ium65k7M5lpurHeZe58853vnnvuuWfvBei5qliWkRQBFpquLRcy4nOHj4g9K5CEh6AXBqFXUR0rXalMAjZPC3e1W99Dwntf2dXd/p+tt0YdFSBxH2Kz5qgLiI8B8KdVy3YBevqRHz/qWh72Yui3MUDEL3q44WPXw3M+fo1pZuQs4tOIBVVTaoiXEI/MxfhGDPsxsNZfoE1q66ro5aJim3XdoLFw72H+n23BaIXzbcOnz5mfPoTvYVz7KzUl5+FRxEuqkp9G/Ajia [...]
+<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAqAAAAKgCAYAAABEPM/FAAAEDWlDQ1BJQ0MgUHJvZmlsZQAAOI2NVV1oHFUUPrtzZyMkzlNsNIV0qD8NJQ2TVjShtLp/3d02bpZJNtoi6GT27s6Yyc44M7v9oU9FUHwx6psUxL+3gCAo9Q/bPrQvlQol2tQgKD60+INQ6Ium65k7M5lpurHeZe58853vnnvuuWfvBei5qliWkRQBFpquLRcy4nOHj4g9K5CEh6AXBqFXUR0rXalMAjZPC3e1W99Dwntf2dXd/p+tt0YdFSBxH2Kz5qgLiI8B8KdVy3YBevqRHz/qWh72Yui3MUDEL3q44WPXw3M+fo1pZuQs4tOIBVVTaoiXEI/MxfhGDPsxsNZfoE1q66ro5aJim3XdoLFw72H+n23BaIXzbcOnz5mfPoTvYVz7KzUl5+FRxEuqkp9G/Ajia [...]
<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r"><span class="kw">ggplot</span>(mpg, <span class="kw">aes</span>(displ, drv, <span class="dt">fill =</span> ..density..)) +<span class="st"> </span>
<span class="st"> </span><span class="kw">stat_density_common</span>(<span class="dt">bandwidth =</span> <span class="dv">1</span>, <span class="dt">geom =</span> <span class="st">"raster"</span>)</code></pre></div>
-<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASAAAAEgCAYAAAAUg66AAAAEDWlDQ1BJQ0MgUHJvZmlsZQAAOI2NVV1oHFUUPrtzZyMkzlNsNIV0qD8NJQ2TVjShtLp/3d02bpZJNtoi6GT27s6Yyc44M7v9oU9FUHwx6psUxL+3gCAo9Q/bPrQvlQol2tQgKD60+INQ6Ium65k7M5lpurHeZe58853vnnvuuWfvBei5qliWkRQBFpquLRcy4nOHj4g9K5CEh6AXBqFXUR0rXalMAjZPC3e1W99Dwntf2dXd/p+tt0YdFSBxH2Kz5qgLiI8B8KdVy3YBevqRHz/qWh72Yui3MUDEL3q44WPXw3M+fo1pZuQs4tOIBVVTaoiXEI/MxfhGDPsxsNZfoE1q66ro5aJim3XdoLFw72H+n23BaIXzbcOnz5mfPoTvYVz7KzUl5+FRxEuqkp9G/Ajia [...]
+<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAqAAAAKgCAYAAABEPM/FAAAEDWlDQ1BJQ0MgUHJvZmlsZQAAOI2NVV1oHFUUPrtzZyMkzlNsNIV0qD8NJQ2TVjShtLp/3d02bpZJNtoi6GT27s6Yyc44M7v9oU9FUHwx6psUxL+3gCAo9Q/bPrQvlQol2tQgKD60+INQ6Ium65k7M5lpurHeZe58853vnnvuuWfvBei5qliWkRQBFpquLRcy4nOHj4g9K5CEh6AXBqFXUR0rXalMAjZPC3e1W99Dwntf2dXd/p+tt0YdFSBxH2Kz5qgLiI8B8KdVy3YBevqRHz/qWh72Yui3MUDEL3q44WPXw3M+fo1pZuQs4tOIBVVTaoiXEI/MxfhGDPsxsNZfoE1q66ro5aJim3XdoLFw72H+n23BaIXzbcOnz5mfPoTvYVz7KzUl5+FRxEuqkp9G/Ajia [...]
</div>
<div id="exercises" class="section level3">
<h3>Exercises</h3>
@@ -360,25 +355,25 @@ geom_simple_point <-<span class="st"> </span>function(<span class="dt">mappin
<span class="kw">ggplot</span>(mpg, <span class="kw">aes</span>(displ, hwy)) +<span class="st"> </span>
<span class="st"> </span><span class="kw">geom_simple_point</span>()</code></pre></div>
-<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASAAAAEgCAYAAAAUg66AAAAEDWlDQ1BJQ0MgUHJvZmlsZQAAOI2NVV1oHFUUPrtzZyMkzlNsNIV0qD8NJQ2TVjShtLp/3d02bpZJNtoi6GT27s6Yyc44M7v9oU9FUHwx6psUxL+3gCAo9Q/bPrQvlQol2tQgKD60+INQ6Ium65k7M5lpurHeZe58853vnnvuuWfvBei5qliWkRQBFpquLRcy4nOHj4g9K5CEh6AXBqFXUR0rXalMAjZPC3e1W99Dwntf2dXd/p+tt0YdFSBxH2Kz5qgLiI8B8KdVy3YBevqRHz/qWh72Yui3MUDEL3q44WPXw3M+fo1pZuQs4tOIBVVTaoiXEI/MxfhGDPsxsNZfoE1q66ro5aJim3XdoLFw72H+n23BaIXzbcOnz5mfPoTvYVz7KzUl5+FRxEuqkp9G/Ajia [...]
+<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAqAAAAKgCAYAAABEPM/FAAAEDWlDQ1BJQ0MgUHJvZmlsZQAAOI2NVV1oHFUUPrtzZyMkzlNsNIV0qD8NJQ2TVjShtLp/3d02bpZJNtoi6GT27s6Yyc44M7v9oU9FUHwx6psUxL+3gCAo9Q/bPrQvlQol2tQgKD60+INQ6Ium65k7M5lpurHeZe58853vnnvuuWfvBei5qliWkRQBFpquLRcy4nOHj4g9K5CEh6AXBqFXUR0rXalMAjZPC3e1W99Dwntf2dXd/p+tt0YdFSBxH2Kz5qgLiI8B8KdVy3YBevqRHz/qWh72Yui3MUDEL3q44WPXw3M+fo1pZuQs4tOIBVVTaoiXEI/MxfhGDPsxsNZfoE1q66ro5aJim3XdoLFw72H+n23BaIXzbcOnz5mfPoTvYVz7KzUl5+FRxEuqkp9G/Ajia [...]
<p>This is very similar to defining a new stat. You always need to provide fields/methods for the four pieces shown above:</p>
<ul>
<li><p><code>required_aes</code> is a character vector which lists all the aesthetics that the user must provide.</p></li>
<li><p><code>default_aes</code> lists the aesthetics that have default values.</p></li>
<li><p><code>draw_key</code> provides the function used to draw the key in the legend. You can see a list of all the build in key functions in <code>?draw_key</code></p></li>
-<li><p><code>draw_group()</code> is where the magic happens. This function takes three arguments and returns a grid grob. It is called once for each panel. It’s the most complicated part and is described in more detail below.</p></li>
+<li><p><code>draw_panel()</code> is where the magic happens. This function takes three arguments and returns a grid grob. It is called once for each panel. It’s the most complicated part and is described in more detail below.</p></li>
</ul>
-<p><code>draw_group()</code> has three arguments:</p>
+<p><code>draw_panel()</code> has three arguments:</p>
<ul>
<li><p><code>data</code>: a data frame with one column for each aesthetic.</p></li>
<li><p><code>panel_scales</code>: a list containing information about the x and y scales for the current panel.</p></li>
<li><p><code>coord</code>: an object describing the coordinate system.</p></li>
</ul>
-<p>Generally you won’t use <code>panel_scales</code> and <code>coord</code> directly, but you will always use them to transform the data: <code>coords <- coord$transform(data, panel_scales)</code>. This creates a data frame where position variables are scaled to the range 0–1. You then take this data and call a grid grob function. (Transforming for non-Cartesian coordinate systems is quite complex - you’re best of transforming your data to the form accepted by an existing ggplot2 geom [...]
+<p>Generally you won’t use <code>panel_scales</code> and <code>coord</code> directly, but you will always use them to transform the data: <code>coords <- coord$transform(data, panel_scales)</code>. This creates a data frame where position variables are scaled to the range 0–1. You then take this data and call a grid grob function. (Transforming for non-Cartesian coordinate systems is quite complex - you’re best off transforming your data to the form accepted by an existing ggplot2 geo [...]
</div>
<div id="collective-geoms" class="section level3">
<h3>Collective geoms</h3>
-<p>Overriding <code>draw_panel()</code> is most appropriate if there is one graphic element per row. In other cases, you want graphic element per group. For example, take polygons: each row gives one vertex of a polygon. In this case, you should instead override <code>draw_group()</code>:</p>
+<p>Overriding <code>draw_panel()</code> is most appropriate if there is one graphic element per row. In other cases, you want graphic element per group. For example, take polygons: each row gives one vertex of a polygon. In this case, you should instead override <code>draw_group()</code>.</p>
<p>The following code makes a simplified version of <code>GeomPolygon</code>:</p>
<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">GeomSimplePolygon <-<span class="st"> </span><span class="kw">ggproto</span>(<span class="st">"GeomPolygon"</span>, Geom,
<span class="dt">required_aes =</span> <span class="kw">c</span>(<span class="st">"x"</span>, <span class="st">"y"</span>),
@@ -423,13 +418,14 @@ geom_simple_polygon <-<span class="st"> </span>function(<span class="dt">mapp
<span class="kw">ggplot</span>(mpg, <span class="kw">aes</span>(displ, hwy)) +<span class="st"> </span>
<span class="st"> </span><span class="kw">geom_point</span>() +<span class="st"> </span>
<span class="st"> </span><span class="kw">geom_simple_polygon</span>(<span class="kw">aes</span>(<span class="dt">colour =</span> class), <span class="dt">fill =</span> <span class="ot">NA</span>)</code></pre></div>
-<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASAAAAEgCAYAAAAUg66AAAAEDWlDQ1BJQ0MgUHJvZmlsZQAAOI2NVV1oHFUUPrtzZyMkzlNsNIV0qD8NJQ2TVjShtLp/3d02bpZJNtoi6GT27s6Yyc44M7v9oU9FUHwx6psUxL+3gCAo9Q/bPrQvlQol2tQgKD60+INQ6Ium65k7M5lpurHeZe58853vnnvuuWfvBei5qliWkRQBFpquLRcy4nOHj4g9K5CEh6AXBqFXUR0rXalMAjZPC3e1W99Dwntf2dXd/p+tt0YdFSBxH2Kz5qgLiI8B8KdVy3YBevqRHz/qWh72Yui3MUDEL3q44WPXw3M+fo1pZuQs4tOIBVVTaoiXEI/MxfhGDPsxsNZfoE1q66ro5aJim3XdoLFw72H+n23BaIXzbcOnz5mfPoTvYVz7KzUl5+FRxEuqkp9G/Ajia [...]
+<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAqAAAAKgCAYAAABEPM/FAAAEDWlDQ1BJQ0MgUHJvZmlsZQAAOI2NVV1oHFUUPrtzZyMkzlNsNIV0qD8NJQ2TVjShtLp/3d02bpZJNtoi6GT27s6Yyc44M7v9oU9FUHwx6psUxL+3gCAo9Q/bPrQvlQol2tQgKD60+INQ6Ium65k7M5lpurHeZe58853vnnvuuWfvBei5qliWkRQBFpquLRcy4nOHj4g9K5CEh6AXBqFXUR0rXalMAjZPC3e1W99Dwntf2dXd/p+tt0YdFSBxH2Kz5qgLiI8B8KdVy3YBevqRHz/qWh72Yui3MUDEL3q44WPXw3M+fo1pZuQs4tOIBVVTaoiXEI/MxfhGDPsxsNZfoE1q66ro5aJim3XdoLFw72H+n23BaIXzbcOnz5mfPoTvYVz7KzUl5+FRxEuqkp9G/Ajia [...]
<p>There are a few things to note here:</p>
<ul>
-<li><p>We override <code>draw_group()</code> instead of <code>draw_layer()</code> because we want one polygon per group, not one polygon per row. If you look at the source code for the original <code>GeomPolygon</code> you’ll see it actually overrides <code>geom_layer()</code> because it uses some tricks to make <code>polygonGrob()</code> produce multiple polygons in one call. This is considerably more complicated, but gives better performance.</p></li>
+<li><p>We override <code>draw_group()</code> instead of <code>draw_panel()</code> because we want one polygon per group, not one polygon per row.</p></li>
<li><p>If the data contains two or fewer points, there’s no point trying to draw a polygon, so we return a <code>nullGrob()</code>. This is the graphical equivalent of <code>NULL</code>: it’s a grob that doesn’t draw anything and doesn’t take up any space.</p></li>
<li><p>Note the units: <code>x</code> and <code>y</code> should always be drawn in “native” units. (The default units for <code>pointGrob()</code> is a native, so we didn’t need to change it there). <code>lwd</code> is measured in points, but ggplot2 uses mm, so we need to multiply it by the adjustment factor <code>.pt</code>.</p></li>
</ul>
+<p>You might want to compare this to the real <code>GeomPolygon</code>. You’ll see it overrides <code>draw_panel()</code> because it uses some tricks to make <code>polygonGrob()</code> produce multiple polygons in one call. This is considerably more complicated, but gives better performance.</p>
</div>
<div id="inheriting-from-an-existing-geom" class="section level3">
<h3>Inheriting from an existing Geom</h3>
@@ -451,7 +447,7 @@ geom_chull <-<span class="st"> </span>function(<span class="dt">mapping =</sp
<span class="kw">ggplot</span>(mpg, <span class="kw">aes</span>(displ, hwy)) +<span class="st"> </span>
<span class="st"> </span><span class="kw">geom_point</span>() +<span class="st"> </span>
<span class="st"> </span><span class="kw">geom_chull</span>()</code></pre></div>
-<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASAAAAEgCAYAAAAUg66AAAAEDWlDQ1BJQ0MgUHJvZmlsZQAAOI2NVV1oHFUUPrtzZyMkzlNsNIV0qD8NJQ2TVjShtLp/3d02bpZJNtoi6GT27s6Yyc44M7v9oU9FUHwx6psUxL+3gCAo9Q/bPrQvlQol2tQgKD60+INQ6Ium65k7M5lpurHeZe58853vnnvuuWfvBei5qliWkRQBFpquLRcy4nOHj4g9K5CEh6AXBqFXUR0rXalMAjZPC3e1W99Dwntf2dXd/p+tt0YdFSBxH2Kz5qgLiI8B8KdVy3YBevqRHz/qWh72Yui3MUDEL3q44WPXw3M+fo1pZuQs4tOIBVVTaoiXEI/MxfhGDPsxsNZfoE1q66ro5aJim3XdoLFw72H+n23BaIXzbcOnz5mfPoTvYVz7KzUl5+FRxEuqkp9G/Ajia [...]
+<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAqAAAAKgCAYAAABEPM/FAAAEDWlDQ1BJQ0MgUHJvZmlsZQAAOI2NVV1oHFUUPrtzZyMkzlNsNIV0qD8NJQ2TVjShtLp/3d02bpZJNtoi6GT27s6Yyc44M7v9oU9FUHwx6psUxL+3gCAo9Q/bPrQvlQol2tQgKD60+INQ6Ium65k7M5lpurHeZe58853vnnvuuWfvBei5qliWkRQBFpquLRcy4nOHj4g9K5CEh6AXBqFXUR0rXalMAjZPC3e1W99Dwntf2dXd/p+tt0YdFSBxH2Kz5qgLiI8B8KdVy3YBevqRHz/qWh72Yui3MUDEL3q44WPXw3M+fo1pZuQs4tOIBVVTaoiXEI/MxfhGDPsxsNZfoE1q66ro5aJim3XdoLFw72H+n23BaIXzbcOnz5mfPoTvYVz7KzUl5+FRxEuqkp9G/Ajia [...]
<p>This doesn’t allow you to use different geoms with the stat, but that seems appropriate here since the convex hull is primarily a polygonal feature.</p>
</div>
<div id="exercises-1" class="section level3">
@@ -474,67 +470,65 @@ geom_chull <-<span class="st"> </span>function(<span class="dt">mapping =</sp
<h3>Overriding elements</h3>
<p>By default, when you add a new theme element, it inherits values from the existing theme. For example, the following code sets the key colour to red, but it inherits the existing fill colour:</p>
<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r"><span class="kw">theme_grey</span>()$legend.key
-<span class="co">#> List of 4</span>
-<span class="co">#> $ fill : chr "grey95"</span>
-<span class="co">#> $ colour : chr "white"</span>
-<span class="co">#> $ size : NULL</span>
-<span class="co">#> $ linetype: NULL</span>
+<span class="co">#> List of 5</span>
+<span class="co">#> $ fill : chr "grey95"</span>
+<span class="co">#> $ colour : chr "white"</span>
+<span class="co">#> $ size : NULL</span>
+<span class="co">#> $ linetype : NULL</span>
+<span class="co">#> $ inherit.blank: logi TRUE</span>
<span class="co">#> - attr(*, "class")= chr [1:2] "element_rect" "element"</span>
new_theme <-<span class="st"> </span><span class="kw">theme_grey</span>() +<span class="st"> </span><span class="kw">theme</span>(<span class="dt">legend.key =</span> <span class="kw">element_rect</span>(<span class="dt">colour =</span> <span class="st">"red"</span>))
new_theme$legend.key
-<span class="co">#> List of 4</span>
-<span class="co">#> $ fill : chr "grey95"</span>
-<span class="co">#> $ colour : chr "red"</span>
-<span class="co">#> $ size : NULL</span>
-<span class="co">#> $ linetype: NULL</span>
+<span class="co">#> List of 5</span>
+<span class="co">#> $ fill : chr "grey95"</span>
+<span class="co">#> $ colour : chr "red"</span>
+<span class="co">#> $ size : NULL</span>
+<span class="co">#> $ linetype : NULL</span>
+<span class="co">#> $ inherit.blank: logi FALSE</span>
<span class="co">#> - attr(*, "class")= chr [1:2] "element_rect" "element"</span></code></pre></div>
<p>To override it completely, use <code>%+replace%</code> instead of <code>+</code>:</p>
<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">new_theme <-<span class="st"> </span><span class="kw">theme_grey</span>() %+replace%<span class="st"> </span><span class="kw">theme</span>(<span class="dt">legend.key =</span> <span class="kw">element_rect</span>(<span class="dt">colour =</span> <span class="st">"red"</span>))
new_theme$legend.key
-<span class="co">#> List of 4</span>
-<span class="co">#> $ fill : NULL</span>
-<span class="co">#> $ colour : chr "red"</span>
-<span class="co">#> $ size : NULL</span>
-<span class="co">#> $ linetype: NULL</span>
+<span class="co">#> List of 5</span>
+<span class="co">#> $ fill : NULL</span>
+<span class="co">#> $ colour : chr "red"</span>
+<span class="co">#> $ size : NULL</span>
+<span class="co">#> $ linetype : NULL</span>
+<span class="co">#> $ inherit.blank: logi FALSE</span>
<span class="co">#> - attr(*, "class")= chr [1:2] "element_rect" "element"</span></code></pre></div>
</div>
<div id="global-elements" class="section level3">
<h3>Global elements</h3>
<p>There are four elements that affect the global appearance of the plot:</p>
-<table style="width:82%;">
-<colgroup>
-<col width="19%"></col>
-<col width="27%"></col>
-<col width="34%"></col>
-</colgroup>
+<table>
<thead>
<tr class="header">
-<th align="left">Element</th>
-<th align="left">Theme function</th>
-<th align="left">Description</th>
+<th>Element</th>
+<th>Theme function</th>
+<th>Description</th>
</tr>
</thead>
<tbody>
<tr class="odd">
-<td align="left">line</td>
-<td align="left"><code>element_line()</code></td>
-<td align="left">all line elements</td>
+<td>line</td>
+<td><code>element_line()</code></td>
+<td>all line elements</td>
</tr>
<tr class="even">
-<td align="left">rect</td>
-<td align="left"><code>element_rect()</code></td>
-<td align="left">all rectangular elements</td>
+<td>rect</td>
+<td><code>element_rect()</code></td>
+<td>all rectangular elements</td>
</tr>
<tr class="odd">
-<td align="left">text</td>
-<td align="left"><code>element_text()</code></td>
-<td align="left">all text</td>
+<td>text</td>
+<td><code>element_text()</code></td>
+<td>all text</td>
</tr>
<tr class="even">
-<td align="left">title</td>
-<td align="left"><code>element_text()</code></td>
-<td align="left">all text in title elements (plot, axes & legend)</td>
+<td>title</td>
+<td><code>element_text()</code></td>
+<td>all text in title elements (plot, axes & legend)</td>
</tr>
</tbody>
</table>
@@ -545,9 +539,9 @@ base <-<span class="st"> </span><span class="kw">ggplot</span>(df, <span clas
<span class="st"> </span><span class="kw">theme_minimal</span>()
base</code></pre></div>
-<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASAAAAEgCAYAAAAUg66AAAAEDWlDQ1BJQ0MgUHJvZmlsZQAAOI2NVV1oHFUUPrtzZyMkzlNsNIV0qD8NJQ2TVjShtLp/3d02bpZJNtoi6GT27s6Yyc44M7v9oU9FUHwx6psUxL+3gCAo9Q/bPrQvlQol2tQgKD60+INQ6Ium65k7M5lpurHeZe58853vnnvuuWfvBei5qliWkRQBFpquLRcy4nOHj4g9K5CEh6AXBqFXUR0rXalMAjZPC3e1W99Dwntf2dXd/p+tt0YdFSBxH2Kz5qgLiI8B8KdVy3YBevqRHz/qWh72Yui3MUDEL3q44WPXw3M+fo1pZuQs4tOIBVVTaoiXEI/MxfhGDPsxsNZfoE1q66ro5aJim3XdoLFw72H+n23BaIXzbcOnz5mfPoTvYVz7KzUl5+FRxEuqkp9G/Ajia [...]
+<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAqAAAAKgCAYAAABEPM/FAAAEDWlDQ1BJQ0MgUHJvZmlsZQAAOI2NVV1oHFUUPrtzZyMkzlNsNIV0qD8NJQ2TVjShtLp/3d02bpZJNtoi6GT27s6Yyc44M7v9oU9FUHwx6psUxL+3gCAo9Q/bPrQvlQol2tQgKD60+INQ6Ium65k7M5lpurHeZe58853vnnvuuWfvBei5qliWkRQBFpquLRcy4nOHj4g9K5CEh6AXBqFXUR0rXalMAjZPC3e1W99Dwntf2dXd/p+tt0YdFSBxH2Kz5qgLiI8B8KdVy3YBevqRHz/qWh72Yui3MUDEL3q44WPXw3M+fo1pZuQs4tOIBVVTaoiXEI/MxfhGDPsxsNZfoE1q66ro5aJim3XdoLFw72H+n23BaIXzbcOnz5mfPoTvYVz7KzUl5+FRxEuqkp9G/Ajia [...]
<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">base +<span class="st"> </span><span class="kw">theme</span>(<span class="dt">text =</span> <span class="kw">element_text</span>(<span class="dt">colour =</span> <span class="st">"red"</span>))</code></pre></div>
-<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASAAAAEgCAYAAAAUg66AAAAEDWlDQ1BJQ0MgUHJvZmlsZQAAOI2NVV1oHFUUPrtzZyMkzlNsNIV0qD8NJQ2TVjShtLp/3d02bpZJNtoi6GT27s6Yyc44M7v9oU9FUHwx6psUxL+3gCAo9Q/bPrQvlQol2tQgKD60+INQ6Ium65k7M5lpurHeZe58853vnnvuuWfvBei5qliWkRQBFpquLRcy4nOHj4g9K5CEh6AXBqFXUR0rXalMAjZPC3e1W99Dwntf2dXd/p+tt0YdFSBxH2Kz5qgLiI8B8KdVy3YBevqRHz/qWh72Yui3MUDEL3q44WPXw3M+fo1pZuQs4tOIBVVTaoiXEI/MxfhGDPsxsNZfoE1q66ro5aJim3XdoLFw72H+n23BaIXzbcOnz5mfPoTvYVz7KzUl5+FRxEuqkp9G/Ajia [...]
+<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAqAAAAKgCAYAAABEPM/FAAAEDWlDQ1BJQ0MgUHJvZmlsZQAAOI2NVV1oHFUUPrtzZyMkzlNsNIV0qD8NJQ2TVjShtLp/3d02bpZJNtoi6GT27s6Yyc44M7v9oU9FUHwx6psUxL+3gCAo9Q/bPrQvlQol2tQgKD60+INQ6Ium65k7M5lpurHeZe58853vnnvuuWfvBei5qliWkRQBFpquLRcy4nOHj4g9K5CEh6AXBqFXUR0rXalMAjZPC3e1W99Dwntf2dXd/p+tt0YdFSBxH2Kz5qgLiI8B8KdVy3YBevqRHz/qWh72Yui3MUDEL3q44WPXw3M+fo1pZuQs4tOIBVVTaoiXEI/MxfhGDPsxsNZfoE1q66ro5aJim3XdoLFw72H+n23BaIXzbcOnz5mfPoTvYVz7KzUl5+FRxEuqkp9G/Ajia [...]
<p>You should generally start creating a theme by modifying these values.</p>
</div>
<div id="complete-vs-incomplete" class="section level3">
@@ -561,6 +555,399 @@ base</code></pre></div>
</ul>
</div>
</div>
+<div id="creating-a-new-facetting" class="section level2">
+<h2>Creating a new facetting</h2>
+<p>One of the more daunting exercises in ggplot2 extensions is to create a new facetting system. The reason for this is that when creating new facettings you take on the responsibility of how (almost) everything is drawn on the screen, and many do not have experience with directly using <a href="https://cran.r-project.org/package=gtable">gtable</a> and <a href="https://cran.r-project.org/package=grid">grid</a> upon which the ggplot2 rendering is build. If you decide to venture into facet [...]
+<p>The <code>Facet</code> class in ggplot2 is very powerfull as it takes on responsibility of a wide range of tasks. The main tasks of a <code>Facet</code> object are:</p>
+<ul>
+<li><p>Define a layout; that is, a partitioning of the data into different plot areas (panels) as well as which panels share position scales.</p></li>
+<li><p>Map plot data into the correct panels, potentially duplicating data if it should exist in multiple panels (e.g. margins in <code>facet_grid()</code>).</p></li>
+<li><p>Assemble all panels into a final gtable, adding axes, strips and decorations in the process.</p></li>
+</ul>
+<p>Apart from these three tasks, for which functionality must be implemented, there are a couple of additional extension points where sensible defaults have been provided. These can generally be ignored, but adventurous developers can override them for even more control:</p>
+<ul>
+<li><p>Initialization and training of positional scales for each panel.</p></li>
+<li><p>Decoration in front of and behind each panel.</p></li>
+<li><p>Drawing of axis labels</p></li>
+</ul>
+<p>To show how a new facetting class is created we will start simple and go through each of the required methods in turn to build up <code>facet_duplicate()</code> that simply duplicate our plot into two panels. After this we will tinker with it a bit to show some of the more powerful possibilities.</p>
+<div id="creating-a-layout-specification" class="section level3">
+<h3>Creating a layout specification</h3>
+<p>A layout in the context of facets is a <code>data.frame</code> that defines a mapping between data and the panels it should reside in as well as which positional scales should be used. The output should at least contain the columns <code>PANEL</code>, <code>SCALE_X</code>, and <code>SCALE_Y</code>, but will often contain more to help assign data to the correct panel (<code>facet_grid()</code> will e.g. also return the facetting variables associated with each panel). Let’s make a funct [...]
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">layout <-<span class="st"> </span>function(data, params) {
+ <span class="kw">data.frame</span>(<span class="dt">PANEL =</span> <span class="kw">c</span>(1L, 2L), <span class="dt">SCALE_X =</span> 1L, <span class="dt">SCALE_Y =</span> 1L)
+}</code></pre></div>
+<p>This is quite simple as the facetting should just define two panels irrespectively of the input data and parameters.</p>
+</div>
+<div id="mapping-data-into-panels" class="section level3">
+<h3>Mapping data into panels</h3>
+<p>In order for ggplot2 to know which data should go where it needs the data to be assigned to a panel. The purpose of the mapping step is to assign a <code>PANEL</code> column to the layer data identifying which panel it belongs to.</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">mapping <-<span class="st"> </span>function(data, layout, params) {
+ if (plyr::<span class="kw">empty</span>(data)) {
+ <span class="kw">return</span>(<span class="kw">cbind</span>(data, <span class="dt">PANEL =</span> <span class="kw">integer</span>(<span class="dv">0</span>)))
+ }
+ <span class="kw">rbind</span>(
+ <span class="kw">cbind</span>(data, <span class="dt">PANEL =</span> 1L),
+ <span class="kw">cbind</span>(data, <span class="dt">PANEL =</span> 2L)
+ )
+}</code></pre></div>
+<p>here we first investigate whether we have gotten an empty <code>data.frame</code> and if not we duplicate the data and assign the original data to the first panel and the new data to the second panel.</p>
+</div>
+<div id="laying-out-the-panels" class="section level3">
+<h3>Laying out the panels</h3>
+<p>While the two functions above has been decievingly simple, this last one is going to take some more work. Our goal is to draw two panels beside (or above) each other with axes etc.</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">render <-<span class="st"> </span>function(panels, layout, x_scales, y_scales, ranges, coord, data,
+ theme, params) {
+ <span class="co"># Place panels according to settings</span>
+ if (params$horizontal) {
+ <span class="co"># Put panels in matrix and convert to a gtable</span>
+ panels <-<span class="st"> </span><span class="kw">matrix</span>(panels, <span class="dt">ncol =</span> <span class="dv">2</span>)
+ panel_table <-<span class="st"> </span>gtable::<span class="kw">gtable_matrix</span>(<span class="st">"layout"</span>, panels,
+ <span class="dt">widths =</span> <span class="kw">unit</span>(<span class="kw">c</span>(<span class="dv">1</span>, <span class="dv">1</span>), <span class="st">"null"</span>), <span class="dt">heights =</span> <span class="kw">unit</span>(<span class="dv">1</span>, <span class="st">"null"</span>), <span class="dt">clip =</span> <span class="st">"on"</span>)
+ <span class="co"># Add spacing according to theme</span>
+ panel_spacing <-<span class="st"> </span>if (<span class="kw">is.null</span>(theme$panel.spacing.x)) {
+ theme$panel.spacing
+ } else {
+ theme$panel.spacing.x
+ }
+ panel_table <-<span class="st"> </span>gtable::<span class="kw">gtable_add_col_space</span>(panel_table, panel_spacing)
+ } else {
+ panels <-<span class="st"> </span><span class="kw">matrix</span>(panels, <span class="dt">ncol =</span> <span class="dv">1</span>)
+ panel_table <-<span class="st"> </span>gtable::<span class="kw">gtable_matrix</span>(<span class="st">"layout"</span>, panels,
+ <span class="dt">widths =</span> <span class="kw">unit</span>(<span class="dv">1</span>, <span class="st">"null"</span>), <span class="dt">heights =</span> <span class="kw">unit</span>(<span class="kw">c</span>(<span class="dv">1</span>, <span class="dv">1</span>), <span class="st">"null"</span>), <span class="dt">clip =</span> <span class="st">"on"</span>)
+ panel_spacing <-<span class="st"> </span>if (<span class="kw">is.null</span>(theme$panel.spacing.y)) {
+ theme$panel.spacing
+ } else {
+ theme$panel.spacing.y
+ }
+ panel_table <-<span class="st"> </span>gtable::<span class="kw">gtable_add_row_space</span>(panel_table, panel_spacing)
+ }
+ <span class="co"># Name panel grobs so they can be found later</span>
+ panel_table$layout$name <-<span class="st"> </span><span class="kw">paste0</span>(<span class="st">"panel-"</span>, <span class="kw">c</span>(<span class="dv">1</span>, <span class="dv">2</span>))
+
+ <span class="co"># Construct the axes</span>
+ axes <-<span class="st"> </span><span class="kw">render_axes</span>(ranges[<span class="dv">1</span>], ranges[<span class="dv">1</span>], coord, theme,
+ <span class="dt">transpose =</span> <span class="ot">TRUE</span>)
+
+ <span class="co"># Add axes around each panel</span>
+ panel_pos_h <-<span class="st"> </span><span class="kw">panel_cols</span>(panel_table)$l
+ panel_pos_v <-<span class="st"> </span><span class="kw">panel_rows</span>(panel_table)$t
+ axis_width_l <-<span class="st"> </span><span class="kw">unit</span>(grid::<span class="kw">convertWidth</span>(
+ grid::<span class="kw">grobWidth</span>(axes$y$left[[<span class="dv">1</span>]]), <span class="st">"cm"</span>, <span class="ot">TRUE</span>), <span class="st">"cm"</span>)
+ axis_width_r <-<span class="st"> </span><span class="kw">unit</span>(grid::<span class="kw">convertWidth</span>(
+ grid::<span class="kw">grobWidth</span>(axes$y$right[[<span class="dv">1</span>]]), <span class="st">"cm"</span>, <span class="ot">TRUE</span>), <span class="st">"cm"</span>)
+ ## We do it reverse so we don't change the position of panels when we add axes
+ for (i in <span class="kw">rev</span>(panel_pos_h)) {
+ panel_table <-<span class="st"> </span>gtable::<span class="kw">gtable_add_cols</span>(panel_table, axis_width_r, i)
+ panel_table <-<span class="st"> </span>gtable::<span class="kw">gtable_add_grob</span>(panel_table,
+ <span class="kw">rep</span>(axes$y$right, <span class="kw">length</span>(panel_pos_v)), <span class="dt">t =</span> panel_pos_v, <span class="dt">l =</span> i +<span class="st"> </span><span class="dv">1</span>,
+ <span class="dt">clip =</span> <span class="st">"off"</span>)
+ panel_table <-<span class="st"> </span>gtable::<span class="kw">gtable_add_cols</span>(panel_table, axis_width_l, i -<span class="st"> </span><span class="dv">1</span>)
+ panel_table <-<span class="st"> </span>gtable::<span class="kw">gtable_add_grob</span>(panel_table,
+ <span class="kw">rep</span>(axes$y$left, <span class="kw">length</span>(panel_pos_v)), <span class="dt">t =</span> panel_pos_v, <span class="dt">l =</span> i,
+ <span class="dt">clip =</span> <span class="st">"off"</span>)
+ }
+ ## Recalculate as gtable has changed
+ panel_pos_h <-<span class="st"> </span><span class="kw">panel_cols</span>(panel_table)$l
+ panel_pos_v <-<span class="st"> </span><span class="kw">panel_rows</span>(panel_table)$t
+ axis_height_t <-<span class="st"> </span><span class="kw">unit</span>(grid::<span class="kw">convertHeight</span>(
+ grid::<span class="kw">grobHeight</span>(axes$x$top[[<span class="dv">1</span>]]), <span class="st">"cm"</span>, <span class="ot">TRUE</span>), <span class="st">"cm"</span>)
+ axis_height_b <-<span class="st"> </span><span class="kw">unit</span>(grid::<span class="kw">convertHeight</span>(
+ grid::<span class="kw">grobHeight</span>(axes$x$bottom[[<span class="dv">1</span>]]), <span class="st">"cm"</span>, <span class="ot">TRUE</span>), <span class="st">"cm"</span>)
+ for (i in <span class="kw">rev</span>(panel_pos_v)) {
+ panel_table <-<span class="st"> </span>gtable::<span class="kw">gtable_add_rows</span>(panel_table, axis_height_b, i)
+ panel_table <-<span class="st"> </span>gtable::<span class="kw">gtable_add_grob</span>(panel_table,
+ <span class="kw">rep</span>(axes$x$bottom, <span class="kw">length</span>(panel_pos_h)), <span class="dt">t =</span> i +<span class="st"> </span><span class="dv">1</span>, <span class="dt">l =</span> panel_pos_h,
+ <span class="dt">clip =</span> <span class="st">"off"</span>)
+ panel_table <-<span class="st"> </span>gtable::<span class="kw">gtable_add_rows</span>(panel_table, axis_height_t, i -<span class="st"> </span><span class="dv">1</span>)
+ panel_table <-<span class="st"> </span>gtable::<span class="kw">gtable_add_grob</span>(panel_table,
+ <span class="kw">rep</span>(axes$x$top, <span class="kw">length</span>(panel_pos_h)), <span class="dt">t =</span> i, <span class="dt">l =</span> panel_pos_h,
+ <span class="dt">clip =</span> <span class="st">"off"</span>)
+ }
+ panel_table
+}</code></pre></div>
+</div>
+<div id="assembling-the-facet-class" class="section level3">
+<h3>Assembling the Facet class</h3>
+<p>Usually all methods are defined within the class definition in the same way as is done for <code>Geom</code> and <code>Stat</code>. Here we have split it out so we could go through each in turn. All that remains is to assign our functions to the correct methods as well as making a constructor</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r"><span class="co"># Constructor: shrink is required to govern whether scales are trained on </span>
+<span class="co"># Stat-transformed data or not.</span>
+facet_duplicate <-<span class="st"> </span>function(<span class="dt">horizontal =</span> <span class="ot">TRUE</span>, <span class="dt">shrink =</span> <span class="ot">TRUE</span>) {
+ <span class="kw">ggproto</span>(<span class="ot">NULL</span>, FacetDuplicate,
+ <span class="dt">shrink =</span> shrink,
+ <span class="dt">params =</span> <span class="kw">list</span>(
+ <span class="dt">horizontal =</span> horizontal
+ )
+ )
+}
+
+FacetDuplicate <-<span class="st"> </span><span class="kw">ggproto</span>(<span class="st">"FacetDuplicate"</span>, Facet,
+ <span class="dt">compute_layout =</span> layout,
+ <span class="dt">map_data =</span> mapping,
+ <span class="dt">draw_panels =</span> render
+)</code></pre></div>
+<p>Now with everything assembled, lets test it out:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">p <-<span class="st"> </span><span class="kw">ggplot</span>(mtcars, <span class="kw">aes</span>(<span class="dt">x =</span> hp, <span class="dt">y =</span> mpg)) +<span class="st"> </span><span class="kw">geom_point</span>()
+p</code></pre></div>
+<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAqAAAAKgCAYAAABEPM/FAAAEDWlDQ1BJQ0MgUHJvZmlsZQAAOI2NVV1oHFUUPrtzZyMkzlNsNIV0qD8NJQ2TVjShtLp/3d02bpZJNtoi6GT27s6Yyc44M7v9oU9FUHwx6psUxL+3gCAo9Q/bPrQvlQol2tQgKD60+INQ6Ium65k7M5lpurHeZe58853vnnvuuWfvBei5qliWkRQBFpquLRcy4nOHj4g9K5CEh6AXBqFXUR0rXalMAjZPC3e1W99Dwntf2dXd/p+tt0YdFSBxH2Kz5qgLiI8B8KdVy3YBevqRHz/qWh72Yui3MUDEL3q44WPXw3M+fo1pZuQs4tOIBVVTaoiXEI/MxfhGDPsxsNZfoE1q66ro5aJim3XdoLFw72H+n23BaIXzbcOnz5mfPoTvYVz7KzUl5+FRxEuqkp9G/Ajia [...]
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">p +<span class="st"> </span><span class="kw">facet_duplicate</span>()</code></pre></div>
+<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAqAAAAKgCAYAAABEPM/FAAAEDWlDQ1BJQ0MgUHJvZmlsZQAAOI2NVV1oHFUUPrtzZyMkzlNsNIV0qD8NJQ2TVjShtLp/3d02bpZJNtoi6GT27s6Yyc44M7v9oU9FUHwx6psUxL+3gCAo9Q/bPrQvlQol2tQgKD60+INQ6Ium65k7M5lpurHeZe58853vnnvuuWfvBei5qliWkRQBFpquLRcy4nOHj4g9K5CEh6AXBqFXUR0rXalMAjZPC3e1W99Dwntf2dXd/p+tt0YdFSBxH2Kz5qgLiI8B8KdVy3YBevqRHz/qWh72Yui3MUDEL3q44WPXw3M+fo1pZuQs4tOIBVVTaoiXEI/MxfhGDPsxsNZfoE1q66ro5aJim3XdoLFw72H+n23BaIXzbcOnz5mfPoTvYVz7KzUl5+FRxEuqkp9G/Ajia [...]
+</div>
+<div id="doing-more-with-facets" class="section level3">
+<h3>Doing more with facets</h3>
+<p>The example above was pretty useless and we’ll now try to expand on it to add some actual usability. We are going to make a facetting that adds panels with y-transformed axes:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r"><span class="kw">library</span>(scales)
+
+facet_trans <-<span class="st"> </span>function(trans, <span class="dt">horizontal =</span> <span class="ot">TRUE</span>, <span class="dt">shrink =</span> <span class="ot">TRUE</span>) {
+ <span class="kw">ggproto</span>(<span class="ot">NULL</span>, FacetTrans,
+ <span class="dt">shrink =</span> shrink,
+ <span class="dt">params =</span> <span class="kw">list</span>(
+ <span class="dt">trans =</span> scales::<span class="kw">as.trans</span>(trans),
+ <span class="dt">horizontal =</span> horizontal
+ )
+ )
+}
+
+FacetTrans <-<span class="st"> </span><span class="kw">ggproto</span>(<span class="st">"FacetTrans"</span>, Facet,
+ <span class="co"># Almost as before but we want different y-scales for each panel</span>
+ <span class="dt">compute_layout =</span> function(data, params) {
+ <span class="kw">data.frame</span>(<span class="dt">PANEL =</span> <span class="kw">c</span>(1L, 2L), <span class="dt">SCALE_X =</span> 1L, <span class="dt">SCALE_Y =</span> <span class="kw">c</span>(1L, 2L))
+ },
+ <span class="co"># Same as before</span>
+ <span class="dt">map_data =</span> function(data, layout, params) {
+ if (plyr::<span class="kw">empty</span>(data)) {
+ <span class="kw">return</span>(<span class="kw">cbind</span>(data, <span class="dt">PANEL =</span> <span class="kw">integer</span>(<span class="dv">0</span>)))
+ }
+ <span class="kw">rbind</span>(
+ <span class="kw">cbind</span>(data, <span class="dt">PANEL =</span> 1L),
+ <span class="kw">cbind</span>(data, <span class="dt">PANEL =</span> 2L)
+ )
+ },
+ <span class="co"># This is new. We create a new scale with the defined transformation</span>
+ <span class="dt">init_scales =</span> function(layout, <span class="dt">x_scale =</span> <span class="ot">NULL</span>, <span class="dt">y_scale =</span> <span class="ot">NULL</span>, params) {
+ scales <-<span class="st"> </span><span class="kw">list</span>()
+ if (!<span class="kw">is.null</span>(x_scale)) {
+ scales$x <-<span class="st"> </span>plyr::<span class="kw">rlply</span>(<span class="kw">max</span>(layout$SCALE_X), x_scale$<span class="kw">clone</span>())
+ }
+ if (!<span class="kw">is.null</span>(y_scale)) {
+ y_scale_orig <-<span class="st"> </span>y_scale$<span class="kw">clone</span>()
+ y_scale_new <-<span class="st"> </span>y_scale$<span class="kw">clone</span>()
+ y_scale_new$trans <-<span class="st"> </span>params$trans
+ <span class="co"># Make sure that oob values are kept</span>
+ y_scale_new$oob <-<span class="st"> </span>function(x, ...) x
+ scales$y <-<span class="st"> </span><span class="kw">list</span>(y_scale_orig, y_scale_new)
+ }
+ scales
+ },
+ <span class="co"># We must make sure that the second scale is trained on transformed data</span>
+ <span class="dt">train_scales =</span> function(x_scales, y_scales, layout, data, params) {
+ <span class="co"># Transform data for second panel prior to scale training</span>
+ if (!<span class="kw">is.null</span>(y_scales)) {
+ data <-<span class="st"> </span><span class="kw">lapply</span>(data, function(layer_data) {
+ match_id <-<span class="st"> </span><span class="kw">match</span>(layer_data$PANEL, layout$PANEL)
+ y_vars <-<span class="st"> </span><span class="kw">intersect</span>(y_scales[[<span class="dv">1</span>]]$aesthetics, <span class="kw">names</span>(layer_data))
+ trans_scale <-<span class="st"> </span>layer_data$PANEL ==<span class="st"> </span>2L
+ for (i in y_vars) {
+ layer_data[trans_scale, i] <-<span class="st"> </span>y_scales[[<span class="dv">2</span>]]$<span class="kw">transform</span>(layer_data[trans_scale, i])
+ }
+ layer_data
+ })
+ }
+ Facet$<span class="kw">train_scales</span>(x_scales, y_scales, layout, data, params)
+ },
+ <span class="co"># this is where we actually modify the data. It cannot be done in $map_data as that function</span>
+ <span class="co"># doesn't have access to the scales</span>
+ <span class="dt">finish_data =</span> function(data, layout, x_scales, y_scales, params) {
+ match_id <-<span class="st"> </span><span class="kw">match</span>(data$PANEL, layout$PANEL)
+ y_vars <-<span class="st"> </span><span class="kw">intersect</span>(y_scales[[<span class="dv">1</span>]]$aesthetics, <span class="kw">names</span>(data))
+ trans_scale <-<span class="st"> </span>data$PANEL ==<span class="st"> </span>2L
+ for (i in y_vars) {
+ data[trans_scale, i] <-<span class="st"> </span>y_scales[[<span class="dv">2</span>]]$<span class="kw">transform</span>(data[trans_scale, i])
+ }
+ data
+ },
+ <span class="co"># A few changes from before to accomodate that axes are now not duplicate of each other</span>
+ <span class="co"># We also add a panel strip to annotate the different panels</span>
+ <span class="dt">draw_panels =</span> function(panels, layout, x_scales, y_scales, ranges, coord,
+ data, theme, params) {
+ <span class="co"># Place panels according to settings</span>
+ if (params$horizontal) {
+ <span class="co"># Put panels in matrix and convert to a gtable</span>
+ panels <-<span class="st"> </span><span class="kw">matrix</span>(panels, <span class="dt">ncol =</span> <span class="dv">2</span>)
+ panel_table <-<span class="st"> </span>gtable::<span class="kw">gtable_matrix</span>(<span class="st">"layout"</span>, panels,
+ <span class="dt">widths =</span> <span class="kw">unit</span>(<span class="kw">c</span>(<span class="dv">1</span>, <span class="dv">1</span>), <span class="st">"null"</span>), <span class="dt">heights =</span> <span class="kw">unit</span>(<span class="dv">1</span>, <span class="st">"null"</span>), <span class="dt">clip =</span> <span class="st">"on"</span>)
+ <span class="co"># Add spacing according to theme</span>
+ panel_spacing <-<span class="st"> </span>if (<span class="kw">is.null</span>(theme$panel.spacing.x)) {
+ theme$panel.spacing
+ } else {
+ theme$panel.spacing.x
+ }
+ panel_table <-<span class="st"> </span>gtable::<span class="kw">gtable_add_col_space</span>(panel_table, panel_spacing)
+ } else {
+ panels <-<span class="st"> </span><span class="kw">matrix</span>(panels, <span class="dt">ncol =</span> <span class="dv">1</span>)
+ panel_table <-<span class="st"> </span>gtable::<span class="kw">gtable_matrix</span>(<span class="st">"layout"</span>, panels,
+ <span class="dt">widths =</span> <span class="kw">unit</span>(<span class="dv">1</span>, <span class="st">"null"</span>), <span class="dt">heights =</span> <span class="kw">unit</span>(<span class="kw">c</span>(<span class="dv">1</span>, <span class="dv">1</span>), <span class="st">"null"</span>), <span class="dt">clip =</span> <span class="st">"on"</span>)
+ panel_spacing <-<span class="st"> </span>if (<span class="kw">is.null</span>(theme$panel.spacing.y)) {
+ theme$panel.spacing
+ } else {
+ theme$panel.spacing.y
+ }
+ panel_table <-<span class="st"> </span>gtable::<span class="kw">gtable_add_row_space</span>(panel_table, panel_spacing)
+ }
+ <span class="co"># Name panel grobs so they can be found later</span>
+ panel_table$layout$name <-<span class="st"> </span><span class="kw">paste0</span>(<span class="st">"panel-"</span>, <span class="kw">c</span>(<span class="dv">1</span>, <span class="dv">2</span>))
+
+ <span class="co"># Construct the axes</span>
+ axes <-<span class="st"> </span><span class="kw">render_axes</span>(ranges[<span class="dv">1</span>], ranges, coord, theme,
+ <span class="dt">transpose =</span> <span class="ot">TRUE</span>)
+
+ <span class="co"># Add axes around each panel</span>
+ grobWidths <-<span class="st"> </span>function(x) {
+ <span class="kw">unit</span>(<span class="kw">vapply</span>(x, function(x) {
+ grid::<span class="kw">convertWidth</span>(
+ grid::<span class="kw">grobWidth</span>(x), <span class="st">"cm"</span>, <span class="ot">TRUE</span>)
+ }, <span class="kw">numeric</span>(<span class="dv">1</span>)), <span class="st">"cm"</span>)
+ }
+ panel_pos_h <-<span class="st"> </span><span class="kw">panel_cols</span>(panel_table)$l
+ panel_pos_v <-<span class="st"> </span><span class="kw">panel_rows</span>(panel_table)$t
+ axis_width_l <-<span class="st"> </span><span class="kw">grobWidths</span>(axes$y$left)
+ axis_width_r <-<span class="st"> </span><span class="kw">grobWidths</span>(axes$y$right)
+ ## We do it reverse so we don't change the position of panels when we add axes
+ for (i in <span class="kw">rev</span>(<span class="kw">seq_along</span>(panel_pos_h))) {
+ panel_table <-<span class="st"> </span>gtable::<span class="kw">gtable_add_cols</span>(panel_table, axis_width_r[i], panel_pos_h[i])
+ if (params$horizontal) {
+ panel_table <-<span class="st"> </span>gtable::<span class="kw">gtable_add_grob</span>(panel_table,
+ <span class="kw">rep</span>(axes$y$right[i], <span class="kw">length</span>(panel_pos_v)), <span class="dt">t =</span> panel_pos_v, <span class="dt">l =</span> panel_pos_h[i] +<span class="st"> </span><span class="dv">1</span>,
+ <span class="dt">clip =</span> <span class="st">"off"</span>)
+ } else {
+ panel_table <-<span class="st"> </span>gtable::<span class="kw">gtable_add_grob</span>(panel_table,
+ <span class="kw">rep</span>(axes$y$right, <span class="kw">length</span>(panel_pos_v)), <span class="dt">t =</span> panel_pos_v, <span class="dt">l =</span> panel_pos_h[i] +<span class="st"> </span><span class="dv">1</span>,
+ <span class="dt">clip =</span> <span class="st">"off"</span>)
+ }
+ panel_table <-<span class="st"> </span>gtable::<span class="kw">gtable_add_cols</span>(panel_table, axis_width_l[i], panel_pos_h[i] -<span class="st"> </span><span class="dv">1</span>)
+ if (params$horizontal) {
+ panel_table <-<span class="st"> </span>gtable::<span class="kw">gtable_add_grob</span>(panel_table,
+ <span class="kw">rep</span>(axes$y$left[i], <span class="kw">length</span>(panel_pos_v)), <span class="dt">t =</span> panel_pos_v, <span class="dt">l =</span> panel_pos_h[i],
+ <span class="dt">clip =</span> <span class="st">"off"</span>)
+ } else {
+ panel_table <-<span class="st"> </span>gtable::<span class="kw">gtable_add_grob</span>(panel_table,
+ <span class="kw">rep</span>(axes$y$left, <span class="kw">length</span>(panel_pos_v)), <span class="dt">t =</span> panel_pos_v, <span class="dt">l =</span> panel_pos_h[i],
+ <span class="dt">clip =</span> <span class="st">"off"</span>)
+ }
+ }
+ ## Recalculate as gtable has changed
+ panel_pos_h <-<span class="st"> </span><span class="kw">panel_cols</span>(panel_table)$l
+ panel_pos_v <-<span class="st"> </span><span class="kw">panel_rows</span>(panel_table)$t
+ axis_height_t <-<span class="st"> </span><span class="kw">unit</span>(grid::<span class="kw">convertHeight</span>(
+ grid::<span class="kw">grobHeight</span>(axes$x$top[[<span class="dv">1</span>]]), <span class="st">"cm"</span>, <span class="ot">TRUE</span>), <span class="st">"cm"</span>)
+ axis_height_b <-<span class="st"> </span><span class="kw">unit</span>(grid::<span class="kw">convertHeight</span>(
+ grid::<span class="kw">grobHeight</span>(axes$x$bottom[[<span class="dv">1</span>]]), <span class="st">"cm"</span>, <span class="ot">TRUE</span>), <span class="st">"cm"</span>)
+ for (i in <span class="kw">rev</span>(panel_pos_v)) {
+ panel_table <-<span class="st"> </span>gtable::<span class="kw">gtable_add_rows</span>(panel_table, axis_height_b, i)
+ panel_table <-<span class="st"> </span>gtable::<span class="kw">gtable_add_grob</span>(panel_table,
+ <span class="kw">rep</span>(axes$x$bottom, <span class="kw">length</span>(panel_pos_h)), <span class="dt">t =</span> i +<span class="st"> </span><span class="dv">1</span>, <span class="dt">l =</span> panel_pos_h,
+ <span class="dt">clip =</span> <span class="st">"off"</span>)
+ panel_table <-<span class="st"> </span>gtable::<span class="kw">gtable_add_rows</span>(panel_table, axis_height_t, i -<span class="st"> </span><span class="dv">1</span>)
+ panel_table <-<span class="st"> </span>gtable::<span class="kw">gtable_add_grob</span>(panel_table,
+ <span class="kw">rep</span>(axes$x$top, <span class="kw">length</span>(panel_pos_h)), <span class="dt">t =</span> i, <span class="dt">l =</span> panel_pos_h,
+ <span class="dt">clip =</span> <span class="st">"off"</span>)
+ }
+
+ <span class="co"># Add strips</span>
+ strips <-<span class="st"> </span><span class="kw">render_strips</span>(
+ <span class="dt">x =</span> <span class="kw">data.frame</span>(<span class="dt">name =</span> <span class="kw">c</span>(<span class="st">"Original"</span>, <span class="kw">paste0</span>(<span class="st">"Transformed ("</span>, params$trans$name, <span class="st">")"</span>))),
+ <span class="dt">labeller =</span> label_value, <span class="dt">theme =</span> theme)
+
+ panel_pos_h <-<span class="st"> </span><span class="kw">panel_cols</span>(panel_table)$l
+ panel_pos_v <-<span class="st"> </span><span class="kw">panel_rows</span>(panel_table)$t
+ strip_height <-<span class="st"> </span><span class="kw">unit</span>(grid::<span class="kw">convertHeight</span>(
+ grid::<span class="kw">grobHeight</span>(strips$x$top[[<span class="dv">1</span>]]), <span class="st">"cm"</span>, <span class="ot">TRUE</span>), <span class="st">"cm"</span>)
+ for (i in <span class="kw">rev</span>(<span class="kw">seq_along</span>(panel_pos_v))) {
+ panel_table <-<span class="st"> </span>gtable::<span class="kw">gtable_add_rows</span>(panel_table, strip_height, panel_pos_v[i] -<span class="st"> </span><span class="dv">1</span>)
+ if (params$horizontal) {
+ panel_table <-<span class="st"> </span>gtable::<span class="kw">gtable_add_grob</span>(panel_table, strips$x$top,
+ <span class="dt">t =</span> panel_pos_v[i], <span class="dt">l =</span> panel_pos_h, <span class="dt">clip =</span> <span class="st">"off"</span>)
+ } else {
+ panel_table <-<span class="st"> </span>gtable::<span class="kw">gtable_add_grob</span>(panel_table, strips$x$top[i],
+ <span class="dt">t =</span> panel_pos_v[i], <span class="dt">l =</span> panel_pos_h, <span class="dt">clip =</span> <span class="st">"off"</span>)
+ }
+ }
+
+
+ panel_table
+ }
+)</code></pre></div>
+<p>As is very apparent, the <code>draw_panel</code> method can become very unwieldy once it begins to take multiple possibilities into account. The fact that we want to support both horizontal and vertical layout leads to a lot of if/else blocks in the above code. In general this is the big challenge when writing facet extensions so be prepared to be very meticulous when writing these methods.</p>
+<p>Enough talk - lets see if our new and powerful facetting extension works:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r"><span class="kw">ggplot</span>(mtcars, <span class="kw">aes</span>(<span class="dt">x =</span> hp, <span class="dt">y =</span> mpg)) +<span class="st"> </span><span class="kw">geom_point</span>() +<span class="st"> </span><span class="kw">facet_trans</span>(<span class="st">'sqrt'</span>)</code></pre></div>
+<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAqAAAAKgCAYAAABEPM/FAAAEDWlDQ1BJQ0MgUHJvZmlsZQAAOI2NVV1oHFUUPrtzZyMkzlNsNIV0qD8NJQ2TVjShtLp/3d02bpZJNtoi6GT27s6Yyc44M7v9oU9FUHwx6psUxL+3gCAo9Q/bPrQvlQol2tQgKD60+INQ6Ium65k7M5lpurHeZe58853vnnvuuWfvBei5qliWkRQBFpquLRcy4nOHj4g9K5CEh6AXBqFXUR0rXalMAjZPC3e1W99Dwntf2dXd/p+tt0YdFSBxH2Kz5qgLiI8B8KdVy3YBevqRHz/qWh72Yui3MUDEL3q44WPXw3M+fo1pZuQs4tOIBVVTaoiXEI/MxfhGDPsxsNZfoE1q66ro5aJim3XdoLFw72H+n23BaIXzbcOnz5mfPoTvYVz7KzUl5+FRxEuqkp9G/Ajia [...]
+</div>
+</div>
+<div id="extending-existing-facet-function" class="section level2">
+<h2>Extending existing facet function</h2>
+<p>As the rendering part of a facet class is often the difficult development step, it is possible to piggyback on the existing facetting classes to achieve a range of new facettings. Below we will subclass <code>facet_wrap()</code> to make a <code>facet_bootstrap()</code> class that splits the input data into a number of panels at random.</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">facet_bootstrap <-<span class="st"> </span>function(<span class="dt">n =</span> <span class="dv">9</span>, <span class="dt">prop =</span> <span class="fl">0.2</span>, <span class="dt">nrow =</span> <span class="ot">NULL</span>, <span class="dt">ncol =</span> <span class="ot">NULL</span>,
+ <span class="dt">scales =</span> <span class="st">"fixed"</span>, <span class="dt">shrink =</span> <span class="ot">TRUE</span>, <span class="dt">strip.position =</span> <span class="st">"top"</span>) {
+
+ facet <-<span class="st"> </span><span class="kw">facet_wrap</span>(~.bootstrap, <span class="dt">nrow =</span> nrow, <span class="dt">ncol =</span> ncol, <span class="dt">scales =</span> scales,
+ <span class="dt">shrink =</span> shrink, <span class="dt">strip.position =</span> strip.position)
+ facet$params$n <-<span class="st"> </span>n
+ facet$params$prop <-<span class="st"> </span>prop
+ <span class="kw">ggproto</span>(<span class="ot">NULL</span>, FacetBootstrap,
+ <span class="dt">shrink =</span> shrink,
+ <span class="dt">params =</span> facet$params
+ )
+}
+
+FacetBootstrap <-<span class="st"> </span><span class="kw">ggproto</span>(<span class="st">"FacetBootstrap"</span>, FacetWrap,
+ <span class="dt">compute_layout =</span> function(data, params) {
+ id <-<span class="st"> </span><span class="kw">seq_len</span>(params$n)
+
+ dims <-<span class="st"> </span><span class="kw">wrap_dims</span>(params$n, params$nrow, params$ncol)
+ layout <-<span class="st"> </span><span class="kw">data.frame</span>(<span class="dt">PANEL =</span> <span class="kw">factor</span>(id))
+
+ if (params$as.table) {
+ layout$ROW <-<span class="st"> </span><span class="kw">as.integer</span>((id -<span class="st"> </span>1L) %/%<span class="st"> </span>dims[<span class="dv">2</span>] +<span class="st"> </span>1L)
+ } else {
+ layout$ROW <-<span class="st"> </span><span class="kw">as.integer</span>(dims[<span class="dv">1</span>] -<span class="st"> </span>(id -<span class="st"> </span>1L) %/%<span class="st"> </span>dims[<span class="dv">2</span>])
+ }
+ layout$COL <-<span class="st"> </span><span class="kw">as.integer</span>((id -<span class="st"> </span>1L) %%<span class="st"> </span>dims[<span class="dv">2</span>] +<span class="st"> </span>1L)
+
+ layout <-<span class="st"> </span>layout[<span class="kw">order</span>(layout$PANEL), , drop =<span class="st"> </span><span class="ot">FALSE</span>]
+ <span class="kw">rownames</span>(layout) <-<span class="st"> </span><span class="ot">NULL</span>
+
+ <span class="co"># Add scale identification</span>
+ layout$SCALE_X <-<span class="st"> </span>if (params$free$x) id else 1L
+ layout$SCALE_Y <-<span class="st"> </span>if (params$free$y) id else 1L
+
+ <span class="kw">cbind</span>(layout, <span class="dt">.bootstrap =</span> id)
+ },
+ <span class="dt">map_data =</span> function(data, layout, params) {
+ if (<span class="kw">is.null</span>(data) ||<span class="st"> </span><span class="kw">nrow</span>(data) ==<span class="st"> </span><span class="dv">0</span>) {
+ <span class="kw">return</span>(<span class="kw">cbind</span>(data, <span class="dt">PANEL =</span> <span class="kw">integer</span>(<span class="dv">0</span>)))
+ }
+ n_samples <-<span class="st"> </span><span class="kw">round</span>(<span class="kw">nrow</span>(data) *<span class="st"> </span>params$prop)
+ new_data <-<span class="st"> </span><span class="kw">lapply</span>(<span class="kw">seq_len</span>(params$n), function(i) {
+ <span class="kw">cbind</span>(data[<span class="kw">sample</span>(<span class="kw">nrow</span>(data), n_samples), , <span class="dt">drop =</span> <span class="ot">FALSE</span>], <span class="dt">PANEL =</span> i)
+ })
+ <span class="kw">do.call</span>(rbind, new_data)
+ }
+)
+
+<span class="kw">ggplot</span>(diamonds, <span class="kw">aes</span>(carat, price)) +<span class="st"> </span>
+<span class="st"> </span><span class="kw">geom_point</span>(<span class="dt">alpha =</span> <span class="fl">0.1</span>) +<span class="st"> </span>
+<span class="st"> </span><span class="kw">facet_bootstrap</span>(<span class="dt">n =</span> <span class="dv">9</span>, <span class="dt">prop =</span> <span class="fl">0.05</span>)</code></pre></div>
+<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAqAAAAKgCAYAAABEPM/FAAAEDWlDQ1BJQ0MgUHJvZmlsZQAAOI2NVV1oHFUUPrtzZyMkzlNsNIV0qD8NJQ2TVjShtLp/3d02bpZJNtoi6GT27s6Yyc44M7v9oU9FUHwx6psUxL+3gCAo9Q/bPrQvlQol2tQgKD60+INQ6Ium65k7M5lpurHeZe58853vnnvuuWfvBei5qliWkRQBFpquLRcy4nOHj4g9K5CEh6AXBqFXUR0rXalMAjZPC3e1W99Dwntf2dXd/p+tt0YdFSBxH2Kz5qgLiI8B8KdVy3YBevqRHz/qWh72Yui3MUDEL3q44WPXw3M+fo1pZuQs4tOIBVVTaoiXEI/MxfhGDPsxsNZfoE1q66ro5aJim3XdoLFw72H+n23BaIXzbcOnz5mfPoTvYVz7KzUl5+FRxEuqkp9G/Ajia [...]
+<p>What we are doing above is to intercept the <code>compute_layout</code> and <code>map_data</code> methods and instead of dividing the data by a variable we randomly assigns rows to a panel based on the sampling parameters (<code>n</code> determines the number of panels, <code>prop</code> determines the proportion of data in each panel). It is important here that the layout returned by <code>compute_layout</code> is a valid layout for <code>FacetWrap</code> as we are counting on the <c [...]
+<div id="exercises-2" class="section level3">
+<h3>Exercises</h3>
+<ol style="list-style-type: decimal">
+<li>Rewrite FacetTrans to take a vector of transformations and create an additional panel for each transformation.</li>
+<li>Based on the FacetWrap implementation rewrite FacetTrans to take the strip.placement theme setting into account.</li>
+<li>Think about which caveats there are in FacetBootstrap specifically related to adding multiple layers with the same data.</li>
+</ol>
+</div>
+</div>
diff --git a/inst/doc/ggplot2-specs.Rmd b/inst/doc/ggplot2-specs.Rmd
index 4a78e6b..03f121f 100644
--- a/inst/doc/ggplot2-specs.Rmd
+++ b/inst/doc/ggplot2-specs.Rmd
@@ -1,7 +1,5 @@
---
title: "Aesthetic specifications"
-author: "Hadley Wickham"
-date: "`r Sys.Date()`"
output: rmarkdown::html_vignette
vignette: >
%\VignetteIndexEntry{Aesthetic specifications}
@@ -67,7 +65,7 @@ Line types can be specified with:
three off followed by one on and finally three off.
The five standard dash-dot line types described above correspond to 44, 13,
- 134, 73, and 2262.
+ 1343, 73, and 2262.
The `size` of a line is its width in mm.
diff --git a/inst/doc/ggplot2-specs.html b/inst/doc/ggplot2-specs.html
index eea923f..e07b1ab 100644
--- a/inst/doc/ggplot2-specs.html
+++ b/inst/doc/ggplot2-specs.html
@@ -7,11 +7,10 @@
<meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="pandoc" />
+
<meta name="viewport" content="width=device-width, initial-scale=1">
-<meta name="author" content="Hadley Wickham" />
-<meta name="date" content="2016-02-29" />
<title>Aesthetic specifications</title>
@@ -57,6 +56,7 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
</style>
+
<link href="data:text/css;charset=utf-8,body%20%7B%0Abackground%2Dcolor%3A%20%23fff%3B%0Amargin%3A%201em%20auto%3B%0Amax%2Dwidth%3A%20700px%3B%0Aoverflow%3A%20visible%3B%0Apadding%2Dleft%3A%202em%3B%0Apadding%2Dright%3A%202em%3B%0Afont%2Dfamily%3A%20%22Open%20Sans%22%2C%20%22Helvetica%20Neue%22%2C%20Helvetica%2C%20Arial%2C%20sans%2Dserif%3B%0Afont%2Dsize%3A%2014px%3B%0Aline%2Dheight%3A%201%2E35%3B%0A%7D%0A%23header%20%7B%0Atext%2Dalign%3A%20center%3B%0A%7D%0A%23TOC%20%7B%0Aclear%3A%20bot [...]
</head>
@@ -65,14 +65,9 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
-<div class="fluid-row" id="header">
+<h1 class="title toc-ignore">Aesthetic specifications</h1>
-<h1 class="title">Aesthetic specifications</h1>
-<h4 class="author"><em>Hadley Wickham</em></h4>
-<h4 class="date"><em>2016-02-29</em></h4>
-
-</div>
<p>This vignette summarises the various formats that grid drawing functions take. Most of this information is available scattered throughout the R documentation. This appendix brings it all together in one place.</p>
@@ -104,9 +99,9 @@ linetypes <-<span class="st"> </span><span class="kw">data.frame</span>(
<span class="st"> </span><span class="kw">geom_text</span>(<span class="kw">aes</span>(<span class="dt">label =</span> lty), <span class="dt">hjust =</span> <span class="dv">0</span>, <span class="dt">nudge_y =</span> <span class="fl">0.2</span>) +
<span class="st"> </span><span class="kw">scale_x_continuous</span>(<span class="ot">NULL</span>, <span class="dt">breaks =</span> <span class="ot">NULL</span>) +<span class="st"> </span>
<span class="st"> </span><span class="kw">scale_y_continuous</span>(<span class="ot">NULL</span>, <span class="dt">breaks =</span> <span class="ot">NULL</span>)</code></pre></div>
-<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASAAAAEgCAYAAAAUg66AAAAEDWlDQ1BJQ0MgUHJvZmlsZQAAOI2NVV1oHFUUPrtzZyMkzlNsNIV0qD8NJQ2TVjShtLp/3d02bpZJNtoi6GT27s6Yyc44M7v9oU9FUHwx6psUxL+3gCAo9Q/bPrQvlQol2tQgKD60+INQ6Ium65k7M5lpurHeZe58853vnnvuuWfvBei5qliWkRQBFpquLRcy4nOHj4g9K5CEh6AXBqFXUR0rXalMAjZPC3e1W99Dwntf2dXd/p+tt0YdFSBxH2Kz5qgLiI8B8KdVy3YBevqRHz/qWh72Yui3MUDEL3q44WPXw3M+fo1pZuQs4tOIBVVTaoiXEI/MxfhGDPsxsNZfoE1q66ro5aJim3XdoLFw72H+n23BaIXzbcOnz5mfPoTvYVz7KzUl5+FRxEuqkp9G/Ajia [...]
+<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASAAAAEgCAYAAAAUg66AAAAEDWlDQ1BJQ0MgUHJvZmlsZQAAOI2NVV1oHFUUPrtzZyMkzlNsNIV0qD8NJQ2TVjShtLp/3d02bpZJNtoi6GT27s6Yyc44M7v9oU9FUHwx6psUxL+3gCAo9Q/bPrQvlQol2tQgKD60+INQ6Ium65k7M5lpurHeZe58853vnnvuuWfvBei5qliWkRQBFpquLRcy4nOHj4g9K5CEh6AXBqFXUR0rXalMAjZPC3e1W99Dwntf2dXd/p+tt0YdFSBxH2Kz5qgLiI8B8KdVy3YBevqRHz/qWh72Yui3MUDEL3q44WPXw3M+fo1pZuQs4tOIBVVTaoiXEI/MxfhGDPsxsNZfoE1q66ro5aJim3XdoLFw72H+n23BaIXzbcOnz5mfPoTvYVz7KzUl5+FRxEuqkp9G/Ajia [...]
<li><p>The lengths of on/off stretches of line. This is done with a string containing 2, 4, 6, or 8 hexadecimal digits which give the lengths of consecutive lengths. For example, the string <code>"33"</code> specifies three units on followed by three off and <code>"3313"</code> specifies three units on followed by three off followed by one on and finally three off.</p>
-<p>The five standard dash-dot line types described above correspond to 44, 13, 134, 73, and 2262.</p></li>
+<p>The five standard dash-dot line types described above correspond to 44, 13, 1343, 73, and 2262.</p></li>
</ul>
<p>The <code>size</code> of a line is its width in mm.</p>
</div>
@@ -127,7 +122,7 @@ linetypes <-<span class="st"> </span><span class="kw">data.frame</span>(
<span class="st"> </span><span class="kw">expand_limits</span>(<span class="dt">x =</span> <span class="fl">4.1</span>) +
<span class="st"> </span><span class="kw">scale_x_continuous</span>(<span class="ot">NULL</span>, <span class="dt">breaks =</span> <span class="ot">NULL</span>) +<span class="st"> </span>
<span class="st"> </span><span class="kw">scale_y_continuous</span>(<span class="ot">NULL</span>, <span class="dt">breaks =</span> <span class="ot">NULL</span>)</code></pre></div>
-<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASAAAAEgCAYAAAAUg66AAAAEDWlDQ1BJQ0MgUHJvZmlsZQAAOI2NVV1oHFUUPrtzZyMkzlNsNIV0qD8NJQ2TVjShtLp/3d02bpZJNtoi6GT27s6Yyc44M7v9oU9FUHwx6psUxL+3gCAo9Q/bPrQvlQol2tQgKD60+INQ6Ium65k7M5lpurHeZe58853vnnvuuWfvBei5qliWkRQBFpquLRcy4nOHj4g9K5CEh6AXBqFXUR0rXalMAjZPC3e1W99Dwntf2dXd/p+tt0YdFSBxH2Kz5qgLiI8B8KdVy3YBevqRHz/qWh72Yui3MUDEL3q44WPXw3M+fo1pZuQs4tOIBVVTaoiXEI/MxfhGDPsxsNZfoE1q66ro5aJim3XdoLFw72H+n23BaIXzbcOnz5mfPoTvYVz7KzUl5+FRxEuqkp9G/Ajia [...]
+<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASAAAAEgCAYAAAAUg66AAAAEDWlDQ1BJQ0MgUHJvZmlsZQAAOI2NVV1oHFUUPrtzZyMkzlNsNIV0qD8NJQ2TVjShtLp/3d02bpZJNtoi6GT27s6Yyc44M7v9oU9FUHwx6psUxL+3gCAo9Q/bPrQvlQol2tQgKD60+INQ6Ium65k7M5lpurHeZe58853vnnvuuWfvBei5qliWkRQBFpquLRcy4nOHj4g9K5CEh6AXBqFXUR0rXalMAjZPC3e1W99Dwntf2dXd/p+tt0YdFSBxH2Kz5qgLiI8B8KdVy3YBevqRHz/qWh72Yui3MUDEL3q44WPXw3M+fo1pZuQs4tOIBVVTaoiXEI/MxfhGDPsxsNZfoE1q66ro5aJim3XdoLFw72H+n23BaIXzbcOnz5mfPoTvYVz7KzUl5+FRxEuqkp9G/Ajia [...]
<li><p>A <strong>single character</strong>, to use that character as a plotting symbol.</p></li>
<li><p>A <code>.</code> to draw the smallest rectangle that is visible, usualy 1 pixel.</p></li>
<li><p>An <code>NA</code>, to draw nothing.</p></li>
@@ -138,7 +133,7 @@ linetypes <-<span class="st"> </span><span class="kw">data.frame</span>(
<span class="st"> </span><span class="kw">geom_abline</span>(<span class="dt">slope =</span> -<span class="dv">1</span>, <span class="dt">intercept =</span> <span class="dv">6</span>, <span class="dt">colour =</span> <span class="st">"white"</span>, <span class="dt">size =</span> <span class="dv">6</span>) +<span class="st"> </span>
<span class="st"> </span><span class="kw">geom_point</span>(<span class="dt">shape =</span> <span class="dv">21</span>, <span class="dt">fill =</span> <span class="st">"red"</span>) +
<span class="st"> </span><span class="kw">scale_size_identity</span>()</code></pre></div>
-<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASAAAAEgCAYAAAAUg66AAAAEDWlDQ1BJQ0MgUHJvZmlsZQAAOI2NVV1oHFUUPrtzZyMkzlNsNIV0qD8NJQ2TVjShtLp/3d02bpZJNtoi6GT27s6Yyc44M7v9oU9FUHwx6psUxL+3gCAo9Q/bPrQvlQol2tQgKD60+INQ6Ium65k7M5lpurHeZe58853vnnvuuWfvBei5qliWkRQBFpquLRcy4nOHj4g9K5CEh6AXBqFXUR0rXalMAjZPC3e1W99Dwntf2dXd/p+tt0YdFSBxH2Kz5qgLiI8B8KdVy3YBevqRHz/qWh72Yui3MUDEL3q44WPXw3M+fo1pZuQs4tOIBVVTaoiXEI/MxfhGDPsxsNZfoE1q66ro5aJim3XdoLFw72H+n23BaIXzbcOnz5mfPoTvYVz7KzUl5+FRxEuqkp9G/Ajia [...]
+<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASAAAAEgCAYAAAAUg66AAAAEDWlDQ1BJQ0MgUHJvZmlsZQAAOI2NVV1oHFUUPrtzZyMkzlNsNIV0qD8NJQ2TVjShtLp/3d02bpZJNtoi6GT27s6Yyc44M7v9oU9FUHwx6psUxL+3gCAo9Q/bPrQvlQol2tQgKD60+INQ6Ium65k7M5lpurHeZe58853vnnvuuWfvBei5qliWkRQBFpquLRcy4nOHj4g9K5CEh6AXBqFXUR0rXalMAjZPC3e1W99Dwntf2dXd/p+tt0YdFSBxH2Kz5qgLiI8B8KdVy3YBevqRHz/qWh72Yui3MUDEL3q44WPXw3M+fo1pZuQs4tOIBVVTaoiXEI/MxfhGDPsxsNZfoE1q66ro5aJim3XdoLFw72H+n23BaIXzbcOnz5mfPoTvYVz7KzUl5+FRxEuqkp9G/Ajia [...]
</div>
<div id="text" class="section level2">
<h2>Text</h2>
@@ -151,7 +146,7 @@ linetypes <-<span class="st"> </span><span class="kw">data.frame</span>(
<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">df <-<span class="st"> </span><span class="kw">data.frame</span>(<span class="dt">x =</span> <span class="dv">1</span>, <span class="dt">y =</span> <span class="dv">3</span>:<span class="dv">1</span>, <span class="dt">family =</span> <span class="kw">c</span>(<span class="st">"sans"</span>, <span class="st">"serif"</span>, <span class="st">"mono"</span>))
<span class="kw">ggplot</span>(df, <span class="kw">aes</span>(x, y)) +<span class="st"> </span>
<span class="st"> </span><span class="kw">geom_text</span>(<span class="kw">aes</span>(<span class="dt">label =</span> family, <span class="dt">family =</span> family))</code></pre></div>
-<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASAAAAEgCAYAAAAUg66AAAAEDWlDQ1BJQ0MgUHJvZmlsZQAAOI2NVV1oHFUUPrtzZyMkzlNsNIV0qD8NJQ2TVjShtLp/3d02bpZJNtoi6GT27s6Yyc44M7v9oU9FUHwx6psUxL+3gCAo9Q/bPrQvlQol2tQgKD60+INQ6Ium65k7M5lpurHeZe58853vnnvuuWfvBei5qliWkRQBFpquLRcy4nOHj4g9K5CEh6AXBqFXUR0rXalMAjZPC3e1W99Dwntf2dXd/p+tt0YdFSBxH2Kz5qgLiI8B8KdVy3YBevqRHz/qWh72Yui3MUDEL3q44WPXw3M+fo1pZuQs4tOIBVVTaoiXEI/MxfhGDPsxsNZfoE1q66ro5aJim3XdoLFw72H+n23BaIXzbcOnz5mfPoTvYVz7KzUl5+FRxEuqkp9G/Ajia [...]
+<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASAAAAEgCAYAAAAUg66AAAAEDWlDQ1BJQ0MgUHJvZmlsZQAAOI2NVV1oHFUUPrtzZyMkzlNsNIV0qD8NJQ2TVjShtLp/3d02bpZJNtoi6GT27s6Yyc44M7v9oU9FUHwx6psUxL+3gCAo9Q/bPrQvlQol2tQgKD60+INQ6Ium65k7M5lpurHeZe58853vnnvuuWfvBei5qliWkRQBFpquLRcy4nOHj4g9K5CEh6AXBqFXUR0rXalMAjZPC3e1W99Dwntf2dXd/p+tt0YdFSBxH2Kz5qgLiI8B8KdVy3YBevqRHz/qWh72Yui3MUDEL3q44WPXw3M+fo1pZuQs4tOIBVVTaoiXEI/MxfhGDPsxsNZfoE1q66ro5aJim3XdoLFw72H+n23BaIXzbcOnz5mfPoTvYVz7KzUl5+FRxEuqkp9G/Ajia [...]
<p>It’s trickier to include a system font on a plot because text drawing is done differently by each graphics device (GD). There are five GDs in common use (<code>png()</code>, <code>pdf()</code>, on screen devices for Windows, Mac and Linux), so to have a font work everywhere you need to configure five devices in five different ways. Two packages simplify the quandary a bit:</p>
<ul>
<li><p><code>showtext</code> makes GD-independent plots by rendering all text as polygons.</p></li>
@@ -187,7 +182,7 @@ just$label <-<span class="st"> </span><span class="kw">paste0</span>(just$hju
<span class="kw">ggplot</span>(just, <span class="kw">aes</span>(hjust, vjust)) +
<span class="st"> </span><span class="kw">geom_point</span>(<span class="dt">colour =</span> <span class="st">"grey70"</span>, <span class="dt">size =</span> <span class="dv">5</span>) +<span class="st"> </span>
<span class="st"> </span><span class="kw">geom_text</span>(<span class="kw">aes</span>(<span class="dt">label =</span> label, <span class="dt">hjust =</span> hjust, <span class="dt">vjust =</span> vjust))</code></pre></div>
-<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASAAAAEgCAYAAAAUg66AAAAEDWlDQ1BJQ0MgUHJvZmlsZQAAOI2NVV1oHFUUPrtzZyMkzlNsNIV0qD8NJQ2TVjShtLp/3d02bpZJNtoi6GT27s6Yyc44M7v9oU9FUHwx6psUxL+3gCAo9Q/bPrQvlQol2tQgKD60+INQ6Ium65k7M5lpurHeZe58853vnnvuuWfvBei5qliWkRQBFpquLRcy4nOHj4g9K5CEh6AXBqFXUR0rXalMAjZPC3e1W99Dwntf2dXd/p+tt0YdFSBxH2Kz5qgLiI8B8KdVy3YBevqRHz/qWh72Yui3MUDEL3q44WPXw3M+fo1pZuQs4tOIBVVTaoiXEI/MxfhGDPsxsNZfoE1q66ro5aJim3XdoLFw72H+n23BaIXzbcOnz5mfPoTvYVz7KzUl5+FRxEuqkp9G/Ajia [...]
+<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASAAAAEgCAYAAAAUg66AAAAEDWlDQ1BJQ0MgUHJvZmlsZQAAOI2NVV1oHFUUPrtzZyMkzlNsNIV0qD8NJQ2TVjShtLp/3d02bpZJNtoi6GT27s6Yyc44M7v9oU9FUHwx6psUxL+3gCAo9Q/bPrQvlQol2tQgKD60+INQ6Ium65k7M5lpurHeZe58853vnnvuuWfvBei5qliWkRQBFpquLRcy4nOHj4g9K5CEh6AXBqFXUR0rXalMAjZPC3e1W99Dwntf2dXd/p+tt0YdFSBxH2Kz5qgLiI8B8KdVy3YBevqRHz/qWh72Yui3MUDEL3q44WPXw3M+fo1pZuQs4tOIBVVTaoiXEI/MxfhGDPsxsNZfoE1q66ro5aJim3XdoLFw72H+n23BaIXzbcOnz5mfPoTvYVz7KzUl5+FRxEuqkp9G/Ajia [...]
<p>Note that you can use numbers outside the range (0, 1), but it’s not recommended.</p>
</div>
</div>
diff --git a/inst/staticdocs/README.md b/inst/staticdocs/README.md
deleted file mode 100644
index e69de29..0000000
diff --git a/inst/staticdocs/footer.html b/inst/staticdocs/footer.html
deleted file mode 100644
index 1dcde5e..0000000
--- a/inst/staticdocs/footer.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<p class="pull-right"><a href="#">Back to top</a></p>
-
-<p class='feedback'>What do you think of the documentation? <a href='http://hadley.wufoo.com/forms/documentation-feedback/def/field0={{name}}'>Please let me know by filling out this short online survey</a>.</p>
-
-<p>Built by <a href="https://github.com/hadley/staticdocs">staticdocs</a>. Styled with <a href="http://twitter.github.com/bootstrap">bootstrap</a>.</p>
diff --git a/inst/staticdocs/head.html b/inst/staticdocs/head.html
deleted file mode 100644
index 2aec39a..0000000
--- a/inst/staticdocs/head.html
+++ /dev/null
@@ -1,27 +0,0 @@
-<meta charset="utf-8">
-<title>{{pagetitle}}. {{#package}}{{package}} {{version}}{{/package}}</title>
-<meta name="viewport" content="width=device-width, initial-scale=1.0">
-<meta name="author" content="{{author}}">
-
-<link href="css/bootstrap.css" rel="stylesheet">
-<link href="css/bootstrap-responsive.css" rel="stylesheet">
-<link href="css/highlight.css" rel="stylesheet">
-<link href="css/staticdocs.css" rel="stylesheet">
-
-<!--[if lt IE 9]>
- <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
-<![endif]-->
-
-<script type="text/javascript">
-
- var _gaq = _gaq || [];
- _gaq.push(['_setAccount', 'UA-67989-15']);
- _gaq.push(['_setDomainName', 'ggplot2.org']);
- _gaq.push(['_trackPageview']);
-
- (function() {
- var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
- ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
- var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
- })();
-</script>
diff --git a/inst/staticdocs/icons.R b/inst/staticdocs/icons.R
deleted file mode 100644
index 4897cd7..0000000
--- a/inst/staticdocs/icons.R
+++ /dev/null
@@ -1,576 +0,0 @@
-# Coords -----------------------------------------------------------------------
-coord_cartesian <- sd_icon({
- gTree(children = gList(
- segmentsGrob(
- c(0, 0.25),
- c(0.25, 0),
- c(1, 0.25),
- c(0.25, 1),
- gp = gpar(col = "grey50", lwd = 0.5)
- ),
- segmentsGrob(
- c(0, 0.75),
- c(0.75, 0),
- c(1, 0.75),
- c(0.75, 1),
- gp = gpar(col = "grey50", lwd = 0.5)
- ),
- segmentsGrob(c(0, 0.5), c(0.5, 0), c(1, 0.5), c(0.5, 1))
- ))
-})
-
-
-coord_fixed <- sd_icon({
- textGrob("=", gp = gpar(cex = 3))
-})
-
-
-coord_flip <- sd_icon({
- angles <- seq(0, pi / 2, length.out = 20)[-c(1, 20)]
- gTree(children = gList(
- segmentsGrob(0, 0, 0, 1),
- segmentsGrob(0, 0, 1, 0),
- linesGrob(0.9 * sin(angles), 0.9 * cos(angles),
- arrow = arrow(length = unit(0.05, "npc"))),
- linesGrob(
- 0.5 * sin(angles),
- 0.5 * cos(angles),
- arrow = arrow(ends = "first", length = unit(0.05, "npc"))
- )
- ))
-})
-
-library(maps)
-coord_map <- sd_icon({
- nz <- data.frame(map("nz", plot = FALSE)[c("x", "y")])
- nz$x <- nz$x - min(nz$x, na.rm = TRUE)
- nz$y <- nz$y - min(nz$y, na.rm = TRUE)
- nz <- nz / max(nz, na.rm = TRUE)
- linesGrob(nz$x, nz$y, default.units = "npc")
-})
-
-coord_polar <- sd_icon({
- circleGrob(r = c(0.1, 0.25, 0.45), gp = gpar(fill = NA))
-})
-
-coord_transform <- sd_icon({
- breaks <- cumsum(1 / 2 ^ (1:5))
- gTree(children = gList(
- segmentsGrob(breaks, 0, breaks, 1),
- segmentsGrob(0, breaks, 1, breaks)
- ))
-})
-
-# Faceting ---------------------------------------------------------------------
-
-facet_grid <- sd_icon({
- gTree(children = gList(
- rectGrob(
- 0,
- 1,
- width = 0.95,
- height = 0.05,
- hjust = 0,
- vjust = 1,
- gp = gpar(fill = "grey60", col = NA)
- ),
- rectGrob(
- 0.95,
- 0.95,
- width = 0.05,
- height = 0.95,
- hjust = 0,
- vjust = 1,
- gp = gpar(fill = "grey60", col = NA)
- ),
- segmentsGrob(c(0, 0.475), c(0.475, 0), c(1, 0.475), c(0.475, 1))
- ))
-})
-
-facet_null <- sd_icon({
- gTree(children = gList(
- rectGrob(
- 0,
- 1,
- width = 0.95,
- height = 0.05,
- hjust = 0,
- vjust = 1,
- gp = gpar(fill = "grey60", col = NA)
- ),
- rectGrob(
- 0.95,
- 0.95,
- width = 0.05,
- height = 0.95,
- hjust = 0,
- vjust = 1,
- gp = gpar(fill = "grey60", col = NA)
- ),
- segmentsGrob(c(0, 0.475), c(0.475, 0), c(1, 0.475), c(0.475, 1))
- ))
-})
-
-# Geoms ------------------------------------------------------------------------
-
-geom_abline <- sd_icon(linesGrob(c(0, 1), c(0.2, 0.8)))
-
-geom_bar <- sd_icon({
- rectGrob(
- c(0.3, 0.7),
- c(0.4, 0.8),
- height = c(0.4, 0.8),
- width = 0.3,
- vjust = 1,
- gp = gpar(fill = "grey20", col = NA)
- )
-})
-
-geom_histogram <- sd_icon({
- y <- c(0.2, 0.3, 0.5, 0.6, 0.2, 0.8, 0.5, 0.3)
- rectGrob(
- seq(0.1, 0.9, by = 0.1),
- y,
- height = y,
- width = 0.1,
- vjust = 1,
- gp = gpar(fill = "grey20", col = NA)
- )
-})
-
-geom_boxplot <- sd_icon({
- gTree(children = gList(
- segmentsGrob(c(0.3, 0.7), c(0.1, 0.2), c(0.3, 0.7), c(0.7, 0.95)),
- rectGrob(
- c(0.3, 0.7),
- c(0.6, 0.8),
- width = 0.3,
- height = c(0.4, 0.4),
- vjust = 1
- ),
- segmentsGrob(c(0.15, 0.55), c(0.5, 0.6), c(0.45, 0.85), c(0.5, 0.6))
- ))
-})
-
-geom_crossbar <- sd_icon({
- gTree(children = gList(
- rectGrob(
- c(0.3, 0.7),
- c(0.6, 0.8),
- width = 0.3,
- height = c(0.4, 0.4),
- vjust = 1
- ),
- segmentsGrob(c(0.15, 0.55), c(0.5, 0.6), c(0.45, 0.85), c(0.5, 0.6))
- ))
-})
-
-geom_dotplot <- sd_icon({
- xpos <- c(1, 1, 2, 3, 3, 3, 4, 4, 5, 5, 5, 5, 6, 7, 7, 7, 8, 8, 9) / 10
- ypos <- c(1, 2, 1, 1, 2, 3, 1, 2, 1, 2, 3, 4, 1, 1, 2, 3, 1, 2, 1) / 10
- pointsGrob(
- x = xpos,
- y = ypos,
- pch = 19,
- size = unit(.1, "npc"),
- gp = gpar(col = "black", cex = 0.5),
- default.units = "npc"
- )
-})
-
-geom_errorbar <- sd_icon({
- gTree(children = gList(
- segmentsGrob(c(0.3, 0.7), c(0.3, 0.5), c(0.3, 0.7), c(0.7, 0.9)),
- segmentsGrob(c(0.15, 0.55), c(0.3, 0.5), c(0.45, 0.85), c(0.3, 0.5)),
- segmentsGrob(c(0.15, 0.55), c(0.7, 0.9), c(0.45, 0.85), c(0.7, 0.9))
- ))
-})
-
-geom_errorbarh <- sd_icon({
- gTree(children = gList(
- segmentsGrob(c(0.5, 0.3), c(0.70, 0.30), c(0.9, 0.7), c(0.70, 0.30)),
- segmentsGrob(c(0.5, 0.3), c(0.55, 0.15), c(0.5, 0.3), c(0.85, 0.45)),
- segmentsGrob(c(0.9, 0.7), c(0.55, 0.15), c(0.9, 0.7), c(0.85, 0.45))
- ))
-})
-
-geom_freqpoly <- sd_icon({
- y <- c(0.2, 0.3, 0.5, 0.6, 0.2, 0.8, 0.5, 0.3)
- linesGrob(seq(0.1, 0.9, by = 0.1), y, gp = gpar(col = "grey20"))
-})
-
-geom_hline <- sd_icon({
- linesGrob(c(0, 1), c(0.5, 0.5))
-})
-
-geom_linerange <- sd_icon({
- segmentsGrob(c(0.3, 0.7), c(0.1, 0.2), c(0.3, 0.7), c(0.7, 0.95))
-})
-
-geom_path <- sd_icon({
- linesGrob(c(0.2, 0.4, 0.8, 0.6, 0.5), c(0.2, 0.7, 0.4, 0.1, 0.5))
-})
-
-geom_contour <- sd_icon({
- gTree(children = gList(polygonGrob(
- c(0.45, 0.5, 0.6, 0.5), c(0.5, 0.4, 0.55, 0.6)
- ),
- polygonGrob(
- c(0.25, 0.6, 0.8, 0.5), c(0.5, 0.2, 0.75, 0.9),
- gp = gpar(fill = NA)
- )))
-})
-
-geom_density2d <- sd_icon(inherit = "geom_contour")
-
-geom_line <- sd_icon({
- pos <- seq(0, 1, length.out = 5)
- linesGrob(pos, c(0.2, 0.7, 0.4, 0.8, 0.3))
-})
-
-geom_step <- sd_icon({
- n <- 15
- xs <- rep(0:n, each = 2)[-2 * (n + 1)] / 15
- ys <- c(0, rep(1:n, each = 2)) / 15
- linesGrob(xs, ys, gp = gpar(col = "grey20"))
-})
-
-geom_point <- sd_icon({
- pos <- seq(0.1, 0.9, length.out = 6)
- pointsGrob(
- x = pos,
- y = pos,
- pch = 19,
- gp = gpar(col = "black", cex = 0.5),
- default.units = "npc"
- )
-})
-
-geom_jitter <- sd_icon({
- pos <- seq(0.1, 0.9, length.out = 6)
- pointsGrob(
- x = pos,
- y = jitter(pos, 3),
- pch = 19,
- gp = gpar(col = "black", cex = 0.5),
- default.units = "npc"
- )
-})
-
-geom_pointrange <- sd_icon({
- gTree(children = gList(
- segmentsGrob(c(0.3, 0.7), c(0.1, 0.2), c(0.3, 0.7), c(0.7, 0.95)),
- pointsGrob(
- c(0.3, 0.7),
- c(0.4, 0.6),
- pch = 19,
- gp = gpar(col = "black", cex = 0.5),
- default.units = "npc"
- )
- ))
-})
-
-geom_polygon <- sd_icon({
- polygonGrob(
- c(0.1, 0.4, 0.7, 0.9, 0.6, 0.3),
- c(0.5, 0.8, 0.9, 0.4, 0.2, 0.3),
- gp = gpar(fill = "grey20", col = NA)
- )
-})
-
-geom_quantile <- sd_icon({
- gTree(children = gList(linesGrob(
- c(0, 0.3, 0.5, 0.8, 1), c(0.8, 0.65, 0.6, 0.6, 0.8)
- ),
- linesGrob(
- c(0, 0.3, 0.5, 0.8, 1), c(0.55, 0.45, 0.5, 0.45, 0.55)
- ),
- linesGrob(
- c(0, 0.3, 0.5, 0.8, 1), c(0.3, 0.25, 0.4, 0.3, 0.2)
- )))
-})
-
-geom_raster <- sd_icon({
- rectGrob(
- c(0.25, 0.25, 0.75, 0.75),
- c(0.25, 0.75, 0.75, 0.25),
- width = 0.5,
- height = c(0.67, 0.5, 0.67, 0.5),
- gp = gpar(col = "grey20", fill = c('#804070', '#668040'))
- )
-})
-
-geom_rect <- sd_icon({
- rectGrob(
- c(0.3, 0.7),
- c(0.4, 0.8),
- height = c(0.4, 0.8),
- width = 0.3,
- vjust = 1,
- gp = gpar(fill = "grey20", col = NA)
- )
-})
-
-geom_ribbon <- sd_icon({
- polygonGrob(
- c(0, 0.3, 0.5, 0.8, 1, 1, 0.8, 0.5, 0.3, 0),
- c(0.5, 0.3, 0.4, 0.2, 0.3, 0.7, 0.5, 0.6, 0.5, 0.7),
- gp = gpar(fill = "grey20", col = NA)
- )
-})
-
-geom_area <- sd_icon({
- polygonGrob(c(0, 0, 0.3, 0.5, 0.8, 1, 1),
- c(0, 1, 0.5, 0.6, 0.3, 0.8, 0),
- gp = gpar(fill = "grey20", col = NA))
-})
-
-geom_density <- sd_icon({
- x <- seq(0, 1, length.out = 80)
- y <- dnorm(x, mean = 0.5, sd = 0.15)
- linesGrob(x, 0.05 + y / max(y) * 0.9, default.units = "npc")
-})
-
-geom_segment <- sd_icon({
- segmentsGrob(c(0.1, 0.3, 0.5, 0.7),
- c(0.3, 0.5, 0.1, 0.9),
- c(0.2, 0.5, 0.7, 0.9),
- c(0.8, 0.7, 0.4, 0.3))
-})
-
-geom_smooth <- sd_icon({
- gTree(children = gList(polygonGrob(
- c(0, 0.3, 0.5, 0.8, 1, 1, 0.8, 0.5, 0.3, 0),
- c(0.5, 0.3, 0.4, 0.2, 0.3, 0.7, 0.5, 0.6, 0.5, 0.7),
- gp = gpar(fill = "grey60", col = NA)
- ),
- linesGrob(
- c(0, 0.3, 0.5, 0.8, 1), c(0.6, 0.4, 0.5, 0.4, 0.6)
- )))
-})
-
-geom_text <- sd_icon({
- textGrob("text", rot = 45, gp = gpar(cex = 1.2))
-})
-
-geom_tile <- sd_icon({
- rectGrob(
- c(0.25, 0.25, 0.75, 0.75),
- c(0.25, 0.75, 0.75, 0.25),
- width = 0.5,
- height = c(0.67, 0.5, 0.67, 0.5),
- gp = gpar(col = "grey20", fill = c('#804070', '#668040'))
- )
-})
-
-geom_violin <- sd_icon({
- y <- seq(-.3, .3, length.out = 40)
- x1 <- dnorm(y, mean = -.15, sd = 0.05) +
- 1.5 * dnorm(y, mean = 0.1, sd = 0.1)
- x2 <-
- dnorm(y, mean = -.1, sd = 0.1) + dnorm(y, mean = 0.1, sd = 0.1)
-
- y <- c(y, rev(y))
- x1 <- c(x1,-rev(x1)) / max(8 * x1)
- x2 <- c(x2,-rev(x2)) / max(8 * x2)
- gp <- gpar(fill = "black")
- gTree(children = gList(
- polygonGrob(x1 + .30, y + .35, default.units = "npc", gp = gp),
- polygonGrob(x2 + .70, y + .55, default.units = "npc", gp = gp)
- ))
-})
-
-geom_vline <- sd_icon({
- linesGrob(c(0.5, 0.5), c(0, 1))
-})
-
-# Position adjustments --------------------------------------------------------
-
-position_dodge <- sd_icon({
- y <- c(0.5, 0.3)
- rectGrob(
- c(0.25, 0.75),
- y,
- width = 0.4,
- height = y,
- gp = gpar(col = "grey60", fill = c('#804070', '#668040')),
- vjust = 1
- )
-})
-
-position_fill <- sd_icon({
- y <- c(0.5, 0.8)
- rectGrob(
- 0.5,
- c(0.625, 1),
- width = 0.4,
- height = c(0.625, 0.375),
- gp = gpar(col = "grey60", fill = c('#804070', '#668040')),
- vjust = 1
- )
-})
-
-position_identity <- sd_icon({
- rectGrob(
- 0.5,
- c(0.5, 0.3),
- width = 0.4,
- height = c(0.5, 0.3),
- gp = gpar(col = "grey60", fill = c('#804070', '#668040')),
- vjust = 1
- )
-})
-
-position_jitter <- sd_icon(inherit = "geom_jitter")
-
-position_stack <- sd_icon({
- y <- c(0.5, 0.8)
- rectGrob(
- 0.5,
- c(0.5, 0.8),
- width = 0.4,
- height = c(0.5, 0.3),
- gp = gpar(col = "grey60", fill = c('#804070', '#668040')),
- vjust = 1
- )
-})
-
-# Scales -----------------------------------------------------------------------
-
-scale_alpha <- sd_icon({
- x <- c(0.1, 0.3, 0.5, 0.7, 0.9)
- rectGrob(x,
- width = 0.25,
- gp = gpar(fill = scales::alpha("black", x), col = NA))
-})
-
-scale_colour_brewer <- sd_icon({
- rectGrob(
- c(0.1, 0.3, 0.5, 0.7, 0.9),
- width = 0.21,
- gp = gpar(fill = RColorBrewer::brewer.pal(5, "PuOr"), col = NA)
- )
-})
-
-scale_colour_gradient <- sd_icon({
- g <- scale_fill_gradient()
- g$train(1:5)
- rectGrob(
- c(0.1, 0.3, 0.5, 0.7, 0.9),
- width = 0.21,
- gp = gpar(fill = g$map(1:5), col = NA)
- )
-})
-
-scale_colour_gradient2 <- sd_icon({
- g <- scale_fill_gradient2()
- g$train(1:5 - 3)
- rectGrob(
- c(0.1, 0.3, 0.5, 0.7, 0.9),
- width = 0.21,
- gp = gpar(fill = g$map(1:5 - 3), col = NA)
- )
-})
-
-scale_colour_gradientn <- sd_icon({
- g <- scale_fill_gradientn(colours = rainbow(7))
- g$train(1:5)
- rectGrob(
- c(0.1, 0.3, 0.5, 0.7, 0.9),
- width = 0.21,
- gp = gpar(fill = g$map(1:5), col = NA)
- )
-})
-
-scale_colour_grey <- sd_icon({
- rectGrob(c(0.1, 0.3, 0.5, 0.7, 0.9),
- width = 0.21,
- gp = gpar(fill = gray(seq(0, 1, length.out = 5)), col = NA))
-})
-
-scale_colour_hue <- sd_icon({
- rectGrob(c(0.1, 0.3, 0.5, 0.7, 0.9),
- width = 0.21,
- gp = gpar(fill = hcl(
- seq(0, 360, length.out = 6)[-6], c = 100, l = 65
- ), col = NA))
-})
-
-scale_identity <- sd_icon({
- textGrob("f(x) = x", gp = gpar(cex = 1.2))
-})
-
-scale_linetype <- sd_icon({
- gTree(children = gList(
- segmentsGrob(0, 0.25, 1, 0.25, gp = gpar(lty = 1)),
- segmentsGrob(0, 0.50, 1, 0.50, gp = gpar(lty = 2)),
- segmentsGrob(0, 0.75, 1, 0.75, gp = gpar(lty = 3))
- ))
-})
-
-scale_manual <- sd_icon({
- textGrob("DIY", gp = gpar(cex = 1.2))
-})
-
-scale_shape <- sd_icon({
- gTree(children = gList(
- circleGrob(0.7, 0.7, r = 0.1),
- segmentsGrob(0.2, 0.3, 0.4, 0.3),
- segmentsGrob(0.3, 0.2, 0.3, 0.4),
- polygonGrob(c(0.2, 0.2, 0.4, 0.4), c(0.8, 0.6, 0.6, 0.8)),
- polygonGrob(c(0.6, 0.7, 0.8), c(0.2, 0.4, 0.2))
- ))
-})
-
-scale_size <- sd_icon({
- pos <- c(0.15, 0.3, 0.5, 0.75)
- circleGrob(pos,
- pos,
- r = (c(0.1, 0.2, 0.3, 0.4) / 2.5),
- gp = gpar(fill = "grey50", col = NA))
-})
-
-scale_x_date <- sd_icon({
- textGrob("14/10/1979", gp = gpar(cex = 1))
-})
-
-scale_x_datetime <- sd_icon({
- textGrob("14/10/1979\n10:14am", gp = gpar(cex = 0.9))
-})
-
-# Statistics -------------------------------------------------------------------
-
-stat_bin <- sd_icon(inherit = "geom_histogram")
-
-stat_bindot <- sd_icon(inherit = "geom_dotplot")
-
-stat_boxplot <- sd_icon(inherit = "geom_boxplot")
-
-stat_contour <- sd_icon(inherit = "geom_contour")
-
-stat_density2d <- sd_icon(inherit = "geom_density2d")
-
-stat_ecdf <- sd_icon(inherit = "geom_step")
-
-stat_density <- sd_icon(inherit = "geom_density")
-
-stat_identity <- sd_icon({
- textGrob('f(x) = x', gp = gpar(cex = 1.2))
-})
-
-stat_quantile <- sd_icon(inherit = "geom_quantile")
-
-stat_smooth <- sd_icon(inherit = "geom_smooth")
-
-stat_sum <- sd_icon({
- textGrob(expression(Sigma), gp = gpar(cex = 4))
-})
-
-# The line stats will be removed in the future
-stat_abline <- sd_icon(inherit = "geom_abline")
-
-stat_vline <- sd_icon(inherit = "geom_vline")
-
-stat_hline <- sd_icon(inherit = "geom_hline")
-
-stat_ydensity <- sd_icon(inherit = "geom_violin")
diff --git a/inst/staticdocs/index.r b/inst/staticdocs/index.r
deleted file mode 100644
index cb02691..0000000
--- a/inst/staticdocs/index.r
+++ /dev/null
@@ -1,241 +0,0 @@
-sd_section("Geoms",
- "Geoms, short for geometric objects, describe the type of plot you will produce.",
- c(
- "geom_abline",
- "geom_area",
- "geom_bar",
- "geom_bin2d",
- "geom_blank",
- "geom_boxplot",
- "geom_contour",
- "geom_count",
- "geom_curve",
- "geom_crossbar",
- "geom_density",
- "geom_density_2d",
- "geom_dotplot",
- "geom_errorbar",
- "geom_errorbarh",
- "geom_freqpoly",
- "geom_hex",
- "geom_histogram",
- "geom_hline",
- "geom_jitter",
- "geom_label",
- "geom_line",
- "geom_linerange",
- "geom_map",
- "geom_path",
- "geom_point",
- "geom_pointrange",
- "geom_polygon",
- "geom_quantile",
- "geom_raster",
- "geom_rect",
- "geom_ribbon",
- "geom_rug",
- "geom_segment",
- "geom_smooth",
- "geom_step",
- "geom_text",
- "geom_tile",
- "geom_violin",
- "geom_vline"
- )
-)
-
-sd_section("Statistics",
- "It's often useful to transform your data before plotting, and that's what statistical transformations do.",
- c(
- "stat_binhex",
- "stat_contour",
- "stat_density",
- "stat_density_2d",
- "stat_ecdf",
- "stat_ellipse",
- "stat_function",
- "stat_identity",
- "stat_qq",
- "stat_spoke",
- "stat_sum",
- "stat_summary",
- "stat_summary_hex",
- "stat_summary_2d",
- "stat_unique"
- )
-)
-
-sd_section("Scales",
- "Scales control the mapping between data and aesthetics.",
- c(
- "expand_limits",
- "guides",
- "guide_legend",
- "guide_colourbar",
- "lims",
- "scale_alpha",
- "scale_area",
- "scale_size_area",
- "scale_colour_brewer",
- "scale_colour_gradient",
- "scale_colour_gradient2",
- "scale_colour_gradientn",
- "scale_colour_grey",
- "scale_colour_hue",
- "scale_identity",
- "scale_manual",
- "scale_linetype",
- "scale_shape",
- "scale_size",
- "scale_x_continuous",
- "scale_x_date",
- "scale_x_datetime",
- "scale_x_discrete",
- "labs",
- "update_labels",
- "xlim"
- )
-)
-
-sd_section("Coordinate systems",
- "Coordinate systems adjust the mapping from coordinates to the 2d plane of the computer screen.",
- c(
- "coord_cartesian",
- "coord_fixed",
- "coord_flip",
- "coord_map",
- "coord_polar",
- "coord_quickmap",
- "coord_trans"
- )
-)
-
-sd_section("Faceting",
- "Facets display subsets of the dataset in different panels.",
- c(
- "facet_grid",
- "facet_null",
- "facet_wrap",
- "labeller",
- "label_both",
- "label_bquote",
- "label_parsed",
- "label_value",
- "label_wrap_gen"
- )
-)
-
-sd_section("Position adjustments",
- "Position adjustments can be used to fine tune positioning of objects to achieve effects like dodging, jittering and stacking.",
- c(
- "position_dodge",
- "position_fill",
- "position_identity",
- "position_nudge",
- "position_stack",
- "position_jitter",
- "position_jitterdodge"
- )
-)
-
-sd_section("Data",
- "Data sets included in ggplot2 and used in examples",
- c(
- "diamonds",
- "economics",
- "faithfuld",
- "luv_colours",
- "midwest",
- "mpg",
- "msleep",
- "presidential",
- "seals",
- "txhousing"
- )
-)
-
-sd_section("Annotation",
- "Specialised functions for adding annotations to a plot",
- c(
- "annotate",
- "annotation_custom",
- "annotation_logticks",
- "annotation_map",
- "annotation_raster",
- "borders"
- )
-)
-
-sd_section("Fortify",
- "Fortify methods make it possible to use ggplot2 with objects of
- various types, not just data frames.",
- c(
- "fortify",
- "fortify-multcomp",
- "fortify.lm",
- "fortify.map",
- "fortify.sp",
- "map_data"
- )
-)
-
-sd_section("Themes",
- "Themes control non-data components of the plot",
- c(
- "add_theme",
- "calc_element",
- "element_blank",
- "element_line",
- "element_rect",
- "element_text",
- "ggtheme",
- "is.rel",
- "is.theme",
- "opts",
- "margin",
- "rel",
- "theme",
- "theme_blank",
- "theme_bw",
- "theme_classic",
- "theme_grey",
- "theme_minimal",
- "theme_update",
- "update_element"
- )
-)
-
-sd_section("Plot creation", "",
- c(
- "ggplot",
- "qplot",
- "+.gg",
- "autoplot",
- "ggplot.data.frame",
- "is.ggplot",
- "print.ggplot"
- )
-)
-
-sd_section("Aesthetics", "",
- c(
- "aes",
- "aes_",
- "aes_all",
- "aes_auto",
- "aes_string",
- "aes_colour_fill_alpha",
- "aes_group_order",
- "aes_linetype_size_shape",
- "aes_position"
- )
-)
-
-sd_section("ggproto", "",
- c(
- "ggproto",
- "print.ggproto",
- "is.ggproto",
- "format.ggproto"
- )
-)
diff --git a/man/add_theme.Rd b/man/add_theme.Rd
index 9c3680e..8b24e8c 100644
--- a/man/add_theme.Rd
+++ b/man/add_theme.Rd
@@ -17,7 +17,5 @@ informative error messages.}
\description{
Modify properties of an element in a theme object
}
-\seealso{
-+.gg
-}
+\keyword{internal}
diff --git a/man/aes.Rd b/man/aes.Rd
index 93787b9..00cb965 100644
--- a/man/aes.Rd
+++ b/man/aes.Rd
@@ -2,21 +2,24 @@
% Please edit documentation in R/aes.r
\name{aes}
\alias{aes}
-\title{Define aesthetic mappings.}
+\title{Construct aesthetic mappings}
\usage{
aes(x, y, ...)
}
\arguments{
\item{x, y, ...}{List of name value pairs giving aesthetics to map to
-variables. The names for x and y aesthetics can be omitted (because
-they are so common); all other aesthetics must be named.}
+variables. The names for x and y aesthetics are typically omitted because
+they are so common; all other aesthetics must be named.}
}
\description{
-Generate aesthetic mappings that describe how variables in the data are
-mapped to visual properties (aesthetics) of geoms. This function also
-standardise aesthetic names by performs partial name matching, converting
-color to colour, and old style R names to ggplot names (eg. pch to shape,
-cex to size)
+Aesthetic mappings describe how variables in the data are mapped to visual
+properties (aesthetics) of geoms. Aesthetic mappings can be set in
+\code{\link{ggplot2}} and in individual layers.
+}
+\details{
+This function also standardise aesthetic names by performing partial
+matching, converting color to colour, and translating old style R names to
+ggplot names (eg. pch to shape, cex to size)
}
\examples{
aes(x = mpg, y = wt)
@@ -39,12 +42,7 @@ ggplot(mpg) + geom_point(aes(displ, hwy))
# you can override them, or supply different aesthetics for each layer
}
\seealso{
-See \code{\link{aes_q}}/\code{\link{aes_string}} for standard
- evaluation versions of \code{aes}.
-
-See
- \code{\link{aes_colour_fill_alpha}}, \code{\link{aes_group_order}},
- \code{\link{aes_linetype_size_shape}} and \code{\link{aes_position}}
- for more specific examples with different aesthetics.
+See \code{\link{aes_}} for a version of \code{aes} that is
+ more suitable for programming with.
}
diff --git a/man/aes_.Rd b/man/aes_.Rd
index c8af665..64732d8 100644
--- a/man/aes_.Rd
+++ b/man/aes_.Rd
@@ -4,7 +4,7 @@
\alias{aes_}
\alias{aes_q}
\alias{aes_string}
-\title{Define aesthetic mappings from strings, or quoted calls and formulas.}
+\title{Define aesthetic mappings programatically}
\usage{
aes_(x, y, ...)
@@ -22,17 +22,18 @@ properties (aesthetics) of geoms. \code{\link{aes}} uses non-standard
evaluation to capture the variable names. \code{aes_} and \code{aes_string}
require you to explicitly quote the inputs either with \code{""} for
\code{aes_string()}, or with \code{quote} or \code{~} for \code{aes_()}.
-(\code{aes_q} is an alias to \code{aes_})
+(\code{aes_q} is an alias to \code{aes_}). This makes \code{aes_} and
+\code{aes_string} easy to program with.
}
\details{
-It's better to use \code{aes_q()}, because there's no easy way to create the
-equivalent to \code{aes(colour = "my colour")} or \code{aes{x = `X$1`}}
-with \code{aes_string()}.
-
\code{aes_string} and \code{aes_} are particularly useful when writing
functions that create plots because you can use strings or quoted
names/calls to define the aesthetic mappings, rather than having to use
\code{\link{substitute}} to generate a call to \code{aes()}.
+
+I recommend using \code{aes_()}, because creating the equivalents of
+\code{aes(colour = "my colour")} or \code{aes{x = `X$1`}}
+with \code{aes_string()} is quite clunky.
}
\examples{
# Three ways of generating the same aesthetics
diff --git a/man/aes_group_order.Rd b/man/aes_group_order.Rd
index c54f680..c04435a 100644
--- a/man/aes_group_order.Rd
+++ b/man/aes_group_order.Rd
@@ -3,9 +3,9 @@
\name{aes_group_order}
\alias{aes_group_order}
\alias{group}
-\title{Aesthetics: group}
+\title{Aesthetics: grouping}
\description{
-Aesthetics: group
+Aesthetics: grouping
}
\examples{
\donttest{
diff --git a/man/annotate.Rd b/man/annotate.Rd
index 5c2e430..6736e4e 100644
--- a/man/annotate.Rd
+++ b/man/annotate.Rd
@@ -2,7 +2,7 @@
% Please edit documentation in R/annotation.r
\name{annotate}
\alias{annotate}
-\title{Create an annotation layer.}
+\title{Create an annotation layer}
\usage{
annotate(geom, x = NULL, y = NULL, xmin = NULL, xmax = NULL,
ymin = NULL, ymax = NULL, xend = NULL, yend = NULL, ...,
@@ -19,11 +19,11 @@ often aesthetics, used to set an aesthetic to a fixed value, like
\code{color = "red"} or \code{size = 3}. They may also be parameters
to the paired geom/stat.}
-\item{na.rm}{If \code{FALSE} (the default), removes missing values with
-a warning. If \code{TRUE} silently removes missing values.}
+\item{na.rm}{If \code{FALSE}, the default, missing values are removed with
+a warning. If \code{TRUE}, missing values are silently removed.}
}
\description{
-This function adds geoms to a plot. Unlike typical a geom function,
+This function adds geoms to a plot, but unlike typical a geom function,
the properties of the geoms are not mapped from variables of a data frame,
but are instead passed in as vectors. This is useful for adding small annotations
(such as text labels) or if you have your data in vectors, and for some
@@ -47,5 +47,10 @@ p + annotate("pointrange", x = 3.5, y = 20, ymin = 12, ymax = 28,
colour = "red", size = 1.5)
p + annotate("text", x = 2:3, y = 20:21, label = c("my label", "label 2"))
+
+p + annotate("text", x = 4, y = 25, label = "italic(R) ^ 2 == 0.75",
+ parse = TRUE)
+p + annotate("text", x = 4, y = 25,
+ label = "paste(italic(R) ^ 2, \\" = .75\\")", parse = TRUE)
}
diff --git a/man/annotation_custom.Rd b/man/annotation_custom.Rd
index 0a4d6a6..e2d2daf 100644
--- a/man/annotation_custom.Rd
+++ b/man/annotation_custom.Rd
@@ -2,7 +2,7 @@
% Please edit documentation in R/annotation-custom.r
\name{annotation_custom}
\alias{annotation_custom}
-\title{Annotation: Custom grob.}
+\title{Annotation: Custom grob}
\usage{
annotation_custom(grob, xmin = -Inf, xmax = Inf, ymin = -Inf,
ymax = Inf)
@@ -20,7 +20,8 @@ location of raster}
This is a special geom intended for use as static annotations
that are the same in every panel. These annotations will not
affect scales (i.e. the x and y axes will not grow to cover the range
-of the grob, and the grob will not be modified by any ggplot settings or mappings).
+of the grob, and the grob will not be modified by any ggplot settings
+or mappings).
}
\details{
Most useful for adding tables, inset plots, and other grid-based decorations.
diff --git a/man/annotation_map.Rd b/man/annotation_map.Rd
index 2b2928a..9e06965 100644
--- a/man/annotation_map.Rd
+++ b/man/annotation_map.Rd
@@ -2,7 +2,7 @@
% Please edit documentation in R/annotation-map.r
\name{annotation_map}
\alias{annotation_map}
-\title{Annotation: maps.}
+\title{Annotation: a maps}
\usage{
annotation_map(map, ...)
}
@@ -13,7 +13,7 @@ converted into the right format by using \code{\link{fortify}}}
\item{...}{other arguments used to modify aesthetics}
}
\description{
-Annotation: maps.
+Display a fixed map on a plot.
}
\examples{
if (require("maps")) {
diff --git a/man/annotation_raster.Rd b/man/annotation_raster.Rd
index c56340f..308c56f 100644
--- a/man/annotation_raster.Rd
+++ b/man/annotation_raster.Rd
@@ -2,7 +2,7 @@
% Please edit documentation in R/annotation-raster.r
\name{annotation_raster}
\alias{annotation_raster}
-\title{Annotation: High-performance rectangular tiling.}
+\title{Annotation: high-performance rectangular tiling}
\usage{
annotation_raster(raster, xmin, xmax, ymin, ymax, interpolate = FALSE)
}
@@ -22,10 +22,8 @@ location of raster}
This is a special version of \code{\link{geom_raster}} optimised for static
annotations that are the same in every panel. These annotations will not
affect scales (i.e. the x and y axes will not grow to cover the range
-of the raster, and the raster must already have its own colours).
-}
-\details{
-Most useful for adding bitmap images.
+of the raster, and the raster must already have its own colours). This
+is useful for adding bitmap images.
}
\examples{
# Generate data
diff --git a/man/as.list.ggproto.Rd b/man/as.list.ggproto.Rd
index 8b0a64e..5f633a7 100644
--- a/man/as.list.ggproto.Rd
+++ b/man/as.list.ggproto.Rd
@@ -17,4 +17,5 @@ the returned list. If \code{FALSE}, do not include any inherited items.}
\description{
This will not include the object's \code{super} member.
}
+\keyword{internal}
diff --git a/man/as_labeller.Rd b/man/as_labeller.Rd
index 20c105b..d6ab2c3 100644
--- a/man/as_labeller.Rd
+++ b/man/as_labeller.Rd
@@ -1,5 +1,5 @@
% Generated by roxygen2: do not edit by hand
-% Please edit documentation in R/facet-labels.r
+% Please edit documentation in R/labeller.r
\name{as_labeller}
\alias{as_labeller}
\title{Coerce to labeller function}
@@ -43,4 +43,5 @@ p + facet_grid(cyl ~ am, labeller = labeller(am = to_string))
\seealso{
\code{\link{labeller}()}, \link{labellers}
}
+\keyword{internal}
diff --git a/man/borders.Rd b/man/borders.Rd
index c54b8f9..379797d 100644
--- a/man/borders.Rd
+++ b/man/borders.Rd
@@ -2,7 +2,7 @@
% Please edit documentation in R/fortify-map.r
\name{borders}
\alias{borders}
-\title{Create a layer of map borders.}
+\title{Create a layer of map borders}
\usage{
borders(database = "world", regions = ".", fill = NA, colour = "grey50",
xlim = NULL, ylim = NULL, ...)
@@ -22,7 +22,10 @@ polygons, see \code{\link[maps]{map}} for details.}
\item{...}{other arguments passed onto \code{\link{geom_polygon}}}
}
\description{
-Create a layer of map borders.
+This is a quick and dirty way to get map data (from the maps package)
+on to your plot. This is a good place to start if you need some crude
+reference lines, but you'll typically want something more sophisticated
+for communication graphics.
}
\examples{
if (require("maps")) {
diff --git a/man/calc_element.Rd b/man/calc_element.Rd
index 09e2a78..6ddc259 100644
--- a/man/calc_element.Rd
+++ b/man/calc_element.Rd
@@ -29,6 +29,6 @@ calc_element('axis.text.x', t, verbose = TRUE)
t$axis.text.x
t$axis.text
t$text
-
}
+\keyword{internal}
diff --git a/man/combine_vars.Rd b/man/combine_vars.Rd
new file mode 100644
index 0000000..f3d405c
--- /dev/null
+++ b/man/combine_vars.Rd
@@ -0,0 +1,28 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/facet-.r
+\name{combine_vars}
+\alias{combine_vars}
+\title{Take input data and define a mapping between facetting variables and ROW,
+COL and PANEL keys}
+\usage{
+combine_vars(data, env = emptyenv(), vars = NULL, drop = TRUE)
+}
+\arguments{
+\item{data}{A list of data.frames, the first being the plot data and the
+subsequent individual layer data}
+
+\item{env}{The environment the vars should be evaluated in}
+
+\item{vars}{A list of quoted symbols matching columns in data}
+
+\item{drop}{should missing combinations/levels be dropped}
+}
+\value{
+A data.frame with columns for PANEL, ROW, COL, and facetting vars
+}
+\description{
+Take input data and define a mapping between facetting variables and ROW,
+COL and PANEL keys
+}
+\keyword{internal}
+
diff --git a/man/continuous_scale.Rd b/man/continuous_scale.Rd
index d135fe1..9db1647 100644
--- a/man/continuous_scale.Rd
+++ b/man/continuous_scale.Rd
@@ -7,7 +7,8 @@
continuous_scale(aesthetics, scale_name, palette, name = waiver(),
breaks = waiver(), minor_breaks = waiver(), labels = waiver(),
limits = NULL, rescaler = rescale, oob = censor, expand = waiver(),
- na.value = NA_real_, trans = "identity", guide = "legend")
+ na.value = NA_real_, trans = "identity", guide = "legend",
+ position = "left", super = ScaleContinuous)
}
\arguments{
\item{aesthetics}{the names of the aesthetics that this scale works with}
@@ -78,6 +79,11 @@ discrete variables.}
transformation with \code{\link[scales]{trans_new}}.}
\item{guide}{Name of guide object, or object itself.}
+
+\item{position}{The position of the axis. "left" or "right" for vertical
+scales, "top" or "bottom" for horizontal scales}
+
+\item{super}{The super class to use for the constructed scale}
}
\description{
Continuous scale constructor.
diff --git a/man/coord_cartesian.Rd b/man/coord_cartesian.Rd
index 7d4a6d1..b124dd6 100644
--- a/man/coord_cartesian.Rd
+++ b/man/coord_cartesian.Rd
@@ -2,7 +2,7 @@
% Please edit documentation in R/coord-cartesian-.r
\name{coord_cartesian}
\alias{coord_cartesian}
-\title{Cartesian coordinates.}
+\title{Cartesian coordinates}
\usage{
coord_cartesian(xlim = NULL, ylim = NULL, expand = TRUE)
}
diff --git a/man/coord_fixed.Rd b/man/coord_fixed.Rd
index 1574f91..5d7e578 100644
--- a/man/coord_fixed.Rd
+++ b/man/coord_fixed.Rd
@@ -3,7 +3,7 @@
\name{coord_fixed}
\alias{coord_equal}
\alias{coord_fixed}
-\title{Cartesian coordinates with fixed relationship between x and y scales.}
+\title{Cartesian coordinates with fixed "aspect ratio"}
\usage{
coord_fixed(ratio = 1, xlim = NULL, ylim = NULL, expand = TRUE)
}
diff --git a/man/coord_flip.Rd b/man/coord_flip.Rd
index 234b0c0..ded5e22 100644
--- a/man/coord_flip.Rd
+++ b/man/coord_flip.Rd
@@ -2,7 +2,7 @@
% Please edit documentation in R/coord-flip.r
\name{coord_flip}
\alias{coord_flip}
-\title{Flipped cartesian coordinates.}
+\title{Cartesian coordinates with x and y flipped}
\usage{
coord_flip(xlim = NULL, ylim = NULL, expand = TRUE)
}
@@ -16,7 +16,7 @@ the limits to ensure that data and axes don't overlap. If \code{FALSE},
limits are taken exactly from the data or \code{xlim}/\code{ylim}.}
}
\description{
-Flipped cartesian coordinates so that horizontal becomes vertical, and
+Flip cartesian coordinates so that horizontal becomes vertical, and
vertical, horizontal. This is primarily useful for converting geoms and
statistics which display y conditional on x, to x conditional on y.
}
diff --git a/man/coord_map.Rd b/man/coord_map.Rd
index 0d6170c..8aec5f6 100644
--- a/man/coord_map.Rd
+++ b/man/coord_map.Rd
@@ -3,10 +3,10 @@
\name{coord_map}
\alias{coord_map}
\alias{coord_quickmap}
-\title{Map projections.}
+\title{Map projections}
\usage{
-coord_map(projection = "mercator", ..., orientation = NULL, xlim = NULL,
- ylim = NULL)
+coord_map(projection = "mercator", ..., parameters = NULL,
+ orientation = NULL, xlim = NULL, ylim = NULL)
coord_quickmap(xlim = NULL, ylim = NULL, expand = TRUE)
}
@@ -14,40 +14,42 @@ coord_quickmap(xlim = NULL, ylim = NULL, expand = TRUE)
\item{projection}{projection to use, see
\code{\link[mapproj]{mapproject}} for list}
-\item{...}{other arguments passed on to
-\code{\link[mapproj]{mapproject}}}
+\item{..., parameters}{Other arguments passed on to
+\code{\link[mapproj]{mapproject}}. Use \code{...} for named parameters to
+the projection, and \code{parameters} for unnamed parameters.
+\code{...} is ignored if the \code{parameters} argument is present.}
\item{orientation}{projection orientation, which defaults to
\code{c(90, 0, mean(range(x)))}. This is not optimal for many
projections, so you will have to supply your own. See
\code{\link[mapproj]{mapproject}} for more information.}
-\item{xlim}{manually specific x limits (in degrees of longitude)}
-
-\item{ylim}{manually specific y limits (in degrees of latitude)}
+\item{xlim, ylim}{Manually specific x/y limits (in degrees of
+longitude/latitude)}
\item{expand}{If \code{TRUE}, the default, adds a small expansion factor to
the limits to ensure that data and axes don't overlap. If \code{FALSE},
limits are taken exactly from the data or \code{xlim}/\code{ylim}.}
}
\description{
-The representation of a portion of the earth, which is approximately spherical,
-onto a flat 2D plane requires a projection. This is what
-\code{\link{coord_map}} does. These projections account for the fact that the
-actual length (in km) of one degree of longitude varies between the equator
-and the pole. Near the equator, the ratio between the lengths of one degree
-of latitude and one degree of longitude is approximately 1. Near the pole, it
-is tends towards infinity because the length of one degree of longitude tends
-towards 0. For regions that span only a few degrees and are not too close to
-the poles, setting the aspect ratio of the plot to the appropriate lat/lon
-ratio approximates the usual mercator projection. This is what
-\code{coord_quickmap} does. With \code{\link{coord_map}} all elements of the
-graphic have to be projected which is not the case here. So
-\code{\link{coord_quickmap}} has the advantage of being much faster, in
-particular for complex plots such as those using with
-\code{\link{geom_tile}}, at the expense of correctness in the projection.
-This coordinate system provides the full range of map projections available
-in the mapproj package.
+\code{coord_map} projects a portion of the earth, which is approximately
+spherical, onto a flat 2D plane using any projection defined by the
+\code{mapproj} package. Map projections do not, in general, preserve straight
+lines, so this requires considerable computation. \code{coord_quickmap} is a
+quick approximation that does preserve straight lines. It works best for
+smaller areas closer to the equator.
+}
+\details{
+In general, map projections must account for the fact that the actual length
+(in km) of one degree of longitude varies between the equator and the pole.
+Near the equator, the ratio between the lengths of one degree of latitude and
+one degree of longitude is approximately 1. Near the pole, it is tends
+towards infinity because the length of one degree of longitude tends towards
+0. For regions that span only a few degrees and are not too close to the
+poles, setting the aspect ratio of the plot to the appropriate lat/lon ratio
+approximates the usual mercator projection. This is what
+\code{coord_quickmap} does, and is much faster (particularly for complex
+plots like \code{\link{geom_tile}}) at the expense of correctness.
}
\examples{
if (require("maps")) {
@@ -65,7 +67,8 @@ nzmap + coord_quickmap()
# Other projections
nzmap + coord_map("cylindrical")
-nzmap + coord_map("azequalarea",orientation=c(-36.92,174.6,0))
+nzmap + coord_map("azequalarea", orientation = c(-36.92, 174.6, 0))
+nzmap + coord_map("lambert", parameters = c(-37, -44))
states <- map_data("state")
usamap <- ggplot(states, aes(long, lat, group = group)) +
@@ -90,7 +93,7 @@ usamap + coord_map("bonne", lat0 = 50)
# World map, using geom_path instead of geom_polygon
world <- map_data("world")
-worldmap <- ggplot(world, aes(x=long, y=lat, group=group)) +
+worldmap <- ggplot(world, aes(x = long, y = lat, group = group)) +
geom_path() +
scale_y_continuous(breaks = (-2:2) * 30) +
scale_x_continuous(breaks = (-4:4) * 45)
diff --git a/man/coord_polar.Rd b/man/coord_polar.Rd
index 49550c9..1219b96 100644
--- a/man/coord_polar.Rd
+++ b/man/coord_polar.Rd
@@ -2,7 +2,7 @@
% Please edit documentation in R/coord-polar.r
\name{coord_polar}
\alias{coord_polar}
-\title{Polar coordinates.}
+\title{Polar coordinates}
\usage{
coord_polar(theta = "x", start = 0, direction = 1)
}
@@ -46,7 +46,7 @@ df <- data.frame(
value = c(20, 80)
)
ggplot(df, aes(x = "", y = value, fill = variable)) +
- geom_bar(width = 1, stat = "identity") +
+ geom_col(width = 1) +
scale_fill_manual(values = c("red", "yellow")) +
coord_polar("y", start = pi / 3) +
labs(title = "Pac man")
diff --git a/man/coord_trans.Rd b/man/coord_trans.Rd
index 50aa82e..83f5c21 100644
--- a/man/coord_trans.Rd
+++ b/man/coord_trans.Rd
@@ -2,7 +2,7 @@
% Please edit documentation in R/coord-transform.r
\name{coord_trans}
\alias{coord_trans}
-\title{Transformed cartesian coordinate system.}
+\title{Transformed Cartesian coordinate system}
\usage{
coord_trans(x = "identity", y = "identity", limx = NULL, limy = NULL,
xtrans, ytrans)
@@ -21,9 +21,9 @@ statistical transformation and will affect the visual appearance of geoms - ther
no guarantee that straight lines will continue to be straight.
}
\details{
-All current transformations only work with continuous values - see
-\code{\link[scales]{trans_new}} for list of transformations, and instructions on
-how to create your own.
+Transformations only work with continuous values: see
+\code{\link[scales]{trans_new}} for list of transformations, and instructions
+on how to create your own.
}
\examples{
\donttest{
diff --git a/man/cut_interval.Rd b/man/cut_interval.Rd
index 27ef250..91e531c 100644
--- a/man/cut_interval.Rd
+++ b/man/cut_interval.Rd
@@ -4,7 +4,7 @@
\alias{cut_interval}
\alias{cut_number}
\alias{cut_width}
-\title{Cut up numeric vector into useful groups.}
+\title{Discretise numeric data into categorical}
\usage{
cut_interval(x, n = NULL, length = NULL, ...)
diff --git a/man/diamonds.Rd b/man/diamonds.Rd
index 08e61e9..e0c24d3 100644
--- a/man/diamonds.Rd
+++ b/man/diamonds.Rd
@@ -5,18 +5,18 @@
\alias{diamonds}
\title{Prices of 50,000 round cut diamonds}
\format{A data frame with 53940 rows and 10 variables:
-\itemize{
- \item price: price in US dollars (\$326--\$18,823)
- \item carat: weight of the diamond (0.2--5.01)
- \item cut: quality of the cut (Fair, Good, Very Good, Premium, Ideal)
- \item color: diamond colour, from J (worst) to D (best)
- \item clarity: a measurement of how clear the diamond is
- (I1 (worst), SI1, SI2, VS1, VS2, VVS1, VVS2, IF (best))
- \item x: length in mm (0--10.74)
- \item y: width in mm (0--58.9)
- \item z: depth in mm (0--31.8)
- \item depth: total depth percentage = z / mean(x, y) = 2 * z / (x + y) (43--79)
- \item table: width of top of diamond relative to widest point (43--95)
+\describe{
+ \item{price}{price in US dollars (\$326--\$18,823)}
+ \item{carat}{weight of the diamond (0.2--5.01)}
+ \item{cut}{quality of the cut (Fair, Good, Very Good, Premium, Ideal)}
+ \item{color}{diamond colour, from J (worst) to D (best)}
+ \item{clarity}{a measurement of how clear the diamond is (I1 (worst), SI1,
+ SI2, VS1, VS2, VVS1, VVS2, IF (best))}
+ \item{x}{length in mm (0--10.74)}
+ \item{y}{width in mm (0--58.9)}
+ \item{z}{depth in mm (0--31.8)}
+ \item{depth}{total depth percentage = z / mean(x, y) = 2 * z / (x + y) (43--79)}
+ \item{table}{width of top of diamond relative to widest point (43--95)}
}}
\usage{
diamonds
diff --git a/man/discrete_scale.Rd b/man/discrete_scale.Rd
index 46b4a9e..a42bba7 100644
--- a/man/discrete_scale.Rd
+++ b/man/discrete_scale.Rd
@@ -6,7 +6,8 @@
\usage{
discrete_scale(aesthetics, scale_name, palette, name = waiver(),
breaks = waiver(), labels = waiver(), limits = NULL,
- expand = waiver(), na.value = NA, drop = TRUE, guide = "legend")
+ expand = waiver(), na.translate = TRUE, na.value = NA, drop = TRUE,
+ guide = "legend", position = "left", super = ScaleDiscrete)
}
\arguments{
\item{aesthetics}{the names of the aesthetics that this scale works with}
@@ -47,7 +48,13 @@ additive constant used to expand the range of the scales so that there
is a small gap between the data and the axes. The defaults are (0,0.6)
for discrete scales and (0.05,0) for continuous scales.}
-\item{na.value}{how should missing values be displayed?}
+\item{na.translate}{Unlike continuous scales, discrete scales can easily show
+missing values, and do so by default. If you want to remove missing values
+from a discrete scale, specify \code{na.translate = FALSE}.}
+
+\item{na.value}{If \code{na.translate = TRUE}, what value aesthetic
+value should missing be displayed as? Does not apply to position scales
+where \code{NA} is always placed at the far right.}
\item{drop}{Should unused factor levels be omitted from the scale?
The default, \code{TRUE}, uses the levels that appear in the data;
@@ -55,6 +62,11 @@ The default, \code{TRUE}, uses the levels that appear in the data;
\item{guide}{the name of, or actual function, used to create the
guide. See \code{\link{guides}} for more info.}
+
+\item{position}{The position of the axis. "left" or "right" for vertical
+scales, "top" or "bottom" for horizontal scales}
+
+\item{super}{The super class to use for the constructed scale}
}
\description{
Discrete scale constructor.
diff --git a/man/economics.Rd b/man/economics.Rd
index 921a79b..780d182 100644
--- a/man/economics.Rd
+++ b/man/economics.Rd
@@ -4,20 +4,20 @@
\name{economics}
\alias{economics}
\alias{economics_long}
-\title{US economic time series.}
+\title{US economic time series}
\format{A data frame with 478 rows and 6 variables
-\itemize{
- \item date. Month of data collection
- \item psavert, personal savings rate,
- \url{http://research.stlouisfed.org/fred2/series/PSAVERT/}
- \item pce, personal consumption expenditures, in billions of dollars,
- \url{http://research.stlouisfed.org/fred2/series/PCE}
- \item unemploy, number of unemployed in thousands,
- \url{http://research.stlouisfed.org/fred2/series/UNEMPLOY}
- \item uempmed, median duration of unemployment, in week,
- \url{http://research.stlouisfed.org/fred2/series/UEMPMED}
- \item pop, total population, in thousands,
- \url{http://research.stlouisfed.org/fred2/series/POP}
+\describe{
+ \item{date}{Month of data collection}
+ \item{psavert}{personal savings rate,
+ \url{http://research.stlouisfed.org/fred2/series/PSAVERT/}}
+ \item{pce}{personal consumption expenditures, in billions of dollars,
+ \url{http://research.stlouisfed.org/fred2/series/PCE}}
+ \item{unemploy}{number of unemployed in thousands,
+ \url{http://research.stlouisfed.org/fred2/series/UNEMPLOY}}
+ \item{uempmed}{median duration of unemployment, in weeks,
+ \url{http://research.stlouisfed.org/fred2/series/UEMPMED}}
+ \item{pop}{total population, in thousands,
+ \url{http://research.stlouisfed.org/fred2/series/POP}}
}}
\usage{
economics
diff --git a/man/element.Rd b/man/element.Rd
new file mode 100644
index 0000000..0df5c1e
--- /dev/null
+++ b/man/element.Rd
@@ -0,0 +1,120 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/margins.R, R/theme-elements.r
+\name{margin}
+\alias{element_blank}
+\alias{element_line}
+\alias{element_rect}
+\alias{element_text}
+\alias{margin}
+\alias{rel}
+\title{Theme elements}
+\usage{
+margin(t = 0, r = 0, b = 0, l = 0, unit = "pt")
+
+element_blank()
+
+element_rect(fill = NULL, colour = NULL, size = NULL, linetype = NULL,
+ color = NULL, inherit.blank = FALSE)
+
+element_line(colour = NULL, size = NULL, linetype = NULL,
+ lineend = NULL, color = NULL, arrow = NULL, inherit.blank = FALSE)
+
+element_text(family = NULL, face = NULL, colour = NULL, size = NULL,
+ hjust = NULL, vjust = NULL, angle = NULL, lineheight = NULL,
+ color = NULL, margin = NULL, debug = NULL, inherit.blank = FALSE)
+
+rel(x)
+}
+\arguments{
+\item{t, r, b, l}{Dimensions of each margin. (To remember order, think trouble).}
+
+\item{unit}{Default units of dimensions. Defaults to "pt" so it
+can be most easily scaled with the text.}
+
+\item{fill}{Fill colour.}
+
+\item{colour, color}{Line/border colour. Color is an alias for colour.}
+
+\item{size}{Line/border size in mm; text size in pts.}
+
+\item{linetype}{Line type. An integer (0:8), a name (blank, solid,
+dashed, dotted, dotdash, longdash, twodash), or a string with
+an even number (up to eight) of hexadecimal digits which give the
+lengths in consecutive positions in the string.}
+
+\item{inherit.blank}{Should this element inherit the existence of an
+\code{element_blank} among its parents? If \code{TRUE} the existence of
+a blank element among its parents will cause this element to be blank as
+well. If \code{FALSE} any blank parent element will be ignored when
+calculating final element state.}
+
+\item{lineend}{Line end Line end style (round, butt, square)}
+
+\item{arrow}{Arrow specification, as created by \code{\link[grid]{arrow}}}
+
+\item{family}{Font family}
+
+\item{face}{Font face ("plain", "italic", "bold", "bold.italic")}
+
+\item{hjust}{Horizontal justification (in [0, 1])}
+
+\item{vjust}{Vertical justification (in [0, 1])}
+
+\item{angle}{Angle (in [0, 360])}
+
+\item{lineheight}{Line height}
+
+\item{margin}{Margins around the text. See \code{\link{margin}} for more
+details. When creating a theme, the margins should be placed on the
+side of the text facing towards the center of the plot.}
+
+\item{debug}{If \code{TRUE}, aids visual debugging by drawing a solid
+rectangle behind the complete text area, and a point where each label
+is anchored.}
+
+\item{x}{A single number specifying size relative to parent element.}
+}
+\value{
+An S3 object of class \code{element}, \code{rel}, or \code{margin}.
+}
+\description{
+In conjunction with the \link{theme} system, the \code{element_} functions
+specify the display of how non-data components of the plot are a drawn.
+
+\itemize{
+ \item \code{element_blank}: draws nothing, and assigns no space.
+ \item \code{element_rect}: borders and backgrounds.
+ \item \code{element_line}: lines.
+ \item \code{element_text}: text.
+}
+
+\code{rel()} is used to specify sizes relative to the parent,
+\code{margins()} is used to specify the margins of elements.
+}
+\examples{
+plot <- ggplot(mpg, aes(displ, hwy)) + geom_point()
+
+plot + theme(
+ panel.background = element_blank(),
+ axis.text = element_blank()
+)
+
+plot + theme(
+ axis.text = element_text(colour = "red", size = rel(1.5))
+)
+
+plot + theme(
+ axis.line = element_line(arrow = arrow())
+)
+
+plot + theme(
+ panel.background = element_rect(fill = "white"),
+ plot.margin = margin(2, 2, 2, 2, "cm"),
+ plot.background = element_rect(
+ fill = "grey90",
+ colour = "black",
+ size = 1
+ )
+)
+}
+
diff --git a/man/element_blank.Rd b/man/element_blank.Rd
deleted file mode 100644
index 042adcb..0000000
--- a/man/element_blank.Rd
+++ /dev/null
@@ -1,14 +0,0 @@
-% Generated by roxygen2: do not edit by hand
-% Please edit documentation in R/theme-elements.r
-\name{element_blank}
-\alias{element_blank}
-\title{Theme element: blank.
-This theme element draws nothing, and assigns no space}
-\usage{
-element_blank()
-}
-\description{
-Theme element: blank.
-This theme element draws nothing, and assigns no space
-}
-
diff --git a/man/element_line.Rd b/man/element_line.Rd
deleted file mode 100644
index 1f2ce79..0000000
--- a/man/element_line.Rd
+++ /dev/null
@@ -1,24 +0,0 @@
-% Generated by roxygen2: do not edit by hand
-% Please edit documentation in R/theme-elements.r
-\name{element_line}
-\alias{element_line}
-\title{Theme element: line.}
-\usage{
-element_line(colour = NULL, size = NULL, linetype = NULL,
- lineend = NULL, color = NULL)
-}
-\arguments{
-\item{colour}{line colour}
-
-\item{size}{line size}
-
-\item{linetype}{line type}
-
-\item{lineend}{line end}
-
-\item{color}{an alias for \code{colour}}
-}
-\description{
-Theme element: line.
-}
-
diff --git a/man/element_rect.Rd b/man/element_rect.Rd
deleted file mode 100644
index bc034f2..0000000
--- a/man/element_rect.Rd
+++ /dev/null
@@ -1,24 +0,0 @@
-% Generated by roxygen2: do not edit by hand
-% Please edit documentation in R/theme-elements.r
-\name{element_rect}
-\alias{element_rect}
-\title{Theme element: rectangle.}
-\usage{
-element_rect(fill = NULL, colour = NULL, size = NULL, linetype = NULL,
- color = NULL)
-}
-\arguments{
-\item{fill}{fill colour}
-
-\item{colour}{border colour}
-
-\item{size}{border size}
-
-\item{linetype}{border linetype}
-
-\item{color}{an alias for \code{colour}}
-}
-\description{
-Most often used for backgrounds and borders.
-}
-
diff --git a/man/element_text.Rd b/man/element_text.Rd
deleted file mode 100644
index 55433b4..0000000
--- a/man/element_text.Rd
+++ /dev/null
@@ -1,41 +0,0 @@
-% Generated by roxygen2: do not edit by hand
-% Please edit documentation in R/theme-elements.r
-\name{element_text}
-\alias{element_text}
-\title{Theme element: text.}
-\usage{
-element_text(family = NULL, face = NULL, colour = NULL, size = NULL,
- hjust = NULL, vjust = NULL, angle = NULL, lineheight = NULL,
- color = NULL, margin = NULL, debug = NULL)
-}
-\arguments{
-\item{family}{font family}
-
-\item{face}{font face ("plain", "italic", "bold", "bold.italic")}
-
-\item{colour}{text colour}
-
-\item{size}{text size (in pts)}
-
-\item{hjust}{horizontal justification (in [0, 1])}
-
-\item{vjust}{vertical justification (in [0, 1])}
-
-\item{angle}{angle (in [0, 360])}
-
-\item{lineheight}{line height}
-
-\item{color}{an alias for \code{colour}}
-
-\item{margin}{margins around the text. See \code{\link{margin}} for more
-details. When creating a theme, the margins should be placed on the
-side of the text facing towards the center of the plot.}
-
-\item{debug}{If \code{TRUE}, aids visual debugging by drawing a solid
-rectangle behind the complete text area, and a point where each label
-is anchored.}
-}
-\description{
-Theme element: text.
-}
-
diff --git a/man/expand_limits.Rd b/man/expand_limits.Rd
index 1759ffc..4ce3190 100644
--- a/man/expand_limits.Rd
+++ b/man/expand_limits.Rd
@@ -2,7 +2,7 @@
% Please edit documentation in R/limits.r
\name{expand_limits}
\alias{expand_limits}
-\title{Expand the plot limits with data.}
+\title{Expand the plot limits, using data}
\usage{
expand_limits(...)
}
@@ -11,6 +11,7 @@ expand_limits(...)
should be included in each scale.}
}
\description{
+Sometimes you may want to ensure limits include a single value, for all
panels or all plots. This function is a thin wrapper around
\code{\link{geom_blank}} that makes it easy to add such values.
}
diff --git a/man/facet.Rd b/man/facet.Rd
deleted file mode 100644
index da462e8..0000000
--- a/man/facet.Rd
+++ /dev/null
@@ -1,18 +0,0 @@
-% Generated by roxygen2: do not edit by hand
-% Please edit documentation in R/facet-.r
-\name{facet}
-\alias{facet}
-\title{Facet specification.}
-\usage{
-facet(..., shrink = TRUE, subclass = c())
-}
-\arguments{
-\item{...}{object fields}
-
-\item{shrink}{shrink scales to fit output of statistics, not raw data}
-}
-\description{
-Create new facetting specification. For internal use only.
-}
-\keyword{internal}
-
diff --git a/man/facet_grid.Rd b/man/facet_grid.Rd
index f099188..2e5d616 100644
--- a/man/facet_grid.Rd
+++ b/man/facet_grid.Rd
@@ -2,7 +2,7 @@
% Please edit documentation in R/facet-grid-.r
\name{facet_grid}
\alias{facet_grid}
-\title{Lay out panels in a grid.}
+\title{Lay out panels in a grid}
\usage{
facet_grid(facets, margins = FALSE, scales = "fixed", space = "fixed",
shrink = TRUE, labeller = "label_value", as.table = TRUE,
@@ -62,7 +62,9 @@ data will automatically be dropped. If \code{FALSE}, all factor levels
will be shown, regardless of whether or not they appear in the data.}
}
\description{
-Lay out panels in a grid.
+\code{facet_grid} forms a matrix of panels defined by row and column
+facetting variables. It is most useful when you have two discrete
+variables, and all combinations of the variables exist in the data.
}
\examples{
p <- ggplot(mpg, aes(displ, cty)) + geom_point()
diff --git a/man/facet_null.Rd b/man/facet_null.Rd
index c1b302b..a9a230a 100644
--- a/man/facet_null.Rd
+++ b/man/facet_null.Rd
@@ -19,4 +19,5 @@ Facet specification: a single panel.
# don't override it with facet_grid or facet_wrap
ggplot(mtcars, aes(mpg, wt)) + geom_point()
}
+\keyword{internal}
diff --git a/man/facet_wrap.Rd b/man/facet_wrap.Rd
index c20baa7..b70c374 100644
--- a/man/facet_wrap.Rd
+++ b/man/facet_wrap.Rd
@@ -2,11 +2,11 @@
% Please edit documentation in R/facet-wrap.r
\name{facet_wrap}
\alias{facet_wrap}
-\title{Wrap a 1d ribbon of panels into 2d.}
+\title{Wrap a 1d ribbon of panels into 2d}
\usage{
facet_wrap(facets, nrow = NULL, ncol = NULL, scales = "fixed",
shrink = TRUE, labeller = "label_value", as.table = TRUE,
- switch = NULL, drop = TRUE, dir = "h")
+ switch = NULL, drop = TRUE, dir = "h", strip.position = "top")
}
\arguments{
\item{facets}{Either a formula or character vector. Use either a
@@ -36,10 +36,11 @@ options.}
a table with highest values at the bottom-right. If \code{FALSE}, the
facets are laid out like a plot with the highest value at the top-right.}
-\item{switch}{By default, the labels are displayed on the top of
-the plot. If \code{switch} is \code{"x"}, they will be displayed
-to the bottom. If \code{"y"}, they will be displayed to the
-left, near the y axis.}
+\item{switch}{By default, the labels are displayed on the top and
+right of the plot. If \code{"x"}, the top labels will be
+displayed to the bottom. If \code{"y"}, the right-hand side
+labels will be displayed to the left. Can also be set to
+\code{"both"}.}
\item{drop}{If \code{TRUE}, the default, all factor levels not used in the
data will automatically be dropped. If \code{FALSE}, all factor levels
@@ -47,12 +48,16 @@ will be shown, regardless of whether or not they appear in the data.}
\item{dir}{Direction: either "h" for horizontal, the default, or "v", for
vertical.}
+
+\item{strip.position}{By default, the labels are displayed on the top of
+the plot. Using \code{strip.position} it is possible to place the labels on
+either of the four sides by setting \code{strip.position = c("top",
+"bottom", "left", "right")}}
}
\description{
-Most displays are roughly rectangular, so if you have a categorical
-variable with many levels, it doesn't make sense to try and display them
-all in one row (or one column). To solve this dilemma, \code{facet_wrap}
-wraps a 1d sequence of panels into 2d, making best use of screen real estate.
+\code{facet_wrap} wraps a 1d sequence of panels into 2d. This is generally
+a better use of screen space than \code{\link{facet_grid}} because most
+displays are roughly rectangular.
}
\examples{
ggplot(mpg, aes(displ, hwy)) +
@@ -101,13 +106,14 @@ ggplot(mpg, aes(displ, hwy)) +
geom_point() +
facet_wrap(~class)
-# Use `switch` to display the facet labels near an axis, acting as
-# a subtitle for this axis. This is typically used with free scales
-# and a theme without boxes around strip labels.
+# Use `strip.position` to display the facet labels at the side of your
+# choice. Setting it to `bottom` makes it act as a subtitle for the axis.
+# This is typically used with free scales and a theme without boxes around
+# strip labels.
ggplot(economics_long, aes(date, value)) +
geom_line() +
- facet_wrap(~variable, scales = "free_y", nrow = 2, switch = "x") +
- theme(strip.background = element_blank())
+ facet_wrap(~variable, scales = "free_y", nrow = 2, strip.position = "bottom") +
+ theme(strip.background = element_blank(), strip.placement = "outside")
}
}
diff --git a/man/find_panel.Rd b/man/find_panel.Rd
new file mode 100644
index 0000000..0587b07
--- /dev/null
+++ b/man/find_panel.Rd
@@ -0,0 +1,29 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/facet-.r
+\name{find_panel}
+\alias{find_panel}
+\alias{panel_cols}
+\alias{panel_rows}
+\title{Find panels in a gtable}
+\usage{
+find_panel(table)
+
+panel_cols(table)
+
+panel_rows(table)
+}
+\arguments{
+\item{table}{A gtable}
+}
+\value{
+A data.frame with some or all of the columns t(op), r(ight),
+b(ottom), and l(eft)
+}
+\description{
+These functions help detect the placement of panels in a gtable, if they are
+named with "panel" in the beginning. \code{find_panel} returns the extend of
+the panel area, while \code{panel_cols} and \code{panel_rows} returns the
+columns and rows that contains panels respectively.
+}
+\keyword{internal}
+
diff --git a/man/format.ggproto.Rd b/man/format.ggproto.Rd
deleted file mode 100644
index e2c12ec..0000000
--- a/man/format.ggproto.Rd
+++ /dev/null
@@ -1,21 +0,0 @@
-% Generated by roxygen2: do not edit by hand
-% Please edit documentation in R/ggproto.r
-\name{format.ggproto}
-\alias{format.ggproto}
-\title{Format a ggproto object}
-\usage{
-\method{format}{ggproto}(x, ..., flat = TRUE)
-}
-\arguments{
-\item{x}{A ggproto object to print.}
-
-\item{...}{If the ggproto object has a \code{print} method, further arguments
-will be passed to it. Otherwise, these arguments are unused.}
-
-\item{flat}{If \code{TRUE} (the default), show a flattened list of all local
-and inherited members. If \code{FALSE}, show the inheritance hierarchy.}
-}
-\description{
-Format a ggproto object
-}
-
diff --git a/man/fortify-multcomp.Rd b/man/fortify-multcomp.Rd
index b2174c2..078256d 100644
--- a/man/fortify-multcomp.Rd
+++ b/man/fortify-multcomp.Rd
@@ -48,4 +48,5 @@ cld <- cld(wht)
fortify(cld)
}
}
+\keyword{internal}
diff --git a/man/fortify.Rd b/man/fortify.Rd
index e80157f..6262f14 100644
--- a/man/fortify.Rd
+++ b/man/fortify.Rd
@@ -14,7 +14,7 @@ fortify(model, data, ...)
\item{...}{other arguments passed to methods}
}
\description{
-Rather than using this function, I now recomend using the \pkg{broom}
+Rather than using this function, I now recommend using the \pkg{broom}
package, which implements a much wider range of methods. \code{fortify}
may be deprecated in the future.
}
diff --git a/man/fortify.lm.Rd b/man/fortify.lm.Rd
index f4e5e3f..4aea9f2 100644
--- a/man/fortify.lm.Rd
+++ b/man/fortify.lm.Rd
@@ -62,7 +62,7 @@ ggplot(mod, aes(.fitted, sqrt(abs(.stdresid)))) +
plot(mod, which = 4)
ggplot(mod, aes(seq_along(.cooksd), .cooksd)) +
- geom_bar(stat = "identity")
+ geom_col()
plot(mod, which = 5)
ggplot(mod, aes(.hat, .stdresid)) +
@@ -85,4 +85,5 @@ ggplot(mod, aes(.hat, .cooksd)) +
geom_point(aes(size = .cooksd / .hat)) +
scale_size_area()
}
+\keyword{internal}
diff --git a/man/fortify.map.Rd b/man/fortify.map.Rd
index 84454ac..fd5f015 100644
--- a/man/fortify.map.Rd
+++ b/man/fortify.map.Rd
@@ -2,7 +2,7 @@
% Please edit documentation in R/fortify-map.r
\name{fortify.map}
\alias{fortify.map}
-\title{Fortify method for map objects.}
+\title{Fortify method for map objects}
\usage{
\method{fortify}{map}(model, data, ...)
}
@@ -33,4 +33,5 @@ ggplot(tx, aes(long, lat)) +
\seealso{
\code{\link{map_data}} and \code{\link{borders}}
}
+\keyword{internal}
diff --git a/man/fortify.sp.Rd b/man/fortify.sp.Rd
index 211b19f..8c67351 100644
--- a/man/fortify.sp.Rd
+++ b/man/fortify.sp.Rd
@@ -46,4 +46,5 @@ if (require("maptools")) {
nc1_df <- fortify(nc1)
}
}
+\keyword{internal}
diff --git a/man/geom_abline.Rd b/man/geom_abline.Rd
index 8fb31c2..a9aaf65 100644
--- a/man/geom_abline.Rd
+++ b/man/geom_abline.Rd
@@ -4,7 +4,7 @@
\alias{geom_abline}
\alias{geom_hline}
\alias{geom_vline}
-\title{Lines: horizontal, vertical, and specified by slope and intercept.}
+\title{Reference lines: horizontal, vertical, and diagonal}
\usage{
geom_abline(mapping = NULL, data = NULL, ..., slope, intercept,
na.rm = FALSE, show.legend = NA)
@@ -40,8 +40,8 @@ often aesthetics, used to set an aesthetic to a fixed value, like
\code{color = "red"} or \code{size = 3}. They may also be parameters
to the paired geom/stat.}
-\item{na.rm}{If \code{FALSE} (the default), removes missing values with
-a warning. If \code{TRUE} silently removes missing values.}
+\item{na.rm}{If \code{FALSE}, the default, missing values are removed with
+a warning. If \code{TRUE}, missing values are silently removed.}
\item{show.legend}{logical. Should this layer be included in the legends?
\code{NA}, the default, includes if any aesthetics are mapped.
@@ -52,9 +52,9 @@ position of the line. If these are set, \code{data}, \code{mapping} and
\code{show.legend} are overridden}
}
\description{
-These paired geoms and stats add straight lines to a plot, either
-horizontal, vertical or specified by slope and intercept. These are useful
-for annotating plots.
+These geoms add reference lines (sometimes called rules) to a plot, either
+horizontal, vertical, or diagonal (specified by slope and intercept).
+These are useful for annotating plots.
}
\details{
These geoms act slightly different to other geoms. You can supply the
@@ -72,8 +72,9 @@ commonly set in the plot. They also do not affect the x and y scales.
\section{Aesthetics}{
These geoms are drawn using with \code{\link{geom_line}} so support the
-same aesthetics: alpha, colour, linetype and size. They also each have
-aesthetics that control the position of the line:
+same aesthetics: \code{alpha}, \code{colour}, \code{linetype} and
+\code{size}. They also each have aesthetics that control the position of
+the line:
\itemize{
\item \code{geom_vline}: \code{xintercept}
diff --git a/man/geom_bar.Rd b/man/geom_bar.Rd
index bfeec9b..1d303ae 100644
--- a/man/geom_bar.Rd
+++ b/man/geom_bar.Rd
@@ -1,14 +1,18 @@
% Generated by roxygen2: do not edit by hand
-% Please edit documentation in R/geom-bar.r, R/stat-count.r
+% Please edit documentation in R/geom-bar.r, R/geom-col.r, R/stat-count.r
\name{geom_bar}
\alias{geom_bar}
+\alias{geom_col}
\alias{stat_count}
-\title{Bars, rectangles with bases on x-axis}
+\title{Bars charts}
\usage{
geom_bar(mapping = NULL, data = NULL, stat = "count",
position = "stack", ..., width = NULL, binwidth = NULL, na.rm = FALSE,
show.legend = NA, inherit.aes = TRUE)
+geom_col(mapping = NULL, data = NULL, position = "stack", ...,
+ width = NULL, na.rm = FALSE, show.legend = NA, inherit.aes = TRUE)
+
stat_count(mapping = NULL, data = NULL, geom = "bar",
position = "stack", ..., width = NULL, na.rm = FALSE,
show.legend = NA, inherit.aes = TRUE)
@@ -47,8 +51,8 @@ to the paired geom/stat.}
you use it you'll get an warning telling to you use
\code{\link{geom_histogram}} instead.}
-\item{na.rm}{If \code{FALSE} (the default), removes missing values with
-a warning. If \code{TRUE} silently removes missing values.}
+\item{na.rm}{If \code{FALSE}, the default, missing values are removed with
+a warning. If \code{TRUE}, missing values are silently removed.}
\item{show.legend}{logical. Should this layer be included in the legends?
\code{NA}, the default, includes if any aesthetics are mapped.
@@ -63,33 +67,31 @@ the default plot specification, e.g. \code{\link{borders}}.}
\code{stat_count}.}
}
\description{
-There are two types of bar charts, determined by what is mapped to bar
-height. By default, \code{geom_bar} uses \code{stat="count"} which makes the
-height of the bar proportion to the number of cases in each group (or if the
+There are two types of bar charts: \code{geom_bar} makes the height of the
+bar proportional to the number of cases in each group (or if the
\code{weight} aethetic is supplied, the sum of the weights). If you want the
heights of the bars to represent values in the data, use
-\code{stat="identity"} and map a variable to the \code{y} aesthetic.
-
-\code{stat_count} counts the number of cases at each x position. If you want
-to bin the data in ranges, you should use \code{\link{stat_bin}} instead.
+\link{geom_col} instead. \code{geom_bar} uses \code{stat_count} by
+default: it counts the number of cases at each x position. \code{geom_col}
+uses \code{stat_identity}: it leaves the data as is.
}
\details{
-A bar chart maps the height of the bar to a variable, and so the base of the
+A bar chart uses height to represent a value, and so the base of the
bar must always be shown to produce a valid visual comparison. Naomi Robbins
has a nice
\href{http://www.b-eye-network.com/view/index.php?cid=2468}{article on this
topic}. This is why it doesn't make sense to use a log-scaled y axis with a
bar chart.
-By default, multiple x's occurring in the same place will be stacked atop one
-another by \code{\link{position_stack}}. If you want them to be dodged
-side-to-side, see \code{\link{position_dodge}}. Finally,
-\code{\link{position_fill}} shows relative proportions at each x by stacking
-the bars and then stretching or squashing to the same height.
+By default, multiple bar occupying the same \code{x} position will be
+stacked atop one another by \code{\link{position_stack}}. If you want them
+to be dodged side-to-side, use \code{\link{position_dodge}}. Finally,
+\code{\link{position_fill}} shows relative proportions at each \code{x} by
+stacking the bars and then standardising each bar to have the same height.
}
\section{Aesthetics}{
- \Sexpr[results=rd,stage=build]{ggplot2:::rd_aesthetics("geom", "bar")}
+\aesthetics{geom}{bar}
}
\section{Computed variables}{
@@ -108,11 +110,12 @@ g + geom_bar()
# Total engine displacement of each class
g + geom_bar(aes(weight = displ))
-# To show (e.g.) means, you need stat = "identity"
+# To show (e.g.) means, you need geom_col()
+# And, even more succinctly with geom_col()
df <- data.frame(trt = c("a", "b", "c"), outcome = c(2.3, 1.9, 3.2))
ggplot(df, aes(trt, outcome)) +
- geom_bar(stat = "identity")
-# But geom_point() display exactly the same information and doesn't
+ geom_col()
+# But geom_point() displays exactly the same information and doesn't
# require the y-axis to touch zero.
ggplot(df, aes(trt, outcome)) +
geom_point()
diff --git a/man/geom_bin2d.Rd b/man/geom_bin2d.Rd
index d87d064..d2f648c 100644
--- a/man/geom_bin2d.Rd
+++ b/man/geom_bin2d.Rd
@@ -4,7 +4,7 @@
\alias{geom_bin2d}
\alias{stat_bin2d}
\alias{stat_bin_2d}
-\title{Add heatmap of 2d bin counts.}
+\title{Heatmap of 2d bin counts}
\usage{
geom_bin2d(mapping = NULL, data = NULL, stat = "bin2d",
position = "identity", ..., na.rm = FALSE, show.legend = NA,
@@ -42,8 +42,8 @@ often aesthetics, used to set an aesthetic to a fixed value, like
\code{color = "red"} or \code{size = 3}. They may also be parameters
to the paired geom/stat.}
-\item{na.rm}{If \code{FALSE} (the default), removes missing values with
-a warning. If \code{TRUE} silently removes missing values.}
+\item{na.rm}{If \code{FALSE}, the default, missing values are removed with
+a warning. If \code{TRUE}, missing values are silently removed.}
\item{show.legend}{logical. Should this layer be included in the legends?
\code{NA}, the default, includes if any aesthetics are mapped.
@@ -66,11 +66,14 @@ horizontal directions. Overrides \code{bins} if both set.}
\item{drop}{if \code{TRUE} removes all cells with 0 counts.}
}
\description{
-Add heatmap of 2d bin counts.
+Divides the plane into rectangles, counts the number of cases in
+each rectangle, and then (by default) maps the number of cases to the
+rectangle's fill. This is a useful alternative to \code{\link{geom_point}}
+in the presence of overplotting.
}
\section{Aesthetics}{
-\Sexpr[results=rd,stage=build]{ggplot2:::rd_aesthetics("stat", "bin2d")}
+\aesthetics{stat}{bin2d}
}
\examples{
d <- ggplot(diamonds, aes(x, y)) + xlim(4, 10) + ylim(4, 10)
diff --git a/man/geom_blank.Rd b/man/geom_blank.Rd
index 5938f28..5b2c7e1 100644
--- a/man/geom_blank.Rd
+++ b/man/geom_blank.Rd
@@ -2,7 +2,7 @@
% Please edit documentation in R/geom-blank.r
\name{geom_blank}
\alias{geom_blank}
-\title{Blank, draws nothing.}
+\title{Draw nothing}
\usage{
geom_blank(mapping = NULL, data = NULL, stat = "identity",
position = "identity", ..., show.legend = NA, inherit.aes = TRUE)
@@ -49,7 +49,8 @@ the default plot specification, e.g. \code{\link{borders}}.}
}
\description{
The blank geom draws nothing, but can be a useful way of ensuring common
-scales between different plots.
+scales between different plots. See \code{\link{expand_limits}} for
+more details.
}
\examples{
ggplot(mtcars, aes(wt, mpg))
diff --git a/man/geom_boxplot.Rd b/man/geom_boxplot.Rd
index da17798..1af18f5 100644
--- a/man/geom_boxplot.Rd
+++ b/man/geom_boxplot.Rd
@@ -3,13 +3,14 @@
\name{geom_boxplot}
\alias{geom_boxplot}
\alias{stat_boxplot}
-\title{Box and whiskers plot.}
+\title{A box and whiskers plot (in the style of Tukey)}
\usage{
geom_boxplot(mapping = NULL, data = NULL, stat = "boxplot",
position = "dodge", ..., outlier.colour = NULL, outlier.color = NULL,
- outlier.shape = 19, outlier.size = 1.5, outlier.stroke = 0.5,
- notch = FALSE, notchwidth = 0.5, varwidth = FALSE, na.rm = FALSE,
- show.legend = NA, inherit.aes = TRUE)
+ outlier.fill = NULL, outlier.shape = 19, outlier.size = 1.5,
+ outlier.stroke = 0.5, outlier.alpha = NULL, notch = FALSE,
+ notchwidth = 0.5, varwidth = FALSE, na.rm = FALSE, show.legend = NA,
+ inherit.aes = TRUE)
stat_boxplot(mapping = NULL, data = NULL, geom = "boxplot",
position = "dodge", ..., coef = 1.5, na.rm = FALSE, show.legend = NA,
@@ -43,7 +44,7 @@ often aesthetics, used to set an aesthetic to a fixed value, like
\code{color = "red"} or \code{size = 3}. They may also be parameters
to the paired geom/stat.}
-\item{outlier.colour, outlier.color, outlier.shape, outlier.size, outlier.stroke}{Default aesthetics for outliers. Set to \code{NULL} to inherit from the
+\item{outlier.colour, outlier.color, outlier.fill, outlier.shape, outlier.size, outlier.stroke, outlier.alpha}{Default aesthetics for outliers. Set to \code{NULL} to inherit from the
aesthetics used for the box.
In the unlikely event you specify both US and UK spellings of colour, the
@@ -62,8 +63,8 @@ the body (default 0.5)}
square-roots of the number of observations in the groups (possibly
weighted, using the \code{weight} aesthetic).}
-\item{na.rm}{If \code{FALSE} (the default), removes missing values with
-a warning. If \code{TRUE} silently removes missing values.}
+\item{na.rm}{If \code{FALSE}, the default, missing values are removed with
+a warning. If \code{TRUE}, missing values are silently removed.}
\item{show.legend}{logical. Should this layer be included in the legends?
\code{NA}, the default, includes if any aesthetics are mapped.
@@ -80,26 +81,33 @@ the default plot specification, e.g. \code{\link{borders}}.}
\item{coef}{length of the whiskers as multiple of IQR. Defaults to 1.5}
}
\description{
-The lower and upper "hinges" correspond to the first and third quartiles
+The boxplot compactly displays the distribution of a continuous variable.
+It visualises five summary statistics (the median, two hinges
+and two whiskers), and all "outlying" points individually.
+}
+\section{Summary statistics}{
+
+The lower and upper hinges correspond to the first and third quartiles
(the 25th and 75th percentiles). This differs slightly from the method used
by the \code{boxplot} function, and may be apparent with small samples.
See \code{\link{boxplot.stats}} for for more information on how hinge
positions are calculated for \code{boxplot}.
-}
-\details{
-The upper whisker extends from the hinge to the highest value that is within
-1.5 * IQR of the hinge, where IQR is the inter-quartile range, or distance
-between the first and third quartiles. The lower whisker extends from the
-hinge to the lowest value within 1.5 * IQR of the hinge. Data beyond the
-end of the whiskers are outliers and plotted as points (as specified by Tukey).
+
+The upper whisker extends from the hinge to the largest value no further than
+1.5 * IQR from the hinge (where IQR is the inter-quartile range, or distance
+between the first and third quartiles). The lower whisker extends from the
+hinge to the smallest value at most 1.5 * IQR of the hinge. Data beyond the
+end of the whiskers are called "outlying" points and are plotted
+individually.
In a notched box plot, the notches extend \code{1.58 * IQR / sqrt(n)}.
-This gives a roughly 95% confidence interval for comparing medians.
+This gives a roughly 95\% confidence interval for comparing medians.
See McGill et al. (1978) for more details.
}
+
\section{Aesthetics}{
-\Sexpr[results=rd,stage=build]{ggplot2:::rd_aesthetics("geom", "boxplot")}
+\aesthetics{geom}{boxplot}
}
\section{Computed variables}{
@@ -137,6 +145,8 @@ ggplot(diamonds, aes(carat, price)) +
geom_boxplot()
ggplot(diamonds, aes(carat, price)) +
geom_boxplot(aes(group = cut_width(carat, 0.25)))
+ggplot(diamonds, aes(carat, price)) +
+ geom_boxplot(aes(group = cut_width(carat, 0.25)), outlier.alpha = 0.1)
\donttest{
# It's possible to draw a boxplot with your own computations if you
@@ -162,8 +172,8 @@ McGill, R., Tukey, J. W. and Larsen, W. A. (1978) Variations of
box plots. The American Statistician 32, 12-16.
}
\seealso{
-\code{\link{stat_quantile}} to view quantiles conditioned on a
- continuous variable, \code{\link{geom_jitter}} for another way to look
- at conditional distributions.
+\code{\link{geom_quantile}} for continuous x,
+ \code{\link{geom_violin}} for a richer display of the distribution, and
+ \code{\link{geom_jitter}} for a useful technique for small data.
}
diff --git a/man/geom_contour.Rd b/man/geom_contour.Rd
index 8e1474b..6bd6dd5 100644
--- a/man/geom_contour.Rd
+++ b/man/geom_contour.Rd
@@ -3,7 +3,7 @@
\name{geom_contour}
\alias{geom_contour}
\alias{stat_contour}
-\title{Display contours of a 3d surface in 2d.}
+\title{2d contours of a 3d surface}
\usage{
geom_contour(mapping = NULL, data = NULL, stat = "contour",
position = "identity", ..., lineend = "butt", linejoin = "round",
@@ -50,8 +50,8 @@ to the paired geom/stat.}
\item{linemitre}{Line mitre limit (number greater than 1)}
-\item{na.rm}{If \code{FALSE} (the default), removes missing values with
-a warning. If \code{TRUE} silently removes missing values.}
+\item{na.rm}{If \code{FALSE}, the default, missing values are removed with
+a warning. If \code{TRUE}, missing values are silently removed.}
\item{show.legend}{logical. Should this layer be included in the legends?
\code{NA}, the default, includes if any aesthetics are mapped.
@@ -65,11 +65,17 @@ the default plot specification, e.g. \code{\link{borders}}.}
\item{geom}{The geometric object to use display the data}
}
\description{
-Display contours of a 3d surface in 2d.
+ggplot2 can not draw true 3d surfaces, but you can use \code{geom_contour}
+and \code{\link{geom_tile}} to visualise 3d surfaces in 2d. To be a valid
+surface, the data must contain only a single row for each unique combination
+of the variables mapped to the \code{x} and \code{y} aesthetics. Contouring
+tends to work best when \code{x} and \code{y} form a (roughly) evenly
+spaced grid. If you data is not evenly spaced, you may want to interpolate
+to a grid before visualising.
}
\section{Aesthetics}{
-\Sexpr[results=rd,stage=build]{ggplot2:::rd_aesthetics("geom", "contour")}
+\aesthetics{geom}{contour}
}
\section{Computed variables}{
diff --git a/man/geom_count.Rd b/man/geom_count.Rd
index e8904f7..8c7079f 100644
--- a/man/geom_count.Rd
+++ b/man/geom_count.Rd
@@ -3,7 +3,7 @@
\name{geom_count}
\alias{geom_count}
\alias{stat_sum}
-\title{Count the number of observations at each location.}
+\title{Count overlapping points}
\usage{
geom_count(mapping = NULL, data = NULL, stat = "sum",
position = "identity", ..., na.rm = FALSE, show.legend = NA,
@@ -41,8 +41,8 @@ often aesthetics, used to set an aesthetic to a fixed value, like
\code{color = "red"} or \code{size = 3}. They may also be parameters
to the paired geom/stat.}
-\item{na.rm}{If \code{FALSE} (the default), removes missing values with
-a warning. If \code{TRUE} silently removes missing values.}
+\item{na.rm}{If \code{FALSE}, the default, missing values are removed with
+a warning. If \code{TRUE}, missing values are silently removed.}
\item{show.legend}{logical. Should this layer be included in the legends?
\code{NA}, the default, includes if any aesthetics are mapped.
@@ -58,12 +58,12 @@ the default plot specification, e.g. \code{\link{borders}}.}
}
\description{
This is a variant \code{\link{geom_point}} that counts the number of
-observations at each location, then maps the count to point size. It
-useful when you have discrete data.
+observations at each location, then maps the count to point area. It
+useful when you have discrete data and overplotting.
}
\section{Aesthetics}{
-\Sexpr[results=rd,stage=build]{ggplot2:::rd_aesthetics("geom", "point")}
+\aesthetics{geom}{point}
}
\section{Computed variables}{
@@ -84,7 +84,7 @@ ggplot(mpg, aes(cty, hwy)) +
# counts of zero would be given size 0. Doesn't make much different
# here because the smallest count is already close to 0.
ggplot(mpg, aes(cty, hwy)) +
- geom_count()
+ geom_count() +
scale_size_area()
# Display proportions instead of counts -------------------------------------
@@ -104,4 +104,7 @@ d + geom_count(aes(size = ..prop.., group = cut)) +
d + geom_count(aes(size = ..prop.., group = clarity)) +
scale_size_area(max_size = 10)
}
+\seealso{
+For continuous \code{x} and \code{x}, use \code{\link{geom_bin2d}}.
+}
diff --git a/man/geom_density.Rd b/man/geom_density.Rd
index 6b6ad69..0cc24e2 100644
--- a/man/geom_density.Rd
+++ b/man/geom_density.Rd
@@ -3,7 +3,7 @@
\name{geom_density}
\alias{geom_density}
\alias{stat_density}
-\title{Display a smooth density estimate.}
+\title{Smoothed density estimates}
\usage{
geom_density(mapping = NULL, data = NULL, stat = "density",
position = "identity", ..., na.rm = FALSE, show.legend = NA,
@@ -11,7 +11,8 @@ geom_density(mapping = NULL, data = NULL, stat = "density",
stat_density(mapping = NULL, data = NULL, geom = "area",
position = "stack", ..., bw = "nrd0", adjust = 1, kernel = "gaussian",
- trim = FALSE, na.rm = FALSE, show.legend = NA, inherit.aes = TRUE)
+ n = 512, trim = FALSE, na.rm = FALSE, show.legend = NA,
+ inherit.aes = TRUE)
}
\arguments{
\item{mapping}{Set of aesthetic mappings created by \code{\link{aes}} or
@@ -41,8 +42,8 @@ often aesthetics, used to set an aesthetic to a fixed value, like
\code{color = "red"} or \code{size = 3}. They may also be parameters
to the paired geom/stat.}
-\item{na.rm}{If \code{FALSE} (the default), removes missing values with
-a warning. If \code{TRUE} silently removes missing values.}
+\item{na.rm}{If \code{FALSE}, the default, missing values are removed with
+a warning. If \code{TRUE}, missing values are silently removed.}
\item{show.legend}{logical. Should this layer be included in the legends?
\code{NA}, the default, includes if any aesthetics are mapped.
@@ -56,14 +57,20 @@ the default plot specification, e.g. \code{\link{borders}}.}
\item{geom, stat}{Use to override the default connection between
\code{geom_density} and \code{stat_density}.}
-\item{bw}{the smoothing bandwidth to be used, see
-\code{\link{density}} for details}
+\item{bw}{The smoothing bandwidth to be used.
+If numeric, the standard deviation of the smoothing kernel.
+If character, a rule to choose the bandwidth, as listed in
+\code{\link[stats]{bw.nrd}}.}
+
+\item{adjust}{A multiplicate bandwidth adjustment. This makes it possible
+to adjust the bandwidth while still using the a bandwidth estimator.
+For exampe, \code{adjust = 1/2} means use half of the default bandwidth.}
-\item{adjust}{adjustment of the bandwidth, see
-\code{\link{density}} for details}
+\item{kernel}{Kernel. See list of available kernels in \code{\link{density}}.}
-\item{kernel}{kernel used for density estimation, see
-\code{\link{density}} for details}
+\item{n}{number of equally spaced points at which the density is to be
+estimated, should be a power of two, see \code{\link{density}} for
+details}
\item{trim}{This parameter only matters if you are displaying multiple
densities in one plot. If \code{FALSE}, the default, each density is
@@ -73,12 +80,13 @@ estimated x values will not line-up, and hence you won't be able to
stack density values.}
}
\description{
-A kernel density estimate, useful for display the distribution of variables
-with underlying smoothness.
+Computes and draws kernel density estimate, which is a smoothed version of
+the histogram. This is a useful alternative to the histogram if for continuous
+data that comes from an underlying smooth distribution.
}
\section{Aesthetics}{
-\Sexpr[results=rd,stage=build]{ggplot2:::rd_aesthetics("geom", "density")}
+\aesthetics{geom}{density}
}
\section{Computed variables}{
diff --git a/man/geom_density_2d.Rd b/man/geom_density_2d.Rd
index 6129c18..16a9e2b 100644
--- a/man/geom_density_2d.Rd
+++ b/man/geom_density_2d.Rd
@@ -5,7 +5,7 @@
\alias{geom_density_2d}
\alias{stat_density2d}
\alias{stat_density_2d}
-\title{Contours from a 2d density estimate.}
+\title{Contours of a 2d density estimate}
\usage{
geom_density_2d(mapping = NULL, data = NULL, stat = "density2d",
position = "identity", ..., lineend = "butt", linejoin = "round",
@@ -49,8 +49,8 @@ to the paired geom/stat.}
\item{linemitre}{Line mitre limit (number greater than 1)}
-\item{na.rm}{If \code{FALSE} (the default), removes missing values with
-a warning. If \code{TRUE} silently removes missing values.}
+\item{na.rm}{If \code{FALSE}, the default, missing values are removed with
+a warning. If \code{TRUE}, missing values are silently removed.}
\item{show.legend}{logical. Should this layer be included in the legends?
\code{NA}, the default, includes if any aesthetics are mapped.
@@ -73,12 +73,13 @@ estimation}
using \code{\link[MASS]{bandwidth.nrd}}.}
}
\description{
-Perform a 2D kernel density estimation using kde2d and display the
-results with contours. This can be useful for dealing with overplotting.
+Perform a 2D kernel density estimation using \code{\link[MASS]{kde2d}} and
+display the results with contours. This can be useful for dealing with
+overplotting. This is a 2d version of \code{\link{geom_density}}.
}
\section{Aesthetics}{
-\Sexpr[results=rd,stage=build]{ggplot2:::rd_aesthetics("geom", "density_2d")}
+\aesthetics{geom}{density_2d}
}
\section{Computed variables}{
@@ -108,7 +109,8 @@ d + stat_density_2d(geom = "point", aes(size = ..density..), n = 20, contour = F
}
}
\seealso{
-\code{\link{geom_contour}} for contour drawing geom,
- \code{\link{stat_sum}} for another way of dealing with overplotting
+\code{\link{geom_contour}} for information about how contours
+ are drawn; \code{\link{geom_bin2d}} for another way of dealing with
+ overplotting.
}
diff --git a/man/geom_dotplot.Rd b/man/geom_dotplot.Rd
index b640123..e1a0cd9 100644
--- a/man/geom_dotplot.Rd
+++ b/man/geom_dotplot.Rd
@@ -75,8 +75,8 @@ for dodging.}
\item{drop}{If TRUE, remove all bins with zero counts}
-\item{na.rm}{If \code{FALSE} (the default), removes missing values with
-a warning. If \code{TRUE} silently removes missing values.}
+\item{na.rm}{If \code{FALSE}, the default, missing values are removed with
+a warning. If \code{TRUE}, missing values are silently removed.}
\item{show.legend}{logical. Should this layer be included in the legends?
\code{NA}, the default, includes if any aesthetics are mapped.
@@ -93,12 +93,12 @@ In a dot plot, the width of a dot corresponds to the bin width
stacked, with each dot representing one observation.
}
\details{
+There are two basic approaches: \emph{dot-density} and \emph{histodot}.
With dot-density binning, the bin positions are determined by the data and
\code{binwidth}, which is the maximum width of each bin. See Wilkinson
-(1999) for details on the dot-density binning algorithm.
-
-With histodot binning, the bins have fixed positions and fixed widths, much
-like a histogram.
+(1999) for details on the dot-density binning algorithm. With histodot
+binning, the bins have fixed positions and fixed widths, much like a
+histogram.
When binning along the x axis and stacking along the y axis, the numbers on
y axis are not meaningful, due to technical limitations of ggplot2. You can
@@ -107,7 +107,7 @@ to match the number of dots.
}
\section{Aesthetics}{
-\Sexpr[results=rd,stage=build]{ggplot2:::rd_aesthetics("geom", "dotplot")}
+\aesthetics{geom}{dotplot}
}
\section{Computed variables}{
diff --git a/man/geom_errorbarh.Rd b/man/geom_errorbarh.Rd
index b19f68e..36e7bc7 100644
--- a/man/geom_errorbarh.Rd
+++ b/man/geom_errorbarh.Rd
@@ -39,8 +39,8 @@ often aesthetics, used to set an aesthetic to a fixed value, like
\code{color = "red"} or \code{size = 3}. They may also be parameters
to the paired geom/stat.}
-\item{na.rm}{If \code{FALSE} (the default), removes missing values with
-a warning. If \code{TRUE} silently removes missing values.}
+\item{na.rm}{If \code{FALSE}, the default, missing values are removed with
+a warning. If \code{TRUE}, missing values are silently removed.}
\item{show.legend}{logical. Should this layer be included in the legends?
\code{NA}, the default, includes if any aesthetics are mapped.
@@ -52,11 +52,11 @@ that define both data and aesthetics and shouldn't inherit behaviour from
the default plot specification, e.g. \code{\link{borders}}.}
}
\description{
-Horizontal error bars
+A rotated version of \code{\link{geom_errorbar}}.
}
\section{Aesthetics}{
-\Sexpr[results=rd,stage=build]{ggplot2:::rd_aesthetics("geom", "errorbarh")}
+\aesthetics{geom}{errorbarh}
}
\examples{
df <- data.frame(
@@ -74,7 +74,4 @@ p + geom_point() +
p + geom_point() +
geom_errorbarh(aes(xmax = resp + se, xmin = resp - se, height = .2))
}
-\seealso{
-\code{\link{geom_errorbar}}: vertical error bars
-}
diff --git a/man/geom_hex.Rd b/man/geom_hex.Rd
index 573da8c..31e019d 100644
--- a/man/geom_hex.Rd
+++ b/man/geom_hex.Rd
@@ -4,7 +4,7 @@
\alias{geom_hex}
\alias{stat_bin_hex}
\alias{stat_binhex}
-\title{Hexagon binning.}
+\title{Hexagonal heatmap of 2d bin counts}
\usage{
geom_hex(mapping = NULL, data = NULL, stat = "binhex",
position = "identity", ..., na.rm = FALSE, show.legend = NA,
@@ -42,8 +42,8 @@ often aesthetics, used to set an aesthetic to a fixed value, like
\code{color = "red"} or \code{size = 3}. They may also be parameters
to the paired geom/stat.}
-\item{na.rm}{If \code{FALSE} (the default), removes missing values with
-a warning. If \code{TRUE} silently removes missing values.}
+\item{na.rm}{If \code{FALSE}, the default, missing values are removed with
+a warning. If \code{TRUE}, missing values are silently removed.}
\item{show.legend}{logical. Should this layer be included in the legends?
\code{NA}, the default, includes if any aesthetics are mapped.
@@ -64,11 +64,14 @@ horizontal directions. Set to 30 by default.}
horizontal directions. Overrides \code{bins} if both set.}
}
\description{
-Hexagon binning.
+Divides the plane into regular hexagons, counts the number of cases in
+each hexagon, and then (by default) maps the number of cases to the hexagon
+fill. Hexagon bins avoid the visual artefacts sometimes generated by
+the very regular alignment of \code{\link{geom_bin2d}}.
}
\section{Aesthetics}{
-\Sexpr[results=rd,stage=build]{ggplot2:::rd_aesthetics("geom", "hex")}
+\aesthetics{geom}{hex}
}
\examples{
d <- ggplot(diamonds, aes(carat, price))
diff --git a/man/geom_histogram.Rd b/man/geom_histogram.Rd
index 5b749ee..8d505d0 100644
--- a/man/geom_histogram.Rd
+++ b/man/geom_histogram.Rd
@@ -4,7 +4,7 @@
\alias{geom_freqpoly}
\alias{geom_histogram}
\alias{stat_bin}
-\title{Histograms and frequency polygons.}
+\title{Histograms and frequency polygons}
\usage{
geom_freqpoly(mapping = NULL, data = NULL, stat = "bin",
position = "identity", ..., na.rm = FALSE, show.legend = NA,
@@ -16,8 +16,8 @@ geom_histogram(mapping = NULL, data = NULL, stat = "bin",
stat_bin(mapping = NULL, data = NULL, geom = "bar", position = "stack",
..., binwidth = NULL, bins = NULL, center = NULL, boundary = NULL,
- closed = c("right", "left"), pad = FALSE, na.rm = FALSE,
- show.legend = NA, inherit.aes = TRUE)
+ breaks = NULL, closed = c("right", "left"), pad = FALSE,
+ na.rm = FALSE, show.legend = NA, inherit.aes = TRUE)
}
\arguments{
\item{mapping}{Set of aesthetic mappings created by \code{\link{aes}} or
@@ -47,8 +47,8 @@ often aesthetics, used to set an aesthetic to a fixed value, like
\code{color = "red"} or \code{size = 3}. They may also be parameters
to the paired geom/stat.}
-\item{na.rm}{If \code{FALSE} (the default), removes missing values with
-a warning. If \code{TRUE} silently removes missing values.}
+\item{na.rm}{If \code{FALSE}, the default, missing values are removed with
+a warning. If \code{TRUE}, missing values are silently removed.}
\item{show.legend}{logical. Should this layer be included in the legends?
\code{NA}, the default, includes if any aesthetics are mapped.
@@ -75,16 +75,20 @@ the default plot specification, e.g. \code{\link{borders}}.}
\item{center}{The center of one of the bins. Note that if center is above or
below the range of the data, things will be shifted by an appropriate
number of \code{width}s. To center on integers, for example, use
-\code{width=1} and \code{center=0}, even if \code{0} is outside the range
+\code{width = 1} and \code{center = 0}, even if \code{0} is outside the range
of the data. At most one of \code{center} and \code{boundary} may be
specified.}
\item{boundary}{A boundary between two bins. As with \code{center}, things
are shifted when \code{boundary} is outside the range of the data. For
example, to center on integers, use \code{width = 1} and \code{boundary =
-0.5}, even if \code{1} is outside the range of the data. At most one of
+0.5}, even if \code{0.5} is outside the range of the data. At most one of
\code{center} and \code{boundary} may be specified.}
+\item{breaks}{Alternatively, you can supply a numeric vector giving
+the bin boundaries. Overrides \code{binwidth}, \code{bins}, \code{center},
+and \code{boundary}.}
+
\item{closed}{One of \code{"right"} or \code{"left"} indicating whether right
or left edges of bins are included in the bin.}
@@ -92,22 +96,26 @@ or left edges of bins are included in the bin.}
frequency polygons touch 0. Defaults to \code{FALSE}.}
}
\description{
-Display a 1d distribution by dividing into bins and counting the number
-of observations in each bin. Histograms use bars; frequency polygons use
-lines.
+Visualise the distribution of a single continuous variable by dividing
+the x axis into bins and counting the number of observations in each bin.
+Histograms (\code{geom_histogram}) display the count with bars; frequency
+polygons (\code{geom_freqpoly}), display the counts with lines. Frequency
+polygons are more suitable when you want to compare the distribution
+across a the levels of a categorical variable.
\code{stat_bin} is suitable only for continuous x data. If your x data is
discrete, you probably want to use \code{\link{stat_count}}.
}
\details{
-By default, \code{stat_bin} uses 30 bins - this is not a good default,
-but the idea is to get you experimenting with different binwidths. You
-may need to look at a few to uncover the full story behind your data.
+By default, the underlying computation (\code{stat_bin}) uses 30 bins -
+this is not a good default, but the idea is to get you experimenting with
+different binwidths. You may need to look at a few to uncover the full
+story behind your data.
}
\section{Aesthetics}{
-\code{geom_histogram} uses the same aesthetics as \code{geom_bar};
-\code{geom_freqpoly} uses the same aesthetics as \code{geom_line}.
+\code{geom_histogram} uses the same aesthetics as \code{\link{geom_bar}};
+\code{geom_freqpoly} uses the same aesthetics as \code{\link{geom_line}}.
}
\section{Computed variables}{
@@ -163,16 +171,15 @@ m + geom_histogram(binwidth = 0.05) + scale_x_log10()
# bar is anchored at zero, and so when transformed becomes negative
# infinity. This is not a problem when transforming the scales, because
# no observations have 0 ratings.
-m + geom_histogram(origin = 0) + coord_trans(x = "log10")
-# Use origin = 0, to make sure we don't take sqrt of negative values
-m + geom_histogram(origin = 0) + coord_trans(x = "sqrt")
+m + geom_histogram(boundary = 0) + coord_trans(x = "log10")
+# Use boundary = 0, to make sure we don't take sqrt of negative values
+m + geom_histogram(boundary = 0) + coord_trans(x = "sqrt")
# You can also transform the y axis. Remember that the base of the bars
# has value 0, so log transformations are not appropriate
m <- ggplot(movies, aes(x = rating))
m + geom_histogram(binwidth = 0.5) + scale_y_sqrt()
}
-rm(movies)
}
\seealso{
\code{\link{stat_count}}, which counts the number of cases at each x
diff --git a/man/geom_jitter.Rd b/man/geom_jitter.Rd
index 787defe..94b554c 100644
--- a/man/geom_jitter.Rd
+++ b/man/geom_jitter.Rd
@@ -2,7 +2,7 @@
% Please edit documentation in R/geom-jitter.r
\name{geom_jitter}
\alias{geom_jitter}
-\title{Points, jittered to reduce overplotting.}
+\title{Jittered points}
\usage{
geom_jitter(mapping = NULL, data = NULL, stat = "identity",
position = "jitter", ..., width = NULL, height = NULL, na.rm = FALSE,
@@ -57,8 +57,8 @@ to the paired geom/stat.}
is aligned on the integers, so a width or height of 0.5 will spread the
data so it's not possible to see the distinction between the categories.}
-\item{na.rm}{If \code{FALSE} (the default), removes missing values with
-a warning. If \code{TRUE} silently removes missing values.}
+\item{na.rm}{If \code{FALSE}, the default, missing values are removed with
+a warning. If \code{TRUE}, missing values are silently removed.}
\item{show.legend}{logical. Should this layer be included in the legends?
\code{NA}, the default, includes if any aesthetics are mapped.
@@ -70,13 +70,14 @@ that define both data and aesthetics and shouldn't inherit behaviour from
the default plot specification, e.g. \code{\link{borders}}.}
}
\description{
-The jitter geom is a convenient default for geom_point with position =
-'jitter'. It's a useful way of handling overplotting caused by discreteness
-in smaller datasets.
+The jitter geom is a convenient shortcut for
+\code{geom_point(position = "jitter")}. It adds a small amount of random
+variation to the location of each point, and is a useful way of handling
+overplotting caused by discreteness in smaller datasets.
}
\section{Aesthetics}{
-\Sexpr[results=rd,stage=build]{ggplot2:::rd_aesthetics("geom", "point")}
+\aesthetics{geom}{point}
}
\examples{
p <- ggplot(mpg, aes(cyl, hwy))
diff --git a/man/geom_linerange.Rd b/man/geom_linerange.Rd
index 2571bb5..354c439 100644
--- a/man/geom_linerange.Rd
+++ b/man/geom_linerange.Rd
@@ -5,7 +5,7 @@
\alias{geom_errorbar}
\alias{geom_linerange}
\alias{geom_pointrange}
-\title{Vertical intervals: lines, crossbars & errorbars.}
+\title{Vertical intervals: lines, crossbars & errorbars}
\usage{
geom_crossbar(mapping = NULL, data = NULL, stat = "identity",
position = "identity", ..., fatten = 2.5, na.rm = FALSE,
@@ -58,8 +58,8 @@ to the paired geom/stat.}
middle bar in \code{geom_crossbar()} and the middle point in
\code{geom_pointrange()}.}
-\item{na.rm}{If \code{FALSE} (the default), removes missing values with
-a warning. If \code{TRUE} silently removes missing values.}
+\item{na.rm}{If \code{FALSE}, the default, missing values are removed with
+a warning. If \code{TRUE}, missing values are silently removed.}
\item{show.legend}{logical. Should this layer be included in the legends?
\code{NA}, the default, includes if any aesthetics are mapped.
@@ -72,11 +72,11 @@ the default plot specification, e.g. \code{\link{borders}}.}
}
\description{
Various ways of representing a vertical interval defined by \code{x},
-\code{ymin} and \code{ymax}.
+\code{ymin} and \code{ymax}. Each case draws a single graphical object.
}
\section{Aesthetics}{
-\Sexpr[results=rd,stage=build]{ggplot2:::rd_aesthetics("geom", "linerange")}
+\aesthetics{geom}{linerange}
}
\examples{
#' # Create a simple example dataset
@@ -103,18 +103,19 @@ p +
# specify the dodge width
p <- ggplot(df, aes(trt, resp, fill = group))
p +
- geom_bar(position = "dodge", stat = "identity") +
+ geom_col(position = "dodge") +
geom_errorbar(aes(ymin = lower, ymax = upper), position = "dodge", width = 0.25)
# Because the bars and errorbars have different widths
# we need to specify how wide the objects we are dodging are
dodge <- position_dodge(width=0.9)
p +
- geom_bar(position = dodge, stat = "identity") +
+ geom_col(position = dodge) +
geom_errorbar(aes(ymin = lower, ymax = upper), position = dodge, width = 0.25)
}
\seealso{
\code{\link{stat_summary}} for examples of these guys in use,
- \code{\link{geom_smooth}} for continuous analog
+ \code{\link{geom_smooth}} for continuous analog,
+ \code{\link{geom_errorbarh}} for a horizontal error bar.
}
diff --git a/man/geom_map.Rd b/man/geom_map.Rd
index 900c430..20da8ea 100644
--- a/man/geom_map.Rd
+++ b/man/geom_map.Rd
@@ -2,7 +2,7 @@
% Please edit documentation in R/geom-map.r
\name{geom_map}
\alias{geom_map}
-\title{Polygons from a reference map.}
+\title{Polygons from a reference map}
\usage{
geom_map(mapping = NULL, data = NULL, stat = "identity", ..., map,
na.rm = FALSE, show.legend = NA, inherit.aes = TRUE)
@@ -40,8 +40,8 @@ typically be created using \code{\link{fortify}} on a spatial object.
It must contain columns \code{x} or \code{long}, \code{y} or
\code{lat}, and \code{region} or \code{id}.}
-\item{na.rm}{If \code{FALSE} (the default), removes missing values with
-a warning. If \code{TRUE} silently removes missing values.}
+\item{na.rm}{If \code{FALSE}, the default, missing values are removed with
+a warning. If \code{TRUE}, missing values are silently removed.}
\item{show.legend}{logical. Should this layer be included in the legends?
\code{NA}, the default, includes if any aesthetics are mapped.
@@ -53,11 +53,11 @@ that define both data and aesthetics and shouldn't inherit behaviour from
the default plot specification, e.g. \code{\link{borders}}.}
}
\description{
-Does not affect position scales.
+This is pure annotation, so does not affect position scales.
}
\section{Aesthetics}{
-\Sexpr[results=rd,stage=build]{ggplot2:::rd_aesthetics("geom", "map")}
+\aesthetics{geom}{map}
}
\examples{
# When using geom_polygon, you will typically need two data frames:
@@ -80,7 +80,8 @@ positions <- data.frame(
2.2, 2.1, 1.7, 2.1, 3.2, 2.8, 2.1, 2.2, 3.3, 3.2)
)
-ggplot(values) + geom_map(aes(map_id = id), map = positions) +
+ggplot(values) +
+ geom_map(aes(map_id = id), map = positions) +
expand_limits(positions)
ggplot(values, aes(fill = value)) +
geom_map(aes(map_id = id), map = positions) +
diff --git a/man/geom_path.Rd b/man/geom_path.Rd
index 458d8d9..f1fad93 100644
--- a/man/geom_path.Rd
+++ b/man/geom_path.Rd
@@ -4,7 +4,7 @@
\alias{geom_line}
\alias{geom_path}
\alias{geom_step}
-\title{Connect observations.}
+\title{Connect observations}
\usage{
geom_path(mapping = NULL, data = NULL, stat = "identity",
position = "identity", ..., lineend = "butt", linejoin = "round",
@@ -58,8 +58,8 @@ to the paired geom/stat.}
\item{arrow}{Arrow specification, as created by \code{\link[grid]{arrow}}}
-\item{na.rm}{If \code{FALSE} (the default), removes missing values with
-a warning. If \code{TRUE} silently removes missing values.}
+\item{na.rm}{If \code{FALSE}, the default, missing values are removed with
+a warning. If \code{TRUE}, missing values are silently removed.}
\item{show.legend}{logical. Should this layer be included in the legends?
\code{NA}, the default, includes if any aesthetics are mapped.
@@ -77,11 +77,16 @@ the default plot specification, e.g. \code{\link{borders}}.}
\code{geom_path()} connects the observations in the order in which they appear
in the data. \code{geom_line()} connects them in order of the variable on the
x axis. \code{geom_step()} creates a stairstep plot, highlighting exactly
-when changes occur.
+when changes occur. The \code{group} aesthetic determines which cases are
+connected together.
+}
+\details{
+An alternative parameterisation is \code{\link{geom_segment}}: each line
+corresponds to a single case which provides the start and end coordinates.
}
\section{Aesthetics}{
-\Sexpr[results=rd,stage=build]{ggplot2:::rd_aesthetics("geom", "path")}
+\aesthetics{geom}{path}
}
\examples{
# geom_line() is suitable for time series
diff --git a/man/geom_point.Rd b/man/geom_point.Rd
index 9f09da5..a9f38ee 100644
--- a/man/geom_point.Rd
+++ b/man/geom_point.Rd
@@ -2,7 +2,7 @@
% Please edit documentation in R/geom-point.r
\name{geom_point}
\alias{geom_point}
-\title{Points, as for a scatterplot}
+\title{Points}
\usage{
geom_point(mapping = NULL, data = NULL, stat = "identity",
position = "identity", ..., na.rm = FALSE, show.legend = NA,
@@ -39,8 +39,8 @@ often aesthetics, used to set an aesthetic to a fixed value, like
\code{color = "red"} or \code{size = 3}. They may also be parameters
to the paired geom/stat.}
-\item{na.rm}{If \code{FALSE} (the default), removes missing values with
-a warning. If \code{TRUE} silently removes missing values.}
+\item{na.rm}{If \code{FALSE}, the default, missing values are removed with
+a warning. If \code{TRUE}, missing values are silently removed.}
\item{show.legend}{logical. Should this layer be included in the legends?
\code{NA}, the default, includes if any aesthetics are mapped.
@@ -52,33 +52,42 @@ that define both data and aesthetics and shouldn't inherit behaviour from
the default plot specification, e.g. \code{\link{borders}}.}
}
\description{
-The point geom is used to create scatterplots.
+The point geom is used to create scatterplots. The scatterplot is most
+useful for displaying the relationship between two continuous variables.
+It can be used to compare one continuous and one categorical variable, or
+two categorical variables, but a variation like \code{\link{geom_jitter}},
+\code{\link{geom_count}}, or \code{\link{geom_bin2d}} is usually more
+appropriate.
}
\details{
-The scatterplot is useful for displaying the relationship between two
-continuous variables, although it can also be used with one continuous
-and one categorical variable, or two categorical variables. See
-\code{\link{geom_jitter}} for possibilities.
-
The \emph{bubblechart} is a scatterplot with a third variable mapped to
-the size of points. There are no special names for scatterplots where
+the size of points. There are no special names for scatterplots where
another variable is mapped to point shape or colour, however.
+}
+\section{Overplotting}{
The biggest potential problem with a scatterplot is overplotting: whenever
you have more than a few points, points may be plotted on top of one
another. This can severely distort the visual appearance of the plot.
There is no one solution to this problem, but there are some techniques
-that can help. You can add additional information with
+that can help. You can add additional information with
\code{\link{geom_smooth}}, \code{\link{geom_quantile}} or
-\code{\link{geom_density_2d}}. If you have few unique x values,
-\code{\link{geom_boxplot}} may also be useful. Alternatively, you can
+\code{\link{geom_density_2d}}. If you have few unique x values,
+\code{\link{geom_boxplot}} may also be useful.
+
+Alternatively, you can
summarise the number of points at each location and display that in some
-way, using \code{\link{stat_sum}}. Another technique is to use transparent
-points, e.g. \code{geom_point(alpha = 0.05)}.
+way, using \code{\link{geom_count}}, \code{\link{geom_hex}}, or
+\code{\link{geom_density2d}}.
+
+Another technique is to make the points transparent (e.g.
+\code{geom_point(alpha = 0.05)}) or very small (e.g.
+\code{geom_point(shape = ".")}).
}
+
\section{Aesthetics}{
-\Sexpr[results=rd,stage=build]{ggplot2:::rd_aesthetics("geom", "point")}
+\aesthetics{geom}{point}
}
\examples{
p <- ggplot(mtcars, aes(wt, mpg))
@@ -133,9 +142,4 @@ ggplot(mtcars2, aes(wt, mpg)) + geom_point()
ggplot(mtcars2, aes(wt, mpg)) + geom_point(na.rm = TRUE)
}
}
-\seealso{
-\code{\link{scale_size}} to see scale area of points, instead of
- radius, \code{\link{geom_jitter}} to jitter points to reduce (mild)
- overplotting
-}
diff --git a/man/geom_polygon.Rd b/man/geom_polygon.Rd
index 74ccde0..cc80c23 100644
--- a/man/geom_polygon.Rd
+++ b/man/geom_polygon.Rd
@@ -2,7 +2,7 @@
% Please edit documentation in R/geom-polygon.r
\name{geom_polygon}
\alias{geom_polygon}
-\title{Polygon, a filled path.}
+\title{Polygons}
\usage{
geom_polygon(mapping = NULL, data = NULL, stat = "identity",
position = "identity", ..., na.rm = FALSE, show.legend = NA,
@@ -39,8 +39,8 @@ often aesthetics, used to set an aesthetic to a fixed value, like
\code{color = "red"} or \code{size = 3}. They may also be parameters
to the paired geom/stat.}
-\item{na.rm}{If \code{FALSE} (the default), removes missing values with
-a warning. If \code{TRUE} silently removes missing values.}
+\item{na.rm}{If \code{FALSE}, the default, missing values are removed with
+a warning. If \code{TRUE}, missing values are silently removed.}
\item{show.legend}{logical. Should this layer be included in the legends?
\code{NA}, the default, includes if any aesthetics are mapped.
@@ -52,11 +52,14 @@ that define both data and aesthetics and shouldn't inherit behaviour from
the default plot specification, e.g. \code{\link{borders}}.}
}
\description{
-Polygon, a filled path.
+Polygons are very similar to paths (as drawn by \code{\link{geom_path}})
+except that the start and end points are connected and the inside is
+coloured by \code{fill}. The \code{group} aesthetic determines which cases
+are connected together into a polygon.
}
\section{Aesthetics}{
-\Sexpr[results=rd,stage=build]{ggplot2:::rd_aesthetics("geom", "polygon")}
+\aesthetics{geom}{polygon}
}
\examples{
# When using geom_polygon, you will typically need two data frames:
@@ -80,9 +83,11 @@ positions <- data.frame(
)
# Currently we need to manually merge the two together
-datapoly <- merge(values, positions, by=c("id"))
+datapoly <- merge(values, positions, by = c("id"))
-(p <- ggplot(datapoly, aes(x=x, y=y)) + geom_polygon(aes(fill=value, group=id)))
+p <- ggplot(datapoly, aes(x = x, y = y)) +
+ geom_polygon(aes(fill = value, group = id))
+p
# Which seems like a lot of work, but then it's easy to add on
# other features in this coordinate system, e.g.:
@@ -92,7 +97,7 @@ stream <- data.frame(
y = cumsum(runif(50,max = 0.1))
)
-p + geom_line(data = stream, colour="grey30", size = 5)
+p + geom_line(data = stream, colour = "grey30", size = 5)
# And if the positions are in longitude and latitude, you can use
# coord_map to produce different map projections.
diff --git a/man/stat_qq.Rd b/man/geom_qq.Rd
similarity index 90%
rename from man/stat_qq.Rd
rename to man/geom_qq.Rd
index 9af1e22..5ed442a 100644
--- a/man/stat_qq.Rd
+++ b/man/geom_qq.Rd
@@ -1,15 +1,15 @@
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/stat-qq.r
-\name{stat_qq}
+\name{geom_qq}
\alias{geom_qq}
\alias{stat_qq}
-\title{Calculation for quantile-quantile plot.}
+\title{A quantile-quantile plot}
\usage{
-stat_qq(mapping = NULL, data = NULL, geom = "point",
+geom_qq(mapping = NULL, data = NULL, geom = "point",
position = "identity", ..., distribution = stats::qnorm,
dparams = list(), na.rm = FALSE, show.legend = NA, inherit.aes = TRUE)
-geom_qq(mapping = NULL, data = NULL, geom = "point",
+stat_qq(mapping = NULL, data = NULL, geom = "point",
position = "identity", ..., distribution = stats::qnorm,
dparams = list(), na.rm = FALSE, show.legend = NA, inherit.aes = TRUE)
}
@@ -48,8 +48,8 @@ to the paired geom/stat.}
\item{dparams}{Additional parameters passed on to \code{distribution}
function.}
-\item{na.rm}{If \code{FALSE} (the default), removes missing values with
-a warning. If \code{TRUE} silently removes missing values.}
+\item{na.rm}{If \code{FALSE}, the default, missing values are removed with
+a warning. If \code{TRUE}, missing values are silently removed.}
\item{show.legend}{logical. Should this layer be included in the legends?
\code{NA}, the default, includes if any aesthetics are mapped.
@@ -61,11 +61,11 @@ that define both data and aesthetics and shouldn't inherit behaviour from
the default plot specification, e.g. \code{\link{borders}}.}
}
\description{
-Calculation for quantile-quantile plot.
+A quantile-quantile plot
}
\section{Aesthetics}{
-\Sexpr[results=rd,stage=build]{ggplot2:::rd_aesthetics("stat", "qq")}
+\aesthetics{stat}{qq}
}
\section{Computed variables}{
diff --git a/man/geom_quantile.Rd b/man/geom_quantile.Rd
index 1760e9f..2fbee12 100644
--- a/man/geom_quantile.Rd
+++ b/man/geom_quantile.Rd
@@ -3,7 +3,7 @@
\name{geom_quantile}
\alias{geom_quantile}
\alias{stat_quantile}
-\title{Add quantile lines from a quantile regression.}
+\title{Quantile regression}
\usage{
geom_quantile(mapping = NULL, data = NULL, stat = "quantile",
position = "identity", ..., lineend = "butt", linejoin = "round",
@@ -48,8 +48,8 @@ to the paired geom/stat.}
\item{linemitre}{Line mitre limit (number greater than 1)}
-\item{na.rm}{If \code{FALSE} (the default), removes missing values with
-a warning. If \code{TRUE} silently removes missing values.}
+\item{na.rm}{If \code{FALSE}, the default, missing values are removed with
+a warning. If \code{TRUE}, missing values are silently removed.}
\item{show.legend}{logical. Should this layer be included in the legends?
\code{NA}, the default, includes if any aesthetics are mapped.
@@ -74,11 +74,12 @@ the default plot specification, e.g. \code{\link{borders}}.}
function defined by \code{method}.}
}
\description{
-This can be used as a continuous analogue of a geom_boxplot.
+This fits a quantile regression to the data and draws the fitted quantiles
+with lines. This is as a continuous analogue to \code{\link{geom_boxplot}}.
}
\section{Aesthetics}{
-\Sexpr[results=rd,stage=build]{ggplot2:::rd_aesthetics("geom", "quantile")}
+\aesthetics{geom}{quantile}
}
\section{Computed variables}{
diff --git a/man/geom_ribbon.Rd b/man/geom_ribbon.Rd
index 73dc930..613bb04 100644
--- a/man/geom_ribbon.Rd
+++ b/man/geom_ribbon.Rd
@@ -3,7 +3,7 @@
\name{geom_ribbon}
\alias{geom_area}
\alias{geom_ribbon}
-\title{Ribbons and area plots.}
+\title{Ribbons and area plots}
\usage{
geom_ribbon(mapping = NULL, data = NULL, stat = "identity",
position = "identity", ..., na.rm = FALSE, show.legend = NA,
@@ -44,8 +44,8 @@ often aesthetics, used to set an aesthetic to a fixed value, like
\code{color = "red"} or \code{size = 3}. They may also be parameters
to the paired geom/stat.}
-\item{na.rm}{If \code{FALSE} (the default), removes missing values with
-a warning. If \code{TRUE} silently removes missing values.}
+\item{na.rm}{If \code{FALSE}, the default, missing values are removed with
+a warning. If \code{TRUE}, missing values are silently removed.}
\item{show.legend}{logical. Should this layer be included in the legends?
\code{NA}, the default, includes if any aesthetics are mapped.
@@ -57,20 +57,21 @@ that define both data and aesthetics and shouldn't inherit behaviour from
the default plot specification, e.g. \code{\link{borders}}.}
}
\description{
-For each continuous x value, \code{geom_interval} displays a y interval.
-\code{geom_area} is a special case of \code{geom_ribbon}, where the
-minimum of the range is fixed to 0.
+For each x value, \code{geom_ribbon} displays a y interval defined
+by \code{ymin} and \code{ymax}. \code{geom_area} is a special case of
+\code{geom_ribbon}, where the \code{ymin} is fixed to 0.
}
\details{
An area plot is the continuous analog of a stacked bar chart (see
\code{\link{geom_bar}}), and can be used to show how composition of the
-whole varies over the range of x. Choosing the order in which different
+whole varies over the range of x. Choosing the order in which different
components is stacked is very important, as it becomes increasing hard to
-see the individual pattern as you move up the stack.
+see the individual pattern as you move up the stack. See
+\code{\link{position_stack}} for the details of stacking algorithm.
}
\section{Aesthetics}{
-\Sexpr[results=rd,stage=build]{ggplot2:::rd_aesthetics("geom", "ribbon")}
+\aesthetics{geom}{ribbon}
}
\examples{
# Generate data
diff --git a/man/geom_rug.Rd b/man/geom_rug.Rd
index 165e913..c8d099a 100644
--- a/man/geom_rug.Rd
+++ b/man/geom_rug.Rd
@@ -2,7 +2,7 @@
% Please edit documentation in R/geom-rug.r
\name{geom_rug}
\alias{geom_rug}
-\title{Marginal rug plots.}
+\title{Rug plots in the margins}
\usage{
geom_rug(mapping = NULL, data = NULL, stat = "identity",
position = "identity", ..., sides = "bl", na.rm = FALSE,
@@ -43,8 +43,8 @@ to the paired geom/stat.}
It can be set to a string containing any of \code{"trbl"}, for top, right,
bottom, and left.}
-\item{na.rm}{If \code{FALSE} (the default), removes missing values with
-a warning. If \code{TRUE} silently removes missing values.}
+\item{na.rm}{If \code{FALSE}, the default, missing values are removed with
+a warning. If \code{TRUE}, missing values are silently removed.}
\item{show.legend}{logical. Should this layer be included in the legends?
\code{NA}, the default, includes if any aesthetics are mapped.
@@ -56,18 +56,34 @@ that define both data and aesthetics and shouldn't inherit behaviour from
the default plot specification, e.g. \code{\link{borders}}.}
}
\description{
-Marginal rug plots.
+A rug plot is a compact visualisation designed to supplement a 2d display
+with the two 1d marginal distributions. Rug plots display individual
+cases so are best used with smaller datasets.
+}
+\details{
+The rug lines are drawn with a fixed size (3% of the total plot size) so
+are dependent on the overall scale expansion in order not to overplot
+existing data.
}
\section{Aesthetics}{
-\Sexpr[results=rd,stage=build]{ggplot2:::rd_aesthetics("geom", "rug")}
+\aesthetics{geom}{rug}
}
\examples{
-p <- ggplot(mtcars, aes(wt, mpg))
-p + geom_point()
-p + geom_point() + geom_rug()
-p + geom_point() + geom_rug(sides="b") # Rug on bottom only
-p + geom_point() + geom_rug(sides="trbl") # All four sides
-p + geom_point() + geom_rug(position='jitter')
+p <- ggplot(mtcars, aes(wt, mpg)) +
+ geom_point()
+p
+p + geom_rug()
+p + geom_rug(sides="b") # Rug on bottom only
+p + geom_rug(sides="trbl") # All four sides
+
+# Use jittering to avoid overplotting for smaller datasets
+ggplot(mpg, aes(displ, cty)) +
+ geom_point() +
+ geom_rug()
+
+ggplot(mpg, aes(displ, cty)) +
+ geom_jitter() +
+ geom_rug(alpha = 1/2, position = "jitter")
}
diff --git a/man/geom_segment.Rd b/man/geom_segment.Rd
index 3a5e0b7..6e5a625 100644
--- a/man/geom_segment.Rd
+++ b/man/geom_segment.Rd
@@ -3,7 +3,7 @@
\name{geom_segment}
\alias{geom_curve}
\alias{geom_segment}
-\title{Line segments and curves.}
+\title{Line segments and curves}
\usage{
geom_segment(mapping = NULL, data = NULL, stat = "identity",
position = "identity", ..., arrow = NULL, lineend = "butt",
@@ -45,12 +45,12 @@ often aesthetics, used to set an aesthetic to a fixed value, like
\code{color = "red"} or \code{size = 3}. They may also be parameters
to the paired geom/stat.}
-\item{arrow}{specification for arrow heads, as created by arrow()}
+\item{arrow}{specification for arrow heads, as created by arrow().}
-\item{lineend}{Line end style (round, butt, square)}
+\item{lineend}{Line end style (round, butt, square).}
-\item{na.rm}{If \code{FALSE} (the default), removes missing values with
-a warning. If \code{TRUE} silently removes missing values.}
+\item{na.rm}{If \code{FALSE}, the default, missing values are removed with
+a warning. If \code{TRUE}, missing values are silently removed.}
\item{show.legend}{logical. Should this layer be included in the legends?
\code{NA}, the default, includes if any aesthetics are mapped.
@@ -75,12 +75,18 @@ the default plot specification, e.g. \code{\link{borders}}.}
More control points creates a smoother curve.}
}
\description{
-\code{geom_segment} draws a straight line between points (x1, y1) and
-(x2, y2). \code{geom_curve} draws a curved line.
+\code{geom_segment} draws a straight line between points (x, y) and
+(xend, yend). \code{geom_curve} draws a curved line. See the underlying
+drawing function \code{\link[grid]{curveGrob}} for the parameters that
+control the curve.
+}
+\details{
+Both geoms draw a single segment/curve per case. See \code{geom_path} if you
+need to connect points across multiple cases.
}
\section{Aesthetics}{
-\Sexpr[results=rd,stage=build]{ggplot2:::rd_aesthetics("geom", "segment")}
+\aesthetics{geom}{segment}
}
\examples{
b <- ggplot(mtcars, aes(wt, mpg)) +
diff --git a/man/geom_smooth.Rd b/man/geom_smooth.Rd
index 67a36ca..9199c56 100644
--- a/man/geom_smooth.Rd
+++ b/man/geom_smooth.Rd
@@ -3,7 +3,7 @@
\name{geom_smooth}
\alias{geom_smooth}
\alias{stat_smooth}
-\title{Add a smoothed conditional mean.}
+\title{Smoothed conditional means}
\usage{
geom_smooth(mapping = NULL, data = NULL, stat = "smooth",
position = "identity", ..., method = "auto", formula = y ~ x,
@@ -43,10 +43,15 @@ often aesthetics, used to set an aesthetic to a fixed value, like
\code{color = "red"} or \code{size = 3}. They may also be parameters
to the paired geom/stat.}
-\item{method}{smoothing method (function) to use, eg. lm, glm, gam, loess,
-rlm. For datasets with n < 1000 default is \code{\link{loess}}. For datasets
-with 1000 or more observations defaults to gam, see \code{\link[mgcv]{gam}}
-for more details.}
+\item{method}{smoothing method (function) to use, eg. "lm", "glm",
+ "gam", "loess", "rlm".
+
+ For \code{method = "auto"} the smoothing method is chosen based on the
+ size of the largest group (across all panels). \code{\link{loess}} is
+ used for than 1,000 observations; otherwise \code{\link[mgcv]{gam}} is
+ used with \code{formula = y ~ s(x, bs = "cs")}. Somewhat anecdotally,
+ \code{loess} gives a better appearance, but is O(n^2) in memory, so does
+ not work for larger datasets.}
\item{formula}{formula to use in smoothing function, eg. \code{y ~ x},
\code{y ~ poly(x, 2)}, \code{y ~ log(x)}}
@@ -54,8 +59,8 @@ for more details.}
\item{se}{display confidence interval around smooth? (TRUE by default, see
level to control}
-\item{na.rm}{If \code{FALSE} (the default), removes missing values with
-a warning. If \code{TRUE} silently removes missing values.}
+\item{na.rm}{If \code{FALSE}, the default, missing values are removed with
+a warning. If \code{TRUE}, missing values are silently removed.}
\item{show.legend}{logical. Should this layer be included in the legends?
\code{NA}, the default, includes if any aesthetics are mapped.
@@ -99,7 +104,7 @@ scale, and then back-transformed to the response scale.
}
\section{Aesthetics}{
-\Sexpr[results=rd,stage=build]{ggplot2:::rd_aesthetics("geom", "smooth")}
+\aesthetics{geom}{smooth}
}
\section{Computed variables}{
diff --git a/man/geom_spoke.Rd b/man/geom_spoke.Rd
index 0e0316f..ad7c191 100644
--- a/man/geom_spoke.Rd
+++ b/man/geom_spoke.Rd
@@ -3,7 +3,7 @@
\name{geom_spoke}
\alias{geom_spoke}
\alias{stat_spoke}
-\title{A line segment parameterised by location, direction and distance.}
+\title{Line segments parameterised by location, direction and distance}
\usage{
geom_spoke(mapping = NULL, data = NULL, stat = "identity",
position = "identity", ..., na.rm = FALSE, show.legend = NA,
@@ -40,8 +40,8 @@ often aesthetics, used to set an aesthetic to a fixed value, like
\code{color = "red"} or \code{size = 3}. They may also be parameters
to the paired geom/stat.}
-\item{na.rm}{If \code{FALSE} (the default), removes missing values with
-a warning. If \code{TRUE} silently removes missing values.}
+\item{na.rm}{If \code{FALSE}, the default, missing values are removed with
+a warning. If \code{TRUE}, missing values are silently removed.}
\item{show.legend}{logical. Should this layer be included in the legends?
\code{NA}, the default, includes if any aesthetics are mapped.
@@ -53,11 +53,12 @@ that define both data and aesthetics and shouldn't inherit behaviour from
the default plot specification, e.g. \code{\link{borders}}.}
}
\description{
-A line segment parameterised by location, direction and distance.
+This is a polar parameterisation of \code{\link{geom_segment}}. It is
+useful when you have variables that describe direction and distance.
}
\section{Aesthetics}{
-\Sexpr[results=rd,stage=build]{ggplot2:::rd_aesthetics("geom", "spoke")}
+\aesthetics{geom}{spoke}
}
\examples{
df <- expand.grid(x = 1:10, y=1:10)
diff --git a/man/geom_text.Rd b/man/geom_text.Rd
index 5efb40f..817aa3d 100644
--- a/man/geom_text.Rd
+++ b/man/geom_text.Rd
@@ -3,7 +3,7 @@
\name{geom_label}
\alias{geom_label}
\alias{geom_text}
-\title{Textual annotations.}
+\title{Text}
\usage{
geom_label(mapping = NULL, data = NULL, stat = "identity",
position = "identity", ..., parse = FALSE, nudge_x = 0, nudge_y = 0,
@@ -59,8 +59,8 @@ Useful for offsetting text from points, particularly on discrete scales.}
\item{label.size}{Size of label border, in mm.}
-\item{na.rm}{If \code{FALSE} (the default), removes missing values with
-a warning. If \code{TRUE} silently removes missing values.}
+\item{na.rm}{If \code{FALSE}, the default, missing values are removed with
+a warning. If \code{TRUE}, missing values are silently removed.}
\item{show.legend}{logical. Should this layer be included in the legends?
\code{NA}, the default, includes if any aesthetics are mapped.
@@ -72,11 +72,11 @@ that define both data and aesthetics and shouldn't inherit behaviour from
the default plot specification, e.g. \code{\link{borders}}.}
\item{check_overlap}{If \code{TRUE}, text that overlaps previous text in the
-same layer will not be plotted. A quick and dirty way}
+same layer will not be plotted.}
}
\description{
\code{geom_text} adds text directly to the plot. \code{geom_label} draws
-a rectangle underneath the text, making it easier to read.
+a rectangle behind the text, making it easier to read.
}
\details{
Note the the "width" and "height" of a text element are 0, so stacking
@@ -88,7 +88,7 @@ resize a plot, labels stay the same size, but the size of the axes changes.
}
\section{Aesthetics}{
-\Sexpr[results=rd,stage=build]{ggplot2:::rd_aesthetics("geom", "text")}
+\aesthetics{geom}{text}
}
\section{\code{geom_label}}{
@@ -158,25 +158,27 @@ df <- data.frame(
# ggplot2 doesn't know you want to give the labels the same virtual width
# as the bars:
-ggplot(data = df, aes(x, y, fill = grp, label = y)) +
- geom_bar(stat = "identity", position = "dodge") +
- geom_text(position = "dodge")
+ggplot(data = df, aes(x, y, group = grp)) +
+ geom_col(aes(fill = grp), position = "dodge") +
+ geom_text(aes(label = y), position = "dodge")
# So tell it:
-ggplot(data = df, aes(x, y, fill = grp, label = y)) +
- geom_bar(stat = "identity", position = "dodge") +
- geom_text(position = position_dodge(0.9))
+ggplot(data = df, aes(x, y, group = grp)) +
+ geom_col(aes(fill = grp), position = "dodge") +
+ geom_text(aes(label = y), position = position_dodge(0.9))
# Use you can't nudge and dodge text, so instead adjust the y postion
-ggplot(data = df, aes(x, y, fill = grp, label = y)) +
- geom_bar(stat = "identity", position = "dodge") +
- geom_text(aes(y = y + 0.05), position = position_dodge(0.9), vjust = 0)
+ggplot(data = df, aes(x, y, group = grp)) +
+ geom_col(aes(fill = grp), position = "dodge") +
+ geom_text(
+ aes(label = y, y = y + 0.05),
+ position = position_dodge(0.9),
+ vjust = 0
+ )
# To place text in the middle of each bar in a stacked barplot, you
-# need to do the computation yourself
-df <- transform(df, mid_y = ave(df$y, df$x, FUN = function(val) cumsum(val) - (0.5 * val)))
-
-ggplot(data = df, aes(x, y, fill = grp, label = y)) +
- geom_bar(stat = "identity") +
- geom_text(aes(y = mid_y))
+# need to set the vjust parameter of position_stack()
+ggplot(data = df, aes(x, y, group = grp)) +
+ geom_col(aes(fill = grp)) +
+ geom_text(aes(label = y), position = position_stack(vjust = 0.5))
# Justification -------------------------------------------------------------
df <- data.frame(
diff --git a/man/geom_tile.Rd b/man/geom_tile.Rd
index 096b101..db4c618 100644
--- a/man/geom_tile.Rd
+++ b/man/geom_tile.Rd
@@ -4,7 +4,7 @@
\alias{geom_raster}
\alias{geom_rect}
\alias{geom_tile}
-\title{Draw rectangles.}
+\title{Rectangles}
\usage{
geom_raster(mapping = NULL, data = NULL, stat = "identity",
position = "identity", ..., hjust = 0.5, vjust = 0.5,
@@ -57,8 +57,8 @@ for both, centering each pixel over its data location.}
\item{interpolate}{If \code{TRUE} interpolate linearly, if \code{FALSE}
(the default) don't interpolate.}
-\item{na.rm}{If \code{FALSE} (the default), removes missing values with
-a warning. If \code{TRUE} silently removes missing values.}
+\item{na.rm}{If \code{FALSE}, the default, missing values are removed with
+a warning. If \code{TRUE}, missing values are silently removed.}
\item{show.legend}{logical. Should this layer be included in the legends?
\code{NA}, the default, includes if any aesthetics are mapped.
@@ -71,15 +71,15 @@ the default plot specification, e.g. \code{\link{borders}}.}
}
\description{
\code{geom_rect} and \code{geom_tile} do the same thing, but are
-parameterised differently. \code{geom_rect} uses the locations of the four
-corners (\code{xmin}, \code{xmax}, \code{ymin} and \code{ymax}).
+parameterised differently: \code{geom_rect} uses the locations of the four
+corners (\code{xmin}, \code{xmax}, \code{ymin} and \code{ymax}), while
\code{geom_tile} uses the center of the tile and its size (\code{x},
\code{y}, \code{width}, \code{height}). \code{geom_raster} is a high
performance special case for when all the tiles are the same size.
}
\section{Aesthetics}{
-\Sexpr[results=rd,stage=build]{ggplot2:::rd_aesthetics("geom", "tile")}
+\aesthetics{geom}{tile}
}
\examples{
# The most common use for rectangles is to draw a surface. You always want
diff --git a/man/geom_violin.Rd b/man/geom_violin.Rd
index 545833b..4dc2941 100644
--- a/man/geom_violin.Rd
+++ b/man/geom_violin.Rd
@@ -3,7 +3,7 @@
\name{geom_violin}
\alias{geom_violin}
\alias{stat_ydensity}
-\title{Violin plot.}
+\title{Violin plot}
\usage{
geom_violin(mapping = NULL, data = NULL, stat = "ydensity",
position = "dodge", ..., draw_quantiles = NULL, trim = TRUE,
@@ -52,8 +52,8 @@ to the range of the data. If \code{FALSE}, don't trim the tails.}
the tails). If "count", areas are scaled proportionally to the number of
observations. If "width", all violins have the same maximum width.}
-\item{na.rm}{If \code{FALSE} (the default), removes missing values with
-a warning. If \code{TRUE} silently removes missing values.}
+\item{na.rm}{If \code{FALSE}, the default, missing values are removed with
+a warning. If \code{TRUE}, missing values are silently removed.}
\item{show.legend}{logical. Should this layer be included in the legends?
\code{NA}, the default, includes if any aesthetics are mapped.
@@ -67,21 +67,26 @@ the default plot specification, e.g. \code{\link{borders}}.}
\item{geom, stat}{Use to override the default connection between
\code{geom_violin} and \code{stat_ydensity}.}
-\item{bw}{the smoothing bandwidth to be used, see
-\code{\link{density}} for details}
+\item{bw}{The smoothing bandwidth to be used.
+If numeric, the standard deviation of the smoothing kernel.
+If character, a rule to choose the bandwidth, as listed in
+\code{\link[stats]{bw.nrd}}.}
-\item{adjust}{adjustment of the bandwidth, see
-\code{\link{density}} for details}
+\item{adjust}{A multiplicate bandwidth adjustment. This makes it possible
+to adjust the bandwidth while still using the a bandwidth estimator.
+For exampe, \code{adjust = 1/2} means use half of the default bandwidth.}
-\item{kernel}{kernel used for density estimation, see
-\code{\link{density}} for details}
+\item{kernel}{Kernel. See list of available kernels in \code{\link{density}}.}
}
\description{
-Violin plot.
+A violin plot is a compact display of a continuous distribution. It is a
+blend of \code{\link{geom_boxplot}} and \code{\link{geom_density}}: a
+violin plot is a mirrored density plot displayed in the same way as a
+boxplot.
}
\section{Aesthetics}{
-\Sexpr[results=rd,stage=build]{ggplot2:::rd_aesthetics("geom", "violin")}
+\aesthetics{geom}{violin}
}
\section{Computed variables}{
@@ -101,8 +106,7 @@ p <- ggplot(mtcars, aes(factor(cyl), mpg))
p + geom_violin()
\donttest{
-p + geom_violin() + geom_jitter(height = 0)
-p + geom_violin() + coord_flip()
+p + geom_violin() + geom_jitter(height = 0, width = 0.1)
# Scale maximum width proportional to sample size:
p + geom_violin(scale = "count")
diff --git a/man/gg-add.Rd b/man/gg-add.Rd
index f72e242..c90224f 100644
--- a/man/gg-add.Rd
+++ b/man/gg-add.Rd
@@ -1,79 +1,54 @@
% Generated by roxygen2: do not edit by hand
-% Please edit documentation in R/plot-construction.r, R/theme.r
+% Please edit documentation in R/plot-construction.r
\name{+.gg}
\alias{+.gg}
\alias{\%+\%}
-\alias{\%+replace\%}
-\title{Add a new component to a ggplot or theme object.}
+\title{Add components to a plot}
\usage{
\method{+}{gg}(e1, e2)
e1 \%+\% e2
-
-e1 \%+replace\% e2
}
\arguments{
-\item{e1}{An object of class \code{ggplot} or \code{theme}}
+\item{e1}{An object of class \code{\link{ggplot}} or a \code{\link{theme}}.}
-\item{e2}{A component to add to \code{e1}}
+\item{e2}{A plot component, as described below.}
}
\description{
-This operator allows you to add objects to a ggplot or theme object.
+\code{+} is the key to constructing sophisticated ggplot2 graphics. It
+allows you to start simple, then get more and more complex, checking your
+work at each step.
}
-\details{
-If the first object is an object of class \code{ggplot}, you can add
-the following types of objects, and it will return a modified ggplot
-object.
+\section{What can you add?}{
+
+You can add any of the following types of objects:
\itemize{
- \item \code{data.frame}: replace current data.frame
- (must use \code{\%+\%})
- \item \code{uneval}: replace current aesthetics
- \item \code{layer}: add new layer
- \item \code{theme}: update plot theme
- \item \code{scale}: replace current scale
- \item \code{coord}: override current coordinate system
- \item \code{facet}: override current coordinate faceting
+ \item A \code{\link{aes}()} objects replaces the default aesthetics.
+ \item A layer created by a \code{geom_} or \code{stat_} function adds
+ new layer.
+ \item A \code{scale} overrides the existing scale.
+ \item A \code{\link{theme}} modifies the current theme.
+ \item A \code{coord} overrides current coordinate system.
+ \item A \code{facet} specificatio override current faceting.
}
-If the first object is an object of class \code{theme}, you can add
-another theme object. This will return a modified theme object.
-
-For theme objects, the \code{+} operator and the \code{\%+replace\%}
-can be used to modify elements in themes.
-
-The \code{+} operator updates the elements of e1 that differ from
-elements specified (not NULL) in e2.
-Thus this operator can be used to incrementally add or modify attributes
-of a ggplot theme.
+To replace the current default data frame, you must use \code{\%+\%},
+due to S3 method precedence issues.
-In contrast, the \code{\%+replace\%} operator replaces the
-entire element; any element of a theme not specified in e2 will not be
-present in the resulting theme (i.e. NULL).
-Thus this operator can be used to overwrite an entire theme.
+You can also supply a list, in which case each element of the list will
+be added in turn.
}
\examples{
-### Adding objects to a ggplot object
-p <- ggplot(mtcars, aes(wt, mpg, colour = disp)) +
- geom_point()
-
-p
-p + coord_cartesian(ylim = c(0, 40))
-p + scale_colour_continuous(breaks = c(100, 300))
-p + guides(colour = "colourbar")
-
-# Use a different data frame
-m <- mtcars[1:10, ]
-p \%+\% m
-
-### Adding objects to a theme object
-# Compare these results of adding theme objects to other theme objects
-add_el <- theme_grey() + theme(text = element_text(family = "Times"))
-rep_el <- theme_grey() \%+replace\% theme(text = element_text(family = "Times"))
+base <- ggplot(mpg, aes(displ, hwy)) + geom_point()
+base + geom_smooth()
-add_el$text
-rep_el$text
+# To override the data, you must use \%+\%
+base \%+\% subset(mpg, fl == "p")
+# Alternatively, you can add multiple components with a list.
+# This can be useful to return from a function.
+base + list(subset(mpg, fl == "p"), geom_smooth())
}
\seealso{
\code{\link{theme}}
diff --git a/man/ggplot.Rd b/man/ggplot.Rd
index a6554ea..a9f6922 100644
--- a/man/ggplot.Rd
+++ b/man/ggplot.Rd
@@ -2,9 +2,7 @@
% Please edit documentation in R/plot.r
\name{ggplot}
\alias{ggplot}
-\alias{ggplot.data.frame}
-\alias{ggplot.default}
-\title{Create a new ggplot plot.}
+\title{Create a new ggplot}
\usage{
ggplot(data = NULL, mapping = aes(), ..., environment = parent.frame())
}
@@ -29,19 +27,16 @@ set of plot aesthetics intended to be common throughout all
subsequent layers unless specifically overridden.
}
\details{
-\code{ggplot()} is typically used to construct a plot
-incrementally, using the + operator to add layers to the
-existing ggplot object. This is advantageous in that the
-code is explicit about which layers are added and the order
-in which they are added. For complex graphics with multiple
-layers, initialization with \code{ggplot} is recommended.
+\code{ggplot()} is used to construct the initial plot object,
+and is almost always followed by \code{+} to add component to the
+plot. There are three common ways to invoke \code{ggplot}:
+
+\enumerate{
+ \item \code{ggplot(df, aes(x, y, <other aesthetics>))}
+ \item \code{ggplot(df)}
+ \item \code{ggplot()}
+}
-There are three common ways to invoke \code{ggplot}:
-\itemize{
- \item \code{ggplot(df, aes(x, y, <other aesthetics>))}
- \item \code{ggplot(df)}
- \item \code{ggplot()}
- }
The first method is recommended if all layers use the same
data and the same set of aesthetics, although this method
can also be used to add a layer using data from another
@@ -56,34 +51,39 @@ multiple data frames are used to produce different layers, as
is often the case in complex graphics.
}
\examples{
-df <- data.frame(gp = factor(rep(letters[1:3], each = 10)),
- y = rnorm(30))
-# Compute sample mean and standard deviation in each group
+# Generate some sample data, then compute mean and standard deviation
+# in each group
+df <- data.frame(
+ gp = factor(rep(letters[1:3], each = 10)),
+ y = rnorm(30)
+)
ds <- plyr::ddply(df, "gp", plyr::summarise, mean = mean(y), sd = sd(y))
-# Declare the data frame and common aesthetics.
-# The summary data frame ds is used to plot
-# larger red points in a second geom_point() layer.
-# If the data = argument is not specified, it uses the
-# declared data frame from ggplot(); ditto for the aesthetics.
-ggplot(df, aes(x = gp, y = y)) +
- geom_point() +
- geom_point(data = ds, aes(y = mean),
- colour = 'red', size = 3)
+# The summary data frame ds is used to plot larger red points on top
+# of the raw data. Note that we don't need to supply `data` or `mapping`
+# in each layer because the defaults from ggplot() are used.
+ggplot(df, aes(gp, y)) +
+ geom_point() +
+ geom_point(data = ds, aes(y = mean), colour = 'red', size = 3)
+
# Same plot as above, declaring only the data frame in ggplot().
# Note how the x and y aesthetics must now be declared in
# each geom_point() layer.
ggplot(df) +
- geom_point(aes(x = gp, y = y)) +
- geom_point(data = ds, aes(x = gp, y = mean),
- colour = 'red', size = 3)
-# Set up a skeleton ggplot object and add layers:
+ geom_point(aes(gp, y)) +
+ geom_point(data = ds, aes(gp, mean), colour = 'red', size = 3)
+
+# Alternatively we can fully specify the plot in each layer. This
+# is not useful here, but can be more clear when working with complex
+# mult-dataset graphics
ggplot() +
- geom_point(data = df, aes(x = gp, y = y)) +
- geom_point(data = ds, aes(x = gp, y = mean),
- colour = 'red', size = 3) +
- geom_errorbar(data = ds, aes(x = gp, y = mean,
- ymin = mean - sd, ymax = mean + sd),
- colour = 'red', width = 0.4)
+ geom_point(data = df, aes(gp, y)) +
+ geom_point(data = ds, aes(gp, mean), colour = 'red', size = 3) +
+ geom_errorbar(
+ data = ds,
+ aes(gp, mean, ymin = mean - sd, ymax = mean + sd),
+ colour = 'red',
+ width = 0.4
+ )
}
diff --git a/man/ggplot2-ggproto.Rd b/man/ggplot2-ggproto.Rd
index 8fb1e01..57db48e 100644
--- a/man/ggplot2-ggproto.Rd
+++ b/man/ggplot2-ggproto.Rd
@@ -1,7 +1,8 @@
% Generated by roxygen2: do not edit by hand
-% Please edit documentation in R/aaa-.r, R/geom-.r, R/annotation-custom.r, R/annotation-logticks.r, R/geom-polygon.r, R/geom-map.r, R/annotation-map.r, R/geom-raster.r, R/annotation-raster.r, R/coord-.r, R/coord-cartesian-.r, R/coord-fixed.r, R/coord-flip.r, R/coord-map.r, R/coord-polar.r, R/coord-quickmap.R, R/coord-transform.r, R/stat-.r, R/geom-abline.r, R/geom-rect.r, R/geom-bar.r, R/geom-blank.r, R/geom-boxplot.r, R/geom-path.r, R/geom-contour.r, R/geom-crossbar.r, R/geom-segment.r, [...]
+% Please edit documentation in R/aaa-.r, R/geom-.r, R/annotation-custom.r, R/annotation-logticks.r, R/geom-polygon.r, R/geom-map.r, R/annotation-map.r, R/geom-raster.r, R/annotation-raster.r, R/axis-secondary.R, R/coord-.r, R/coord-cartesian-.r, R/coord-fixed.r, R/coord-flip.r, R/coord-map.r, R/coord-polar.r, R/coord-quickmap.R, R/coord-transform.r, R/facet-.r, R/facet-grid-.r, R/facet-null.r, R/facet-wrap.r, R/stat-.r, R/geom-abline.r, R/geom-rect.r, R/geom-bar.r, R/geom-blank.r, R/geom [...]
\docType{data}
\name{ggplot2-ggproto}
+\alias{AxisSecondary}
\alias{Coord}
\alias{CoordCartesian}
\alias{CoordFixed}
@@ -10,6 +11,10 @@
\alias{CoordPolar}
\alias{CoordQuickmap}
\alias{CoordTrans}
+\alias{Facet}
+\alias{FacetGrid}
+\alias{FacetNull}
+\alias{FacetWrap}
\alias{Geom}
\alias{GeomAbline}
\alias{GeomAnnotationMap}
@@ -17,6 +22,7 @@
\alias{GeomBar}
\alias{GeomBlank}
\alias{GeomBoxplot}
+\alias{GeomCol}
\alias{GeomContour}
\alias{GeomCrossbar}
\alias{GeomCurve}
@@ -169,8 +175,8 @@ object, you typically will want to implement one or more of the following:
\item \code{labels}: Returns a list containing labels for x and y.
\item \code{render_fg}: Renders foreground elements.
\item \code{render_bg}: Renders background elements.
- \item \code{render_axis_h}: Renders the horizontal axis.
- \item \code{render_axis_v}: Renders the vertical axis.
+ \item \code{render_axis_h}: Renders the horizontal axes.
+ \item \code{render_axis_v}: Renders the vertical axes.
\item \code{range}: Returns the x and y ranges
\item \code{train}: Return the trained scale ranges.
\item \code{transform}: Transforms x and y coordinates.
@@ -180,6 +186,85 @@ object, you typically will want to implement one or more of the following:
}
}
+\section{Facets}{
+
+
+All \code{facet_*} functions returns a \code{Facet} object or an object of a
+\code{Facet} subclass. This object describes how to assign data to different
+panels, how to apply positional scales and how to lay out the panels, once
+rendered.
+
+Extending facets can range from the simple modifications of current facets,
+to very laborious rewrites with a lot of \code{\link{gtable}} manipulation.
+For some examples of both, please see the extension vignette.
+
+\code{Facet} subclasses, like other extendible ggproto classes, have a range
+of methods that can be modified. Some of these are required for all new
+subclasses, while other only need to be modified if need arises.
+
+The required methods are:
+
+\itemize{
+ \item \code{compute_layout}: Based on layer data compute a mapping between
+ panels, axes, and potentially other parameters such as faceting variable
+ level etc. This method must return a data.frame containing at least the
+ columns \code{PANEL}, \code{SCALE_X}, and \code{SCALE_Y} each containing
+ integer keys mapping a PANEL to which axes it should use. In addition the
+ data.frame can contain whatever other information is necessary to assign
+ observations to the correct panel as well as determining the position of
+ the panel.
+
+ \item \code{map_data}: This method is supplied the data for each layer in
+ turn and is expected to supply a \code{PANEL} column mapping each row to a
+ panel defined in the layout. Additionally this method can also add or
+ subtract data points as needed e.g. in the case of adding margins to
+ \code{facet_grid}.
+
+ \item \code{draw_panels}: This is where the panels are assembled into a
+ \code{gtable} object. The method recieves, among others, a list of grobs
+ defining the content of each panel as generated by the Geoms and Coord
+ objects. The responsibility of the method is to decorate the panels with
+ axes and strips as needed, as well as position them relative to each other
+ in a gtable. For some of the automatic functions to work correctly, each
+ panel, axis, and strip grob name must be prefixed with "panel", "axis", and
+ "strip" respectively.
+}
+
+In addition to the methods described above, it is also possible to override
+the default behaviour of one or more of the following methods:
+
+\itemize{
+ \item \code{setup_params}:
+ \item \code{init_scales}: Given a master scale for x and y, create panel
+ specific scales for each panel defined in the layout. The default is to
+ simply clone the master scale.
+
+ \item \code{train_scales}: Based on layer data train each set of panel
+ scales. The default is to train it on the data related to the panel.
+
+ \item \code{finish_data}: Make last-minute modifications to layer data
+ before it is rendered by the Geoms. The default is to not modify it.
+
+ \item \code{draw_back}: Add a grob in between the background defined by the
+ Coord object (usually the axis grid) and the layer stack. The default is to
+ return an empty grob for each panel.
+
+ \item \code{draw_front}: As above except the returned grob is placed
+ between the layer stack and the foreground defined by the Coord object
+ (usually empty). The default is, as above, to return an empty grob.
+
+ \item \code{draw_labels}: Given the gtable returned by \code{draw_panels},
+ add axis titles to the gtable. The default is to add one title at each side
+ depending on the position and existance of axes.
+}
+
+All extension methods recieve the content of the params field as the params
+argument, so the constructor function will generally put all relevant
+information into this field. The only exception is the \code{shrink}
+parameter which is used to determine if scales are retrained after Stat
+transformations has been applied.
+}
+
\section{Stats}{
@@ -213,13 +298,18 @@ implement one or more of the following:
before the facets are trained, so they are global scales, not local
to the individual panels.\code{...} contains the parameters returned by
\code{setup_params()}.
+ \item \code{finish_layer(data, params)}: called once for each layer. Used
+ to modify the data after scales has been applied, but before the data is
+ handed of to the geom for rendering. The default is to not modify the
+ data. Use this hook if the stat needs access to the actual aesthetic
+ values rather than the values that are mapped to the aesthetic.
\item \code{setup_params(data, params)}: called once for each layer.
- Used to setup defaults that need to complete dataset, and to inform
- the user of important choices. Should return list of parameters.
+ Used to setup defaults that need to complete dataset, and to inform
+ the user of important choices. Should return list of parameters.
\item \code{setup_data(data, params)}: called once for each layer,
- after \code{setp_params()}. Should return modified \code{data}.
- Default methods removes all rows containing a missing value in
- required aesthetics (with a warning if \code{!na.rm}).
+ after \code{setp_params()}. Should return modified \code{data}.
+ Default methods removes all rows containing a missing value in
+ required aesthetics (with a warning if \code{!na.rm}).
\item \code{required_aes}: A character vector of aesthetics needed to
render the geom.
\item \code{default_aes}: A list (generated by \code{\link{aes}()} of
diff --git a/man/ggplot2-package.Rd b/man/ggplot2-package.Rd
new file mode 100644
index 0000000..6294647
--- /dev/null
+++ b/man/ggplot2-package.Rd
@@ -0,0 +1,15 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/ggplot2.r
+\docType{package}
+\name{ggplot2-package}
+\alias{ggplot2}
+\alias{ggplot2-package}
+\title{Create Elegant Data Visualisations Using the Grammar of Graphics}
+\description{
+A system for 'declaratively' creating graphics,
+based on "The Grammar of Graphics". You provide the data, tell 'ggplot2'
+how to map variables to aesthetics, what graphical primitives to use,
+and it takes care of the details.
+}
+\keyword{internal}
+
diff --git a/man/ggproto.Rd b/man/ggproto.Rd
index a1c8cf8..2184d3a 100644
--- a/man/ggproto.Rd
+++ b/man/ggproto.Rd
@@ -3,31 +3,44 @@
\name{ggproto}
\alias{ggproto}
\alias{ggproto_parent}
+\alias{is.ggproto}
\title{Create a new ggproto object}
\usage{
ggproto(`_class` = NULL, `_inherit` = NULL, ...)
ggproto_parent(parent, self)
+
+is.ggproto(x)
}
\arguments{
\item{_class}{Class name to assign to the object. This is stored as the class
-attribute of the object. If \code{NULL} (the default), no class name will
-be added to the object.}
+attribute of the object. This is optional: if \code{NULL} (the default),
+no class name will be added to the object.}
-\item{_inherit}{ggproto object to inherit from. If \code{NULL}, don't inherit
-from any object.}
+\item{_inherit}{ggproto object to inherit from. If \code{NULL}, don't
+inherit from any object.}
\item{...}{A list of members in the ggproto object.}
\item{parent, self}{Access parent class \code{parent} of object \code{self}.}
+
+\item{x}{An object to test.}
}
\description{
-ggproto is inspired by the proto package, but it has some important
-differences. Notably, it cleanly supports cross-package inheritance, and has
-faster performance.
+Construct a new object with \code{ggproto}, test with \code{is.proto},
+and access parent methods/fields with \code{ggproto_parent}.
}
-\section{Calling ggproto methods}{
+\details{
+ggproto implements a protype based OO system which blurs the lines between
+classes and instances. It is inspired by the proto package, but it has some
+important differences. Notably, it cleanly supports cross-package
+inheritance, and has faster performance.
+In most cases, creating a new OO system to be used by a single package is
+not a good idea. However, it was the least-bad solution for ggplot2 because
+it required the fewest changes to an already complex code base.
+}
+\section{Calling methods}{
ggproto methods can take an optional \code{self} argument: if it is present,
it is a regular method; if it's absent, it's a "static" method (i.e. it
@@ -42,8 +55,28 @@ in the function signature, although customarily it comes first.
\section{Calling methods in a parent}{
-
To explicitly call a methods in a parent, use
\code{ggproto_parent(Parent, self)}.
}
+\examples{
+Adder <- ggproto("Adder",
+ x = 0,
+ add = function(self, n) {
+ self$x <- self$x + n
+ self$x
+ }
+ )
+is.ggproto(Adder)
+
+Adder$add(10)
+Adder$add(10)
+
+Doubler <- ggproto("Doubler", Adder,
+ add = function(self, n) {
+ ggproto_parent(Adder, self)$add(n * 2)
+ }
+)
+Doubler$x
+Doubler$add(10)
+}
diff --git a/man/ggsave.Rd b/man/ggsave.Rd
index d2ea2ff..e651740 100644
--- a/man/ggsave.Rd
+++ b/man/ggsave.Rd
@@ -13,28 +13,24 @@ ggsave(filename, plot = last_plot(), device = NULL, path = NULL,
\item{plot}{Plot to save, defaults to last plot displayed.}
-\item{device}{Device to use (function or any of the recognized extensions,
-e.g. \code{"pdf"}). By default, extracted from filename extension.
-\code{ggsave} currently recognises eps/ps, tex (pictex), pdf, jpeg, tiff,
-png, bmp, svg and wmf (windows only).}
+\item{device}{Device to use. Can be either be a device function
+(e.g. \code{\link{png}}), or one of "eps", "ps", "tex" (pictex),
+"pdf", "jpeg", "tiff", "png", "bmp", "svg" or "wmf" (windows only).}
\item{path}{Path to save plot to (combined with filename).}
\item{scale}{Multiplicative scaling factor.}
-\item{width, height}{Plot dimensions, defaults to size of current graphics
-device.}
+\item{width, height, units}{Plot size in \code{units} ("in", "cm", or "mm").
+If not supplied, uses the size of current graphics device.}
-\item{units}{Units for width and height when specified explicitly (in, cm,
-or mm)}
-
-\item{dpi}{Resolution used for raster outputs.}
+\item{dpi}{Plot resolution. Applies only to raster output types.}
\item{limitsize}{When \code{TRUE} (the default), \code{ggsave} will not
save images larger than 50x50 inches, to prevent the common error of
specifying dimensions in pixels.}
-\item{...}{Other arguments passed on to graphics device}
+\item{...}{Other arguments passed on to graphics \code{device}.}
}
\description{
\code{ggsave()} is a convenient function for saving a plot. It defaults to
diff --git a/man/ggtheme.Rd b/man/ggtheme.Rd
index a0d6677..9adf44a 100644
--- a/man/ggtheme.Rd
+++ b/man/ggtheme.Rd
@@ -1,7 +1,6 @@
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/theme-defaults.r
\name{ggtheme}
-\alias{ggtheme}
\alias{theme_bw}
\alias{theme_classic}
\alias{theme_dark}
@@ -11,25 +10,25 @@
\alias{theme_linedraw}
\alias{theme_minimal}
\alias{theme_void}
-\title{ggplot2 themes}
+\title{Complete themes}
\usage{
theme_grey(base_size = 11, base_family = "")
theme_gray(base_size = 11, base_family = "")
-theme_bw(base_size = 12, base_family = "")
+theme_bw(base_size = 11, base_family = "")
-theme_linedraw(base_size = 12, base_family = "")
+theme_linedraw(base_size = 11, base_family = "")
-theme_light(base_size = 12, base_family = "")
+theme_light(base_size = 11, base_family = "")
-theme_minimal(base_size = 12, base_family = "")
+theme_dark(base_size = 11, base_family = "")
-theme_classic(base_size = 12, base_family = "")
+theme_minimal(base_size = 11, base_family = "")
-theme_dark(base_size = 12, base_family = "")
+theme_classic(base_size = 11, base_family = "")
-theme_void(base_size = 12, base_family = "")
+theme_void(base_size = 11, base_family = "")
}
\arguments{
\item{base_size}{base font size}
@@ -37,8 +36,9 @@ theme_void(base_size = 12, base_family = "")
\item{base_family}{base font family}
}
\description{
-Themes set the general aspect of the plot such as the colour of the
-background, gridlines, the size and colour of fonts.
+These are complete themes which control all non-data display. Use
+\code{\link{theme}} if you just need to tweak the display of an existing
+theme.
}
\details{
\describe{
@@ -77,10 +77,8 @@ A completely empty theme.}
}
\examples{
p <- ggplot(mtcars) + geom_point(aes(x = wt, y = mpg,
- colour=factor(gear))) + facet_wrap(~am)
-
-p
-p + theme_gray()
+ colour = factor(gear))) + facet_wrap(~am)
+p + theme_gray() # the default
p + theme_bw()
p + theme_linedraw()
p + theme_light()
@@ -88,6 +86,5 @@ p + theme_dark()
p + theme_minimal()
p + theme_classic()
p + theme_void()
-
}
diff --git a/man/graphical-units.Rd b/man/graphical-units.Rd
index c6b406d..1eee346 100644
--- a/man/graphical-units.Rd
+++ b/man/graphical-units.Rd
@@ -4,7 +4,6 @@
\name{graphical-units}
\alias{.pt}
\alias{.stroke}
-\alias{graphical-units}
\title{Graphical units}
\format{An object of class \code{numeric} of length 1.}
\usage{
@@ -17,4 +16,5 @@ Multiply size in mm by these constants in order to convert to the units
that grid uses internally for \code{lwd} and \code{fontsize}.
}
\keyword{datasets}
+\keyword{internal}
diff --git a/man/guide_colourbar.Rd b/man/guide_colourbar.Rd
index d210244..860445d 100644
--- a/man/guide_colourbar.Rd
+++ b/man/guide_colourbar.Rd
@@ -3,7 +3,7 @@
\name{guide_colourbar}
\alias{guide_colorbar}
\alias{guide_colourbar}
-\title{Continuous colour bar guide.}
+\title{Continuous colour bar guide}
\usage{
guide_colourbar(title = waiver(), title.position = NULL,
title.theme = NULL, title.hjust = NULL, title.vjust = NULL,
@@ -150,7 +150,7 @@ p1 + guides(fill = guide_colorbar(nbin = 3))
p1 + guides(fill = guide_colorbar(nbin = 100))
# make top- and bottom-most ticks invisible
-p1 + scale_fill_continuous(limits = c(0,20), breaks=c(0, 5, 10, 15, 20),
+p1 + scale_fill_continuous(limits = c(0,20), breaks = c(0, 5, 10, 15, 20),
guide = guide_colorbar(nbin=100, draw.ulim = FALSE, draw.llim = FALSE))
# guides can be controlled independently
diff --git a/man/guide_legend.Rd b/man/guide_legend.Rd
index e40dc89..d95714e 100644
--- a/man/guide_legend.Rd
+++ b/man/guide_legend.Rd
@@ -2,7 +2,7 @@
% Please edit documentation in R/guide-legend.r
\name{guide_legend}
\alias{guide_legend}
-\title{Legend guide.}
+\title{Legend guide}
\usage{
guide_legend(title = waiver(), title.position = NULL, title.theme = NULL,
title.hjust = NULL, title.vjust = NULL, label = TRUE,
diff --git a/man/guides.Rd b/man/guides.Rd
index c4ead6b..dc5f793 100644
--- a/man/guides.Rd
+++ b/man/guides.Rd
@@ -2,19 +2,22 @@
% Please edit documentation in R/guides-.r
\name{guides}
\alias{guides}
-\title{Set guides for each scale.}
+\title{Set guides for each scale}
\usage{
guides(...)
}
\arguments{
-\item{...}{List of scale guide pairs}
+\item{...}{List of scale name-guide pairs. The guide can either
+be a string (i.e. "colorbar" or "legend"), or a call to a guide function
+(i.e. \code{\link{guide_colourbar}} or \code{\link{guide_legend}})
+specifying additional arguments.}
}
\value{
A list containing the mapping between scale and guide.
}
\description{
-Guides for each scale can be set in call of \code{scale_*} with argument
-\code{guide}, or in \code{guides}.
+Guides for each scale can be set scale-by-scale with the \code{guide}
+argument, or en masse with \code{guides()}.
}
\examples{
\donttest{
@@ -54,8 +57,6 @@ p + theme(legend.position = "bottom")
# position of guides
-p + theme(legend.position = "bottom", legend.box = "horizontal")
-
# Set order for multiple guides
ggplot(mpg, aes(displ, cty)) +
geom_point(aes(size = hwy, colour = cyl, shape = drv)) +
diff --git a/man/hmisc.Rd b/man/hmisc.Rd
index 8de36cd..08c82a6 100644
--- a/man/hmisc.Rd
+++ b/man/hmisc.Rd
@@ -6,8 +6,7 @@
\alias{mean_cl_normal}
\alias{mean_sdl}
\alias{median_hilow}
-\title{Wrap up a selection of summary functions from Hmisc to make it easy to use
-with \code{\link{stat_summary}}.}
+\title{A selection of summary functions from Hmisc}
\usage{
mean_cl_boot(x, ...)
@@ -22,12 +21,26 @@ median_hilow(x, ...)
\item{...}{other arguments passed on to the respective Hmisc function.}
}
+\value{
+A data frame with columns \code{y}, \code{ymin}, and \code{ymax}.
+}
\description{
-See the Hmisc documentation for details of their options.
+These are wrappers around functions from \pkg{Hmsic} designed to make them
+easier to use with \code{\link{stat_summary}}. See the Hmisc documentation
+for more details:
+
+\itemize{
+ \item \code{\link[Hmisc]{smean.cl.boot}}
+ \item \code{\link[Hmisc]{smean.cl.normal}}
+ \item \code{\link[Hmisc]{smean.sdl}}
+ \item \code{\link[Hmisc]{smedian.hilow}}
+}
}
-\seealso{
-\code{\link[Hmisc]{smean.cl.boot}},
- \code{\link[Hmisc]{smean.cl.normal}}, \code{\link[Hmisc]{smean.sdl}},
- \code{\link[Hmisc]{smedian.hilow}}
+\examples{
+x <- rnorm(100)
+mean_cl_boot(x)
+mean_cl_normal(x)
+mean_sdl(x)
+median_hilow(x)
}
diff --git a/man/is.ggproto.Rd b/man/is.ggproto.Rd
deleted file mode 100644
index 49ad4ef..0000000
--- a/man/is.ggproto.Rd
+++ /dev/null
@@ -1,15 +0,0 @@
-% Generated by roxygen2: do not edit by hand
-% Please edit documentation in R/ggproto.r
-\name{is.ggproto}
-\alias{is.ggproto}
-\title{Is an object a ggproto object?}
-\usage{
-is.ggproto(x)
-}
-\arguments{
-\item{x}{An object to test.}
-}
-\description{
-Is an object a ggproto object?
-}
-
diff --git a/man/is.rel.Rd b/man/is.rel.Rd
index 12aafc1..7d708ad 100644
--- a/man/is.rel.Rd
+++ b/man/is.rel.Rd
@@ -12,4 +12,5 @@ is.rel(x)
\description{
Reports whether x is a rel object
}
+\keyword{internal}
diff --git a/man/is.theme.Rd b/man/is.theme.Rd
index b8e94f5..88b6911 100644
--- a/man/is.theme.Rd
+++ b/man/is.theme.Rd
@@ -12,4 +12,5 @@ is.theme(x)
\description{
Reports whether x is a theme object
}
+\keyword{internal}
diff --git a/man/label_bquote.Rd b/man/label_bquote.Rd
index 6e0fa6e..9bf33f7 100644
--- a/man/label_bquote.Rd
+++ b/man/label_bquote.Rd
@@ -1,8 +1,8 @@
% Generated by roxygen2: do not edit by hand
-% Please edit documentation in R/facet-labels.r
+% Please edit documentation in R/labeller.r
\name{label_bquote}
\alias{label_bquote}
-\title{Backquoted labeller}
+\title{Label with mathematical expressions}
\usage{
label_bquote(rows = NULL, cols = NULL, default = label_value)
}
@@ -15,7 +15,7 @@ label_bquote(rows = NULL, cols = NULL, default = label_value)
columns when no plotmath expression is provided.}
}
\description{
-\code{\link{label_bquote}()} offers a flexible way of labelling
+\code{label_bquote()} offers a flexible way of labelling
facet rows or columns with plotmath expressions. Backquoted
variables will be replaced with their value in the facet.
}
diff --git a/man/labeller.Rd b/man/labeller.Rd
index 99bba09..b4f33d3 100644
--- a/man/labeller.Rd
+++ b/man/labeller.Rd
@@ -1,8 +1,8 @@
% Generated by roxygen2: do not edit by hand
-% Please edit documentation in R/facet-labels.r
+% Please edit documentation in R/labeller.r
\name{labeller}
\alias{labeller}
-\title{Generic labeller function for facets}
+\title{Construct labelling specification}
\usage{
labeller(..., .rows = NULL, .cols = NULL, keep.as.numeric = NULL,
.multi_line = TRUE, .default = label_value)
diff --git a/man/labellers.Rd b/man/labellers.Rd
index e006f95..13bd4ae 100644
--- a/man/labellers.Rd
+++ b/man/labellers.Rd
@@ -1,5 +1,5 @@
% Generated by roxygen2: do not edit by hand
-% Please edit documentation in R/facet-labels.r
+% Please edit documentation in R/labeller.r
\name{labellers}
\alias{label_both}
\alias{label_context}
@@ -7,7 +7,7 @@
\alias{label_value}
\alias{label_wrap_gen}
\alias{labellers}
-\title{Labeller functions}
+\title{Useful labeller functions}
\usage{
label_value(labels, multi_line = TRUE)
diff --git a/man/labs.Rd b/man/labs.Rd
index 637792e..8b47d8f 100644
--- a/man/labs.Rd
+++ b/man/labs.Rd
@@ -5,7 +5,7 @@
\alias{labs}
\alias{xlab}
\alias{ylab}
-\title{Change axis labels and legend titles}
+\title{Modify axis, legend, and plot labels}
\usage{
labs(...)
@@ -13,34 +13,41 @@ xlab(label)
ylab(label)
-ggtitle(label)
+ggtitle(label, subtitle = NULL)
}
\arguments{
-\item{...}{a list of new names in the form aesthetic = "new name"}
+\item{...}{A list of new name-value pairs. The name should either be
+an aesthetic, or one of "title", "subtitle", or "caption".}
-\item{label}{The text for the axis or plot title.}
+\item{label}{The text for the axis, plot title or caption below the plot.}
+
+\item{subtitle}{the text for the subtitle for the plot which will be
+displayed below the title. Leave \code{NULL} for no subtitle.}
}
\description{
-Change axis labels and legend titles
+Good labels are critical for making your plots accessible to a wider
+audience. Ensure the axis and legend labels display the full variable name.
+Use the plot \code{title} and \code{subtitle} to explain the main findings.
+It's common to use the \code{caption} to provide information about the
+data source.
+}
+\details{
+You can also set axis and legend labels in the individual scales (using
+the first argument, the \code{name}. I recommend doing that if you're
+changing other scale options.
}
\examples{
-p <- ggplot(mtcars, aes(mpg, wt)) + geom_point()
-p + labs(title = "New plot title")
-p + labs(x = "New x label")
-p + xlab("New x label")
-p + ylab("New y label")
-p + ggtitle("New plot title")
-
-# This should work independently of other functions that modify the
-# the scale names
-p + ylab("New y label") + ylim(2, 4)
-p + ylim(2, 4) + ylab("New y label")
-
-# The labs function also modifies legend labels
p <- ggplot(mtcars, aes(mpg, wt, colour = cyl)) + geom_point()
p + labs(colour = "Cylinders")
+p + labs(x = "New x label")
+
+# The plot title appears at the top-left, with the subtitle
+# display in smaller text underneath it
+p + labs(title = "New plot title")
+p + labs(title = "New plot title", subtitle = "A subtitle")
-# Can also pass in a list, if that is more convenient
-p + labs(list(title = "Title", x = "X", y = "Y"))
+# The caption appears in the bottom-right, and is often used for
+# sources, notes or copyright
+p + labs(caption = "(based on data from ...)")
}
diff --git a/man/last_plot.Rd b/man/last_plot.Rd
index 53590c4..7145783 100644
--- a/man/last_plot.Rd
+++ b/man/last_plot.Rd
@@ -12,4 +12,5 @@ Retrieve the last plot to be modified or created.
\seealso{
\code{\link{ggsave}}
}
+\keyword{internal}
diff --git a/man/layer.Rd b/man/layer.Rd
index 050e38e..a94e245 100644
--- a/man/layer.Rd
+++ b/man/layer.Rd
@@ -5,8 +5,8 @@
\title{Create a new layer}
\usage{
layer(geom = NULL, stat = NULL, data = NULL, mapping = NULL,
- position = NULL, params = list(), inherit.aes = TRUE, subset = NULL,
- show.legend = NA)
+ position = NULL, params = list(), inherit.aes = TRUE,
+ check.aes = TRUE, check.param = TRUE, subset = NULL, show.legend = NA)
}
\arguments{
\item{geom}{The geometric object to use display the data}
@@ -43,6 +43,10 @@ rather than combining with them. This is most useful for helper functions
that define both data and aesthetics and shouldn't inherit behaviour from
the default plot specification, e.g. \code{\link{borders}}.}
+\item{check.aes, check.param}{If \code{TRUE}, the default, will check that
+supplied parameters and aesthetics are understood by the \code{geom} or
+\code{stat}. Use \code{FALSE} to suppress the checks.}
+
\item{subset}{DEPRECATED. An older way of subsetting the dataset used in a
layer.}
@@ -71,4 +75,5 @@ ggplot(mpg, aes(displ, hwy)) +
)
}
+\keyword{internal}
diff --git a/man/lims.Rd b/man/lims.Rd
index 8894f10..6c14355 100644
--- a/man/lims.Rd
+++ b/man/lims.Rd
@@ -4,7 +4,7 @@
\alias{lims}
\alias{xlim}
\alias{ylim}
-\title{Convenience functions to set the axis limits.}
+\title{Set scale limits}
\usage{
lims(...)
@@ -13,37 +13,54 @@ xlim(...)
ylim(...)
}
\arguments{
-\item{...}{If numeric, will create a continuous scale, if factor or
-character, will create a discrete scale. For \code{lims}, every
-argument must be named.}
+\item{...}{A name-value pair. The name must be an aesthetic, and the value
+ must be either a length-2 numeric, a character, a factor, or a date/time.
+
+ A numeric value will create a continuous scale. If the larger value
+ comes first, the scale will be reversed. You can leave one value as
+ \code{NA} to compute from the range of the data.
+
+ A character or factor value will create a discrete scale.
+
+ A date-time value will create a continuous date/time scale.}
}
\description{
-Observations not in this range will be dropped completely and
-not passed to any other layers. If a NA value is substituted for one of the
-limits that limit is automatically calculated.
+This is a shortcut for supplying the \code{limits} argument to the
+individual scales. Note that, by default, any values outside the limits
+will be replaced with \code{NA}.
}
\examples{
-# xlim
-xlim(15, 20)
-xlim(20, 15)
-xlim(c(10, 20))
-xlim("a", "b", "c")
-
+# Zoom into a specified area
ggplot(mtcars, aes(mpg, wt)) +
geom_point() +
xlim(15, 20)
+
+# reverse scale
+ggplot(mtcars, aes(mpg, wt)) +
+ geom_point() +
+ xlim(20, 15)
+
# with automatic lower limit
ggplot(mtcars, aes(mpg, wt)) +
geom_point() +
xlim(NA, 20)
-# Change both xlim and ylim
-ggplot(mtcars, aes(mpg, wt)) +
+# You can also supply limits that are larger than the data.
+# This is useful if you want to match scales across different plots
+small <- subset(mtcars, cyl == 4)
+big <- subset(mtcars, cyl > 4)
+
+ggplot(small, aes(mpg, wt, colour = factor(cyl))) +
+ geom_point() +
+ lims(colour = c("4", "6", "8"))
+
+ggplot(big, aes(mpg, wt, colour = factor(cyl))) +
geom_point() +
- lims(x = c(10, 20), y = c(3, 5))
+ lims(colour = c("4", "6", "8"))
}
\seealso{
For changing x or y axis limits \strong{without} dropping data
- observations, see \code{\link{coord_cartesian}}.
+ observations, see \code{\link{coord_cartesian}}. To expand the range of
+ a plot to always include certain values, see \code{\link{expand_limits}}.
}
diff --git a/man/luv_colours.Rd b/man/luv_colours.Rd
index f36ca05..b69c596 100644
--- a/man/luv_colours.Rd
+++ b/man/luv_colours.Rd
@@ -3,9 +3,9 @@
\docType{data}
\name{luv_colours}
\alias{luv_colours}
-\title{\code{colors()} in Luv space.}
+\title{\code{colors()} in Luv space}
\format{A data frame with 657 observations and 4 variables:
-\itemize{
+\describe{
\item{L,u,v}{Position in Luv colour space}
\item{col}{Colour name}
}}
diff --git a/man/macros/aesthetics.Rd b/man/macros/aesthetics.Rd
new file mode 100644
index 0000000..5909cb2
--- /dev/null
+++ b/man/macros/aesthetics.Rd
@@ -0,0 +1 @@
+\newcommand{\aesthetics}{\Sexpr[results=rd,stage=build]{ggplot2:::rd_aesthetics("#1", "#2")}}
diff --git a/man/map_data.Rd b/man/map_data.Rd
index f73b6ae..7447ec9 100644
--- a/man/map_data.Rd
+++ b/man/map_data.Rd
@@ -2,7 +2,7 @@
% Please edit documentation in R/fortify-map.r
\name{map_data}
\alias{map_data}
-\title{Create a data frame of map data.}
+\title{Create a data frame of map data}
\usage{
map_data(map, region = ".", exact = FALSE, ...)
}
@@ -23,7 +23,8 @@ for more details.}
\item{...}{all other arguments passed on to \code{\link[maps]{map}}}
}
\description{
-Create a data frame of map data.
+Easily turn data from the \pkg{maps} package in to a data frame suitable
+for plotting with ggplot2.
}
\examples{
if (require("maps")) {
@@ -43,4 +44,5 @@ ggplot(choro, aes(long, lat)) +
coord_map("albers", at0 = 45.5, lat1 = 29.5)
}
}
+\keyword{internal}
diff --git a/man/margin.Rd b/man/margin.Rd
deleted file mode 100644
index 96c15ed..0000000
--- a/man/margin.Rd
+++ /dev/null
@@ -1,24 +0,0 @@
-% Generated by roxygen2: do not edit by hand
-% Please edit documentation in R/margins.R
-\name{margin}
-\alias{margin}
-\title{Define margins.}
-\usage{
-margin(t = 0, r = 0, b = 0, l = 0, unit = "pt")
-}
-\arguments{
-\item{t, r, b, l}{Dimensions of each margin. (To remember order, think trouble).}
-
-\item{unit}{Default units of dimensions. Defaults to "pt" so it
-can be most easily scaled with the text.}
-}
-\description{
-This is a convenient function that creates a grid unit object of the
-correct length to use for setting margins.
-}
-\examples{
-margin(4)
-margin(4, 2)
-margin(4, 3, 2, 1)
-}
-
diff --git a/man/max_height.Rd b/man/max_height.Rd
new file mode 100644
index 0000000..9b28a67
--- /dev/null
+++ b/man/max_height.Rd
@@ -0,0 +1,22 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/facet-.r
+\name{max_height}
+\alias{max_height}
+\alias{max_width}
+\title{Get the maximal width/length of a list of grobs}
+\usage{
+max_height(grobs)
+
+max_width(grobs)
+}
+\arguments{
+\item{grobs}{A list of grobs}
+}
+\value{
+The largest value. measured in cm as a unit object
+}
+\description{
+Get the maximal width/length of a list of grobs
+}
+\keyword{internal}
+
diff --git a/man/mean_se.Rd b/man/mean_se.Rd
index b0a0d25..374e6e2 100644
--- a/man/mean_se.Rd
+++ b/man/mean_se.Rd
@@ -2,7 +2,7 @@
% Please edit documentation in R/stat-summary.r
\name{mean_se}
\alias{mean_se}
-\title{Calculate mean and standard errors on either side.}
+\title{Calculate mean and standard error}
\usage{
mean_se(x, mult = 1)
}
@@ -11,10 +11,14 @@ mean_se(x, mult = 1)
\item{mult}{number of multiples of standard error}
}
+\value{
+A data frame with columns \code{y}, \code{ymin}, and \code{ymax}.
+}
\description{
-Calculate mean and standard errors on either side.
+For use with \code{\link{stat_summary}}
}
-\seealso{
-for use with \code{\link{stat_summary}}
+\examples{
+x <- rnorm(100)
+mean_se(x)
}
diff --git a/man/midwest.Rd b/man/midwest.Rd
index adce5e7..9293ae8 100644
--- a/man/midwest.Rd
+++ b/man/midwest.Rd
@@ -3,37 +3,37 @@
\docType{data}
\name{midwest}
\alias{midwest}
-\title{Midwest demographics.}
+\title{Midwest demographics}
\format{A data frame with 437 rows and 28 variables
-\itemize{
- \item PID
- \item county
- \item state
- \item area
- \item poptotal. Total population
- \item popdensity. Population density
- \item popwhite. Number of whites.
- \item popblack. Number of blacks.
- \item popamerindian. Number of American Indians.
- \item popasian. Number of Asians.
- \item popother. Number of other races.
- \item percwhite. Percent white.
- \item percblack. Percent black.
- \item percamerindan. Percent American Indian.
- \item percasian. Percent Asian.
- \item percother. Percent other races.
- \item popadults. Number of adults.
- \item perchsd.
- \item percollege. Percent college educated.
- \item percprof. Percent profession.
- \item poppovertyknown.
- \item percpovertyknown
- \item percbelowpoverty
- \item percchildbelowpovert
- \item percadultpoverty
- \item percelderlypoverty
- \item inmetro. In a metro area.
- \item category'
+\describe{
+ \item{PID}{}
+ \item{county}{}
+ \item{state}{}
+ \item{area}{}
+ \item{poptotal}{Total population}
+ \item{popdensity}{Population density}
+ \item{popwhite}{Number of whites.}
+ \item{popblack}{Number of blacks.}
+ \item{popamerindian}{Number of American Indians.}
+ \item{popasian}{Number of Asians.}
+ \item{popother}{Number of other races.}
+ \item{percwhite}{Percent white.}
+ \item{percblack}{Percent black.}
+ \item{percamerindan}{Percent American Indian.}
+ \item{percasian}{Percent Asian.}
+ \item{percother}{Percent other races.}
+ \item{popadults}{Number of adults.}
+ \item{perchsd}{}
+ \item{percollege}{Percent college educated.}
+ \item{percprof}{Percent profession.}
+ \item{poppovertyknown}{}
+ \item{percpovertyknown}{}
+ \item{percbelowpoverty}{}
+ \item{percchildbelowpovert}{}
+ \item{percadultpoverty}{}
+ \item{percelderlypoverty}{}
+ \item{inmetro}{In a metro area.}
+ \item{category}{}
}}
\usage{
midwest
diff --git a/man/mpg.Rd b/man/mpg.Rd
index 5cd4935..6f41816 100644
--- a/man/mpg.Rd
+++ b/man/mpg.Rd
@@ -5,18 +5,18 @@
\alias{mpg}
\title{Fuel economy data from 1999 and 2008 for 38 popular models of car}
\format{A data frame with 234 rows and 11 variables
-\itemize{
- \item manufacturer.
- \item model.
- \item displ. engine displacement, in litres
- \item year.
- \item cyl. number of cylinders
- \item trans. type of transmission
- \item drv. f = front-wheel drive, r = rear wheel drive, 4 = 4wd
- \item cty. city miles per gallon
- \item hwy. highway miles per gallon
- \item fl.
- \item class.
+\describe{
+ \item{manufacturer}{}
+ \item{model}{model name}
+ \item{displ}{engine displacement, in litres}
+ \item{year}{year of manufacture}
+ \item{cyl}{number of cylinders}
+ \item{trans}{type of transmission}
+ \item{drv}{f = front-wheel drive, r = rear wheel drive, 4 = 4wd}
+ \item{cty}{city miles per gallon}
+ \item{hwy}{highway miles per gallon}
+ \item{fl}{fuel type}
+ \item{class}{"type" of car}
}}
\usage{
mpg
diff --git a/man/msleep.Rd b/man/msleep.Rd
index b453719..51a8287 100644
--- a/man/msleep.Rd
+++ b/man/msleep.Rd
@@ -3,20 +3,20 @@
\docType{data}
\name{msleep}
\alias{msleep}
-\title{An updated and expanded version of the mammals sleep dataset.}
+\title{An updated and expanded version of the mammals sleep dataset}
\format{A data frame with 83 rows and 11 variables
-\itemize{
- \item name. common name
- \item genus.
- \item vore. carnivore, omnivore or herbivore?
- \item order.
- \item conservation. the conservation status of the animal
- \item sleep\_total. total amount of sleep, in hours
- \item sleep\_rem. rem sleep, in hours
- \item sleep\_cycle. length of sleep cycle, in hours
- \item awake. amount of time spent awake, in hours
- \item brainwt. brain weight in kilograms
- \item bodywt. body weight in kilograms
+\describe{
+ \item{name}{common name}
+ \item{genus}{}
+ \item{vore}{carnivore, omnivore or herbivore?}
+ \item{order}{}
+ \item{conservation}{the conservation status of the animal}
+ \item{sleep_total}{total amount of sleep, in hours}
+ \item{sleep_rem}{rem sleep, in hours}
+ \item{sleep_cycle}{length of sleep cycle, in hours}
+ \item{awake}{amount of time spent awake, in hours}
+ \item{brainwt}{brain weight in kilograms}
+ \item{bodywt}{body weight in kilograms}
}}
\usage{
msleep
diff --git a/man/position_dodge.Rd b/man/position_dodge.Rd
index 10c8466..e7fdc9f 100644
--- a/man/position_dodge.Rd
+++ b/man/position_dodge.Rd
@@ -2,17 +2,18 @@
% Please edit documentation in R/position-dodge.r
\name{position_dodge}
\alias{position_dodge}
-\title{Adjust position by dodging overlaps to the side.}
+\title{Dodge overlapping objects side-to-side}
\usage{
position_dodge(width = NULL)
}
\arguments{
\item{width}{Dodging width, when different to the width of the individual
elements. This is useful when you want to align narrow geoms with wider
-geoms. See the examples for a use case.}
+geoms. See the examples.}
}
\description{
-Adjust position by dodging overlaps to the side.
+Dodging preserves the vertical position of an geom while adjusting the
+horizontal position.
}
\examples{
ggplot(mtcars, aes(factor(cyl), fill = factor(vs))) +
@@ -22,33 +23,45 @@ ggplot(diamonds, aes(price, fill = cut)) +
geom_histogram(position="dodge")
# see ?geom_boxplot and ?geom_bar for more examples
+# In this case a frequency polygon is probably a better choice
+ggplot(diamonds, aes(price, colour = cut)) +
+ geom_freqpoly()
+}
+
+# Dodging with various widths -------------------------------------
# To dodge items with different widths, you need to be explicit
-df <- data.frame(x=c("a","a","b","b"), y=2:5, g = rep(1:2, 2))
+df <- data.frame(x = c("a","a","b","b"), y = 2:5, g = rep(1:2, 2))
p <- ggplot(df, aes(x, y, group = g)) +
- geom_bar(
- stat = "identity", position = "dodge",
- fill = "grey50", colour = "black"
- )
+ geom_col(position = "dodge", fill = "grey50", colour = "black")
p
# A line range has no width:
-p + geom_linerange(aes(ymin = y-1, ymax = y+1), position = "dodge")
-# You need to explicitly specify the width for dodging
-p + geom_linerange(aes(ymin = y-1, ymax = y+1),
- position = position_dodge(width = 0.9))
+p + geom_linerange(aes(ymin = y - 1, ymax = y + 1), position = "dodge")
-# Similarly with error bars:
-p + geom_errorbar(aes(ymin = y-1, ymax = y+1), width = 0.2,
- position = "dodge")
-p + geom_errorbar(aes(ymin = y-1, ymax = y+1, width = 0.2),
- position = position_dodge(width = 0.90))
-}
+# So you must explicitly specify the width
+p + geom_linerange(
+ aes(ymin = y - 1, ymax = y + 1),
+ position = position_dodge(width = 0.9)
+)
+
+# The same principle applies to error bars, which are usually
+# narrower than the bars
+p + geom_errorbar(
+ aes(ymin = y - 1, ymax = y + 1),
+ width = 0.2,
+ position = "dodge"
+)
+p + geom_errorbar(
+ aes(ymin = y - 1, ymax = y + 1),
+ width = 0.2,
+ position = position_dodge(width = 0.9)
+)
}
\seealso{
-Other position adjustments: \code{\link{position_fill}},
- \code{\link{position_identity}},
+Other position adjustments: \code{\link{position_identity}},
\code{\link{position_jitterdodge}},
\code{\link{position_jitter}},
- \code{\link{position_nudge}}
+ \code{\link{position_nudge}},
+ \code{\link{position_stack}}
}
diff --git a/man/position_identity.Rd b/man/position_identity.Rd
index d15fe2c..c651fc1 100644
--- a/man/position_identity.Rd
+++ b/man/position_identity.Rd
@@ -11,9 +11,9 @@ Don't adjust position
}
\seealso{
Other position adjustments: \code{\link{position_dodge}},
- \code{\link{position_fill}},
\code{\link{position_jitterdodge}},
\code{\link{position_jitter}},
- \code{\link{position_nudge}}
+ \code{\link{position_nudge}},
+ \code{\link{position_stack}}
}
diff --git a/man/position_jitter.Rd b/man/position_jitter.Rd
index 8676fbd..4092e72 100644
--- a/man/position_jitter.Rd
+++ b/man/position_jitter.Rd
@@ -2,7 +2,7 @@
% Please edit documentation in R/position-jitter.r
\name{position_jitter}
\alias{position_jitter}
-\title{Jitter points to avoid overplotting.}
+\title{Jitter points to avoid overplotting}
\usage{
position_jitter(width = NULL, height = NULL)
}
@@ -17,32 +17,33 @@ position_jitter(width = NULL, height = NULL)
data so it's not possible to see the distinction between the categories.}
}
\description{
-Jitter points to avoid overplotting.
+Couterintuitively adding random noise to a plot can sometimes make it
+easier to read. Jittering is particularly useful for small datasets with
+at least one discrete position.
}
\examples{
-ggplot(mtcars, aes(am, vs)) + geom_point()
+# Jittering is useful when you have a discrete position, and a relatively
+# small number of points
+# take up as much space as a boxplot or a bar
+ggplot(mpg, aes(class, hwy)) +
+ geom_boxplot(colour = "grey50") +
+ geom_jitter()
-# Default amount of jittering will generally be too much for
-# small datasets:
-ggplot(mtcars, aes(am, vs)) + geom_jitter()
+# If the default jittering is too much, as in this plot:
+ggplot(mtcars, aes(am, vs)) +
+ geom_jitter()
-# Two ways to override
+# You can adjust it in two ways
ggplot(mtcars, aes(am, vs)) +
geom_jitter(width = 0.1, height = 0.1)
ggplot(mtcars, aes(am, vs)) +
geom_jitter(position = position_jitter(width = 0.1, height = 0.1))
-
-# The default works better for large datasets, where it will
-# take up as much space as a boxplot or a bar
-ggplot(mpg, aes(class, hwy)) +
- geom_jitter() +
- geom_boxplot()
}
\seealso{
Other position adjustments: \code{\link{position_dodge}},
- \code{\link{position_fill}},
\code{\link{position_identity}},
\code{\link{position_jitterdodge}},
- \code{\link{position_nudge}}
+ \code{\link{position_nudge}},
+ \code{\link{position_stack}}
}
diff --git a/man/position_jitterdodge.Rd b/man/position_jitterdodge.Rd
index c792ccf..d316364 100644
--- a/man/position_jitterdodge.Rd
+++ b/man/position_jitterdodge.Rd
@@ -2,7 +2,7 @@
% Please edit documentation in R/position-jitterdodge.R
\name{position_jitterdodge}
\alias{position_jitterdodge}
-\title{Adjust position by simultaneously dodging and jittering}
+\title{Simultaneously dodge and jitter}
\usage{
position_jitterdodge(jitter.width = NULL, jitter.height = 0,
dodge.width = 0.75)
@@ -29,9 +29,9 @@ ggplot(dsub, aes(x = cut, y = carat, fill = clarity)) +
}
\seealso{
Other position adjustments: \code{\link{position_dodge}},
- \code{\link{position_fill}},
\code{\link{position_identity}},
\code{\link{position_jitter}},
- \code{\link{position_nudge}}
+ \code{\link{position_nudge}},
+ \code{\link{position_stack}}
}
diff --git a/man/position_nudge.Rd b/man/position_nudge.Rd
index fee17cd..98e4524 100644
--- a/man/position_nudge.Rd
+++ b/man/position_nudge.Rd
@@ -2,7 +2,7 @@
% Please edit documentation in R/position-nudge.R
\name{position_nudge}
\alias{position_nudge}
-\title{Nudge points.}
+\title{Nudge points a fixed distance}
\usage{
position_nudge(x = 0, y = 0)
}
@@ -10,8 +10,10 @@ position_nudge(x = 0, y = 0)
\item{x, y}{Amount of vertical and horizontal distance to move.}
}
\description{
-This is useful if you want to nudge labels a little ways from their
-points.
+\code{position_nudge} is generally useful for adjusting the position of
+items on discrete scales by a small amount. Nudging is built in to
+\code{\link{geom_text}} because it's so useful for moving labels a small
+distance from what they're labelling.
}
\examples{
df <- data.frame(
@@ -26,12 +28,17 @@ ggplot(df, aes(x, y)) +
ggplot(df, aes(x, y)) +
geom_point() +
geom_text(aes(label = y), position = position_nudge(y = -0.1))
+
+# Or, in brief
+ggplot(df, aes(x, y)) +
+ geom_point() +
+ geom_text(aes(label = y), nudge_y = -0.1)
}
\seealso{
Other position adjustments: \code{\link{position_dodge}},
- \code{\link{position_fill}},
\code{\link{position_identity}},
\code{\link{position_jitterdodge}},
- \code{\link{position_jitter}}
+ \code{\link{position_jitter}},
+ \code{\link{position_stack}}
}
diff --git a/man/position_stack.Rd b/man/position_stack.Rd
index 6bbde03..0c2a9c7 100644
--- a/man/position_stack.Rd
+++ b/man/position_stack.Rd
@@ -1,52 +1,131 @@
% Generated by roxygen2: do not edit by hand
-% Please edit documentation in R/position-fill.r, R/position-stack.r
-\name{position_fill}
+% Please edit documentation in R/position-stack.r
+\name{position_stack}
\alias{position_fill}
\alias{position_stack}
-\title{Stack overlapping objects on top of one another.}
+\title{Stack overlapping objects on top of each another}
\usage{
-position_fill()
+position_stack(vjust = 1, reverse = FALSE)
-position_stack()
+position_fill(vjust = 1, reverse = FALSE)
+}
+\arguments{
+\item{vjust}{Vertical adjustment for geoms that have a position
+(like points or lines), not a dimension (like bars or areas). Set to
+\code{0} to align with the bottom, \code{0.5} for the middle,
+and \code{1} (the default) for the top.}
+
+\item{reverse}{If \code{TRUE}, will reverse the default stacking order.
+This is useful if you're rotating both the plot and legend.}
}
\description{
-\code{position_fill} additionally standardises each stack to have unit
-height.
+\code{position_stack()} stacks bars on top of each other;
+\code{position_fill()} stacks bars and standardises each stack to have
+constant height.
+}
+\details{
+\code{position_fill()} and \code{position_stack()} automatically stack
+values in reverse order of the group aesthetic, which for bar charts is
+usually defined by the fill aesthetic (the default group aesthetic is formed
+by the combination of all discrete aesthetics except for x and y). This
+default ensures that bar colours align with the default legend.
+
+There are three ways to override the defaults depending on what you want:
+
+\enumerate{
+ \item Change the order of the levels in the underyling factor. This
+ will change the stacking order, and the order of keys in the legend.
+
+\item Set the legend \code{breaks} to change the order of the keys
+ without affecting the stacking.
+
+\item Manually set the group aesthetic to change the stacking order
+ without affecting the legend.
+}
+
+Stacking of positive and negative values are performed separately so that
+positive values stack upwards from the x-axis and negative values stack
+downward.
}
\examples{
-# Stacking is the default behaviour for most area plots:
-ggplot(mtcars, aes(factor(cyl), fill = factor(vs))) + geom_bar()
+# Stacking and filling ------------------------------------------------------
+
+# Stacking is the default behaviour for most area plots.
# Fill makes it easier to compare proportions
ggplot(mtcars, aes(factor(cyl), fill = factor(vs))) +
+ geom_bar()
+ggplot(mtcars, aes(factor(cyl), fill = factor(vs))) +
geom_bar(position = "fill")
-# To change stacking order, use factor() to change order of levels
-mtcars$vs <- factor(mtcars$vs, levels = c(1,0))
-ggplot(mtcars, aes(factor(cyl), fill = factor(vs))) + geom_bar()
-
ggplot(diamonds, aes(price, fill = cut)) +
geom_histogram(binwidth = 500)
-# When used with a histogram, position_fill creates a conditional density
-# estimate
ggplot(diamonds, aes(price, fill = cut)) +
geom_histogram(binwidth = 500, position = "fill")
# Stacking is also useful for time series
-data.set <- data.frame(
- Time = c(rep(1, 4),rep(2, 4), rep(3, 4), rep(4, 4)),
- Type = rep(c('a', 'b', 'c', 'd'), 4),
- Value = rpois(16, 10)
+series <- data.frame(
+ time = c(rep(1, 4),rep(2, 4), rep(3, 4), rep(4, 4)),
+ type = rep(c('a', 'b', 'c', 'd'), 4),
+ value = rpois(16, 10)
)
+ggplot(series, aes(time, value)) +
+ geom_area(aes(fill = type))
+
+# Stacking order ------------------------------------------------------------
+
+# You control the stacking order by setting the levels of the underlying
+# factor. See the forcats package for convenient helpers.
+series$type2 <- factor(series$type, levels = c('c', 'b', 'd', 'a'))
+ggplot(series, aes(time, value)) +
+ geom_area(aes(fill = type2))
+
+# You can change the order of the levels in the legend using the scale
+ggplot(series, aes(time, value)) +
+ geom_area(aes(fill = type)) +
+ scale_fill_discrete(breaks = c('a', 'b', 'c', 'd'))
+
+# Non-area plots ------------------------------------------------------------
-ggplot(data.set, aes(Time, Value)) + geom_area(aes(fill = Type))
+# When stacking across multiple layers it's a good idea to always set
+# the `group` aethetic in the ggplot() call. This ensures that all layers
+# are stacked in the same way.
+ggplot(series, aes(time, value, group = type)) +
+ geom_line(aes(colour = type), position = "stack") +
+ geom_point(aes(colour = type), position = "stack")
-# If you want to stack lines, you need to say so:
-ggplot(data.set, aes(Time, Value)) + geom_line(aes(colour = Type))
-ggplot(data.set, aes(Time, Value)) +
- geom_line(position = "stack", aes(colour = Type))
+ggplot(series, aes(time, value, group = type)) +
+ geom_area(aes(fill = type)) +
+ geom_line(aes(group = type), position = "stack")
+
+# You can also stack labels, but the default position is suboptimal.
+ggplot(series, aes(time, value, group = type)) +
+ geom_area(aes(fill = type)) +
+ geom_text(aes(label = type), position = "stack")
+
+# You can override this with the vjust parameter. A vjust of 0.5
+# will center the labels inside the corresponding area
+ggplot(series, aes(time, value, group = type)) +
+ geom_area(aes(fill = type)) +
+ geom_text(aes(label = type), position = position_stack(vjust = 0.5))
+
+# Negative values -----------------------------------------------------------
+
+df <- tibble::tribble(
+ ~x, ~y, ~grp,
+ "a", 1, "x",
+ "a", 2, "y",
+ "b", 1, "x",
+ "b", 3, "y",
+ "b", -1, "y"
+)
+ggplot(data = df, aes(x, y, group = grp)) +
+ geom_col(aes(fill = grp), position = position_stack(reverse = TRUE)) +
+ geom_hline(yintercept = 0)
-# But realise that this makes it *much* harder to compare individual
-# trends
+ggplot(data = df, aes(x, y, group = grp)) +
+ geom_col(aes(fill = grp)) +
+ geom_hline(yintercept = 0) +
+ geom_text(aes(label = grp), position = position_stack(vjust = 0.5))
}
\seealso{
See \code{\link{geom_bar}} and \code{\link{geom_area}} for
diff --git a/man/presidential.Rd b/man/presidential.Rd
index 26a70da..3215aaf 100644
--- a/man/presidential.Rd
+++ b/man/presidential.Rd
@@ -3,7 +3,7 @@
\docType{data}
\name{presidential}
\alias{presidential}
-\title{Terms of 11 presidents from Eisenhower to Obama.}
+\title{Terms of 11 presidents from Eisenhower to Obama}
\format{A data frame with 11 rows and 4 variables}
\usage{
presidential
diff --git a/man/print.ggplot.Rd b/man/print.ggplot.Rd
index ce6a8f1..2130007 100644
--- a/man/print.ggplot.Rd
+++ b/man/print.ggplot.Rd
@@ -3,7 +3,7 @@
\name{print.ggplot}
\alias{plot.ggplot}
\alias{print.ggplot}
-\title{Draw plot on current graphics device.}
+\title{Explicitly draw plot}
\usage{
\method{print}{ggplot}(x, newpage = is.null(vp), vp = NULL, ...)
@@ -24,7 +24,25 @@ Invisibly returns the result of \code{\link{ggplot_build}}, which
information about the scales, panels etc.
}
\description{
-Draw plot on current graphics device.
+Generally, you do not need to print or plot a ggplot2 plot explicitly: the
+default top-level print method will do it for you. You will, however, need
+to call \code{print()} explicitly if you want to draw a plot inside a
+function or for loop.
+}
+\examples{
+colours <- list(~class, ~drv, ~fl)
+
+# Doesn't seem to do anything!
+for (colour in colours) {
+ ggplot(mpg, aes_(~ displ, ~ hwy, colour = colour)) +
+ geom_point()
+}
+
+# Works when we explicitly print the plots
+for (colour in colours) {
+ print(ggplot(mpg, aes_(~ displ, ~ hwy, colour = colour)) +
+ geom_point())
+}
}
\keyword{hplot}
diff --git a/man/print.ggproto.Rd b/man/print.ggproto.Rd
index 273236d..1bf8d95 100644
--- a/man/print.ggproto.Rd
+++ b/man/print.ggproto.Rd
@@ -1,10 +1,13 @@
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/ggproto.r
\name{print.ggproto}
+\alias{format.ggproto}
\alias{print.ggproto}
-\title{Print a ggproto object}
+\title{Format or print a ggproto object}
\usage{
\method{print}{ggproto}(x, ..., flat = TRUE)
+
+\method{format}{ggproto}(x, ..., flat = TRUE)
}
\arguments{
\item{x}{A ggproto object to print.}
@@ -20,4 +23,13 @@ If a ggproto object has a \code{$print} method, this will call that method.
Otherwise, it will print out the members of the object, and optionally, the
members of the inherited objects.
}
+\examples{
+Dog <- ggproto(
+ print = function(self, n) {
+ cat("Woof!\\n")
+ }
+ )
+Dog
+cat(format(Dog), "\\n")
+}
diff --git a/man/qplot.Rd b/man/qplot.Rd
index dbe1241..81d3f95 100644
--- a/man/qplot.Rd
+++ b/man/qplot.Rd
@@ -42,10 +42,12 @@ x axis label, and y axis label respectively.}
\item{stat, position}{DEPRECATED.}
}
\description{
-\code{qplot} is the basic plotting function in the ggplot2 package,
-designed to be familiar if you're used to base \code{\link{plot}()}.
-It's a convenient wrapper for creating a number of different types of plots
-using a consistent calling scheme.
+\code{qplot} is a shortcut designed to be familiar if you're used to base
+\code{\link{plot}()}. It's a convenient wrapper for creating a number of
+different types of plots using a consistent calling scheme. It's great
+for allowing you to produce plots quickly, but I highly recommend
+learning \code{\link{ggplot}()} as it makes it easier to create
+complex graphics.
}
\examples{
# Use data from data.frame
@@ -57,7 +59,7 @@ qplot(mpg, wt, data = mtcars, facets = vs ~ am)
\donttest{
qplot(1:10, rnorm(10), colour = runif(10))
qplot(1:10, letters[1:10])
-mod <- lm(mpg ~ wt, data=mtcars)
+mod <- lm(mpg ~ wt, data = mtcars)
qplot(resid(mod), fitted(mod))
f <- function() {
diff --git a/man/rel.Rd b/man/rel.Rd
deleted file mode 100644
index 8728154..0000000
--- a/man/rel.Rd
+++ /dev/null
@@ -1,21 +0,0 @@
-% Generated by roxygen2: do not edit by hand
-% Please edit documentation in R/theme-elements.r
-\name{rel}
-\alias{rel}
-\title{Relative sizing for theme elements}
-\usage{
-rel(x)
-}
-\arguments{
-\item{x}{A number representing the relative size}
-}
-\description{
-Relative sizing for theme elements
-}
-\examples{
-df <- data.frame(x = 1:3, y = 1:3)
-ggplot(df, aes(x, y)) +
- geom_point() +
- theme(axis.title.x = element_text(size = rel(2.5)))
-}
-
diff --git a/man/render_axes.Rd b/man/render_axes.Rd
new file mode 100644
index 0000000..e1104d3
--- /dev/null
+++ b/man/render_axes.Rd
@@ -0,0 +1,33 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/facet-.r
+\name{render_axes}
+\alias{render_axes}
+\title{Render panel axes}
+\usage{
+render_axes(x = NULL, y = NULL, coord, theme, transpose = FALSE)
+}
+\arguments{
+\item{x, y}{A list of ranges as available to the draw_panel method in
+\code{Facet} subclasses.}
+
+\item{coord}{A \code{Coord} object}
+
+\item{theme}{A \code{theme} object}
+
+\item{transpose}{Should the output be transposed?}
+}
+\value{
+A list with the element "x" and "y" each containing axis
+specifications for the ranges passed in. Each axis specification is a list
+with a "top" and "bottom" element for x-axes and "left" and "right" element
+for y-axis, holding the respective axis grobs. Depending on the content of x
+and y some of the grobs might be zeroGrobs. If \code{transpose=TRUE} the
+content of the x and y elements will be transposed so e.g. all left-axes are
+collected in a left element as a list of grobs.
+}
+\description{
+These helpers facilitates generating theme compliant axes when
+building up the plot.
+}
+\keyword{internal}
+
diff --git a/man/render_strips.Rd b/man/render_strips.Rd
new file mode 100644
index 0000000..8b56dfb
--- /dev/null
+++ b/man/render_strips.Rd
@@ -0,0 +1,26 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/facet-.r
+\name{render_strips}
+\alias{render_strips}
+\title{Render panel strips}
+\usage{
+render_strips(x = NULL, y = NULL, labeller, theme)
+}
+\arguments{
+\item{x, y}{A data.frame with a column for each variable and a row for each
+combination to draw}
+
+\item{labeller}{A labeller function}
+
+\item{theme}{a \code{theme} object}
+}
+\value{
+A list with an "x" and a "y" element, each containing a "top" and
+"bottom" or "left" and "right" element respectively. These contains a list of
+rendered strips as gtables.
+}
+\description{
+All positions are rendered and it is up to the facet to decide which to use
+}
+\keyword{internal}
+
diff --git a/man/resolution.Rd b/man/resolution.Rd
index be2e1dd..ade3def 100644
--- a/man/resolution.Rd
+++ b/man/resolution.Rd
@@ -2,7 +2,7 @@
% Please edit documentation in R/utilities-resolution.r
\name{resolution}
\alias{resolution}
-\title{Compute the "resolution" of a data vector.}
+\title{Compute the "resolution" of a numeric vector}
\usage{
resolution(x, zero = TRUE)
}
@@ -13,19 +13,18 @@ resolution(x, zero = TRUE)
computation of resolution}
}
\description{
-The resolution is is the smallest non-zero distance between adjacent
+The resolution is the smallest non-zero distance between adjacent
values. If there is only one unique value, then the resolution is defined
-to be one.
-}
-\details{
-If x is an integer vector, then it is assumed to represent a discrete
-variable, and the resolution is 1.
+to be one. If x is an integer vector, then it is assumed to represent a
+discrete variable, and the resolution is 1.
}
\examples{
resolution(1:10)
resolution((1:10) - 0.5)
resolution((1:10) - 0.5, FALSE)
-resolution(c(1,2, 10, 20, 50))
-resolution(as.integer(c(1, 10, 20, 50))) # Returns 1
+
+# Note the difference between numeric and integer vectors
+resolution(c(2, 10, 20, 50))
+resolution(c(2L, 10L, 20L, 50L))
}
diff --git a/man/scale_alpha.Rd b/man/scale_alpha.Rd
index 8da57a0..9ce1217 100644
--- a/man/scale_alpha.Rd
+++ b/man/scale_alpha.Rd
@@ -4,7 +4,7 @@
\alias{scale_alpha}
\alias{scale_alpha_continuous}
\alias{scale_alpha_discrete}
-\title{Alpha scales.}
+\title{Alpha transparency scales}
\usage{
scale_alpha(..., range = c(0.1, 1))
@@ -17,22 +17,26 @@ scale_alpha_discrete(..., range = c(0.1, 1))
or \code{\link{discrete_scale}} as appropriate, to control name, limits,
breaks, labels and so forth.}
-\item{range}{range of output alpha values. Should lie between 0 and 1.}
+\item{range}{Output range of alpha values. Must lie between 0 and 1.}
}
\description{
+Alpha-transparency scales are not tremendously useful, but can be a
+convenient way to visually down-weight less important observations.
\code{scale_alpha} is an alias for \code{scale_alpha_continuous} since
that is the most common use of alpha, and it saves a bit of typing.
}
\examples{
-(p <- ggplot(mtcars, aes(mpg, cyl)) +
- geom_point(aes(alpha = cyl)))
-p + scale_alpha("cylinders")
-p + scale_alpha("number\\nof\\ncylinders")
+p <- ggplot(mpg, aes(displ, hwy)) +
+ geom_point(aes(alpha = year))
+p
+p + scale_alpha("cylinders")
p + scale_alpha(range = c(0.4, 0.8))
-
-(p <- ggplot(mtcars, aes(mpg, cyl)) +
- geom_point(aes(alpha = factor(cyl))))
-p + scale_alpha_discrete(range = c(0.4, 0.8))
+}
+\seealso{
+Other colour scales: \code{\link{scale_colour_brewer}},
+ \code{\link{scale_colour_gradient}},
+ \code{\link{scale_colour_grey}},
+ \code{\link{scale_colour_hue}}
}
diff --git a/man/scale_brewer.Rd b/man/scale_brewer.Rd
index 8814fb7..deaea8a 100644
--- a/man/scale_brewer.Rd
+++ b/man/scale_brewer.Rd
@@ -48,15 +48,19 @@ other values are deprecated.}
colour bar, or \code{"legend"} for discrete colour legend.}
}
\description{
-ColorBrewer provides sequential, diverging and qualitative colour schemes
-which are particularly suited and tested to display discrete values (levels
-of a factor) on a map. ggplot2 can use those colours in discrete scales. It
-also allows to smoothly interpolate 6 colours from any palette to a
-continuous scale (6 colours per palette gives nice gradients; more results in
-more saturated colours which do not look as good). However, the original
-colour schemes (particularly the qualitative ones) were not intended for this
-and the perceptual result is left to the appreciation of the user.
-See \url{http://colorbrewer2.org} for more information.
+The \code{brewer} scales provides sequential, diverging and qualitative
+colour schemes from ColorBrewer. These are particularly well suited to
+display discrete values on a map. See \url{http://colorbrewer2.org} for
+more information.
+}
+\details{
+The \code{brewer} scales were carefully designed and tested on discrete data.
+They were not designed to be extended to continuous data, but results often
+look good. Your mileage may vary.
+}
+\note{
+The \code{distiller} scales extends brewer to continuous scales by smoothly
+interpolate 6 colours from any palette to a continuous scale.
}
\section{Palettes}{
@@ -72,9 +76,9 @@ The following palettes are available for use with these scales:
dsamp <- diamonds[sample(nrow(diamonds), 1000), ]
(d <- ggplot(dsamp, aes(carat, price)) +
geom_point(aes(colour = clarity)))
+d + scale_colour_brewer()
# Change scale label
-d + scale_colour_brewer()
d + scale_colour_brewer("Diamond\\nclarity")
# Select brewer palette to use, see ?scales::brewer_pal for more details
@@ -101,7 +105,7 @@ v + scale_fill_distiller()
v + scale_fill_distiller(palette = "Spectral")
}
\seealso{
-Other colour scales:
+Other colour scales: \code{\link{scale_alpha}},
\code{\link{scale_colour_gradient}},
\code{\link{scale_colour_grey}},
\code{\link{scale_colour_hue}}
diff --git a/man/scale_continuous.Rd b/man/scale_continuous.Rd
index 9602800..e14b5c4 100644
--- a/man/scale_continuous.Rd
+++ b/man/scale_continuous.Rd
@@ -1,7 +1,6 @@
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/scale-continuous.r
\name{scale_continuous}
-\alias{scale_continuous}
\alias{scale_x_continuous}
\alias{scale_x_log10}
\alias{scale_x_reverse}
@@ -10,17 +9,17 @@
\alias{scale_y_log10}
\alias{scale_y_reverse}
\alias{scale_y_sqrt}
-\title{Continuous position scales (x & y).}
+\title{Position scales for continuous data (x & y)}
\usage{
scale_x_continuous(name = waiver(), breaks = waiver(),
minor_breaks = waiver(), labels = waiver(), limits = NULL,
expand = waiver(), oob = censor, na.value = NA_real_,
- trans = "identity")
+ trans = "identity", position = "bottom", sec.axis = waiver())
scale_y_continuous(name = waiver(), breaks = waiver(),
minor_breaks = waiver(), labels = waiver(), limits = NULL,
expand = waiver(), oob = censor, na.value = NA_real_,
- trans = "identity")
+ trans = "identity", position = "left", sec.axis = waiver())
scale_x_log10(...)
@@ -90,80 +89,80 @@ discrete variables.}
\code{\link[scales]{boxcox_trans}}. You can create your own
transformation with \code{\link[scales]{trans_new}}.}
+\item{position}{The position of the axis. "left" or "right" for vertical
+scales, "top" or "bottom" for horizontal scales}
+
+\item{sec.axis}{specifify a secondary axis}
+
\item{...}{Other arguments passed on to \code{scale_(x|y)_continuous}}
}
\description{
-\code{scale_x_continuous} and \code{scale_y_continuous} are the key functions.
-The others, \code{scale_x_log10}, \code{scale_y_sqrt} etc, are aliases
-that set the \code{trans} argument to commonly used transformations.
+\code{scale_x_continuous} and \code{scale_y_continuous} are the default
+scales for continuous x and y aesthetics. There are three variants
+that set the \code{trans} argument for commonly used transformations:
+\code{scale_*_log10}, \code{scale_*_sqrt} and \code{scale_*_reverse}.
+}
+\details{
+For simple manipulation of labels and limits, you may wish to use
+\code{\link{labs}()} and \code{\link{lims}()} instead.
}
\examples{
-\donttest{
-if (require(ggplot2movies)) {
-m <- ggplot(subset(movies, votes > 1000), aes(rating, votes)) +
- geom_point(na.rm = TRUE)
-m
+p1 <- ggplot(mpg, aes(displ, hwy)) +
+ geom_point()
+p1
# Manipulating the default position scales lets you:
-
# * change the axis labels
-m + scale_y_continuous("number of votes")
-m + scale_y_continuous(quote(votes ^ alpha))
+p1 +
+ scale_x_continuous("Engine displacement (L)") +
+ scale_y_continuous("Highway MPG")
+
+# You can also use the short-cut labs().
+# Use NULL to suppress axis labels
+p1 + labs(x = NULL, y = NULL)
# * modify the axis limits
-m + scale_y_continuous(limits = c(0, 5000))
-m + scale_y_continuous(limits = c(1000, 10000))
-m + scale_x_continuous(limits = c(7, 8))
+p1 + scale_x_continuous(limits = c(2, 6))
+p1 + scale_x_continuous(limits = c(0, 10))
-# you can also use the short hand functions xlim and ylim
-m + ylim(0, 5000)
-m + ylim(1000, 10000)
-m + xlim(7, 8)
+# you can also use the short hand functions `xlim()` and `ylim()`
+p1 + xlim(2, 6)
# * choose where the ticks appear
-m + scale_x_continuous(breaks = 1:10)
-m + scale_x_continuous(breaks = c(1,3,7,9))
-
-# * manually label the ticks
-m + scale_x_continuous(breaks = c(2,5,8), labels = c("two", "five", "eight"))
-m + scale_x_continuous(breaks = c(2,5,8), labels = c("horrible", "ok", "awesome"))
-m + scale_x_continuous(breaks = c(2,5,8), labels = expression(Alpha, Beta, Omega))
-
-# There are a few built in transformation that you can use:
-m + scale_y_log10()
-m + scale_y_sqrt()
-m + scale_y_reverse()
-# You can also create your own and supply them to the trans argument.
-# See ?scales::trans_new
-
-# You can control the formatting of the labels with the formatter
-# argument. Some common formats are built into the scales package:
+p1 + scale_x_continuous(breaks = c(2, 4, 6))
+
+# * add what labels they have
+p1 + scale_x_continuous(
+ breaks = c(2, 4, 6),
+ label = c("two", "four", "six")
+)
+
+# Typically you'll pass a function to the `labels` argument.
+# Some common formats are built into the scales package:
df <- data.frame(
x = rnorm(10) * 100000,
y = seq(0, 1, length.out = 10)
)
-p <- ggplot(df, aes(x, y)) + geom_point()
-p + scale_y_continuous(labels = scales::percent)
-p + scale_y_continuous(labels = scales::dollar)
-p + scale_x_continuous(labels = scales::comma)
-
-# Other shortcut functions
-ggplot(movies, aes(rating, votes)) +
- geom_point() +
- ylim(1e4, 5e4)
-# * axis labels
-ggplot(movies, aes(rating, votes)) +
- geom_point() +
- labs(x = "My x axis", y = "My y axis")
-# * log scaling
-ggplot(movies, aes(rating, votes)) +
- geom_point() +
- scale_x_log10() +
- scale_y_log10()
-}
-}
+p2 <- ggplot(df, aes(x, y)) + geom_point()
+p2 + scale_y_continuous(labels = scales::percent)
+p2 + scale_y_continuous(labels = scales::dollar)
+p2 + scale_x_continuous(labels = scales::comma)
+
+# You can also override the default linear mapping by using a
+# transformation. There are three shortcuts:
+p1 + scale_y_log10()
+p1 + scale_y_sqrt()
+p1 + scale_y_reverse()
+
+# Or you can supply a transformation in the `trans` argument:
+p1 + scale_y_continuous(trans = scales::reciprocal_trans())
+
+# You can also create your own. See ?scales::trans_new
}
\seealso{
-\code{\link{scale_date}} for date/time position scales.
+\code{\link{sec_axis}} for how to specify secondary axes
+
+Other position scales: \code{\link{scale_x_date}},
+ \code{\link{scale_x_discrete}}
}
diff --git a/man/scale_date.Rd b/man/scale_date.Rd
index cc75964..fa98a90 100644
--- a/man/scale_date.Rd
+++ b/man/scale_date.Rd
@@ -1,30 +1,41 @@
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/scale-date.r
\name{scale_date}
-\alias{scale_date}
\alias{scale_x_date}
\alias{scale_x_datetime}
+\alias{scale_x_time}
\alias{scale_y_date}
\alias{scale_y_datetime}
-\title{Position scale, date & date times}
+\alias{scale_y_time}
+\title{Position scales for date/time data}
\usage{
scale_x_date(name = waiver(), breaks = waiver(), date_breaks = waiver(),
labels = waiver(), date_labels = waiver(), minor_breaks = waiver(),
- date_minor_breaks = waiver(), limits = NULL, expand = waiver())
+ date_minor_breaks = waiver(), limits = NULL, expand = waiver(),
+ position = "bottom")
scale_y_date(name = waiver(), breaks = waiver(), date_breaks = waiver(),
labels = waiver(), date_labels = waiver(), minor_breaks = waiver(),
- date_minor_breaks = waiver(), limits = NULL, expand = waiver())
+ date_minor_breaks = waiver(), limits = NULL, expand = waiver(),
+ position = "left")
scale_x_datetime(name = waiver(), breaks = waiver(),
date_breaks = waiver(), labels = waiver(), date_labels = waiver(),
- minor_breaks = waiver(), date_minor_breaks = waiver(), limits = NULL,
- expand = waiver())
+ minor_breaks = waiver(), date_minor_breaks = waiver(), timezone = NULL,
+ limits = NULL, expand = waiver(), position = "bottom")
scale_y_datetime(name = waiver(), breaks = waiver(),
date_breaks = waiver(), labels = waiver(), date_labels = waiver(),
- minor_breaks = waiver(), date_minor_breaks = waiver(), limits = NULL,
- expand = waiver())
+ minor_breaks = waiver(), date_minor_breaks = waiver(), timezone = NULL,
+ limits = NULL, expand = waiver(), position = "left")
+
+scale_x_time(name = waiver(), breaks = waiver(), minor_breaks = waiver(),
+ labels = waiver(), limits = NULL, expand = waiver(), oob = censor,
+ na.value = NA_real_, position = "bottom")
+
+scale_y_time(name = waiver(), breaks = waiver(), minor_breaks = waiver(),
+ labels = waiver(), limits = NULL, expand = waiver(), oob = censor,
+ na.value = NA_real_, position = "left")
}
\arguments{
\item{name}{The name of the scale. Used as axis or legend title. If
@@ -77,10 +88,24 @@ additive expansion constants. These constants ensure that the data is
placed some distance away from the axes. The defaults are
\code{c(0.05, 0)} for continuous variables, and \code{c(0, 0.6)} for
discrete variables.}
+
+\item{position}{The position of the axis. "left" or "right" for vertical
+scales, "top" or "bottom" for horizontal scales}
+
+\item{timezone}{The timezone to use for display on the axes. The default
+(\code{NULL}) uses the timezone encoded in the data.}
+
+\item{oob}{Function that handles limits outside of the scale limits
+(out of bounds). The default replaces out of bounds values with NA.}
+
+\item{na.value}{Missing values will be replaced with this value.}
}
\description{
-Use \code{scale_*_date} with \code{Date} variables, and
-\code{scale_*_datetime} with \code{POSIXct} variables.
+These are the default scales for the three date/time class. These will
+usually be added automatically. To override manually, use
+\code{scale_*_date} for dates (class \code{Date}),
+\code{scale_*_datetime} for datetimes (class \code{POSIXct}), and
+\code{scale_*_time} for times (class \code{hms}).
}
\examples{
last_month <- Sys.Date() - 0:29
@@ -102,6 +127,7 @@ base + scale_x_date(date_minor_breaks = "1 day")
base + scale_x_date(limits = c(Sys.Date() - 7, NA))
}
\seealso{
-\code{\link{scale_continuous}} for continuous position scales.
+Other position scales: \code{\link{scale_x_continuous}},
+ \code{\link{scale_x_discrete}}
}
diff --git a/man/scale_discrete.Rd b/man/scale_discrete.Rd
index 9efab43..8b9076e 100644
--- a/man/scale_discrete.Rd
+++ b/man/scale_discrete.Rd
@@ -3,11 +3,11 @@
\name{scale_x_discrete}
\alias{scale_x_discrete}
\alias{scale_y_discrete}
-\title{Discrete position.}
+\title{Position scales for discrete data}
\usage{
-scale_x_discrete(..., expand = waiver())
+scale_x_discrete(..., expand = waiver(), position = "bottom")
-scale_y_discrete(..., expand = waiver())
+scale_y_discrete(..., expand = waiver(), position = "left")
}
\arguments{
\item{...}{common discrete scale parameters: \code{name}, \code{breaks},
@@ -17,6 +17,9 @@ scale_y_discrete(..., expand = waiver())
\item{expand}{a numeric vector of length two giving multiplicative and
additive expansion constants. These constants ensure that the data is
placed some distance away from the axes.}
+
+\item{position}{The position of the axis. \code{left} or \code{right} for y
+axes, \code{top} or \code{bottom} for x axes}
}
\description{
You can use continuous positions even with a discrete position scale -
@@ -41,7 +44,7 @@ d + scale_x_discrete("Cut", labels = c("Fair" = "F","Good" = "G",
# Use limits to adjust the which levels (and in what order)
# are displayed
-d + scale_x_discrete(limits=c("Fair","Ideal"))
+d + scale_x_discrete(limits = c("Fair","Ideal"))
# you can also use the short hand functions xlim and ylim
d + xlim("Fair","Ideal", "Good")
@@ -58,4 +61,8 @@ ggplot(mpg, aes(reorder(manufacturer, displ), cty)) +
scale_x_discrete(labels = abbreviate)
}
}
+\seealso{
+Other position scales: \code{\link{scale_x_continuous}},
+ \code{\link{scale_x_date}}
+}
diff --git a/man/scale_gradient.Rd b/man/scale_gradient.Rd
index 1f77123..f9d925c 100644
--- a/man/scale_gradient.Rd
+++ b/man/scale_gradient.Rd
@@ -17,7 +17,7 @@
\alias{scale_fill_gradient}
\alias{scale_fill_gradient2}
\alias{scale_fill_gradientn}
-\title{Smooth gradient between two colours}
+\title{Gradient colour scales}
\usage{
scale_colour_gradient(..., low = "#132B43", high = "#56B1F7",
space = "Lab", na.value = "grey50", guide = "colourbar")
@@ -40,7 +40,7 @@ scale_fill_gradientn(..., colours, values = NULL, space = "Lab",
na.value = "grey50", guide = "colourbar", colors)
}
\arguments{
-\item{...}{Other arguments passed on to \code{\link{discrete_scale}}
+\item{...}{Other arguments passed on to \code{\link{continuous_scale}}
to control name, limits, breaks, labels and so forth.}
\item{low, high}{Colours for low and high ends of the gradient.}
@@ -72,7 +72,7 @@ to map an arbitrary range to between 0 and 1.}
}
\details{
Default colours are generated with \pkg{munsell} and
-\code{mnsl(c("2.5PB 2/4", "2.5PB 7/10")}. Generally, for continuous
+\code{mnsl(c("2.5PB 2/4", "2.5PB 7/10"))}. Generally, for continuous
colour scales you want to keep hue constant, but vary chroma and
luminance. The \pkg{munsell} package makes this easy to do using the
Munsell colour system.
@@ -115,7 +115,7 @@ ggplot(df, aes(x, y)) +
\code{\link[scales]{seq_gradient_pal}} for details on underlying
palette
-Other colour scales:
+Other colour scales: \code{\link{scale_alpha}},
\code{\link{scale_colour_brewer}},
\code{\link{scale_colour_grey}},
\code{\link{scale_colour_hue}}
diff --git a/man/scale_grey.Rd b/man/scale_grey.Rd
index 7b9cf75..2c461c9 100644
--- a/man/scale_grey.Rd
+++ b/man/scale_grey.Rd
@@ -4,7 +4,7 @@
\alias{scale_color_grey}
\alias{scale_colour_grey}
\alias{scale_fill_grey}
-\title{Sequential grey colour scale.}
+\title{Sequential grey colour scales}
\usage{
scale_colour_grey(..., start = 0.2, end = 0.8, na.value = "red")
@@ -21,7 +21,8 @@ to control name, limits, breaks, labels and so forth.}
\item{na.value}{Colour to use for missing values}
}
\description{
-Based on \code{\link{gray.colors}}
+Based on \code{\link{gray.colors}}. This is black and white equivalent
+of \code{\link{scale_colour_gradient}}.
}
\examples{
p <- ggplot(mtcars, aes(mpg, wt)) + geom_point(aes(colour = factor(cyl)))
@@ -41,7 +42,7 @@ ggplot(mtcars, aes(mpg, wt)) +
scale_colour_grey(na.value = "green")
}
\seealso{
-Other colour scales:
+Other colour scales: \code{\link{scale_alpha}},
\code{\link{scale_colour_brewer}},
\code{\link{scale_colour_gradient}},
\code{\link{scale_colour_hue}}
diff --git a/man/scale_hue.Rd b/man/scale_hue.Rd
index 7cc572e..8b9f99b 100644
--- a/man/scale_hue.Rd
+++ b/man/scale_hue.Rd
@@ -7,7 +7,7 @@
\alias{scale_colour_hue}
\alias{scale_fill_discrete}
\alias{scale_fill_hue}
-\title{Qualitative colour scale with evenly spaced hues.}
+\title{Evenly spaced colours for discrete data}
\usage{
scale_colour_hue(..., h = c(0, 360) + 15, c = 100, l = 65, h.start = 0,
direction = 1, na.value = "grey50")
@@ -34,7 +34,9 @@ combination of hue and luminance.}
\item{na.value}{Colour to use for missing values}
}
\description{
-Qualitative colour scale with evenly spaced hues.
+This is the default colour scale for categorical variables. It maps each
+level to an evenly spaced hue on the colour wheel. It does not generate
+colour-blind safe palettes.
}
\examples{
\donttest{
@@ -47,16 +49,16 @@ d + scale_colour_hue("clarity")
d + scale_colour_hue(expression(clarity[beta]))
# Adjust luminosity and chroma
-d + scale_colour_hue(l=40, c=30)
-d + scale_colour_hue(l=70, c=30)
-d + scale_colour_hue(l=70, c=150)
-d + scale_colour_hue(l=80, c=150)
+d + scale_colour_hue(l = 40, c = 30)
+d + scale_colour_hue(l = 70, c = 30)
+d + scale_colour_hue(l = 70, c = 150)
+d + scale_colour_hue(l = 80, c = 150)
# Change range of hues used
-d + scale_colour_hue(h=c(0, 90))
-d + scale_colour_hue(h=c(90, 180))
-d + scale_colour_hue(h=c(180, 270))
-d + scale_colour_hue(h=c(270, 360))
+d + scale_colour_hue(h = c(0, 90))
+d + scale_colour_hue(h = c(90, 180))
+d + scale_colour_hue(h = c(180, 270))
+d + scale_colour_hue(h = c(270, 360))
# Vary opacity
# (only works with pdf, quartz and cairo devices)
@@ -74,7 +76,7 @@ ggplot(mtcars, aes(mpg, wt)) +
}
}
\seealso{
-Other colour scales:
+Other colour scales: \code{\link{scale_alpha}},
\code{\link{scale_colour_brewer}},
\code{\link{scale_colour_gradient}},
\code{\link{scale_colour_grey}}
diff --git a/man/scale_identity.Rd b/man/scale_identity.Rd
index 8b63754..029769a 100644
--- a/man/scale_identity.Rd
+++ b/man/scale_identity.Rd
@@ -5,11 +5,10 @@
\alias{scale_color_identity}
\alias{scale_colour_identity}
\alias{scale_fill_identity}
-\alias{scale_identity}
\alias{scale_linetype_identity}
\alias{scale_shape_identity}
\alias{scale_size_identity}
-\title{Use values without scaling.}
+\title{Use values without scaling}
\usage{
scale_colour_identity(..., guide = "none")
@@ -27,10 +26,13 @@ scale_size_identity(..., guide = "none")
\item{...}{Other arguments passed on to \code{\link{discrete_scale}} or
\code{\link{continuous_scale}}}
-\item{guide}{Guide to use for this scale - defaults to \code{"none"}.}
+\item{guide}{Guide to use for this scale. Defaults to \code{"none"}.}
}
\description{
-Use values without scaling.
+Use this set of scales when your data has already been scaled, i.e. it
+already represents aesthetic values that ggplot2 can handle directly
+This will not produce a legend unless you also supply the \code{breaks}
+and \code{labels}.
}
\examples{
ggplot(luv_colours, aes(u, v)) +
@@ -59,7 +61,8 @@ ggplot(df, aes(x, y)) +
guide = "legend")
# cyl scaled to appropriate size
-ggplot(mtcars, aes(mpg, wt)) + geom_point(aes(size = cyl))
+ggplot(mtcars, aes(mpg, wt)) +
+ geom_point(aes(size = cyl))
# cyl used as point size
ggplot(mtcars, aes(mpg, wt)) +
diff --git a/man/scale_linetype.Rd b/man/scale_linetype.Rd
index 35f5b31..3b5c8e2 100644
--- a/man/scale_linetype.Rd
+++ b/man/scale_linetype.Rd
@@ -4,7 +4,7 @@
\alias{scale_linetype}
\alias{scale_linetype_continuous}
\alias{scale_linetype_discrete}
-\title{Scale for line patterns.}
+\title{Scale for line patterns}
\usage{
scale_linetype(..., na.value = "blank")
@@ -21,8 +21,8 @@ scale_linetype_discrete(..., na.value = "blank")
}
\description{
Default line types based on a set supplied by Richard Pearson,
-University of Manchester. Line types can not be mapped to continuous
-values.
+University of Manchester. Continuous values can not be mapped to
+line types.
}
\examples{
base <- ggplot(economics_long, aes(date, value01))
@@ -30,5 +30,18 @@ base + geom_line(aes(group = variable))
base + geom_line(aes(linetype = variable))
# See scale_manual for more flexibility
+
+# Common line types ----------------------------
+df_lines <- data.frame(
+ linetype = factor(
+ 1:4,
+ labels = c("solid", "longdash", "dashed", "dotted")
+ )
+)
+ggplot(df_lines) +
+ geom_hline(aes(linetype = linetype, yintercept = 0), size = 2) +
+ scale_linetype_identity() +
+ facet_grid(linetype ~ .) +
+ theme_void(20)
}
diff --git a/man/scale_manual.Rd b/man/scale_manual.Rd
index 06e7786..188a37d 100644
--- a/man/scale_manual.Rd
+++ b/man/scale_manual.Rd
@@ -6,10 +6,9 @@
\alias{scale_colour_manual}
\alias{scale_fill_manual}
\alias{scale_linetype_manual}
-\alias{scale_manual}
\alias{scale_shape_manual}
\alias{scale_size_manual}
-\title{Create your own discrete scale.}
+\title{Create your own discrete scale}
\usage{
scale_colour_manual(..., values)
@@ -28,42 +27,36 @@ scale_alpha_manual(..., values)
\code{labels}, \code{na.value}, \code{limits} and \code{guide}. See
\code{\link{discrete_scale}} for more details}
-\item{values}{a set of aesthetic values to map data values to. If this
+\item{values}{a set of aesthetic values to map data values to. If this
is a named vector, then the values will be matched based on the names.
If unnamed, values will be matched in order (usually alphabetical) with
-the limits of the scale. Any data values that don't match will be
+the limits of the scale. Any data values that don't match will be
given \code{na.value}.}
}
\description{
-Create your own discrete scale.
+This allows you to specify you own set of mappings from levels in the
+data to aesthetic values.
}
\examples{
-\donttest{
p <- ggplot(mtcars, aes(mpg, wt)) +
geom_point(aes(colour = factor(cyl)))
+p + scale_colour_manual(values = c("red", "blue", "green"))
-p + scale_colour_manual(values = c("red","blue", "green"))
-p + scale_colour_manual(
- values = c("8" = "red","4" = "blue","6" = "green"))
-# With rgb hex values
-p + scale_colour_manual(values = c("#FF0000", "#0000FF", "#00FF00"))
+# It's recommended to use a named vector
+cols <- c("8" = "red", "4" = "blue", "6" = "darkgreen", "10" = "orange")
+p + scale_colour_manual(values = cols)
# As with other scales you can use breaks to control the appearance
-# of the legend
-cols <- c("8" = "red","4" = "blue","6" = "darkgreen", "10" = "orange")
+# of the legend.
p + scale_colour_manual(values = cols)
-p + scale_colour_manual(values = cols, breaks = c("4", "6", "8"))
-p + scale_colour_manual(values = cols, breaks = c("8", "6", "4"))
-p + scale_colour_manual(values = cols, breaks = c("4", "6", "8"),
- labels = c("four", "six", "eight"))
+p + scale_colour_manual(
+ values = cols,
+ breaks = c("4", "6", "8"),
+ labels = c("four", "six", "eight")
+)
# And limits to control the possible values of the scale
p + scale_colour_manual(values = cols, limits = c("4", "8"))
p + scale_colour_manual(values = cols, limits = c("4", "6", "8", "10"))
-
-# Notice that the values are matched with limits, and not breaks
-p + scale_colour_manual(limits = c(6, 8, 4), breaks = c(8, 4, 6),
- values = c("grey50", "grey80", "black"))
-}
}
diff --git a/man/scale_shape.Rd b/man/scale_shape.Rd
index fc4f6ea..010d70e 100644
--- a/man/scale_shape.Rd
+++ b/man/scale_shape.Rd
@@ -4,7 +4,7 @@
\alias{scale_shape}
\alias{scale_shape_continuous}
\alias{scale_shape_discrete}
-\title{Scale for shapes, aka glyphs.}
+\title{Scales for shapes, aka glyphs}
\usage{
scale_shape(..., solid = TRUE)
}
@@ -13,10 +13,15 @@ scale_shape(..., solid = TRUE)
\code{labels}, \code{na.value}, \code{limits} and \code{guide}. See
\code{\link{discrete_scale}} for more details}
-\item{solid}{Are the shapes solid, \code{TRUE}, or hollow \code{FALSE}?}
+\item{solid}{Should the shapes be solid, \code{TRUE}, or hollow,
+\code{FALSE}?}
}
\description{
-A continuous variable can not be mapped to shape.
+\code{scale_shape} maps discrete variables to six easily discernible shapes.
+If you have more than six levels, you will get a warning message, and the
+seventh and subsequence levels will not appear on the plot. Use
+\code{\link{scale_shape_manual}} to supply your own values. You can not map
+a continuous variable to shape.
}
\examples{
dsmall <- diamonds[sample(nrow(diamonds), 100), ]
@@ -25,7 +30,6 @@ dsmall <- diamonds[sample(nrow(diamonds), 100), ]
d + scale_shape(solid = TRUE) # the default
d + scale_shape(solid = FALSE)
d + scale_shape(name = "Cut of diamond")
-d + scale_shape(name = "Cut of\\ndiamond")
# To change order of levels, change order of
# underlying factor
@@ -34,7 +38,12 @@ levels(dsmall$cut) <- c("Fair", "Good", "Very Good", "Premium", "Ideal")
# Need to recreate plot to pick up new data
ggplot(dsmall, aes(price, carat)) + geom_point(aes(shape = cut))
-# Or for short:
-d \%+\% dsmall
+# Show a list of available shapes
+df_shapes <- data.frame(shape = 0:24)
+ggplot(df_shapes, aes(0, 0, shape = shape)) +
+ geom_point(aes(shape = shape), size = 5, fill = 'red') +
+ scale_shape_identity() +
+ facet_wrap(~shape) +
+ theme_void()
}
diff --git a/man/scale_size.Rd b/man/scale_size.Rd
index 86c76e8..3683da9 100644
--- a/man/scale_size.Rd
+++ b/man/scale_size.Rd
@@ -8,7 +8,7 @@
\alias{scale_size_date}
\alias{scale_size_datetime}
\alias{scale_size_discrete}
-\title{Scale size (area or radius).}
+\title{Scales for area or radius}
\usage{
scale_radius(name = waiver(), breaks = waiver(), labels = waiver(),
limits = NULL, range = c(1, 6), trans = "identity", guide = "legend")
diff --git a/man/seals.Rd b/man/seals.Rd
index 1e9d475..51f1b3c 100644
--- a/man/seals.Rd
+++ b/man/seals.Rd
@@ -3,7 +3,7 @@
\docType{data}
\name{seals}
\alias{seals}
-\title{Vector field of seal movements.}
+\title{Vector field of seal movements}
\format{A data frame with 1155 rows and 4 variables}
\usage{
seals
diff --git a/man/sec_axis.Rd b/man/sec_axis.Rd
new file mode 100644
index 0000000..7413af0
--- /dev/null
+++ b/man/sec_axis.Rd
@@ -0,0 +1,69 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/axis-secondary.R
+\name{sec_axis}
+\alias{derive}
+\alias{dup_axis}
+\alias{sec_axis}
+\title{Specify a secondary axis}
+\usage{
+sec_axis(trans = NULL, name = waiver(), breaks = waiver(),
+ labels = waiver())
+
+dup_axis(trans = ~., name = derive(), breaks = derive(),
+ labels = derive())
+
+derive()
+}
+\arguments{
+\item{trans}{A transformation formula}
+
+\item{name}{The name of the secondary axis}
+
+\item{breaks}{One of:
+\itemize{
+ \item{\code{NULL} for no breaks}
+ \item{\code{waiver()} for the default breaks computed by the transformation object}
+ \item{A numeric vector of positions}
+ \item{A function that takes the limits as input and returns breaks as output}
+}}
+
+\item{labels}{One of:
+\itemize{
+ \item{\code{NULL} for no labels}
+ \item{\code{waiver()} for the default labels computed by the transformation object}
+ \item{A character vector giving labels (must be same length as \code{breaks})}
+ \item{A function that takes the breaks as input and returns labels as output}
+}}
+}
+\description{
+This function is used in conjunction with a position scale to create a
+secondary axis, positioned opposite of the primary axis. All secondary
+axes must be based on a one-to-one transformation of the primary axes.
+}
+\details{
+\code{sec_axis} is used to create the specifications for a secondary axis.
+Except for the \code{trans} argument any of the arguments can be set to
+\code{derive()} which would result in the secondary axis inheriting the
+settings from the primary axis.
+
+\code{dup_axis} is provide as a shorthand for creating a secondary axis that
+is a duplication of the primary axis, effectively mirroring the primary axis.
+}
+\examples{
+p <- ggplot(mtcars, aes(cyl, mpg)) +
+ geom_point()
+
+# Create a simple secondary axis
+p + scale_y_continuous(sec.axis = sec_axis(~.+10))
+
+# Inherit the name from the primary axis
+p + scale_y_continuous("Miles/gallon", sec.axis = sec_axis(~.+10, name = derive()))
+
+# Duplicate the primary axis
+p + scale_y_continuous(sec.axis = dup_axis())
+
+# You can pass in a formula as a shorthand
+p + scale_y_continuous(sec.axis = ~.^2)
+
+}
+
diff --git a/man/stat_ecdf.Rd b/man/stat_ecdf.Rd
index d396dba..2e5b83f 100644
--- a/man/stat_ecdf.Rd
+++ b/man/stat_ecdf.Rd
@@ -2,7 +2,7 @@
% Please edit documentation in R/stat-ecdf.r
\name{stat_ecdf}
\alias{stat_ecdf}
-\title{Empirical Cumulative Density Function}
+\title{Compute empirical cumulative distribution}
\usage{
stat_ecdf(mapping = NULL, data = NULL, geom = "step",
position = "identity", ..., n = NULL, pad = TRUE, na.rm = FALSE,
@@ -57,7 +57,12 @@ that define both data and aesthetics and shouldn't inherit behaviour from
the default plot specification, e.g. \code{\link{borders}}.}
}
\description{
-Empirical Cumulative Density Function
+The empirical cumulative distribution function (ECDF) provides an alternative
+visualisation of distribution. Compared to other visualisations that rely on
+density (like \code{\link{geom_histogram}}), the ECDF doesn't require any
+tuning parameters and handles both continuous and categorical variables.
+The downside is that it requires more training to accurately interpret,
+and the underlying visual tasks are somewhat more challenging.
}
\section{Computed variables}{
@@ -67,14 +72,16 @@ Empirical Cumulative Density Function
}
}
\examples{
-\donttest{
-df <- data.frame(x = rnorm(1000))
+df <- data.frame(
+ x = c(rnorm(100, 0, 3), rnorm(100, 0, 10)),
+ g = gl(2, 100)
+)
ggplot(df, aes(x)) + stat_ecdf(geom = "step")
-df <- data.frame(x = c(rnorm(100, 0, 3), rnorm(100, 0, 10)),
- g = gl(2, 100))
+# Don't go to positive/negative infinity
+ggplot(df, aes(x)) + stat_ecdf(geom = "step", pad = FALSE)
+# Multiple ECDFs
ggplot(df, aes(x, colour = g)) + stat_ecdf()
}
-}
diff --git a/man/stat_ellipse.Rd b/man/stat_ellipse.Rd
index 9fdb2b8..953f8ef 100644
--- a/man/stat_ellipse.Rd
+++ b/man/stat_ellipse.Rd
@@ -2,7 +2,7 @@
% Please edit documentation in R/stat-ellipse.R
\name{stat_ellipse}
\alias{stat_ellipse}
-\title{Plot data ellipses.}
+\title{Compute normal confidence ellipses}
\usage{
stat_ellipse(mapping = NULL, data = NULL, geom = "path",
position = "identity", ..., type = "t", level = 0.95, segments = 51,
@@ -50,8 +50,8 @@ or, if \code{type="euclid"}, the radius of the circle to be drawn.}
\item{segments}{The number of segments to be used in drawing the ellipse.}
-\item{na.rm}{If \code{FALSE} (the default), removes missing values with
-a warning. If \code{TRUE} silently removes missing values.}
+\item{na.rm}{If \code{FALSE}, the default, missing values are removed with
+a warning. If \code{TRUE}, missing values are silently removed.}
\item{show.legend}{logical. Should this layer be included in the legends?
\code{NA}, the default, includes if any aesthetics are mapped.
diff --git a/man/stat_function.Rd b/man/stat_function.Rd
index fedd28d..2b06b20 100644
--- a/man/stat_function.Rd
+++ b/man/stat_function.Rd
@@ -2,7 +2,7 @@
% Please edit documentation in R/stat-function.r
\name{stat_function}
\alias{stat_function}
-\title{Superimpose a function.}
+\title{Compute function for each x value}
\usage{
stat_function(mapping = NULL, data = NULL, geom = "path",
position = "identity", ..., fun, xlim = NULL, n = 101, args = list(),
@@ -38,7 +38,7 @@ often aesthetics, used to set an aesthetic to a fixed value, like
\code{color = "red"} or \code{size = 3}. They may also be parameters
to the paired geom/stat.}
-\item{fun}{function to use}
+\item{fun}{function to use. Must be vectorised.}
\item{xlim}{Optionally, restrict the range of the function to this range.}
@@ -46,8 +46,8 @@ to the paired geom/stat.}
\item{args}{list of additional arguments to pass to \code{fun}}
-\item{na.rm}{If \code{FALSE} (the default), removes missing values with
-a warning. If \code{TRUE} silently removes missing values.}
+\item{na.rm}{If \code{FALSE}, the default, missing values are removed with
+a warning. If \code{TRUE}, missing values are silently removed.}
\item{show.legend}{logical. Should this layer be included in the legends?
\code{NA}, the default, includes if any aesthetics are mapped.
@@ -59,11 +59,13 @@ that define both data and aesthetics and shouldn't inherit behaviour from
the default plot specification, e.g. \code{\link{borders}}.}
}
\description{
-Superimpose a function.
+This stat makes it easy to superimpose a function on top of an existing
+plot. The function is called with a grid of evenly spaced values along
+the x axis, and the results are drawn (by default) with a line.
}
\section{Aesthetics}{
-\Sexpr[results=rd,stage=build]{ggplot2:::rd_aesthetics("stat", "function")}
+\aesthetics{stat}{function}
}
\section{Computed variables}{
diff --git a/man/stat_identity.Rd b/man/stat_identity.Rd
index ffeb036..b57e6c3 100644
--- a/man/stat_identity.Rd
+++ b/man/stat_identity.Rd
@@ -2,7 +2,7 @@
% Please edit documentation in R/stat-identity.r
\name{stat_identity}
\alias{stat_identity}
-\title{Identity statistic.}
+\title{Leave data as is}
\usage{
stat_identity(mapping = NULL, data = NULL, geom = "point",
position = "identity", ..., show.legend = NA, inherit.aes = TRUE)
diff --git a/man/stat_summary.Rd b/man/stat_summary.Rd
index 2b2b973..a2e3bfc 100644
--- a/man/stat_summary.Rd
+++ b/man/stat_summary.Rd
@@ -3,7 +3,7 @@
\name{stat_summary_bin}
\alias{stat_summary}
\alias{stat_summary_bin}
-\title{Summarise y values at unique/binned x x.}
+\title{Summarise y values at unique/binned x}
\usage{
stat_summary_bin(mapping = NULL, data = NULL, geom = "pointrange",
position = "identity", ..., fun.data = NULL, fun.y = NULL,
@@ -55,8 +55,8 @@ single number.}
\item{fun.args}{Optional additional arguments passed on to the functions.}
-\item{na.rm}{If \code{FALSE} (the default), removes missing values with
-a warning. If \code{TRUE} silently removes missing values.}
+\item{na.rm}{If \code{FALSE}, the default, missing values are removed with
+a warning. If \code{TRUE}, missing values are silently removed.}
\item{show.legend}{logical. Should this layer be included in the legends?
\code{NA}, the default, includes if any aesthetics are mapped.
@@ -75,7 +75,7 @@ aggregate.
}
\section{Aesthetics}{
-\Sexpr[results=rd,stage=build]{ggplot2:::rd_aesthetics("stat", "summary")}
+\aesthetics{stat}{summary}
}
\section{Summary functions}{
@@ -130,7 +130,7 @@ p + coord_cartesian(ylim = c(15, 30))
# A set of useful summary functions is provided from the Hmisc package:
stat_sum_df <- function(fun, geom="crossbar", ...) {
- stat_summary(fun.data=fun, colour="red", geom=geom, width=0.2, ...)
+ stat_summary(fun.data = fun, colour = "red", geom = geom, width = 0.2, ...)
}
d <- ggplot(mtcars, aes(cyl, mpg)) + geom_point()
# The crossbar geom needs grouping to be specified when used with
@@ -144,7 +144,7 @@ d + stat_sum_df("median_hilow", mapping = aes(group = cyl))
if (require("ggplot2movies")) {
set.seed(596)
mov <- movies[sample(nrow(movies), 1000), ]
- m2 <- ggplot(mov, aes(x= factor(round(rating)), y=votes)) + geom_point()
+ m2 <- ggplot(mov, aes(x = factor(round(rating)), y = votes)) + geom_point()
m2 <- m2 + stat_summary(fun.data = "mean_cl_boot", geom = "crossbar",
colour = "red", width = 0.3) + xlab("rating")
m2
diff --git a/man/stat_summary_2d.Rd b/man/stat_summary_2d.Rd
index 6674b31..e3fc1f6 100644
--- a/man/stat_summary_2d.Rd
+++ b/man/stat_summary_2d.Rd
@@ -58,8 +58,8 @@ horizontal directions. Overrides \code{bins} if both set.}
\item{fun.args}{A list of extra arguments to pass to \code{fun}}
-\item{na.rm}{If \code{FALSE} (the default), removes missing values with
-a warning. If \code{TRUE} silently removes missing values.}
+\item{na.rm}{If \code{FALSE}, the default, missing values are removed with
+a warning. If \code{TRUE}, missing values are silently removed.}
\item{show.legend}{logical. Should this layer be included in the legends?
\code{NA}, the default, includes if any aesthetics are mapped.
diff --git a/man/stat_unique.Rd b/man/stat_unique.Rd
index 886614e..26200b8 100644
--- a/man/stat_unique.Rd
+++ b/man/stat_unique.Rd
@@ -2,7 +2,7 @@
% Please edit documentation in R/stat-unique.r
\name{stat_unique}
\alias{stat_unique}
-\title{Remove duplicates.}
+\title{Remove duplicates}
\usage{
stat_unique(mapping = NULL, data = NULL, geom = "point",
position = "identity", ..., na.rm = FALSE, show.legend = NA,
@@ -38,8 +38,8 @@ often aesthetics, used to set an aesthetic to a fixed value, like
\code{color = "red"} or \code{size = 3}. They may also be parameters
to the paired geom/stat.}
-\item{na.rm}{If \code{FALSE} (the default), removes missing values with
-a warning. If \code{TRUE} silently removes missing values.}
+\item{na.rm}{If \code{FALSE}, the default, missing values are removed with
+a warning. If \code{TRUE}, missing values are silently removed.}
\item{show.legend}{logical. Should this layer be included in the legends?
\code{NA}, the default, includes if any aesthetics are mapped.
@@ -51,14 +51,16 @@ that define both data and aesthetics and shouldn't inherit behaviour from
the default plot specification, e.g. \code{\link{borders}}.}
}
\description{
-Remove duplicates.
+Remove duplicates
}
\section{Aesthetics}{
-\Sexpr[results=rd,stage=build]{ggplot2:::rd_aesthetics("stat", "unique")}
+\aesthetics{stat}{unique}
}
\examples{
-ggplot(mtcars, aes(vs, am)) + geom_point(alpha = 0.1)
-ggplot(mtcars, aes(vs, am)) + geom_point(alpha = 0.1, stat="unique")
+ggplot(mtcars, aes(vs, am)) +
+ geom_point(alpha = 0.1)
+ggplot(mtcars, aes(vs, am)) +
+ geom_point(alpha = 0.1, stat = "unique")
}
diff --git a/man/theme.Rd b/man/theme.Rd
index b26b8e0..04bbc98 100644
--- a/man/theme.Rd
+++ b/man/theme.Rd
@@ -2,296 +2,342 @@
% Please edit documentation in R/theme.r
\name{theme}
\alias{theme}
-\title{Set theme elements}
+\title{Modify components of a theme}
\usage{
-theme(..., complete = FALSE, validate = TRUE)
+theme(line, rect, text, title, aspect.ratio, axis.title, axis.title.x,
+ axis.title.x.top, axis.title.y, axis.title.y.right, axis.text, axis.text.x,
+ axis.text.x.top, axis.text.y, axis.text.y.right, axis.ticks, axis.ticks.x,
+ axis.ticks.y, axis.ticks.length, axis.line, axis.line.x, axis.line.y,
+ legend.background, legend.margin, legend.spacing, legend.spacing.x,
+ legend.spacing.y, legend.key, legend.key.size, legend.key.height,
+ legend.key.width, legend.text, legend.text.align, legend.title,
+ legend.title.align, legend.position, legend.direction, legend.justification,
+ legend.box, legend.box.just, legend.box.margin, legend.box.background,
+ legend.box.spacing, panel.background, panel.border, panel.spacing,
+ panel.spacing.x, panel.spacing.y, panel.grid, panel.grid.major,
+ panel.grid.minor, panel.grid.major.x, panel.grid.major.y, panel.grid.minor.x,
+ panel.grid.minor.y, panel.ontop, plot.background, plot.title, plot.subtitle,
+ plot.caption, plot.margin, strip.background, strip.placement, strip.text,
+ strip.text.x, strip.text.y, strip.switch.pad.grid, strip.switch.pad.wrap, ...,
+ complete = FALSE, validate = TRUE)
}
\arguments{
-\item{...}{a list of element name, element pairings that modify the
-existing theme.}
+\item{line}{all line elements (\code{element_line})}
+
+\item{rect}{all rectangular elements (\code{element_rect})}
+
+\item{text}{all text elements (\code{element_text})}
+
+\item{title}{all title elements: plot, axes, legends (\code{element_text};
+inherits from \code{text})}
+
+\item{aspect.ratio}{aspect ratio of the panel}
+
+\item{axis.title}{label of axes (\code{element_text}; inherits from
+\code{text})}
+
+\item{axis.title.x}{x axis label (\code{element_text}; inherits from
+\code{axis.title})}
+
+\item{axis.title.x.top}{x axis label on top axis (\code{element_text};
+inherits from \code{axis.title.x})}
+
+\item{axis.title.y}{y axis label (\code{element_text}; inherits from
+\code{axis.title})}
+
+\item{axis.title.y.right}{y axis label on right axis (\code{element_text};
+inherits from \code{axis.title.y})}
+
+\item{axis.text}{tick labels along axes (\code{element_text}; inherits from
+\code{text})}
+
+\item{axis.text.x}{x axis tick labels (\code{element_text}; inherits from
+\code{axis.text})}
+
+\item{axis.text.x.top}{x axis tick labels on top axis (\code{element_text};
+inherits from \code{axis.text.x})}
+
+\item{axis.text.y}{y axis tick labels (\code{element_text}; inherits from
+\code{axis.text})}
+
+\item{axis.text.y.right}{y axis tick labels on right axis
+(\code{element_text}; inherits from \code{axis.text.y})}
+
+\item{axis.ticks}{tick marks along axes (\code{element_line}; inherits from
+\code{line})}
+
+\item{axis.ticks.x}{x axis tick marks (\code{element_line}; inherits from
+\code{axis.ticks})}
+
+\item{axis.ticks.y}{y axis tick marks (\code{element_line}; inherits from
+\code{axis.ticks})}
+
+\item{axis.ticks.length}{length of tick marks (\code{unit})}
+
+\item{axis.line}{lines along axes (\code{element_line}; inherits from
+\code{line})}
+
+\item{axis.line.x}{line along x axis (\code{element_line}; inherits from
+\code{axis.line})}
+
+\item{axis.line.y}{line along y axis (\code{element_line}; inherits from
+\code{axis.line})}
+
+\item{legend.background}{background of legend (\code{element_rect}; inherits
+from \code{rect})}
+
+\item{legend.margin}{the margin around each legend (\code{margin})}
+
+\item{legend.spacing}{the spacing between legends (\code{unit})}
+
+\item{legend.spacing.x}{the horizontal spacing between legends (\code{unit});
+inherits from \code{legend.spacing}}
+
+\item{legend.spacing.y}{the horizontal spacing between legends (\code{unit});
+inherits from \code{legend.spacing}}
+
+\item{legend.key}{background underneath legend keys (\code{element_rect};
+inherits from \code{rect})}
+
+\item{legend.key.size}{size of legend keys (\code{unit})}
+
+\item{legend.key.height}{key background height (\code{unit}; inherits from
+\code{legend.key.size})}
+
+\item{legend.key.width}{key background width (\code{unit}; inherits from
+\code{legend.key.size})}
+
+\item{legend.text}{legend item labels (\code{element_text}; inherits from
+\code{text})}
+
+\item{legend.text.align}{alignment of legend labels (number from 0 (left) to
+1 (right))}
+
+\item{legend.title}{title of legend (\code{element_text}; inherits from
+\code{title})}
+
+\item{legend.title.align}{alignment of legend title (number from 0 (left) to
+1 (right))}
+
+\item{legend.position}{the position of legends ("none", "left", "right",
+"bottom", "top", or two-element numeric vector)}
+
+\item{legend.direction}{layout of items in legends ("horizontal" or
+"vertical")}
+
+\item{legend.justification}{anchor point for positioning legend inside plot
+("center" or two-element numeric vector) or the justification according to
+the plot area when positioned outside the plot}
+
+\item{legend.box}{arrangement of multiple legends ("horizontal" or
+"vertical")}
+
+\item{legend.box.just}{justification of each legend within the overall
+bounding box, when there are multiple legends ("top", "bottom", "left", or
+"right")}
+
+\item{legend.box.margin}{margins around the full legend area, as specified
+using \code{\link{margin}}}
+
+\item{legend.box.background}{background of legend area (\code{element_rect};
+inherits from \code{rect})}
+
+\item{legend.box.spacing}{The spacing between the plotting area and the
+legend box (\code{unit})}
+
+\item{panel.background}{background of plotting area, drawn underneath plot
+(\code{element_rect}; inherits from \code{rect})}
+
+\item{panel.border}{border around plotting area, drawn on top of plot so that
+ it covers tick marks and grid lines. This should be used with
+ \code{fill=NA}
+(\code{element_rect}; inherits from \code{rect})}
+
+\item{panel.spacing}{spacing between facet panels (\code{unit})}
+
+\item{panel.spacing.x}{horizontal spacing between facet panels (\code{unit};
+inherits from \code{panel.spacing})}
+
+\item{panel.spacing.y}{vertical spacing between facet panels (\code{unit};
+inherits from \code{panel.spacing})}
+
+\item{panel.grid}{grid lines (\code{element_line}; inherits from \code{line})}
+
+\item{panel.grid.major}{major grid lines (\code{element_line}; inherits from
+\code{panel.grid})}
+
+\item{panel.grid.minor}{minor grid lines (\code{element_line}; inherits from
+\code{panel.grid})}
+
+\item{panel.grid.major.x}{vertical major grid lines (\code{element_line};
+inherits from \code{panel.grid.major})}
+
+\item{panel.grid.major.y}{horizontal major grid lines (\code{element_line};
+inherits from \code{panel.grid.major})}
+
+\item{panel.grid.minor.x}{vertical minor grid lines (\code{element_line};
+inherits from \code{panel.grid.minor})}
+
+\item{panel.grid.minor.y}{horizontal minor grid lines (\code{element_line};
+inherits from \code{panel.grid.minor})}
+
+\item{panel.ontop}{option to place the panel (background, gridlines) over
+the data layers. Usually used with a transparent or blank
+\code{panel.background}. (\code{logical})}
+
+\item{plot.background}{background of the entire plot (\code{element_rect};
+inherits from \code{rect})}
+
+\item{plot.title}{plot title (text appearance) (\code{element_text}; inherits
+from \code{title}) left-aligned by default}
+
+\item{plot.subtitle}{plot subtitle (text appearance) (\code{element_text};
+inherits from \code{title}) left-aligned by default}
+
+\item{plot.caption}{caption below the plot (text appearance)
+(\code{element_text}; inherits from \code{title}) right-aligned by default}
+
+\item{plot.margin}{margin around entire plot (\code{unit} with the sizes of
+the top, right, bottom, and left margins)}
+
+\item{strip.background}{background of facet labels (\code{element_rect};
+inherits from \code{rect})}
+
+\item{strip.placement}{placement of strip with respect to axes,
+either "inside" or "outside". Only important when axes and strips are
+on the same side of the plot.}
+
+\item{strip.text}{facet labels (\code{element_text}; inherits from
+\code{text})}
+
+\item{strip.text.x}{facet labels along horizontal direction
+(\code{element_text}; inherits from \code{strip.text})}
+
+\item{strip.text.y}{facet labels along vertical direction
+(\code{element_text}; inherits from \code{strip.text})}
+
+\item{strip.switch.pad.grid}{space between strips and axes when strips are
+switched (\code{unit})}
+
+\item{strip.switch.pad.wrap}{space between strips and axes when strips are
+switched (\code{unit})}
+
+\item{...}{additional element specifications not part of base ggplot2. If
+supplied \code{validate} needs to be set to \code{FALSE}.}
\item{complete}{set this to TRUE if this is a complete theme, such as
the one returned \code{by theme_grey()}. Complete themes behave
-differently when added to a ggplot object.}
+differently when added to a ggplot object. Also, when setting
+\code{complete = TRUE} all elements will be set to inherit from blank
+elements.}
-\item{validate}{TRUE to run validate_element, FALSE to bypass checks.}
+\item{validate}{\code{TRUE} to run validate_element, \code{FALSE} to bypass checks.}
}
\description{
-Use this function to modify theme settings.
+Use \code{theme()} to modify individual components of a theme, allowing
+you to control the appearance of all non-data components of the plot.
+\code{theme()} only affects a single plot: see \code{\link{theme_update}} if
+you want modify the active theme, to affect all subsequent plots.
}
-\details{
-Theme elements can inherit properties from other theme elements.
+\section{Theme inheritance}{
+
+Theme elements inherit properties from other theme elements.
For example, \code{axis.title.x} inherits from \code{axis.title},
which in turn inherits from \code{text}. All text elements inherit
directly or indirectly from \code{text}; all lines inherit from
\code{line}, and all rectangular objects inherit from \code{rect}.
-
-For more examples of modifying properties using inheritance, see
-\code{\link{+.gg}} and \code{\link{\%+replace\%}}.
-
-To see a graphical representation of the inheritance tree, see the
-last example below.
-}
-\section{Theme elements}{
-
-The individual theme elements are:
-
-\tabular{ll}{
- line \tab all line elements
- (\code{element_line}) \cr
- rect \tab all rectangular elements
- (\code{element_rect}) \cr
- text \tab all text elements
- (\code{element_text}) \cr
- title \tab all title elements: plot, axes, legends
- (\code{element_text}; inherits from \code{text}) \cr
- aspect.ratio \tab aspect ratio of the panel \cr
-
- axis.title \tab label of axes
- (\code{element_text}; inherits from \code{text}) \cr
- axis.title.x \tab x axis label
- (\code{element_text}; inherits from \code{axis.title}) \cr
- axis.title.y \tab y axis label
- (\code{element_text}; inherits from \code{axis.title}) \cr
- axis.text \tab tick labels along axes
- (\code{element_text}; inherits from \code{text}) \cr
- axis.text.x \tab x axis tick labels
- (\code{element_text}; inherits from \code{axis.text}) \cr
- axis.text.y \tab y axis tick labels
- (\code{element_text}; inherits from \code{axis.text}) \cr
- axis.ticks \tab tick marks along axes
- (\code{element_line}; inherits from \code{line}) \cr
- axis.ticks.x \tab x axis tick marks
- (\code{element_line}; inherits from \code{axis.ticks}) \cr
- axis.ticks.y \tab y axis tick marks
- (\code{element_line}; inherits from \code{axis.ticks}) \cr
- axis.ticks.length \tab length of tick marks
- (\code{unit}) \cr
- axis.line \tab lines along axes
- (\code{element_line}; inherits from \code{line}) \cr
- axis.line.x \tab line along x axis
- (\code{element_line}; inherits from \code{axis.line}) \cr
- axis.line.y \tab line along y axis
- (\code{element_line}; inherits from \code{axis.line}) \cr
-
- legend.background \tab background of legend
- (\code{element_rect}; inherits from \code{rect}) \cr
- legend.margin \tab extra space added around legend
- (\code{unit}) \cr
- legend.key \tab background underneath legend keys
- (\code{element_rect}; inherits from \code{rect}) \cr
- legend.key.size \tab size of legend keys
- (\code{unit}; inherits from \code{legend.key.size}) \cr
- legend.key.height \tab key background height
- (\code{unit}; inherits from \code{legend.key.size}) \cr
- legend.key.width \tab key background width
- (\code{unit}; inherits from \code{legend.key.size}) \cr
- legend.text \tab legend item labels
- (\code{element_text}; inherits from \code{text}) \cr
- legend.text.align \tab alignment of legend labels
- (number from 0 (left) to 1 (right)) \cr
- legend.title \tab title of legend
- (\code{element_text}; inherits from \code{title}) \cr
- legend.title.align \tab alignment of legend title
- (number from 0 (left) to 1 (right)) \cr
- legend.position \tab the position of legends
- ("none", "left", "right", "bottom", "top", or two-element
- numeric vector) \cr
- legend.direction \tab layout of items in legends
- ("horizontal" or "vertical") \cr
- legend.justification \tab anchor point for positioning legend inside plot
- ("center" or two-element numeric vector) \cr
- legend.box \tab arrangement of multiple legends
- ("horizontal" or "vertical") \cr
- legend.box.just \tab justification of each legend within the overall
- bounding box, when there are multiple legends
- ("top", "bottom", "left", or "right")\cr
-
- panel.background \tab background of plotting area, drawn underneath plot
- (\code{element_rect}; inherits from \code{rect}) \cr
- panel.border \tab border around plotting area, drawn on top of plot
- so that it covers tick marks and grid lines. This should
- be used with \code{fill=NA}
- (\code{element_rect}; inherits from \code{rect}) \cr
- panel.margin \tab margin around facet panels
- (\code{unit}) \cr
- panel.margin.x \tab horizontal margin around facet panels
- (\code{unit}; inherits from \code{panel.margin}) \cr
- panel.margin.y \tab vertical margin around facet panels
- (\code{unit}; inherits from \code{panel.margin}) \cr
- panel.grid \tab grid lines
- (\code{element_line}; inherits from \code{line}) \cr
- panel.grid.major \tab major grid lines
- (\code{element_line}; inherits from \code{panel.grid}) \cr
- panel.grid.minor \tab minor grid lines
- (\code{element_line}; inherits from \code{panel.grid}) \cr
- panel.grid.major.x \tab vertical major grid lines
- (\code{element_line}; inherits from \code{panel.grid.major}) \cr
- panel.grid.major.y \tab horizontal major grid lines
- (\code{element_line}; inherits from \code{panel.grid.major}) \cr
- panel.grid.minor.x \tab vertical minor grid lines
- (\code{element_line}; inherits from \code{panel.grid.minor}) \cr
- panel.grid.minor.y \tab horizontal minor grid lines
- (\code{element_line}; inherits from \code{panel.grid.minor}) \cr
- panel.ontop \tab option to place the panel (background, gridlines)
- over the data layers. Usually used with a transparent
- or blank \code{panel.background}. (\code{logical}) \cr
-
- plot.background \tab background of the entire plot
- (\code{element_rect}; inherits from \code{rect}) \cr
- plot.title \tab plot title (text appearance)
- (\code{element_text}; inherits from \code{title}) \cr
- plot.margin \tab margin around entire plot
- (\code{unit} with the sizes of the top, right, bottom, and
- left margins) \cr
-
- strip.background \tab background of facet labels
- (\code{element_rect}; inherits from \code{rect}) \cr
- strip.text \tab facet labels
- (\code{element_text}; inherits from \code{text}) \cr
- strip.text.x \tab facet labels along horizontal direction
- (\code{element_text}; inherits from \code{strip.text}) \cr
- strip.text.y \tab facet labels along vertical direction
- (\code{element_text}; inherits from \code{strip.text}) \cr
- strip.switch.pad.grid \tab space between strips and axes when strips are switched
- (\code{unit}) \cr
- strip.switch.pad.wrap \tab space between strips and axes when strips are switched
- (\code{unit}) \cr
-}
+This means that you can modify the appearance of multiple elements by
+setting a single high-level component.
}
\examples{
+p1 <- ggplot(mtcars, aes(wt, mpg)) +
+ geom_point() +
+ labs(title = "Fuel economy declines as weight increases")
+p1
+
+# Plot ---------------------------------------------------------------------
+p1 + theme(plot.title = element_text(size = rel(2)))
+p1 + theme(plot.background = element_rect(fill = "green"))
+
+# Panels --------------------------------------------------------------------
+
+p1 + theme(panel.background = element_rect(fill = "white", colour = "grey50"))
+p1 + theme(panel.border = element_rect(linetype = "dashed", fill = NA))
+p1 + theme(panel.grid.major = element_line(colour = "black"))
+p1 + theme(
+ panel.grid.major.y = element_blank(),
+ panel.grid.minor.y = element_blank()
+)
+
+# Put gridlines on top of data
+p1 + theme(
+ panel.background = element_rect(fill = NA),
+ panel.grid.major = element_line(colour = "grey50"),
+ panel.ontop = TRUE
+)
+
+# Axes ----------------------------------------------------------------------
+p1 + theme(axis.line = element_line(size = 3, colour = "grey80"))
+p1 + theme(axis.text = element_text(colour = "blue"))
+p1 + theme(axis.ticks = element_line(size = 2))
+p1 + theme(axis.ticks.length = unit(.25, "cm"))
+p1 + theme(axis.title.y = element_text(size = rel(1.5), angle = 90))
+
\donttest{
-p <- ggplot(mtcars, aes(mpg, wt)) +
- geom_point()
-p
-p + theme(panel.background = element_rect(colour = "pink"))
-p + theme_bw()
-
-# Scatter plot of gas mileage by vehicle weight
-p <- ggplot(mtcars, aes(wt, mpg)) +
- geom_point()
-# Calculate slope and intercept of line of best fit
-coef(lm(mpg ~ wt, data = mtcars))
-p + geom_abline(intercept = 37, slope = -5)
-# Calculate correlation coefficient
-with(mtcars, cor(wt, mpg, use = "everything", method = "pearson"))
-#annotate the plot
-p + geom_abline(intercept = 37, slope = -5) +
-geom_text(data = data.frame(), aes(4.5, 30, label = "Pearson-R = -.87"))
-
-# Change the axis labels
-# Original plot
-p
-p + labs(x = "Vehicle Weight", y = "Miles per Gallon")
-# Or
-p + labs(x = "Vehicle Weight", y = "Miles per Gallon")
-
-# Change title appearance
-p <- p + labs(title = "Vehicle Weight-Gas Mileage Relationship")
-# Set title to twice the base font size
-p + theme(plot.title = element_text(size = rel(2)))
-p + theme(plot.title = element_text(size = rel(2), colour = "blue"))
-
-# Changing plot look with themes
-DF <- data.frame(x = rnorm(400))
-m <- ggplot(DF, aes(x = x)) +
- geom_histogram()
-# Default is theme_grey()
-m
-# Compare with
-m + theme_bw()
-
-# Manipulate Axis Attributes
-m + theme(axis.line = element_line(size = 3, colour = "red", linetype = "dotted"))
-m + theme(axis.text = element_text(colour = "blue"))
-m + theme(axis.text.y = element_blank())
-m + theme(axis.ticks = element_line(size = 2))
-m + theme(axis.title.y = element_text(size = rel(1.5), angle = 90))
-m + theme(axis.title.x = element_blank())
-m + theme(axis.ticks.length = unit(.85, "cm"))
-
-# Legend Attributes
-z <- ggplot(mtcars, aes(wt, mpg)) +
- geom_point(aes(colour = factor(cyl)))
-z
-z + theme(legend.position = "none")
-z + theme(legend.position = "bottom")
-# Or use relative coordinates between 0 and 1
-z + theme(legend.position = c(.5, .5))
-# Add a border to the whole legend
-z + theme(legend.background = element_rect(colour = "black"))
-# Legend margin controls extra space around outside of legend:
-z + theme(legend.background = element_rect(),
- legend.margin = unit(1, "cm"))
-z + theme(legend.background = element_rect(),
- legend.margin = unit(0, "cm"))
-# Or to just the keys
-z + theme(legend.key = element_rect(colour = "black"))
-z + theme(legend.key = element_rect(fill = "yellow"))
-z + theme(legend.key.size = unit(2.5, "cm"))
-z + theme(legend.text = element_text(size = 20, colour = "red", angle = 45))
-z + theme(legend.title = element_text(face = "italic"))
-
-# To change the title of the legend use the name argument
-# in one of the scale options
-z + scale_colour_brewer(name = "My Legend")
-z + scale_colour_grey(name = "Number of \\nCylinders")
-
-# Panel and Plot Attributes
-z + theme(panel.background = element_rect(fill = "black"))
-z + theme(panel.border = element_rect(linetype = "dashed", colour = "black"))
-z + theme(panel.grid.major = element_line(colour = "blue"))
-z + theme(panel.grid.minor = element_line(colour = "red", linetype = "dotted"))
-z + theme(panel.grid.major = element_line(size = 2))
-z + theme(panel.grid.major.y = element_blank(),
- panel.grid.minor.y = element_blank())
-z + theme(plot.background = element_rect())
-z + theme(plot.background = element_rect(fill = "green"))
-
-# Faceting Attributes
-set.seed(4940)
-dsmall <- diamonds[sample(nrow(diamonds), 1000), ]
-k <- ggplot(dsmall, aes(carat, ..density..)) +
- geom_histogram(binwidth = 0.2) +
- facet_grid(. ~ cut)
-k + theme(strip.background = element_rect(colour = "purple", fill = "pink",
- size = 3, linetype = "dashed"))
-k + theme(strip.text.x = element_text(colour = "red", angle = 45, size = 10,
- hjust = 0.5, vjust = 0.5))
-k + theme(panel.margin = unit(5, "lines"))
-k + theme(panel.margin.y = unit(0, "lines"))
-
-# Put gridlines on top
-meanprice <- tapply(diamonds$price, diamonds$cut, mean)
-cut <- factor(levels(diamonds$cut), levels = levels(diamonds$cut))
-df <- data.frame(meanprice, cut)
-g <- ggplot(df, aes(cut, meanprice)) + geom_bar(stat = "identity")
-g + geom_bar(stat = "identity") +
- theme(panel.background = element_blank(),
- panel.grid.major.x = element_blank(),
- panel.grid.minor.x = element_blank(),
- panel.grid.minor.y = element_blank(),
- panel.ontop = TRUE)
-
-# Modify a theme and save it
-mytheme <- theme_grey() + theme(plot.title = element_text(colour = "red"))
-p + mytheme
+# Legend --------------------------------------------------------------------
+p2 <- ggplot(mtcars, aes(wt, mpg)) +
+ geom_point(aes(colour = factor(cyl), shape = factor(vs))) +
+ labs(
+ x = "Weight (1000 lbs)",
+ y = "Fuel economy (mpg)",
+ colour = "Cylinders",
+ shape = "Transmission"
+ )
+p2
-}
-}
-\seealso{
-\code{\link{+.gg}}
+# Position
+p2 + theme(legend.position = "none")
+p2 + theme(legend.justification = "top")
+p2 + theme(legend.position = "bottom")
-\code{\link{\%+replace\%}}
+# Or place inside the plot using relative coordinates between 0 and 1
+# legend.justification sets the corner that the position refers to
+p2 + theme(
+ legend.position = c(.95, .95),
+ legend.justification = c("right", "top"),
+ legend.box.just = "right",
+ legend.margin = margin(6, 6, 6, 6)
+)
-\code{\link{rel}}
+# The legend.box properties work similarly for the space around
+# all the legends
+p2 + theme(
+ legend.box.background = element_rect(),
+ legend.box.margin = margin(6, 6, 6, 6)
+)
-\code{\link{element_blank}}
+# You can also control the display of the keys
+# and the justifaction related to the plot area can be set
+p2 + theme(legend.key = element_rect(fill = "white", colour = "black"))
+p2 + theme(legend.text = element_text(size = 8, colour = "red"))
+p2 + theme(legend.title = element_text(face = "bold"))
-\code{\link{element_line}}
+# Strips --------------------------------------------------------------------
-\code{\link{element_rect}}
+p3 <- ggplot(mtcars, aes(wt, mpg)) +
+ geom_point() +
+ facet_wrap(~ cyl)
+p3
-\code{\link{element_text}}
+p3 + theme(strip.background = element_rect(colour = "black", fill = "white"))
+p3 + theme(strip.text.x = element_text(colour = "white", face = "bold"))
+p3 + theme(panel.spacing = unit(1, "lines"))
+}
+}
+\seealso{
+\code{\link{+.gg}} and \code{\link{\%+replace\%}},
+ \code{\link{element_blank}}, \code{\link{element_line}},
+ \code{\link{element_rect}}, and \code{\link{element_text}} for
+ details of the specific theme elements.
}
diff --git a/man/theme_get.Rd b/man/theme_get.Rd
new file mode 100644
index 0000000..7f6825f
--- /dev/null
+++ b/man/theme_get.Rd
@@ -0,0 +1,92 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/theme-current.R
+\name{theme_get}
+\alias{\%+replace\%}
+\alias{theme_get}
+\alias{theme_replace}
+\alias{theme_set}
+\alias{theme_update}
+\title{Get, set, and modify the active theme}
+\usage{
+theme_get()
+
+theme_set(new)
+
+theme_update(...)
+
+theme_replace(...)
+
+e1 \%+replace\% e2
+}
+\arguments{
+\item{new}{new theme (a list of theme elements)}
+
+\item{...}{named list of theme settings}
+
+\item{e1, e2}{Theme and element to combine}
+}
+\value{
+\code{theme_set}, \code{theme_update}, and \code{theme_replace}
+ invisibly return the previous theme so you can easily save it, then
+ later restore it.
+}
+\description{
+The current/active theme is automatically applied to every plot you draw.
+Use \code{theme_get} to get the current theme, and \code{theme_set} to
+completely override it. \code{theme_update} and \code{theme_replace} are
+shorthands for changing individual elements.
+}
+\section{Adding on to a theme}{
+
+
+\code{+} and \code{\%+replace\%} can be used to modify elements in themes.
+
+\code{+} updates the elements of e1 that differ from elements specified (not
+NULL) in e2. Thus this operator can be used to incrementally add or modify
+attributes of a ggplot theme.
+
+In contrast, \code{\%+replace\%} replaces the entire element; any element of
+a theme not specified in e2 will not be present in the resulting theme (i.e.
+NULL). Thus this operator can be used to overwrite an entire theme.
+
+\code{theme_update} uses the \code{+} operator, so that any unspecified
+values in the theme element will default to the values they are set in the
+theme. \code{theme_replace} uses \code{\%+replace\%} tocompletely replace
+the element, so any unspecified values will overwrite the current value in
+the theme with \code{NULL}s.
+}
+\examples{
+p <- ggplot(mtcars, aes(mpg, wt)) +
+ geom_point()
+p
+
+# Use theme_set() to completely override the current theme.
+# Here we have the old theme so we can later restore it.
+# Note that the theme is applied when the plot is drawn, not
+# when it is created.
+old <- theme_set(theme_bw())
+p
+theme_set(old)
+p
+
+
+# Modifying theme objects -----------------------------------------
+# You can use + and \%+replace\% to modify a theme object.
+# They differ in how they deal with missing arguments in
+# the theme elements.
+
+add_el <- theme_grey() +
+ theme(text = element_text(family = "Times"))
+add_el$text
+
+rep_el <- theme_grey() \%+replace\%
+ theme(text = element_text(family = "Times"))
+rep_el$text
+
+# theme_update() and theme_replace() are similar except they
+# apply directly to the current/active theme.
+}
+\seealso{
+\code{\link{+.gg}}
+}
+
diff --git a/man/theme_update.Rd b/man/theme_update.Rd
deleted file mode 100644
index ec90647..0000000
--- a/man/theme_update.Rd
+++ /dev/null
@@ -1,69 +0,0 @@
-% Generated by roxygen2: do not edit by hand
-% Please edit documentation in R/theme.r
-\name{theme_update}
-\alias{theme_get}
-\alias{theme_replace}
-\alias{theme_set}
-\alias{theme_update}
-\title{Get, set and update themes.}
-\usage{
-theme_update(...)
-
-theme_replace(...)
-
-theme_get()
-
-theme_set(new)
-}
-\arguments{
-\item{...}{named list of theme settings}
-
-\item{new}{new theme (a list of theme elements)}
-}
-\description{
-Use \code{theme_get} to get the current theme, and \code{theme_set} to
-completely override it. \code{theme_update} and \code{theme_replace} are
-shorthands for changing individual elements in the current theme.
-\code{theme_update} uses the \code{+} operator, so that any unspecified
-values in the theme element will default to the values they are set in the
-theme. \code{theme_replace} will completely replace the element, so any
-unspecified values will overwrite the current value in the theme with \code{NULL}s.
-}
-\examples{
-p <- ggplot(mtcars, aes(mpg, wt)) +
- geom_point()
-p
-old <- theme_set(theme_bw())
-p
-theme_set(old)
-p
-
-#theme_replace NULLs out the fill attribute of panel.background,
-#resulting in a white background:
-theme_get()$panel.background
-old <- theme_replace(panel.background = element_rect(colour = "pink"))
-theme_get()$panel.background
-p
-theme_set(old)
-
-#theme_update only changes the colour attribute, leaving the others intact:
-old <- theme_update(panel.background = element_rect(colour = "pink"))
-theme_get()$panel.background
-p
-theme_set(old)
-
-theme_get()
-
-
-ggplot(mtcars, aes(mpg, wt)) +
- geom_point(aes(color = mpg)) +
- theme(legend.position = c(0.95, 0.95),
- legend.justification = c(1, 1))
-last_plot() +
- theme(legend.background = element_rect(fill = "white", colour = "white", size = 3))
-
-}
-\seealso{
-\code{\link{\%+replace\%}} and \code{\link{+.gg}}
-}
-
diff --git a/man/translate_qplot_ggplot.Rd b/man/translate_qplot_ggplot.Rd
index 7c705af..701adbf 100644
--- a/man/translate_qplot_ggplot.Rd
+++ b/man/translate_qplot_ggplot.Rd
@@ -83,4 +83,5 @@ syntax.
# qplot(x, y, data = data, main="title", asp = 1)
# ggplot(data, aes(x, y)) + geom_point() + labs(title = "title") + theme(aspect.ratio = 1)
}
+\keyword{internal}
diff --git a/man/translate_qplot_lattice.Rd b/man/translate_qplot_lattice.Rd
index 4ffbeb4..ea8d49c 100644
--- a/man/translate_qplot_lattice.Rd
+++ b/man/translate_qplot_lattice.Rd
@@ -77,4 +77,5 @@ qplot(mpg, wt, data = mtcars, asp = 1)
# before using ggplot2.
}
}
+\keyword{internal}
diff --git a/man/txhousing.Rd b/man/txhousing.Rd
index 54de48b..4455f9f 100644
--- a/man/txhousing.Rd
+++ b/man/txhousing.Rd
@@ -3,9 +3,9 @@
\docType{data}
\name{txhousing}
\alias{txhousing}
-\title{Housing sales in TX.}
+\title{Housing sales in TX}
\format{A data frame with 8602 observations and 9 variables:
-\itemize{
+\describe{
\item{city}{Name of MLS area}
\item{year,month,date}{Date}
\item{sales}{Number of sales}
diff --git a/man/update_defaults.Rd b/man/update_defaults.Rd
index f97f003..3153e96 100644
--- a/man/update_defaults.Rd
+++ b/man/update_defaults.Rd
@@ -24,4 +24,5 @@ update_geom_defaults("point", list(colour = "darkblue"))
ggplot(mtcars, aes(mpg, wt)) + geom_point()
update_geom_defaults("point", list(colour = "black"))
}
+\keyword{internal}
diff --git a/man/update_labels.Rd b/man/update_labels.Rd
index 7c4363e..45707ce 100644
--- a/man/update_labels.Rd
+++ b/man/update_labels.Rd
@@ -21,4 +21,5 @@ update_labels(p, list(x = expression(x / y ^ 2)))
update_labels(p, list(x = "New x", y = "New Y"))
update_labels(p, list(colour = "Fail silently"))
}
+\keyword{internal}
diff --git a/man/wrap_dims.Rd b/man/wrap_dims.Rd
new file mode 100644
index 0000000..25a30b7
--- /dev/null
+++ b/man/wrap_dims.Rd
@@ -0,0 +1,21 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/facet-wrap.r
+\name{wrap_dims}
+\alias{wrap_dims}
+\title{Arrange 1d structure into a grid}
+\usage{
+wrap_dims(n, nrow = NULL, ncol = NULL)
+}
+\arguments{
+\item{n}{length of structure}
+
+\item{nrow, ncol}{desired dimensions for the grid}
+}
+\value{
+the grid dimension as a vector with nrow and then ncol
+}
+\description{
+Arrange 1d structure into a grid
+}
+\keyword{internal}
+
diff --git a/tests/testthat/Rplots.pdf b/tests/testthat/Rplots.pdf
index bb4cfc6..26c66fc 100644
Binary files a/tests/testthat/Rplots.pdf and b/tests/testthat/Rplots.pdf differ
diff --git a/tests/testthat/helper-plot-data.r b/tests/testthat/helper-plot-data.r
index 69e8717..a5b829f 100644
--- a/tests/testthat/helper-plot-data.r
+++ b/tests/testthat/helper-plot-data.r
@@ -4,7 +4,7 @@ cdata <- function(plot) {
lapply(pieces$data, function(d) {
plyr::ddply(d, "PANEL", function(panel_data) {
- scales <- panel_scales(pieces$panel, panel_data$PANEL[1])
+ scales <- pieces$layout$get_scales(panel_data$PANEL[1])
details <- plot$coordinates$train(scales)
plot$coordinates$transform(panel_data, details)
})
@@ -12,10 +12,10 @@ cdata <- function(plot) {
}
pranges <- function(plot) {
- panels <- ggplot_build(plot)$panel
+ layout <- ggplot_build(plot)$layout
- x_ranges <- lapply(panels$x_scales, function(scale) scale$get_limits())
- y_ranges <- lapply(panels$y_scales, function(scale) scale$get_limits())
+ x_ranges <- lapply(layout$panel_scales$x, function(scale) scale$get_limits())
+ y_ranges <- lapply(layout$panel_scales$y, function(scale) scale$get_limits())
npscales <- plot$scales$non_position_scales()
diff --git a/tests/testthat/test-add.R b/tests/testthat/test-add.R
new file mode 100644
index 0000000..ee97995
--- /dev/null
+++ b/tests/testthat/test-add.R
@@ -0,0 +1,6 @@
+context("Adding plot elements")
+
+test_that("Mapping class is preserved when adding uneval objects", {
+ p <- ggplot(mtcars) + aes(wt, mpg)
+ expect_identical(class(p$mapping), "uneval")
+})
diff --git a/tests/testthat/test-annotate.r b/tests/testthat/test-annotate.r
index 4308fdf..d5b8b7b 100644
--- a/tests/testthat/test-annotate.r
+++ b/tests/testthat/test-annotate.r
@@ -26,3 +26,23 @@ test_that("segment annotations transform with scales", {
annotate("segment", x = 2, y = 10, xend = 5, yend = 30, colour = "red") +
scale_y_reverse()
})
+
+test_that("annotation_* has dummy data assigned and don't inherit aes", {
+ custom <- annotation_custom(zeroGrob())
+ logtick <- annotation_logticks()
+ library(maps)
+ usamap <- map_data("state")
+ map <- annotation_map(usamap)
+ rainbow <- matrix(hcl(seq(0, 360, length.out = 50 * 50), 80, 70), nrow = 50)
+ raster <- annotation_raster(rainbow, 15, 20, 3, 4)
+ dummy <- dummy_data()
+ expect_equal(custom$data, dummy)
+ expect_equal(logtick$data, dummy)
+ expect_equal(map$data, dummy)
+ expect_equal(raster$data, dummy)
+
+ expect_false(custom$inherit.aes)
+ expect_false(logtick$inherit.aes)
+ expect_false(map$inherit.aes)
+ expect_false(raster$inherit.aes)
+})
diff --git a/tests/testthat/test-boxplot.r b/tests/testthat/test-boxplot.r
index 8f25116..082992a 100644
--- a/tests/testthat/test-boxplot.r
+++ b/tests/testthat/test-boxplot.r
@@ -5,8 +5,8 @@ test_that("geom_boxplot range includes all outliers", {
dat <- data.frame(x = 1, y = c(-(1:20) ^ 3, (1:20) ^ 3) )
p <- ggplot_build(ggplot(dat, aes(x,y)) + geom_boxplot())
- miny <- p$panel$ranges[[1]]$y.range[1]
- maxy <- p$panel$ranges[[1]]$y.range[2]
+ miny <- p$layout$panel_ranges[[1]]$y.range[1]
+ maxy <- p$layout$panel_ranges[[1]]$y.range[2]
expect_true(miny <= min(dat$y))
expect_true(maxy >= max(dat$y))
diff --git a/tests/testthat/test-build.r b/tests/testthat/test-build.r
index b6082cd..6b10c1d 100644
--- a/tests/testthat/test-build.r
+++ b/tests/testthat/test-build.r
@@ -30,12 +30,17 @@ test_that("position aesthetics coerced to correct type", {
})
test_that("non-position aesthetics are mapped", {
- l1 <- ggplot(df, aes(x, y, fill = z, colour = z, shape = z, size = z)) +
+ l1 <- ggplot(df, aes(x, y, fill = z, colour = z, shape = z)) +
geom_point()
- d1 <- layer_data(l1, 1)
- expect_equal(sort(names(d1)), sort(c("x", "y", "fill", "group",
- "colour", "shape", "size", "PANEL", "alpha", "stroke")))
+ expect_named(
+ layer_data(l1, 1),
+ c(
+ "x", "y", "fill", "group", "colour", "shape", "size", "PANEL",
+ "alpha", "stroke"
+ ),
+ ignore.order = TRUE
+ )
l2 <- l1 + scale_colour_manual(values = c("blue", "red", "yellow"))
d2 <- layer_data(l2, 1)
diff --git a/tests/testthat/test-data.r b/tests/testthat/test-data.r
index d295088..edbc201 100644
--- a/tests/testthat/test-data.r
+++ b/tests/testthat/test-data.r
@@ -1,13 +1,14 @@
context("Data")
test_that("stringsAsFactors doesn't affect results", {
+ old <- getOption("stringsAsFactors")
+ on.exit(options(stringsAsFactors = old), add = TRUE)
- sAF <- getOption("stringsAsFactors")
dat.character <- data.frame(x = letters[5:1], y = 1:5, stringsAsFactors = FALSE)
dat.factor <- data.frame(x = letters[5:1], y = 1:5, stringsAsFactors = TRUE)
base <- ggplot(mapping = aes(x, y)) + geom_point()
- xlabels <- function(x) x$panel$ranges[[1]]$x.labels
+ xlabels <- function(x) x$layout$panel_ranges[[1]]$x.labels
options(stringsAsFactors = TRUE)
char_true <- ggplot_build(base %+% dat.character)
@@ -17,8 +18,6 @@ test_that("stringsAsFactors doesn't affect results", {
char_false <- ggplot_build(base %+% dat.character)
factor_false <- ggplot_build(base %+% dat.factor)
- options(stringsAsFactors = sAF)
-
expect_equal(xlabels(char_true), letters[1:5])
expect_equal(xlabels(char_false), letters[1:5])
expect_equal(xlabels(factor_true), letters[1:5])
diff --git a/tests/testthat/test-dotplot.r b/tests/testthat/test-dotplot.r
index d92456b..40bc25a 100644
--- a/tests/testthat/test-dotplot.r
+++ b/tests/testthat/test-dotplot.r
@@ -63,3 +63,17 @@ test_that("NA's result in warning from stat_bindot", {
expect_warning(ggplot_build(ggplot(dat, aes(x)) + geom_dotplot(binwidth = .2)),
"Removed 2 rows.*stat_bindot")
})
+
+test_that("When binning on y-axis, limits depend on the panel", {
+ p <- ggplot(mtcars, aes(factor(cyl), mpg)) +
+ geom_dotplot(binaxis='y')
+
+ b1 <- ggplot_build(p + facet_wrap(~am))
+ b2 <- ggplot_build(p + facet_wrap(~am, scales = "free_y"))
+
+ equal_limits1 <- (b1$layout$panel_ranges[[1]]$y.range == b1$layout$panel_ranges[[2]]$y.range)
+ equal_limits2 <- (b2$layout$panel_ranges[[1]]$y.range == b2$layout$panel_ranges[[2]]$y.range)
+
+ expect_true(all(equal_limits1))
+ expect_false(all(equal_limits2))
+})
diff --git a/tests/testthat/test-facet-labels.r b/tests/testthat/test-facet-labels.r
index 9abf3a9..399f3aa 100644
--- a/tests/testthat/test-facet-labels.r
+++ b/tests/testthat/test-facet-labels.r
@@ -2,11 +2,10 @@ context("Facet Labels")
get_labels_matrix <- function(plot, ...) {
data <- ggplot_build(plot)
- facet <- data$plot$facet
- panel <- data$panel
+ layout <- data$layout
- labels <- get_labels_info(facet, panel, ...)
- labeller <- match.fun(facet$labeller)
+ labels <- get_labels_info(layout$facet, layout, ...)
+ labeller <- match.fun(layout$facet$params$labeller)
# Create matrix of labels
matrix <- lapply(labeller(labels), cbind)
@@ -18,23 +17,23 @@ get_labels_info <- function(facet, panel, ...) {
UseMethod("get_labels_info")
}
-get_labels_info.grid <- function(facet, panel, type) {
+get_labels_info.FacetGrid <- function(facet, layout, type) {
if (type == "rows") {
- labels <- unique(panel$layout[names(facet$rows)])
+ labels <- unique(layout$panel_layout[names(facet$params$rows)])
attr(labels, "type") <- "rows"
attr(labels, "facet") <- "grid"
} else {
- labels <- unique(panel$layout[names(facet$cols)])
+ labels <- unique(layout$panel_layout[names(facet$params$cols)])
attr(labels, "type") <- "cols"
attr(labels, "facet") <- "grid"
}
labels
}
-get_labels_info.wrap <- function(facet, panel) {
- labels <- panel$layout[names(facet$facets)]
+get_labels_info.FacetWrap <- function(facet, layout) {
+ labels <- layout$panel_layout[names(facet$params$facets)]
attr(labels, "facet") <- "wrap"
- if (!is.null(facet$switch) && facet$switch == "x") {
+ if (!is.null(facet$params$switch) && facet$params$switch == "x") {
attr(labels, "type") <- "rows"
} else {
attr(labels, "type") <- "cols"
@@ -42,7 +41,6 @@ get_labels_info.wrap <- function(facet, panel) {
labels
}
-
test_that("labellers handle facet labels properly", {
labels <- list(var1 = letters[1:2], var2 = letters[3:4])
diff --git a/tests/testthat/test-facet-layout.r b/tests/testthat/test-facet-layout.r
index 9e91120..401a073 100644
--- a/tests/testthat/test-facet-layout.r
+++ b/tests/testthat/test-facet-layout.r
@@ -5,21 +5,16 @@ b <- data.frame(a = 3)
c <- data.frame(b = 3)
empty <- data.frame()
-test_that("all: no rows and cols gives null layout", {
- expect_equal(layout_grid(list(a)), layout_null())
- expect_equal(layout_wrap(list(a)), layout_null())
-})
-
test_that("grid: single row and single col equivalent", {
- row <- layout_grid(list(a), rows = "a")
- col <- layout_grid(list(a), cols = "a")
+ row <- facet_grid(a~.)$train(list(a))
+ col <- facet_grid(.~a)$train(list(a))
expect_equal(row$ROW, 1:2)
expect_equal(row$ROW, col$COL)
expect_equal(row[c("PANEL", "a")], col[c("PANEL", "a")])
- row <- layout_grid(list(a, b), rows = "a")
- col <- layout_grid(list(a, b), cols = "a")
+ row <- facet_grid(a~.)$train(list(a, b))
+ col <- facet_grid(.~a)$train(list(a, b))
expect_equal(row$ROW, 1:3)
expect_equal(row$ROW, col$COL)
@@ -28,62 +23,64 @@ test_that("grid: single row and single col equivalent", {
test_that("grid: includes all combinations", {
d <- data.frame(a = c(1, 2), b = c(2, 1))
- all <- layout_grid(list(d), rows = "a", cols = "b")
+ all <- facet_grid(a~b)$train(list(d))
expect_equal(nrow(all), 4)
})
test_that("wrap and grid equivalent for 1d data", {
- rowg <- layout_grid(list(a), rows = "a")
- roww <- layout_wrap(list(a), "a", ncol = 1)
+ rowg <- facet_grid(a~.)$train(list(a))
+ roww <- facet_wrap(~a, ncol = 1)$train(list(a))
expect_equal(roww, rowg)
- colg <- layout_grid(list(a), cols = "a")
- colw <- layout_wrap(list(a), "a", nrow = 1)
+ colg <- facet_grid(.~a)$train(list(a))
+ colw <- facet_wrap(~a, nrow = 1)$train(list(a))
expect_equal(colw, colg)
})
test_that("grid: crossed rows/cols create no more combinations than necessary", {
- one <- layout_grid(list(a), "a", "b")
+ facet <- facet_grid(a~b)
+ facet$params$plot_env <- emptyenv()
+ one <- facet$train(list(a))
expect_equal(nrow(one), 4)
- one_a <- layout_grid(list(a, empty), "a", "b")
+ one_a <- facet$train(list(a, empty))
expect_equal(nrow(one_a), 4)
- two <- layout_grid(list(a, b), "a", "b")
+ two <- facet$train(list(a, b))
expect_equal(nrow(two), 4 + 2)
- three <- layout_grid(list(a, b, c), "a", "b")
+ three <- facet$train(list(a, b, c))
expect_equal(nrow(three), 9)
- four <- layout_grid(list(b, c), "a", "b")
+ four <- facet$train(list(b, c))
expect_equal(nrow(four), 1)
})
test_that("grid: nested rows/cols create no more combinations than necessary", {
- one <- layout_grid(list(mpg), c("drv", "cyl"))
+ one <- facet_grid(drv+cyl~.)$train(list(mpg))
expect_equal(one$PANEL, factor(1:9))
expect_equal(one$ROW, 1:9)
})
test_that("grid: margins add correct combinations", {
- one <- layout_grid(list(a), "a", "b", margins = TRUE)
+ one <- facet_grid(a~b, margins = TRUE)$train(list(a))
expect_equal(nrow(one), 4 + 2 + 2 + 1)
})
test_that("wrap: as.table reverses rows", {
- one <- layout_wrap(list(a), "a", ncol = 1, as.table = FALSE)
+ one <- facet_wrap(~a, ncol = 1, as.table = FALSE)$train(list(a))
expect_equal(one$ROW, c(2, 1))
- two <- layout_wrap(list(a), "a", nrow = 1, as.table = FALSE)
+ two <- facet_wrap(~a, nrow = 1, as.table = FALSE)$train(list(a))
expect_equal(two$ROW, c(1, 1))
})
test_that("grid: as.table reverses rows", {
- one <- layout_grid(list(a), "a", as.table = FALSE)
+ one <- facet_grid(a~., as.table = FALSE)$train(list(a))
expect_equal(as.character(one$a), c("2", "1"))
- two <- layout_grid(list(a), "a", as.table = TRUE)
+ two <- facet_grid(a~., as.table = TRUE)$train(list(a))
expect_equal(as.character(two$a), c("1", "2"))
})
@@ -94,27 +91,27 @@ a2 <- data.frame(
b = factor(1:3, levels = 4:1)
)
-test_that("layout_wrap: drop = FALSE preserves unused levels", {
- wrap_a <- layout_wrap(list(a2), "a", drop = FALSE)
+test_that("wrap: drop = FALSE preserves unused levels", {
+ wrap_a <- facet_wrap(~a, drop = FALSE)$train(list(a2))
expect_equal(nrow(wrap_a), 4)
expect_equal(as.character(wrap_a$a), as.character(1:4))
- wrap_b <- layout_wrap(list(a2), "b", drop = FALSE)
+ wrap_b <- facet_wrap(~b, drop = FALSE)$train(list(a2))
expect_equal(nrow(wrap_b), 4)
expect_equal(as.character(wrap_b$b), as.character(4:1))
})
-test_that("layout_grid: drop = FALSE preserves unused levels", {
- grid_a <- layout_grid(list(a2), "a", drop = FALSE)
+test_that("grid: drop = FALSE preserves unused levels", {
+ grid_a <- facet_grid(a~., drop = FALSE)$train(list(a2))
expect_equal(nrow(grid_a), 4)
expect_equal(as.character(grid_a$a), as.character(1:4))
- grid_b <- layout_grid(list(a2), "b", drop = FALSE)
+ grid_b <- facet_grid(b~., drop = FALSE)$train(list(a2))
expect_equal(nrow(grid_b), 4)
expect_equal(as.character(grid_b$b), as.character(4:1))
- grid_ab <- layout_grid(list(a2), "a", "b", drop = FALSE)
+ grid_ab <- facet_grid(a~b, drop = FALSE)$train(list(a2))
expect_equal(nrow(grid_ab), 16)
expect_equal(as.character(grid_ab$a), as.character(rep(1:4, each = 4)))
expect_equal(as.character(grid_ab$b), as.character(rep(4:1, 4)))
@@ -130,12 +127,12 @@ a3 <- data.frame(
)
test_that("missing values get a panel", {
- wrap_a <- layout_wrap(list(a3), "a")
- wrap_b <- layout_wrap(list(a3), "b")
- wrap_c <- layout_wrap(list(a3), "c")
- grid_a <- layout_grid(list(a3), "a")
- grid_b <- layout_grid(list(a3), "b")
- grid_c <- layout_grid(list(a3), "c")
+ wrap_a <- facet_wrap(~a)$train(list(a3))
+ wrap_b <- facet_wrap(~b)$train(list(a3))
+ wrap_c <- facet_wrap(~c)$train(list(a3))
+ grid_a <- facet_grid(a~.)$train(list(a3))
+ grid_b <- facet_grid(b~.)$train(list(a3))
+ grid_c <- facet_grid(c~.)$train(list(a3))
expect_equal(nrow(wrap_a), 4)
expect_equal(nrow(wrap_b), 4)
diff --git a/tests/testthat/test-facet-locate.r b/tests/testthat/test-facet-map.r
similarity index 79%
rename from tests/testthat/test-facet-locate.r
rename to tests/testthat/test-facet-map.r
index a2b0628..d809317 100644
--- a/tests/testthat/test-facet-locate.r
+++ b/tests/testthat/test-facet-map.r
@@ -1,4 +1,4 @@
-context("Facetting (location)")
+context("Facetting (mapping)")
df <- expand.grid(a = 1:2, b = 1:2)
df_a <- unique(df["a"])
@@ -7,8 +7,9 @@ df_c <- unique(data.frame(c = 1))
test_that("two col cases with no missings adds single extra column", {
- vscyl <- layout_grid(list(mtcars), "cyl", "vs")
- loc <- locate_grid(mtcars, vscyl, "cyl", "vs")
+ facet <- facet_grid(cyl~vs)
+ layout <- facet$train(list(mtcars))
+ loc <- facet$map(mtcars, layout)
expect_equal(nrow(loc), nrow(mtcars))
expect_equal(ncol(loc), ncol(mtcars) + 1)
@@ -19,42 +20,45 @@ test_that("two col cases with no missings adds single extra column", {
})
test_that("margins add extra data", {
- panel <- layout_grid(list(df), "a", "b", margins = "b")
- loc <- locate_grid(df, panel, "a", "b", margins = "b")
+ facet <- facet_grid(a~b, margins = "b")
+ layout <- facet$train(list(df))
+ loc <- facet$map(df, layout)
expect_equal(nrow(loc), nrow(df) * 2)
})
test_that("grid: missing facet columns are duplicated", {
- panel <- layout_grid(list(df), "a", "b")
+ facet <- facet_grid(a~b)
+ layout <- facet$train(list(df))
- loc_a <- locate_grid(df_a, panel, "a", "b")
+ loc_a <- facet$map(df_a, layout)
expect_equal(nrow(loc_a), 4)
expect_equal(loc_a$PANEL, factor(1:4))
- loc_b <- locate_grid(df_b, panel, "a", "b")
+ loc_b <- facet$map(df_b, layout)
expect_equal(nrow(loc_b), 4)
expect_equal(loc_b$PANEL, factor(1:4))
- loc_c <- locate_grid(df_c, panel, "a", "b")
+ loc_c <- facet$map(df_c, layout)
expect_equal(nrow(loc_c), 4)
expect_equal(loc_c$PANEL, factor(1:4))
})
test_that("wrap: missing facet columns are duplicated", {
- panel <- layout_wrap(list(df), c("a", "b"), ncol = 1)
+ facet <- facet_wrap(~a+b, ncol = 1)
+ layout <- facet$train(list(df))
- loc_a <- locate_wrap(df_a, panel, c("a", "b"))
+ loc_a <- facet$map(df_a, layout)
expect_equal(nrow(loc_a), 4)
expect_equal(loc_a$PANEL, factor(1:4))
expect_equal(loc_a$a, c(1, 1, 2, 2))
- loc_b <- locate_wrap(df_b, panel, c("a", "b"))
+ loc_b <- facet$map(df_b, layout)
expect_equal(nrow(loc_b), 4)
expect_equal(loc_b$PANEL, factor(1:4))
- loc_c <- locate_wrap(df_c, panel, c("a", "b"))
+ loc_c <- facet$map(df_c, layout)
expect_equal(nrow(loc_c), 4)
expect_equal(loc_c$PANEL, factor(1:4))
@@ -69,29 +73,33 @@ a3 <- data.frame(
)
test_that("wrap: missing values located correctly", {
- panel_b <- layout_wrap(list(a3), "b", ncol = 1)
- loc_b <- locate_wrap(data.frame(b = NA), panel_b, "b")
+ facet <- facet_wrap(~b, ncol = 1)
+ layout_b <- facet$train(list(a3))
+ loc_b <- facet$map(data.frame(b = NA), layout_b)
expect_equal(as.character(loc_b$PANEL), "4")
- panel_c <- layout_wrap(list(a3), "c", ncol = 1)
- loc_c <- locate_wrap(data.frame(c = NA), panel_c, "c")
+ facet <- facet_wrap(~c, ncol = 1)
+ layout_c <- facet$train(list(a3))
+ loc_c <- facet$map(data.frame(c = NA), layout_c)
expect_equal(as.character(loc_c$PANEL), "4")
})
test_that("grid: missing values located correctly", {
- panel_b <- layout_grid(list(a3), "b")
- loc_b <- locate_grid(data.frame(b = NA), panel_b, "b")
+ facet <- facet_grid(b~.)
+ layout_b <- facet$train(list(a3))
+ loc_b <- facet$map(data.frame(b = NA), layout_b)
expect_equal(as.character(loc_b$PANEL), "4")
- panel_c <- layout_grid(list(a3), "c")
- loc_c <- locate_grid(data.frame(c = NA), panel_c, "c")
+ facet <- facet_grid(c~.)
+ layout_c <- facet$train(list(a3))
+ loc_c <- facet$map(data.frame(c = NA), layout_c)
expect_equal(as.character(loc_c$PANEL), "4")
})
# Facet order ----------------------------------------------------------------
-get_layout <- function(p) ggplot_build(p)$panel$layout
+get_layout <- function(p) ggplot_build(p)$layout$panel_layout
# Data with factor f with levels CBA
d <- data.frame(x = 1:9, y = 1:9,
diff --git a/tests/testthat/test-facet-strips.r b/tests/testthat/test-facet-strips.r
index c3f4e7e..dec2ffa 100644
--- a/tests/testthat/test-facet-strips.r
+++ b/tests/testthat/test-facet-strips.r
@@ -3,14 +3,14 @@ context("Facet Strips")
strip_layout <- function(p) {
data <- ggplot_build(p)
plot <- data$plot
- panel <- data$panel
+ layout <- data$layout
data <- data$data
theme <- plot_theme(plot)
- geom_grobs <- Map(function(l, d) l$draw_geom(d, panel, plot$coordinates),
+ geom_grobs <- Map(function(l, d) l$draw_geom(d, layout, plot$coordinates),
plot$layers, data)
- facet <- facet_render(plot$facet, panel, plot$coordinates, theme, geom_grobs)
+ facet <- layout$render(geom_grobs, data, plot$coordinates, theme, plot$labels)
layout <- facet$layout
strip_layout <- layout[grepl("^strip", layout$name), 1:4]
as.list(strip_layout)
@@ -23,50 +23,62 @@ test_that("facet_wrap() builds correct output", {
wrap <- p + facet_wrap(~cyl)
wrap_expected <- list(
- t = c(1, 1, 1),
- l = c(2, 5, 8),
- b = c(1, 1, 1),
- r = c(2, 5, 8)
+ t = c(3, 3, 3),
+ l = c(3, 7, 11),
+ b = c(3, 3, 3),
+ r = c(3, 7, 11)
)
expect_equal(strip_layout(wrap), wrap_expected)
})
-test_that("facet_wrap() switches to 'x'", {
- wrap_x <- p + facet_wrap(~cyl, switch = "x")
+test_that("facet_wrap() switches to 'bottom'", {
+ wrap_b <- p + facet_wrap(~cyl, strip.position = "bottom")
+
+ wrap_b_expected <- list(
+ t = c(4, 4, 4),
+ l = c(3, 7, 11),
+ b = c(4, 4, 4),
+ r = c(3, 7, 11)
+ )
+
+ expect_equal(strip_layout(wrap_b), wrap_b_expected)
+})
+
+test_that("facet_wrap() switches to 'left'", {
+ wrap_l <- p + facet_wrap(~cyl, strip.position = "left")
- wrap_x_expected <- list(
+ wrap_l_expected <- list(
t = c(3, 3, 3),
- l = c(2, 5, 8),
+ l = c(13, 8, 3),
b = c(3, 3, 3),
- r = c(2, 5, 8)
+ r = c(13, 8, 3)
)
- expect_equal(strip_layout(wrap_x), wrap_x_expected)
+ expect_equal(strip_layout(wrap_l), wrap_l_expected)
})
-test_that("facet_wrap() switches to 'y'", {
- wrap_y <- p + facet_wrap(~cyl, switch = "y")
+test_that("facet_wrap() switches to 'right'", {
+ wrap_r <- p + facet_wrap(~cyl, strip.position = "right")
- wrap_y_expected <- list(
- t = c(1, 1, 1),
- l = c(1, 5, 9),
- b = c(1, 1, 1),
- r = c(1, 5, 9)
+ wrap_r_expected <- list(
+ t = c(3, 3, 3),
+ l = c(14, 9, 4),
+ b = c(3, 3, 3),
+ r = c(14, 9, 4)
)
- expect_equal(strip_layout(wrap_y), wrap_y_expected)
+ expect_equal(strip_layout(wrap_r), wrap_r_expected)
})
-
test_that("facet_grid() builds correct output", {
grid <- p + facet_grid(~cyl)
grid_expected <- list(
- t = c(1, 1, 1),
- l = c(2, 4, 6),
- b = c(1, 1, 1),
- r = c(2, 4, 6)
+ t = c(3, 3, 3),
+ l = c(3, 5, 7),
+ b = c(3, 3, 3),
+ r = c(3, 5, 7)
)
expect_equal(strip_layout(grid), grid_expected)
@@ -76,10 +88,10 @@ test_that("facet_grid() switches to 'x'", {
grid_x <- p + facet_grid(am ~ cyl, switch = "x")
grid_x_expected <- list(
- t = c(1, 3, 5),
- l = c(7, 7, 2),
- b = c(1, 3, 6),
- r = c(7, 7, 6)
+ t = c(6, 6, 6, 3, 5),
+ l = c(3, 5, 7, 8, 8),
+ b = c(6, 6, 6, 3, 5),
+ r = c(3, 5, 7, 8, 8)
)
expect_equal(strip_layout(grid_x), grid_x_expected)
@@ -89,10 +101,10 @@ test_that("facet_grid() switches to 'y'", {
grid_y <- p + facet_grid(am ~ cyl, switch = "y")
grid_y_expected <- list(
- t = c(1, 1, 1, 2),
- l = c(4, 6, 8, 1),
- b = c(1, 1, 1, 4),
- r = c(4, 6, 8, 2)
+ t = c(3, 3, 3, 4, 6),
+ l = c(4, 6, 8, 3, 3),
+ b = c(3, 3, 3, 4, 6),
+ r = c(4, 6, 8, 3, 3)
)
expect_equal(strip_layout(grid_y), grid_y_expected)
@@ -102,10 +114,10 @@ test_that("facet_grid() switches to both 'x' and 'y'", {
grid_xy <- p + facet_grid(am ~ cyl, switch = "both")
grid_xy_expected <- list(
- t = c(1, 5),
- l = c(1, 4),
- b = c(3, 6),
- r = c(2, 8)
+ t = c(6, 6, 6, 3, 5),
+ l = c(4, 6, 8, 3, 3),
+ b = c(6, 6, 6, 3, 5),
+ r = c(4, 6, 8, 3, 3)
)
expect_equal(strip_layout(grid_xy), grid_xy_expected)
diff --git a/tests/testthat/test-function-args.r b/tests/testthat/test-function-args.r
index eeaf682..84d5c34 100644
--- a/tests/testthat/test-function-args.r
+++ b/tests/testthat/test-function-args.r
@@ -13,7 +13,7 @@ test_that("geom_xxx and GeomXxx$draw arg defaults match", {
# These aren't actually geoms, or need special parameters and can't be tested this way.
geom_fun_names <- setdiff(
geom_fun_names,
- c("geom_aesthetics", "geom_map", "annotation_custom", "annotation_map",
+ c("geom_map", "annotation_custom", "annotation_map",
"annotation_raster", "annotation_id")
)
@@ -46,7 +46,7 @@ test_that("stat_xxx and StatXxx$draw arg defaults match", {
# These aren't actually stats, or need special parameters and can't be tested this way.
stat_fun_names <- setdiff(
stat_fun_names,
- c("stat_aesthetics", "stat_function")
+ c("stat_function")
)
# For each geom_xxx function and the corresponding GeomXxx$draw and
diff --git a/tests/testthat/test-geom-hex.R b/tests/testthat/test-geom-hex.R
new file mode 100644
index 0000000..726afe5
--- /dev/null
+++ b/tests/testthat/test-geom-hex.R
@@ -0,0 +1,12 @@
+context("geom_hex")
+
+test_that("density and value summaries available", {
+ df <- data.frame(x = c(1, 1, 1, 2), y = c(1, 1, 1, 2))
+ base <- ggplot(df, aes(x, y)) +
+ geom_hex()
+
+ out <- layer_data(base)
+ expect_equal(nrow(out), 2)
+ expect_equal(out$density, c(0.75, 0.25), tolerance = 1e-7)
+ expect_equal(out$count, c(3, 1), tolerance = 1e-7)
+})
diff --git a/tests/testthat/test-geom-violin.R b/tests/testthat/test-geom-violin.R
index 2f5208b..84fef52 100644
--- a/tests/testthat/test-geom-violin.R
+++ b/tests/testthat/test-geom-violin.R
@@ -1,27 +1,36 @@
context("geom_violin")
-test_that("", {
+test_that("range is expanded", {
df <- rbind(
data.frame(x = "a", y = c(0, runif(10), 1)),
data.frame(x = "b", y = c(0, runif(10), 2))
)
p <- ggplot(df, aes(1, y)) +
- geom_violin() +
+ geom_violin(trim = FALSE) +
facet_grid(x ~ ., scales = "free") +
coord_cartesian(expand = FALSE)
-
- expect_equal(layer_scales(p, 1)$y$dimension(), c(0, 1))
- expect_equal(layer_scales(p, 2)$y$dimension(), c(0, 2))
+ expand_a <- stats::bw.nrd0(df$y[df$x == "a"]) * 3
+ expand_b <- stats::bw.nrd0(df$y[df$x == "b"]) * 3
+ expect_equal(layer_scales(p, 1)$y$dimension(), c(0 - expand_a, 1 + expand_a))
+ expect_equal(layer_scales(p, 2)$y$dimension(), c(0 - expand_b, 2 + expand_b))
})
# create_quantile_segment_frame -------------------------------------------------
test_that("create_quantile_segment_frame functions for 3 quantiles", {
- density.data <- data.frame(y=(1:256)/256, density=1/256) # uniform density
+ density.data <- data.frame(y = (1:256)/256, density = 1/256) # uniform density
qs <- c(0.25, 0.5, 0.75) # 3 quantiles
expect_equal(create_quantile_segment_frame(density.data, qs)$y,
- rep(qs, each=2))
+ rep(qs, each = 2))
+})
+
+test_that("quantiles do not fail on zero-range data", {
+ zero.range.data <- data.frame(y = rep(1,3))
+ p <- ggplot(zero.range.data) + geom_violin(aes(1, y), draw_quantiles = 0.5)
+
+ # This should return without error and have length one
+ expect_equal(length(layer_grob(p)), 1)
})
diff --git a/tests/testthat/test-guides.R b/tests/testthat/test-guides.R
index 0a1aa6d..ba24899 100644
--- a/tests/testthat/test-guides.R
+++ b/tests/testthat/test-guides.R
@@ -8,3 +8,12 @@ test_that("colourbar trains without labels", {
expect_equal(names(out$key), c("colour", ".value"))
})
+test_that("Colorbar respects show.legend in layer", {
+ df <- data.frame(x = 1:3, y = 1)
+ p <- ggplot(df, aes(x = x, y = y, color = x)) +
+ geom_point(size = 20, shape = 21, show.legend = FALSE)
+ expect_false("guide-box" %in% ggplotGrob(p)$layout$name)
+ p <- ggplot(df, aes(x = x, y = y, color = x)) +
+ geom_point(size = 20, shape = 21, show.legend = TRUE)
+ expect_true("guide-box" %in% ggplotGrob(p)$layout$name)
+})
diff --git a/tests/testthat/test-labels.r b/tests/testthat/test-labels.r
index 36f9610..86a21d1 100644
--- a/tests/testthat/test-labels.r
+++ b/tests/testthat/test-labels.r
@@ -8,6 +8,16 @@ test_that("Setting guide labels", {
expect_identical(ylab("my label")$y, "my label")
expect_identical(labs(y = "my label")$y, "my label")
+ # Plot titles
+ expect_identical(labs(title = "my title")$title, "my title")
+ expect_identical(labs(title = "my title",
+ subtitle = "my subtitle")$subtitle, "my subtitle")
+
+ # whole plot annotations
+ expect_identical(labs(caption = "my notice")$caption, "my notice")
+ expect_identical(labs(title = "my title",
+ caption = "my notice")$caption, "my notice")
+
# Colour
expect_identical(labs(colour = "my label")$colour, "my label")
# American spelling
diff --git a/tests/testthat/test-layer.r b/tests/testthat/test-layer.r
index 7e4e9f5..1e58519 100644
--- a/tests/testthat/test-layer.r
+++ b/tests/testthat/test-layer.r
@@ -8,8 +8,12 @@ test_that("aesthetics go in aes_params", {
expect_equal(l$aes_params, list(size = "red"))
})
-test_that("unknown params create error", {
- expect_error(geom_point(blah = "red"), "Unknown parameters")
+test_that("unknown params create warning", {
+ expect_warning(geom_point(blah = "red"), "unknown parameters")
+})
+
+test_that("unknown aesthietcs create warning", {
+ expect_warning(geom_point(aes(blah = "red")), "unknown aesthetics")
})
# Calculated aesthetics ---------------------------------------------------
diff --git a/tests/testthat/test-position-stack.R b/tests/testthat/test-position-stack.R
new file mode 100644
index 0000000..120b898
--- /dev/null
+++ b/tests/testthat/test-position-stack.R
@@ -0,0 +1,54 @@
+context("position-stack")
+
+test_that("data is sorted prior to stacking", {
+ df <- data.frame(
+ x = rep(c(1:10), 3),
+ var = rep(c("a", "b", "c"), 10),
+ y = round(runif(30, 1, 5))
+ )
+ p <- ggplot(df, aes(x = x, y = y, fill = var)) +
+ geom_area(position = "stack")
+ dat <- layer_data(p)
+ expect_true(all(dat$group == 3:1))
+})
+
+test_that("negative and positive values are handled separately", {
+ df <- data.frame(
+ x = c(1,1,1,2,2),
+ g = c(1,2,3,1,2),
+ y = c(1,-1,1,2,-3)
+ )
+ p <- ggplot(df, aes(x, y, fill = factor(g))) + geom_col()
+ dat <- layer_data(p)
+
+ expect_equal(dat$ymin[dat$x == 1], c(-1, 0, 1))
+ expect_equal(dat$ymax[dat$x == 1], c(0, 1, 2))
+
+ expect_equal(dat$ymin[dat$x == 2], c(-3, 0))
+ expect_equal(dat$ymax[dat$x == 2], c(0, 2))
+})
+
+test_that("can request reverse stacking", {
+ df <- data.frame(
+ y = c(-2, 2, -1, 1),
+ g = c("a", "a", "b", "b")
+ )
+ p <- ggplot(df, aes(1, y, fill = g)) +
+ geom_col(position = position_stack(reverse = TRUE))
+ dat <- layer_data(p)
+ expect_equal(dat$ymin, c(-2, -3, 0, 2))
+})
+
+test_that("data with no extent is stacked correctly", {
+ df = data.frame(
+ x = c(1, 1),
+ y = c(-40, -75),
+ group = letters[1:2]
+ )
+ base <- ggplot(df, aes(x, y, group = group))
+ p0 <- base + geom_text(aes(label = y), position = position_stack(vjust = 0))
+ p1 <- base + geom_text(aes(label = y), position = position_stack(vjust = 1))
+
+ expect_equal(layer_data(p0)$y, c(-75, -115))
+ expect_equal(layer_data(p1)$y, c(0, -75))
+})
diff --git a/tests/testthat/test-scale-date.R b/tests/testthat/test-scale-date.R
new file mode 100644
index 0000000..b913774
--- /dev/null
+++ b/tests/testthat/test-scale-date.R
@@ -0,0 +1,46 @@
+context("scale_date")
+
+base_time <- function(tz = "") {
+ as.POSIXct(strptime("2015-06-01", "%Y-%m-%d", tz = tz))
+}
+
+df <- data.frame(
+ time1 = base_time("") + 0:6 * 3600,
+ time2 = base_time("UTC") + 0:6 * 3600,
+ time3 = base_time("Australia/Lord_Howe") + (0:6 + 13) * 3600, # has half hour offset
+ y = seq_along(base_time)
+)
+
+test_that("inherits timezone from data", {
+ # Local time
+ p <- ggplot(df, aes(y = y)) + geom_point(aes(time1))
+ sc <- layer_scales(p)$x
+ expect_equal(sc$timezone, NULL)
+ expect_equal(sc$get_labels()[1], "00:00")
+
+ # UTC
+ p <- ggplot(df, aes(y = y)) + geom_point(aes(time2))
+ sc <- layer_scales(p)$x
+ expect_equal(sc$timezone, "UTC")
+ expect_equal(sc$get_labels()[1], "00:00")
+})
+
+
+test_that("first timezone wins", {
+ p <- ggplot(df, aes(y = y)) +
+ geom_point(aes(time2)) +
+ geom_point(aes(time3), colour = "red") +
+ scale_x_datetime(date_breaks = "hour", date_labels = "%H:%M")
+ sc <- layer_scales(p)$x
+ expect_equal(sc$timezone, "UTC")
+})
+
+test_that("not cached across calls", {
+ scale_x <- scale_x_datetime(date_breaks = "hour", date_labels = "%H:%M")
+
+ p1 <- ggplot(df, aes(y = y)) + geom_point(aes(time2)) + scale_x
+ p2 <- ggplot(df, aes(y = y)) + geom_point(aes(time3)) + scale_x
+
+ expect_equal(layer_scales(p1)$x$timezone, "UTC")
+ expect_equal(layer_scales(p2)$x$timezone, "Australia/Lord_Howe")
+})
diff --git a/tests/testthat/test-scale-discrete.R b/tests/testthat/test-scale-discrete.R
index 3485576..3dd26c9 100644
--- a/tests/testthat/test-scale-discrete.R
+++ b/tests/testthat/test-scale-discrete.R
@@ -1,5 +1,51 @@
context("scale_discrete")
+# Missing values ----------------------------------------------------------
+
+df <- tibble::tibble(
+ x1 = c("a", "b", NA),
+ x2 = factor(x1),
+ x3 = addNA(x2),
+
+ y = 1:3
+)
+
+test_that("NAs translated/preserved for position scales", {
+ p1a <- ggplot(df, aes(x1, y)) + geom_point()
+ p2a <- ggplot(df, aes(x2, y)) + geom_point()
+ p3a <- ggplot(df, aes(x3, y)) + geom_point()
+
+ expect_equal(layer_data(p1a)$x, c(1, 2, 3))
+ expect_equal(layer_data(p2a)$x, c(1, 2, 3))
+ expect_equal(layer_data(p3a)$x, c(1, 2, 3))
+
+ rm_na_x <- scale_x_discrete(na.translate = FALSE)
+ p1b <- p1a + rm_na_x
+ p2b <- p2a + rm_na_x
+ p3b <- p3a + rm_na_x
+
+ expect_equal(layer_data(p1b)$x, c(1, 2, NA))
+ expect_equal(layer_data(p2b)$x, c(1, 2, NA))
+ expect_equal(layer_data(p3b)$x, c(1, 2, NA))
+})
+
+test_that("NAs translated/preserved for non-position scales", {
+ p1a <- ggplot(df, aes(y, y, colour = x1)) + geom_point()
+ p2a <- ggplot(df, aes(y, y, colour = x2)) + geom_point()
+ p3a <- ggplot(df, aes(y, y, colour = x3)) + geom_point()
+ expect_equal(layer_data(p1a)$colour, c("#F8766D", "#00BFC4", "grey50"))
+ expect_equal(layer_data(p2a)$colour, c("#F8766D", "#00BFC4", "grey50"))
+ expect_equal(layer_data(p3a)$colour, c("#F8766D", "#00BFC4", "grey50"))
+
+ rm_na_colour <- scale_colour_discrete(na.translate = FALSE)
+ p1b <- p1a + rm_na_colour
+ p2b <- p2a + rm_na_colour
+ p3b <- p3a + rm_na_colour
+ expect_equal(layer_data(p1b)$colour, c("#F8766D", "#00BFC4", NA))
+ expect_equal(layer_data(p2b)$colour, c("#F8766D", "#00BFC4", NA))
+ expect_equal(layer_data(p3b)$colour, c("#F8766D", "#00BFC4", NA))
+})
+
# Ranges ------------------------------------------------------------------
test_that("discrete ranges also encompas continuous values", {
@@ -16,3 +62,10 @@ test_that("discrete ranges also encompas continuous values", {
expect_equal(x_range(base + geom_point(aes(x1)) + geom_point(aes(x2))), c(0, 4))
})
+test_that("discrete scale shrinks to range when setting limits", {
+ df <- data.frame(x = letters[1:10], y = 1:10)
+ p <- ggplot(df, aes(x, y)) + geom_point() +
+ scale_x_discrete(limits = c("a", "b"))
+
+ expect_equal(layer_scales(p)$x$dimension(c(0, 1)), c(0, 3))
+})
diff --git a/tests/testthat/test-scales.r b/tests/testthat/test-scales.r
index 5a205b0..1f22279 100644
--- a/tests/testthat/test-scales.r
+++ b/tests/testthat/test-scales.r
@@ -97,7 +97,7 @@ test_that("position scales generate after stats", {
test_that("oob affects position values", {
dat <- data.frame(x = c("a", "b", "c"), y = c(1, 5, 10))
base <- ggplot(dat, aes(x, y)) +
- geom_bar(stat = "identity") +
+ geom_col() +
annotate("point", x = "a", y = c(-Inf, Inf))
y_scale <- function(limits, oob = censor) {
@@ -129,7 +129,7 @@ test_that("oob affects position values", {
})
test_that("scales looked for in appropriate place", {
- xlabel <- function(x) ggplot_build(x)$panel$x_scales[[1]]$name
+ xlabel <- function(x) ggplot_build(x)$layout$panel_scales$x[[1]]$name
p0 <- qplot(mpg, wt, data = mtcars) + scale_x_continuous("0")
expect_equal(xlabel(p0), "0")
@@ -167,3 +167,24 @@ test_that("find_global searches in the right places", {
expect_identical(find_global("scale_colour_hue", emptyenv()),
ggplot2::scale_colour_hue)
})
+
+test_that("Scales warn when transforms introduces non-finite values", {
+ df <- data.frame(x = c(1e1, 1e5), y = c(0, 100))
+
+ p <- ggplot(df, aes(x, y)) +
+ geom_point(size = 5) +
+ scale_y_log10()
+
+ expect_warning(ggplot_build(p), "Transformation introduced infinite values")
+})
+
+test_that("Scales get their correct titles through layout", {
+ df <- data.frame(x = c(1e1, 1e5), y = c(0, 100))
+
+ p <- ggplot(df, aes(x, y)) +
+ geom_point(size = 5)
+
+ p <- ggplot_build(p)
+ expect_identical(p$layout$xlabel(p$plot$labels)$primary, "x")
+ expect_identical(p$layout$ylabel(p$plot$labels)$primary, "y")
+})
diff --git a/tests/testthat/test-stat-bin.R b/tests/testthat/test-stat-bin.R
index 395d369..50f1e9e 100644
--- a/tests/testthat/test-stat-bin.R
+++ b/tests/testthat/test-stat-bin.R
@@ -6,8 +6,10 @@ test_that("stat_bin throws error when y aesthetic present", {
expect_error(ggplot_build(ggplot(dat, aes(x, y)) + stat_bin()),
"must not be used with a y aesthetic.")
- expect_error(p <- ggplot_build(ggplot(dat, aes(x)) + stat_bin(y = 5)),
- "Unknown parameters: y")
+ expect_error(
+ ggplot_build(ggplot(dat, aes(x)) + stat_bin(y = 5)),
+ "StatBin requires a continuous x"
+ )
})
test_that("bins specifies the number of bins", {
@@ -34,6 +36,22 @@ test_that("geom_freqpoly defaults to pad = TRUE", {
expect_equal(out$count, c(0, 1, 1, 1, 0))
})
+test_that("can use breaks argument", {
+ df <- data.frame(x = 1:3)
+ out <- layer_data(ggplot(df, aes(x)) + geom_histogram(breaks = c(0, 1.5, 5)))
+
+ expect_equal(out$count, c(1, 2))
+})
+
+
+test_that("fuzzy breaks used when cutting", {
+ df <- data.frame(x = c(-1, -0.5, -0.4, 0))
+ p <- ggplot(df, aes(x)) +
+ geom_histogram(binwidth = 0.1, boundary = 0.1, closed = "left")
+
+ bins <- layer_data(p) %>% subset(count > 0) %>% .[1:5]
+ expect_equal(bins$count, c(1, 1, 1, 1))
+})
# Underlying binning algorithm --------------------------------------------
@@ -97,11 +115,14 @@ test_that("weights are added", {
test_that("stat_count throws error when y aesthetic present", {
dat <- data.frame(x = c("a", "b", "c"), y = c(1, 5, 10))
- expect_error(ggplot_build(ggplot(dat, aes(x, y)) + stat_count()),
+ expect_error(
+ ggplot_build(ggplot(dat, aes(x, y)) + stat_count()),
"must not be used with a y aesthetic.")
- expect_error(p <- ggplot_build(ggplot(dat, aes(x)) + stat_count(y = 5)),
- "Unknown parameters: y")
+ expect_error(
+ ggplot_build(ggplot(dat, aes(x)) + stat_count(y = 5)),
+ "must not be used with a y aesthetic."
+ )
})
test_that("stat_count preserves x order for continuous and discrete", {
@@ -120,6 +141,6 @@ test_that("stat_count preserves x order for continuous and discrete", {
mtcars$carb3 <- factor(mtcars$carb, levels = c(4,1,2,3,6,8))
b <- ggplot_build(ggplot(mtcars, aes(carb3)) + geom_bar())
expect_identical(b$data[[1]]$x, 1:6)
- expect_identical(b$panel$ranges[[1]]$x.labels, c("4","1","2","3","6","8"))
+ expect_identical(b$layout$panel_ranges[[1]]$x.labels, c("4","1","2","3","6","8"))
expect_identical(b$data[[1]]$y, c(10,7,10,3,1,1))
})
diff --git a/tests/testthat/test-stat-hex.R b/tests/testthat/test-stat-hex.R
new file mode 100644
index 0000000..76ce997
--- /dev/null
+++ b/tests/testthat/test-stat-hex.R
@@ -0,0 +1,8 @@
+context("stat-hex")
+
+test_that("can use length 1 binwidth", {
+ df <- data.frame(x = c(1, 1, 2), y = c(1, 1, 2))
+ p <- ggplot(df, aes(x, y)) + stat_binhex(binwidth = 1)
+
+ expect_equal(nrow(layer_data(p)), 2)
+})
diff --git a/tests/testthat/test-stats.r b/tests/testthat/test-stats.r
index 3de4e17..a5d26e5 100644
--- a/tests/testthat/test-stats.r
+++ b/tests/testthat/test-stats.r
@@ -11,3 +11,8 @@ test_that("plot succeeds even if some computation fails", {
expect_warning(b2 <- ggplot_build(p2), "Computation failed")
expect_equal(length(b2$data), 2)
})
+
+test_that("error message is thrown when aesthetics are missing", {
+ p <- ggplot(mtcars) + stat_bin()
+ expect_error(ggplot_build(p), "x$")
+})
diff --git a/tests/testthat/test-theme.r b/tests/testthat/test-theme.r
index e3aac62..12e56f2 100644
--- a/tests/testthat/test-theme.r
+++ b/tests/testthat/test-theme.r
@@ -4,7 +4,7 @@ test_that("Modifying theme element properties with + operator", {
# Changing a "leaf node" works
t <- theme_grey() + theme(axis.title.x = element_text(colour = 'red', margin = margin()))
- expect_identical(t$axis.title.x, element_text(colour = 'red', margin = margin()))
+ expect_identical(t$axis.title.x, element_text(colour = 'red', margin = margin(), vjust = 1))
# Make sure the theme class didn't change or get dropped
expect_true(is.theme(t))
# Make sure the element class didn't change or get dropped
@@ -51,6 +51,8 @@ test_that("Adding theme object to ggplot object with + operator", {
expect_true(p$theme$text$colour == 'red')
tt <- theme_grey()$text
tt$colour <- 'red'
+ expect_true(tt$inherit.blank)
+ tt$inherit.blank <- FALSE
expect_identical(p$theme$text, tt)
})
@@ -161,15 +163,6 @@ test_that("Complete and non-complete themes interact correctly with ggplot objec
expect_false(attr(p$plot$theme, "complete"))
expect_equal(p$plot$theme$text$colour, "red")
expect_equal(p$plot$theme$text$face, "italic")
-
-
- # Only gets red property; because of the way lists are processed in R, the
- # the second item doesn't get used properly. But I think that's OK.
- p <- ggplot_build(qplot(1:3, 1:3) +
- theme(text = element_text(colour = 'red'), text = element_text(face = 'italic')))
- expect_false(attr(p$plot$theme, "complete"))
- expect_equal(p$plot$theme$text$colour, "red")
- expect_equal(p$plot$theme$text$face, "plain")
})
test_that("theme(validate=FALSE) means do not validate_element", {
@@ -188,3 +181,23 @@ test_that("theme(validate=FALSE) means do not validate_element", {
red.before <- p + red.text + theme(animint.width = 500, validate = FALSE)
expect_equal(red.before$theme$animint.width, 500)
})
+
+test_that("All elements in complete themes have inherit.blank=TRUE", {
+ inherit_blanks <- function(theme) {
+ all(vapply(theme, function(el) {
+ if (inherits(el, "element") && !inherits(el, "element_blank")) {
+ el$inherit.blank
+ } else {
+ TRUE
+ }
+ }, logical(1)))
+ }
+ expect_true(inherit_blanks(theme_grey()))
+ expect_true(inherit_blanks(theme_bw()))
+ expect_true(inherit_blanks(theme_classic()))
+ expect_true(inherit_blanks(theme_dark()))
+ expect_true(inherit_blanks(theme_light()))
+ expect_true(inherit_blanks(theme_linedraw()))
+ expect_true(inherit_blanks(theme_minimal()))
+ expect_true(inherit_blanks(theme_void()))
+})
diff --git a/tests/testthat/test-utilities.r b/tests/testthat/test-utilities.r
index 9d4ecc4..073ea32 100644
--- a/tests/testthat/test-utilities.r
+++ b/tests/testthat/test-utilities.r
@@ -27,3 +27,17 @@ test_that("add_group", {
expect_true(has_groups(add_group(data[1:3]))) # discrete column
expect_false(has_groups(add_group(data[2:3]))) # no group or discrete column
})
+
+test_that("find_args behaves correctly", {
+ test_fun <- function(arg1, arg2 = FALSE, ...) {
+ find_args(...)
+ }
+ # Missing args are removed
+ expect_false("arg1" %in% names(test_fun()))
+ # Ellipsis is not an element
+ expect_false("..." %in% names(test_fun()))
+ # Args are added
+ expect_true(all(c("arg1", "arg2", "arg3") %in% names(test_fun(arg1 = 1, arg2 = 1, arg3 = 1))))
+ # Defaults are overwritten
+ expect_true(test_fun(arg2 = TRUE)$arg2)
+})
diff --git a/vignettes/extending-ggplot2.Rmd b/vignettes/extending-ggplot2.Rmd
index 921c095..fa740f9 100644
--- a/vignettes/extending-ggplot2.Rmd
+++ b/vignettes/extending-ggplot2.Rmd
@@ -1,7 +1,5 @@
---
title: "Extending ggplot2"
-author: "Hadley Wickham"
-date: "`r Sys.Date()`"
output: rmarkdown::html_vignette
vignette: >
%\VignetteIndexEntry{Extending ggplot2}
@@ -10,7 +8,7 @@ vignette: >
---
```{r, include = FALSE}
-knitr::opts_chunk$set(collapse = TRUE, comment = "#>")
+knitr::opts_chunk$set(collapse = TRUE, comment = "#>", fig.width = 7, fig.height = 7, fig.align = "center")
library(ggplot2)
```
@@ -364,11 +362,11 @@ This is very similar to defining a new stat. You always need to provide fields/m
* `draw_key` provides the function used to draw the key in the legend.
You can see a list of all the build in key functions in `?draw_key`
-* `draw_group()` is where the magic happens. This function takes three
+* `draw_panel()` is where the magic happens. This function takes three
arguments and returns a grid grob. It is called once for each panel.
It's the most complicated part and is described in more detail below.
-`draw_group()` has three arguments:
+`draw_panel()` has three arguments:
* `data`: a data frame with one column for each aesthetic.
@@ -377,11 +375,11 @@ This is very similar to defining a new stat. You always need to provide fields/m
* `coord`: an object describing the coordinate system.
-Generally you won't use `panel_scales` and `coord` directly, but you will always use them to transform the data: `coords <- coord$transform(data, panel_scales)`. This creates a data frame where position variables are scaled to the range 0--1. You then take this data and call a grid grob function. (Transforming for non-Cartesian coordinate systems is quite complex - you're best of transforming your data to the form accepted by an existing ggplot2 geom and passing it.)
+Generally you won't use `panel_scales` and `coord` directly, but you will always use them to transform the data: `coords <- coord$transform(data, panel_scales)`. This creates a data frame where position variables are scaled to the range 0--1. You then take this data and call a grid grob function. (Transforming for non-Cartesian coordinate systems is quite complex - you're best off transforming your data to the form accepted by an existing ggplot2 geom and passing it.)
### Collective geoms
-Overriding `draw_panel()` is most appropriate if there is one graphic element per row. In other cases, you want graphic element per group. For example, take polygons: each row gives one vertex of a polygon. In this case, you should instead override `draw_group()`:
+Overriding `draw_panel()` is most appropriate if there is one graphic element per row. In other cases, you want graphic element per group. For example, take polygons: each row gives one vertex of a polygon. In this case, you should instead override `draw_group()`.
The following code makes a simplified version of `GeomPolygon`:
@@ -433,12 +431,8 @@ ggplot(mpg, aes(displ, hwy)) +
There are a few things to note here:
-* We override `draw_group()` instead of `draw_layer()` because we want
- one polygon per group, not one polygon per row. If you look at the source
- code for the original `GeomPolygon` you'll see it actually overrides
- `geom_layer()` because it uses some tricks to make `polygonGrob()` produce
- multiple polygons in one call. This is considerably more complicated, but
- gives better performance.
+* We override `draw_group()` instead of `draw_panel()` because we want
+ one polygon per group, not one polygon per row.
* If the data contains two or fewer points, there's no point trying to draw
a polygon, so we return a `nullGrob()`. This is the graphical equivalent
@@ -450,6 +444,8 @@ There are a few things to note here:
change it there). `lwd` is measured in points, but ggplot2 uses mm,
so we need to multiply it by the adjustment factor `.pt`.
+You might want to compare this to the real `GeomPolygon`. You'll see it overrides `draw_panel()` because it uses some tricks to make `polygonGrob()` produce multiple polygons in one call. This is considerably more complicated, but gives better performance.
+
### Inheriting from an existing Geom
Sometimes you just want to make a small modification to an existing geom. In this case, rather than inheriting from `Geom` you can inherit from an existing subclass. For example, we might want to change the defaults for `GeomPolygon` to work better with `StatChull`:
@@ -545,3 +541,426 @@ Complete and incomplete themes behave somewhat differently when added to a ggplo
those properties of elements defined in the call to `theme()`.
* Adding a complete theme wipes away the existing theme and applies the new theme.
+
+## Creating a new facetting
+
+One of the more daunting exercises in ggplot2 extensions is to create a new facetting system. The reason for this is that when creating new facettings you take on the responsibility of how (almost) everything is drawn on the screen, and many do not have experience with directly using [gtable](https://cran.r-project.org/package=gtable) and [grid](https://cran.r-project.org/package=grid) upon which the ggplot2 rendering is build. If you decide to venture into facetting extensions it is hig [...]
+
+The `Facet` class in ggplot2 is very powerfull as it takes on responsibility of a wide range of tasks. The main tasks of a `Facet` object are:
+
+* Define a layout; that is, a partitioning of the data into different plot areas (panels) as well as which panels share position scales.
+
+* Map plot data into the correct panels, potentially duplicating data if it should exist in multiple panels (e.g. margins in `facet_grid()`).
+
+* Assemble all panels into a final gtable, adding axes, strips and decorations in the process.
+
+Apart from these three tasks, for which functionality must be implemented, there are a couple of additional extension points where sensible defaults have been provided. These can generally be ignored, but adventurous developers can override them for even more control:
+
+* Initialization and training of positional scales for each panel.
+
+* Decoration in front of and behind each panel.
+
+* Drawing of axis labels
+
+To show how a new facetting class is created we will start simple and go through each of the required methods in turn to build up `facet_duplicate()` that simply duplicate our plot into two panels. After this we will tinker with it a bit to show some of the more powerful possibilities.
+
+### Creating a layout specification
+
+A layout in the context of facets is a `data.frame` that defines a mapping between data and the panels it should reside in as well as which positional scales should be used. The output should at least contain the columns `PANEL`, `SCALE_X`, and `SCALE_Y`, but will often contain more to help assign data to the correct panel (`facet_grid()` will e.g. also return the facetting variables associated with each panel). Let's make a function that defines a duplicate layout:
+
+```{r}
+layout <- function(data, params) {
+ data.frame(PANEL = c(1L, 2L), SCALE_X = 1L, SCALE_Y = 1L)
+}
+```
+
+This is quite simple as the facetting should just define two panels irrespectively of the input data and parameters.
+
+### Mapping data into panels
+
+In order for ggplot2 to know which data should go where it needs the data to be assigned to a panel. The purpose of the mapping step is to assign a `PANEL` column to the layer data identifying which panel it belongs to.
+
+```{r}
+mapping <- function(data, layout, params) {
+ if (plyr::empty(data)) {
+ return(cbind(data, PANEL = integer(0)))
+ }
+ rbind(
+ cbind(data, PANEL = 1L),
+ cbind(data, PANEL = 2L)
+ )
+}
+```
+
+here we first investigate whether we have gotten an empty `data.frame` and if not we duplicate the data and assign the original data to the first panel and the new data to the second panel.
+
+### Laying out the panels
+
+While the two functions above has been decievingly simple, this last one is going to take some more work. Our goal is to draw two panels beside (or above) each other with axes etc.
+
+```{r}
+render <- function(panels, layout, x_scales, y_scales, ranges, coord, data,
+ theme, params) {
+ # Place panels according to settings
+ if (params$horizontal) {
+ # Put panels in matrix and convert to a gtable
+ panels <- matrix(panels, ncol = 2)
+ panel_table <- gtable::gtable_matrix("layout", panels,
+ widths = unit(c(1, 1), "null"), heights = unit(1, "null"), clip = "on")
+ # Add spacing according to theme
+ panel_spacing <- if (is.null(theme$panel.spacing.x)) {
+ theme$panel.spacing
+ } else {
+ theme$panel.spacing.x
+ }
+ panel_table <- gtable::gtable_add_col_space(panel_table, panel_spacing)
+ } else {
+ panels <- matrix(panels, ncol = 1)
+ panel_table <- gtable::gtable_matrix("layout", panels,
+ widths = unit(1, "null"), heights = unit(c(1, 1), "null"), clip = "on")
+ panel_spacing <- if (is.null(theme$panel.spacing.y)) {
+ theme$panel.spacing
+ } else {
+ theme$panel.spacing.y
+ }
+ panel_table <- gtable::gtable_add_row_space(panel_table, panel_spacing)
+ }
+ # Name panel grobs so they can be found later
+ panel_table$layout$name <- paste0("panel-", c(1, 2))
+
+ # Construct the axes
+ axes <- render_axes(ranges[1], ranges[1], coord, theme,
+ transpose = TRUE)
+
+ # Add axes around each panel
+ panel_pos_h <- panel_cols(panel_table)$l
+ panel_pos_v <- panel_rows(panel_table)$t
+ axis_width_l <- unit(grid::convertWidth(
+ grid::grobWidth(axes$y$left[[1]]), "cm", TRUE), "cm")
+ axis_width_r <- unit(grid::convertWidth(
+ grid::grobWidth(axes$y$right[[1]]), "cm", TRUE), "cm")
+ ## We do it reverse so we don't change the position of panels when we add axes
+ for (i in rev(panel_pos_h)) {
+ panel_table <- gtable::gtable_add_cols(panel_table, axis_width_r, i)
+ panel_table <- gtable::gtable_add_grob(panel_table,
+ rep(axes$y$right, length(panel_pos_v)), t = panel_pos_v, l = i + 1,
+ clip = "off")
+ panel_table <- gtable::gtable_add_cols(panel_table, axis_width_l, i - 1)
+ panel_table <- gtable::gtable_add_grob(panel_table,
+ rep(axes$y$left, length(panel_pos_v)), t = panel_pos_v, l = i,
+ clip = "off")
+ }
+ ## Recalculate as gtable has changed
+ panel_pos_h <- panel_cols(panel_table)$l
+ panel_pos_v <- panel_rows(panel_table)$t
+ axis_height_t <- unit(grid::convertHeight(
+ grid::grobHeight(axes$x$top[[1]]), "cm", TRUE), "cm")
+ axis_height_b <- unit(grid::convertHeight(
+ grid::grobHeight(axes$x$bottom[[1]]), "cm", TRUE), "cm")
+ for (i in rev(panel_pos_v)) {
+ panel_table <- gtable::gtable_add_rows(panel_table, axis_height_b, i)
+ panel_table <- gtable::gtable_add_grob(panel_table,
+ rep(axes$x$bottom, length(panel_pos_h)), t = i + 1, l = panel_pos_h,
+ clip = "off")
+ panel_table <- gtable::gtable_add_rows(panel_table, axis_height_t, i - 1)
+ panel_table <- gtable::gtable_add_grob(panel_table,
+ rep(axes$x$top, length(panel_pos_h)), t = i, l = panel_pos_h,
+ clip = "off")
+ }
+ panel_table
+}
+```
+
+### Assembling the Facet class
+
+Usually all methods are defined within the class definition in the same way as is done for `Geom` and `Stat`. Here we have split it out so we could go through each in turn. All that remains is to assign our functions to the correct methods as well as making a constructor
+
+```{r}
+# Constructor: shrink is required to govern whether scales are trained on
+# Stat-transformed data or not.
+facet_duplicate <- function(horizontal = TRUE, shrink = TRUE) {
+ ggproto(NULL, FacetDuplicate,
+ shrink = shrink,
+ params = list(
+ horizontal = horizontal
+ )
+ )
+}
+
+FacetDuplicate <- ggproto("FacetDuplicate", Facet,
+ compute_layout = layout,
+ map_data = mapping,
+ draw_panels = render
+)
+```
+
+Now with everything assembled, lets test it out:
+
+```{r}
+p <- ggplot(mtcars, aes(x = hp, y = mpg)) + geom_point()
+p
+p + facet_duplicate()
+```
+
+### Doing more with facets
+
+The example above was pretty useless and we'll now try to expand on it to add some actual usability. We are going to make a facetting that adds panels with y-transformed axes:
+
+```{r}
+library(scales)
+
+facet_trans <- function(trans, horizontal = TRUE, shrink = TRUE) {
+ ggproto(NULL, FacetTrans,
+ shrink = shrink,
+ params = list(
+ trans = scales::as.trans(trans),
+ horizontal = horizontal
+ )
+ )
+}
+
+FacetTrans <- ggproto("FacetTrans", Facet,
+ # Almost as before but we want different y-scales for each panel
+ compute_layout = function(data, params) {
+ data.frame(PANEL = c(1L, 2L), SCALE_X = 1L, SCALE_Y = c(1L, 2L))
+ },
+ # Same as before
+ map_data = function(data, layout, params) {
+ if (plyr::empty(data)) {
+ return(cbind(data, PANEL = integer(0)))
+ }
+ rbind(
+ cbind(data, PANEL = 1L),
+ cbind(data, PANEL = 2L)
+ )
+ },
+ # This is new. We create a new scale with the defined transformation
+ init_scales = function(layout, x_scale = NULL, y_scale = NULL, params) {
+ scales <- list()
+ if (!is.null(x_scale)) {
+ scales$x <- plyr::rlply(max(layout$SCALE_X), x_scale$clone())
+ }
+ if (!is.null(y_scale)) {
+ y_scale_orig <- y_scale$clone()
+ y_scale_new <- y_scale$clone()
+ y_scale_new$trans <- params$trans
+ # Make sure that oob values are kept
+ y_scale_new$oob <- function(x, ...) x
+ scales$y <- list(y_scale_orig, y_scale_new)
+ }
+ scales
+ },
+ # We must make sure that the second scale is trained on transformed data
+ train_scales = function(x_scales, y_scales, layout, data, params) {
+ # Transform data for second panel prior to scale training
+ if (!is.null(y_scales)) {
+ data <- lapply(data, function(layer_data) {
+ match_id <- match(layer_data$PANEL, layout$PANEL)
+ y_vars <- intersect(y_scales[[1]]$aesthetics, names(layer_data))
+ trans_scale <- layer_data$PANEL == 2L
+ for (i in y_vars) {
+ layer_data[trans_scale, i] <- y_scales[[2]]$transform(layer_data[trans_scale, i])
+ }
+ layer_data
+ })
+ }
+ Facet$train_scales(x_scales, y_scales, layout, data, params)
+ },
+ # this is where we actually modify the data. It cannot be done in $map_data as that function
+ # doesn't have access to the scales
+ finish_data = function(data, layout, x_scales, y_scales, params) {
+ match_id <- match(data$PANEL, layout$PANEL)
+ y_vars <- intersect(y_scales[[1]]$aesthetics, names(data))
+ trans_scale <- data$PANEL == 2L
+ for (i in y_vars) {
+ data[trans_scale, i] <- y_scales[[2]]$transform(data[trans_scale, i])
+ }
+ data
+ },
+ # A few changes from before to accomodate that axes are now not duplicate of each other
+ # We also add a panel strip to annotate the different panels
+ draw_panels = function(panels, layout, x_scales, y_scales, ranges, coord,
+ data, theme, params) {
+ # Place panels according to settings
+ if (params$horizontal) {
+ # Put panels in matrix and convert to a gtable
+ panels <- matrix(panels, ncol = 2)
+ panel_table <- gtable::gtable_matrix("layout", panels,
+ widths = unit(c(1, 1), "null"), heights = unit(1, "null"), clip = "on")
+ # Add spacing according to theme
+ panel_spacing <- if (is.null(theme$panel.spacing.x)) {
+ theme$panel.spacing
+ } else {
+ theme$panel.spacing.x
+ }
+ panel_table <- gtable::gtable_add_col_space(panel_table, panel_spacing)
+ } else {
+ panels <- matrix(panels, ncol = 1)
+ panel_table <- gtable::gtable_matrix("layout", panels,
+ widths = unit(1, "null"), heights = unit(c(1, 1), "null"), clip = "on")
+ panel_spacing <- if (is.null(theme$panel.spacing.y)) {
+ theme$panel.spacing
+ } else {
+ theme$panel.spacing.y
+ }
+ panel_table <- gtable::gtable_add_row_space(panel_table, panel_spacing)
+ }
+ # Name panel grobs so they can be found later
+ panel_table$layout$name <- paste0("panel-", c(1, 2))
+
+ # Construct the axes
+ axes <- render_axes(ranges[1], ranges, coord, theme,
+ transpose = TRUE)
+
+ # Add axes around each panel
+ grobWidths <- function(x) {
+ unit(vapply(x, function(x) {
+ grid::convertWidth(
+ grid::grobWidth(x), "cm", TRUE)
+ }, numeric(1)), "cm")
+ }
+ panel_pos_h <- panel_cols(panel_table)$l
+ panel_pos_v <- panel_rows(panel_table)$t
+ axis_width_l <- grobWidths(axes$y$left)
+ axis_width_r <- grobWidths(axes$y$right)
+ ## We do it reverse so we don't change the position of panels when we add axes
+ for (i in rev(seq_along(panel_pos_h))) {
+ panel_table <- gtable::gtable_add_cols(panel_table, axis_width_r[i], panel_pos_h[i])
+ if (params$horizontal) {
+ panel_table <- gtable::gtable_add_grob(panel_table,
+ rep(axes$y$right[i], length(panel_pos_v)), t = panel_pos_v, l = panel_pos_h[i] + 1,
+ clip = "off")
+ } else {
+ panel_table <- gtable::gtable_add_grob(panel_table,
+ rep(axes$y$right, length(panel_pos_v)), t = panel_pos_v, l = panel_pos_h[i] + 1,
+ clip = "off")
+ }
+ panel_table <- gtable::gtable_add_cols(panel_table, axis_width_l[i], panel_pos_h[i] - 1)
+ if (params$horizontal) {
+ panel_table <- gtable::gtable_add_grob(panel_table,
+ rep(axes$y$left[i], length(panel_pos_v)), t = panel_pos_v, l = panel_pos_h[i],
+ clip = "off")
+ } else {
+ panel_table <- gtable::gtable_add_grob(panel_table,
+ rep(axes$y$left, length(panel_pos_v)), t = panel_pos_v, l = panel_pos_h[i],
+ clip = "off")
+ }
+ }
+ ## Recalculate as gtable has changed
+ panel_pos_h <- panel_cols(panel_table)$l
+ panel_pos_v <- panel_rows(panel_table)$t
+ axis_height_t <- unit(grid::convertHeight(
+ grid::grobHeight(axes$x$top[[1]]), "cm", TRUE), "cm")
+ axis_height_b <- unit(grid::convertHeight(
+ grid::grobHeight(axes$x$bottom[[1]]), "cm", TRUE), "cm")
+ for (i in rev(panel_pos_v)) {
+ panel_table <- gtable::gtable_add_rows(panel_table, axis_height_b, i)
+ panel_table <- gtable::gtable_add_grob(panel_table,
+ rep(axes$x$bottom, length(panel_pos_h)), t = i + 1, l = panel_pos_h,
+ clip = "off")
+ panel_table <- gtable::gtable_add_rows(panel_table, axis_height_t, i - 1)
+ panel_table <- gtable::gtable_add_grob(panel_table,
+ rep(axes$x$top, length(panel_pos_h)), t = i, l = panel_pos_h,
+ clip = "off")
+ }
+
+ # Add strips
+ strips <- render_strips(
+ x = data.frame(name = c("Original", paste0("Transformed (", params$trans$name, ")"))),
+ labeller = label_value, theme = theme)
+
+ panel_pos_h <- panel_cols(panel_table)$l
+ panel_pos_v <- panel_rows(panel_table)$t
+ strip_height <- unit(grid::convertHeight(
+ grid::grobHeight(strips$x$top[[1]]), "cm", TRUE), "cm")
+ for (i in rev(seq_along(panel_pos_v))) {
+ panel_table <- gtable::gtable_add_rows(panel_table, strip_height, panel_pos_v[i] - 1)
+ if (params$horizontal) {
+ panel_table <- gtable::gtable_add_grob(panel_table, strips$x$top,
+ t = panel_pos_v[i], l = panel_pos_h, clip = "off")
+ } else {
+ panel_table <- gtable::gtable_add_grob(panel_table, strips$x$top[i],
+ t = panel_pos_v[i], l = panel_pos_h, clip = "off")
+ }
+ }
+
+
+ panel_table
+ }
+)
+```
+
+As is very apparent, the `draw_panel` method can become very unwieldy once it begins to take multiple possibilities into account. The fact that we want to support both horizontal and vertical layout leads to a lot of if/else blocks in the above code. In general this is the big challenge when writing facet extensions so be prepared to be very meticulous when writing these methods.
+
+Enough talk - lets see if our new and powerful facetting extension works:
+
+```{r}
+ggplot(mtcars, aes(x = hp, y = mpg)) + geom_point() + facet_trans('sqrt')
+```
+
+## Extending existing facet function
+
+As the rendering part of a facet class is often the difficult development step, it is possible to piggyback on the existing facetting classes to achieve a range of new facettings. Below we will subclass `facet_wrap()` to make a `facet_bootstrap()` class that splits the input data into a number of panels at random.
+
+```{r}
+facet_bootstrap <- function(n = 9, prop = 0.2, nrow = NULL, ncol = NULL,
+ scales = "fixed", shrink = TRUE, strip.position = "top") {
+
+ facet <- facet_wrap(~.bootstrap, nrow = nrow, ncol = ncol, scales = scales,
+ shrink = shrink, strip.position = strip.position)
+ facet$params$n <- n
+ facet$params$prop <- prop
+ ggproto(NULL, FacetBootstrap,
+ shrink = shrink,
+ params = facet$params
+ )
+}
+
+FacetBootstrap <- ggproto("FacetBootstrap", FacetWrap,
+ compute_layout = function(data, params) {
+ id <- seq_len(params$n)
+
+ dims <- wrap_dims(params$n, params$nrow, params$ncol)
+ layout <- data.frame(PANEL = factor(id))
+
+ if (params$as.table) {
+ layout$ROW <- as.integer((id - 1L) %/% dims[2] + 1L)
+ } else {
+ layout$ROW <- as.integer(dims[1] - (id - 1L) %/% dims[2])
+ }
+ layout$COL <- as.integer((id - 1L) %% dims[2] + 1L)
+
+ layout <- layout[order(layout$PANEL), , drop = FALSE]
+ rownames(layout) <- NULL
+
+ # Add scale identification
+ layout$SCALE_X <- if (params$free$x) id else 1L
+ layout$SCALE_Y <- if (params$free$y) id else 1L
+
+ cbind(layout, .bootstrap = id)
+ },
+ map_data = function(data, layout, params) {
+ if (is.null(data) || nrow(data) == 0) {
+ return(cbind(data, PANEL = integer(0)))
+ }
+ n_samples <- round(nrow(data) * params$prop)
+ new_data <- lapply(seq_len(params$n), function(i) {
+ cbind(data[sample(nrow(data), n_samples), , drop = FALSE], PANEL = i)
+ })
+ do.call(rbind, new_data)
+ }
+)
+
+ggplot(diamonds, aes(carat, price)) +
+ geom_point(alpha = 0.1) +
+ facet_bootstrap(n = 9, prop = 0.05)
+```
+
+What we are doing above is to intercept the `compute_layout` and `map_data` methods and instead of dividing the data by a variable we randomly assigns rows to a panel based on the sampling parameters (`n` determines the number of panels, `prop` determines the proportion of data in each panel). It is important here that the layout returned by `compute_layout` is a valid layout for `FacetWrap` as we are counting on the `draw_panel` method from `FacetWrap` to do all the work for us. Thus if [...]
+
+### Exercises
+
+1. Rewrite FacetTrans to take a vector of transformations and create an additional panel for each transformation.
+2. Based on the FacetWrap implementation rewrite FacetTrans to take the strip.placement theme setting into account.
+3. Think about which caveats there are in FacetBootstrap specifically related to adding multiple layers with the same data.
+
diff --git a/vignettes/ggplot2-specs.Rmd b/vignettes/ggplot2-specs.Rmd
index 4a78e6b..03f121f 100644
--- a/vignettes/ggplot2-specs.Rmd
+++ b/vignettes/ggplot2-specs.Rmd
@@ -1,7 +1,5 @@
---
title: "Aesthetic specifications"
-author: "Hadley Wickham"
-date: "`r Sys.Date()`"
output: rmarkdown::html_vignette
vignette: >
%\VignetteIndexEntry{Aesthetic specifications}
@@ -67,7 +65,7 @@ Line types can be specified with:
three off followed by one on and finally three off.
The five standard dash-dot line types described above correspond to 44, 13,
- 134, 73, and 2262.
+ 1343, 73, and 2262.
The `size` of a line is its width in mm.
diff --git a/vignettes/releases/ggplot2-1.0.0.Rmd b/vignettes/releases/ggplot2-1.0.0.Rmd
new file mode 100644
index 0000000..a316434
--- /dev/null
+++ b/vignettes/releases/ggplot2-1.0.0.Rmd
@@ -0,0 +1,15 @@
+---
+title: "ggplot2 updates"
+---
+
+## ggplot2 1.0.0
+
+As you might have noticed, ggplot2 recently [turned 1.0.0](http://cran.r-project.org/web/packages/ggplot2/index.html). This release incorporated a handful of [new features and bug fixes](https://github.com/hadley/ggplot2/releases/tag/v1.0.0), but most importantly reflects that ggplot2 is now a mature plotting system and it will not change significantly in the future.
+
+This does not mean ggplot2 is dead! The ggplot2 community is [rich](https://groups.google.com/forum/#!forum/ggplot2) and [vibrant](http://stackoverflow.com/tags/ggplot2) and the number of packages that build on top of ggplot2 continues to grow. We are committed to maintaining ggplot2 so that you can continue to rely on it for years to come.
+
+## The ggplot2 book
+
+Since ggplot2 is now stable, and the [ggplot2 book](http://ggplot2.org/book/) is over five years old and rather out of date, I'm also happy to announce that I'm working on a second edition. I'll be ably assisted in this endeavour by [Carson Sievert](http://cpsievert.github.io), who's so far done a great job of converting the source to Rmd and updating many of the examples to work with ggplot2 1.0.0. In the coming months we'll be rewriting the data chapter to reflect modern best practices [...]
+
+We'd love your help! The source code for the book is available on [github](https://github.com/hadley/ggplot2-book). If you've spotted any mistakes in the first edition that you'd like to correct, we'd really appreciate a [pull request](https://github.com/hadley/ggplot2-book/pulls). If there's a particular section of the book that you think needs an update (or is just plain missing), please let us know by filing an [issue](https://github.com/hadley/ggplot2-book/issues). Unfortunately we c [...]
diff --git a/vignettes/releases/ggplot2-2.0.0.Rmd b/vignettes/releases/ggplot2-2.0.0.Rmd
new file mode 100644
index 0000000..c7e32bc
--- /dev/null
+++ b/vignettes/releases/ggplot2-2.0.0.Rmd
@@ -0,0 +1,327 @@
+---
+title: "ggplot2 2.0.0"
+---
+
+```{r, include = FALSE}
+knitr::opts_chunk$set(
+ comment = "#>",
+ collapse = TRUE,
+ fig.show = "hold",
+ out.width = "50%",
+ fig.width = 5,
+ fig.asp = 2/3,
+ fig.retina = NULL
+)
+library(ggplot2)
+library(dplyr)
+```
+
+I'm very pleased to announce the release of ggplot2 2.0.0. I know I promised [that there wouldn't be any more updates](http://blog.rstudio.org/2015/01/09/ggplot2-updates/), but while working on the 2nd edition of the ggplot2 book, I just couldn't stop myself from fixing some long standing problems.
+
+On the scale of ggplot2 releases, this one is huge with over one hundred fixes and improvements. This might break some of your existing code (although I've tried to minimise breakage as much as possible), but I hope the new features make up for any short term hassle. This blog post documents the most important changes:
+
+* ggplot2 now has an official extension mechanism.
+
+* There are a handful of new geoms, and updates to existing geoms.
+
+* The default appearance has been thoroughly tweaked so most plots should
+ look better.
+
+* Facets have a much richer set of labelling options.
+
+* The documentation has been overhauled to be more helpful, and require
+ less integration across multiple pages.
+
+* A number of older and less used features have been deprecated.
+
+These are described in more detail below. See the [release notes](https://github.com/hadley/ggplot2/releases/tag/v2.0.0) for a complete list of all changes.
+
+## Extensibility
+
+Perhaps the bigggest news in this release is that ggplot2 now has an official extension mechanism. This means that others can now easily create their on stats, geoms and positions, and provide them in other packages. This should allow the ggplot2 community to flourish, even as less development work happens in ggplot2 itself. See [`vignette("extending-ggplot2")`](https://cran.r-project.org/web/packages/ggplot2/vignettes/extending-ggplot2.html) for details.
+
+Coupled with this change, ggplot2 no longer uses proto or reference classes. Instead, we now use ggproto, a new OO system designed specifically for ggplot2. Unlike proto and RC, ggproto supports clean cross-package inheritance, which is necessary for extensibility. Creating a new OO system isn't usually the right solution, but I'm pretty sure it was necessary here. Read more about it in the vignette.
+
+## New and updated geoms
+
+* ggplot no longer throws an error if you your plot has no layers. Instead it
+ automatically adds `geom_blank()`:
+
+ ```{r}
+ ggplot(mpg, aes(cyl, hwy))
+ ```
+
+* `geom_count()` (a new alias for the old `stat_sum()`) counts the number of
+ points at unique locations on a scatterplot, and maps the size of the point
+ to the count:
+
+ ```{r}
+ ggplot(mpg, aes(cty, hwy)) +
+ geom_point()
+ ggplot(mpg, aes(cty, hwy)) +
+ geom_count()
+ ```
+
+* `geom_curve()` draws curved lines in the same way that `geom_segment()`
+ draws straight lines:
+
+ ```{r}
+ df <- expand.grid(x = 1:2, y = 1:2)
+ ggplot(df, aes(x, y, xend = x + 0.5, yend = y + 0.5)) +
+ geom_curve(aes(colour = "curve")) +
+ geom_segment(aes(colour = "segment"))
+ ```
+
+* `geom_bar()` now behaves differently from `geom_histogram()`. Instead of
+ binning the data, it counts the number of unique observations at each
+ location:
+
+ ```{r}
+ ggplot(mpg, aes(cyl)) +
+ geom_bar()
+
+ ggplot(mpg, aes(cyl)) +
+ geom_histogram(binwidth = 1)
+ ```
+
+ If you got into the (bad) habit of using `geom_histogram()` to create bar
+ charts, or `geom_bar()` to create histograms, you'll need to switch.
+
+* Layers are now much stricter about their arguments - you will get an error
+ if you've supplied an argument that isn't an aesthetic or a parameter.
+ This breaks the handful of geoms/stats that used `...` to pass
+ additional arguments on to the underlying computation. Now
+ `geom_smooth()`/`stat_smooth()` and `geom_quantile()`/`stat_quantile()`
+ use `method.args` instead; and `stat_summary()`, `stat_summary_hex()`,
+ and `stat_summary2d()` use `fun.args`. This is likely to cause some
+ short-term pain but in the long-term it will make it much easier to spot
+ spelling mistakes and other errors.
+
+* `geom_text()` has been overhauled to make labelling your data a little
+ easier. You can use `nudge_x` and `nudge_y` arguments to offset labels from
+ their corresponding points. `check_overlap = TRUE` provides a simple way
+ to avoid overplotting of labels: labels that would otherwise overlap are
+ omitted.
+
+ ```{r}
+ ggplot(mtcars, aes(wt, mpg, label = rownames(mtcars))) +
+ geom_point() +
+ geom_text(nudge_y = 0.5, check_overlap = TRUE)
+ ```
+
+ (Labelling points well is still a huge pain, but at least these new
+ features make life a lit better.)
+
+* `geom_label()` works like `geom_text()` but draws a rounded rectangle
+ underneath each label:
+
+ ```{r, fig.asp = 1}
+ grid <- expand.grid(
+ x = seq(-pi, pi, length = 50),
+ y = seq(-pi, pi, length = 50)
+ ) %>% mutate(r = x ^ 2 + y ^ 2, z = cos(r ^ 2) * exp(-r / 6))
+
+ ggplot(grid, aes(x, y)) +
+ geom_raster(aes(fill = z)) +
+ geom_label(data = data.frame(x = 0, y = 0), label = "Center") +
+ theme(legend.position = "none") +
+ coord_fixed()
+ ```
+
+* `aes_()` replaces `aes_q()`, and works like the SE functions in dplyr
+ and my other recent packages. It supports formulas, so the most concise
+ SE version of `aes(carat, price)` is now `aes_(~carat, ~price)`. You may
+ want to use this form in packages, as it will avoid spurious `R CMD check`
+ warnings about undefined global variables.
+
+ ```{r, eval = FALSE}
+ ggplot(mpg, aes_(~displ, ~cty)) +
+ geom_point()
+ # Same as
+ ggplot(mpg, aes(displ, cty)) +
+ geom_point()
+ ```
+
+## Appearance
+
+I've made a number of small tweaks to the default appearance:
+
+* The default `theme_grey()` background colour has been changed from "grey90"
+ to "grey92": this makes the background a little less visually prominent.
+
+* Labels and titles have been tweaked for readability. Axis labels are darker,
+ and legend titles get the same visual treatment as axis labels.
+
+* The default font size dropped from 12 to 11. You might be surprised that
+ I've made the default text size smaller as it was already hard for
+ many people to read. It turns out there was a bug in RStudio ([fixed in
+ 0.99.724](https://www.rstudio.com/products/rstudio/download/preview/)),
+ that shrunk the text of all grid based graphics. Once that was resolved
+ the defaults seemed too big to my eyes.
+
+* `scale_size()` now maps values to _area_, not radius. Use `scale_radius()`
+ if you want the old behaviour (not recommended, except perhaps for lines).
+ Continue to use `scale_size_area()` if you want 0 values to have 0 area.
+
+* Bar and rectangle legends no longer get a diagonal line. Instead, the
+ border has been tweaked to make it visible, and more closely match the
+ size of line drawn on the plot.
+
+ ```{r}
+ ggplot(mpg, aes(factor(cyl), fill = drv)) +
+ geom_bar(colour = "black", size = 1) +
+ coord_flip()
+ ```
+
+* `geom_point()` now uses shape 19 instead of 16. This looks much better on
+ the default Linux graphics device. (It's very slightly smaller than the old
+ point, but it shouldn't affect any graphics significantly). You can now
+ control the width of the outline on shapes 21-25 with the `stroke`
+ parameter.
+
+* The default legend will now allocate multiple rows (if vertical) or
+ columns (if horizontal) in order to make a legend that is more likely to
+ fit on the screen. You can override with the `nrow`/`ncol` arguments
+ to `guide_legend()`
+
+ ```{r}
+ p <- ggplot(mpg, aes(displ,hwy, colour = manufacturer)) +
+ geom_point() +
+ theme(legend.position = "bottom")
+ p
+ # Revert back to previous behaviour
+ p + guides(colour = guide_legend(nrow = 1))
+ ```
+
+* Two new themes were contributed by
+ [Jean-Olivier Irisson](http://github.com/jiho): `theme_void()` is completely
+ empty and `theme_dark()` has a dark background designed to make colours pop
+ out.
+
+## Facet labels
+
+Thanks to the work of [Lionel Henry](https://github.com/lionel-), facet labels have received three major improvements:
+
+1. You can switch the position of facet labels so they're next to the axes.
+
+1. `facet_wrap()` now supports custom labellers.
+
+1. You can create combined labels when facetting by multiple variables.
+
+### Switching the labels
+
+The new `switch` argument allows you to switch the labels to display near the axes:
+
+```{r}
+data <- transform(mtcars,
+ am = factor(am, levels = 0:1, c("Automatic", "Manual")),
+ gear = factor(gear, levels = 3:5, labels = c("Three", "Four", "Five"))
+)
+
+ggplot(data, aes(mpg, disp)) +
+ geom_point() +
+ facet_grid(am ~ gear, switch = "both")
+```
+
+This is especially useful when the labels directly characterise the axes. In that situation, switching the labels can make the plot clearer and more readable. You may also want to use a neutral label background by setting `strip.background` to `element_blank()`:
+
+```{r}
+data <- mtcars %>%
+ mutate(
+ Logarithmic = log(mpg),
+ Inverse = 1 / mpg,
+ Cubic = mpg ^ 3,
+ Original = mpg
+) %>% tidyr::gather(transformation, mpg2, Logarithmic:Original)
+
+ggplot(data, aes(mpg2, disp)) +
+ geom_point() +
+ facet_wrap(~transformation, scales = "free", switch = "x") +
+ theme(strip.background = element_blank())
+```
+
+### Wrap labeller
+
+A longstanding issue in ggplot was that `facet_wrap()` did not support
+custom labellers. Labellers are small functions that make it easy to
+customise the labels. You can now supply labellers to both wrap and
+grid facets:
+
+```{r}
+ggplot(data, aes(mpg2, disp)) +
+ geom_point() +
+ facet_wrap(~transformation, scales = "free", labeller = "label_both")
+```
+
+### Composite margins
+
+Labellers have now better support for composite margins when you facet over multiple variable with `+`. All labellers gain a `multi_line` argument to control whether labels should be displayed as a single line or over multiple lines, one for each factor.
+
+The labellers still work the same way except for `label_bquote()`. That labeller makes it easy to write mathematical expression involving the values of facetted factors. Historically, `label_bquote()` could only specify a single expression for all margins and factor. The factor value was referred to via the backquoted placeholder `.(x)`. Now that it supports expressions combining multiple factors, you must backquote the variable names themselves. In addition, you can provide different ex [...]
+
+```{r}
+my_labeller <- label_bquote(
+ rows = .(am) / alpha,
+ cols = .(vs) ^ .(cyl)
+)
+
+ggplot(mtcars, aes(wt, mpg)) +
+ geom_point() +
+ facet_grid(am ~ vs + cyl, labeller = my_labeller)
+```
+
+## Documentation
+
+I've given the documentation a thorough overhaul:
+
+* Tighly linked geoms and stats (e.g. `geom_boxplot()` and `stat_boxplot()`)
+ are now documented in the same file so you can see all the arguments in one
+ place. Similarly, variations on a theme (like `geom_path()`, `geom_line()`, and
+ `geom_step()`) are documented together.
+
+* I've tried to reduce the use of `...` so that you can see all the
+ documentation in one place rather than having to follow links around.
+ In some cases this has involved adding additional arguments to geoms
+ to make it more clear what you can do.
+
+* Thanks to [Bob Rudis](https://github.com/hrbrmstr), the use of `qplot()`
+ in examples has been grealy reduced. This is inline with the 2nd edition
+ of the ggplot2 book, which eliminates `qplot()` in favour of `ggplot()`.
+
+## Deprecated features
+
+* The `order` aesthetic is officially deprecated. It never really worked, and
+ was poorly documented.
+
+* The `stat` and `position` arguments to `qplot()` have been deprecated.
+ `qplot()` is designed for quick plots - if you need to specify position
+ or stat, use `ggplot()` instead.
+
+* The theme setting `axis.ticks.margin` has been deprecated: now use the margin
+ property of `axis.ticks`.
+
+* `stat_abline()`, `stat_hline()` and `stat_vline()` have been removed:
+ these were never suitable for use other than with their corresponding
+ geoms and were not documented.
+
+* `show_guide` has been renamed to `show.legend`: this more accurately
+ reflects what it does (controls appearance of layer in legend), and uses the
+ same convention as other ggplot2 arguments (i.e. a `.` between names).
+ (Yes, I know that's inconsistent with function names (which use `_`) but it's
+ too late to change now.)
+
+A number of geoms have been renamed to be more consistent. The previous names will continue to work for the forseeable future, but you should switch to the new names for new work.
+
+* `stat_binhex()` and `stat_bin2d()` have been renamed to `stat_bin_hex()`
+ and `stat_bin_2d()`. `stat_summary2d()` has been renamed to
+ `stat_summary_2d()`, `geom_density2d()`/`stat_density2d()` has been renamed
+ to `geom_density_2d()`/`stat_density_2d()`.
+
+* `stat_spoke()` is now `geom_spoke()` since I realised it's a
+ reparameterisation of `geom_segment()`.
+
+* `stat_bindot()` has been removed because it's so tightly coupled to
+ `geom_dotplot()`. If you happened to use `stat_bindot()`, just change to
+ `geom_dotplot()`.
+
+All defunct functions have been removed.
diff --git a/vignettes/releases/ggplot2-2.1.0.Rmd b/vignettes/releases/ggplot2-2.1.0.Rmd
new file mode 100644
index 0000000..d7c2290
--- /dev/null
+++ b/vignettes/releases/ggplot2-2.1.0.Rmd
@@ -0,0 +1,74 @@
+---
+title: "ggplot2 2.1.0"
+---
+
+```{r setup, include = FALSE}
+knitr::opts_chunk$set(
+ comment = "#>",
+ collapse = TRUE,
+ fig.show = "hold",
+ out.width = "75%",
+ fig.width = 4,
+ fig.asp = 2/3,
+ dpi = 96,
+ fig.retina = NULL
+)
+library(ggplot2)
+library(dplyr)
+```
+
+I'm very pleased to announce the release of ggplot2 2.1.0, scales 0.4.0, and gtable 0.2.0. These are set of relatively minor updates that fix a whole bunch of little problems that crept in during the [last big update](http://blog.rstudio.org/2015/12/21/ggplot2-2-0-0/). The most important changes are described below.
+
+1. When mapping an aesthetic to a constant the default guide title is the name
+ of the aesthetic (i.e. "colour"), not the value (i.e. "loess"). This is a
+ really handy technique for labelling individual layers:
+
+ ```{r}
+ ggplot(mpg, aes(displ, 1 / hwy)) +
+ geom_point() +
+ geom_smooth(method = lm, aes(colour = "lm"), se = FALSE) +
+ geom_smooth(aes(colour = "loess"), se = FALSE)
+ ```
+
+
+1. `stat_bin()` (which powers `geom_histogram()` and `geom_freqpoly()`), has
+ been overhauled to use the same algorithm as ggvis. This has considerably
+ better parameters and defaults thanks to the work of
+ [Randall Pruim](http://www.calvin.edu/~rpruim/). Changes include:
+
+ * Better arguments and a better algorithm for determining the origin.
+ You can now specify either `boundary` (i.e. the position of the left or
+ right side) or the `center` of a bin. `origin` has been deprecated in favour
+ of these arguments.
+
+ * `drop` is deprecated in favour of `pad`, which adds extra 0-count bins
+ at either end, as is needed for frequency polygons. `geom_histogram()`
+ defaults to `pad = FALSE` which considerably improves the default limits
+ for the histogram, especially when the bins are big.
+
+ * The default algorithm does a (somewhat) better job at picking nice widths
+ and origins across a wider range of input data.
+
+ You can see the impact of these changes on the following two histograms:
+
+ ```{r, out.width = "50%"}
+ ggplot(diamonds, aes(carat)) +
+ geom_histogram(binwidth = 1)
+ ggplot(diamonds, aes(carat)) +
+ geom_histogram(binwidth = 1, boundary = 0)
+ ```
+
+1. All layer functions (`geom_*()` + `stat_*()`) functions now have a
+ consistent argument order: `data`, `mapping`, then `geom`/`stat`/`position`,
+ then `...`, then layer specific arguments, then common layer arguments.
+ This might break some code if you were relying on partial name matching,
+ but in the long-term should make ggplot2 easier to use. In particular, you
+ can now set the `n` parameter in `geom_density2d()` without it partially
+ matching `na.rm`.
+
+1. For geoms with both `colour` and `fill`, `alpha` once again only affects
+ fill. `alpha` was changed to modify both `colour` and `fill` in 2.0.0, but
+ I've reverted it to the old behaviour because it was causing pain for
+ quite a few people.
+
+You can see a full list of changes in the [release notes](https://github.com/hadley/ggplot2/releases/tag/v2.1.0).
diff --git a/vignettes/releases/ggplot2-2.2.0.Rmd b/vignettes/releases/ggplot2-2.2.0.Rmd
new file mode 100644
index 0000000..953359c
--- /dev/null
+++ b/vignettes/releases/ggplot2-2.2.0.Rmd
@@ -0,0 +1,227 @@
+---
+title: ggplot2 2.2.0
+output:
+ html_document:
+ self_contained: false
+ highlight: NULL
+---
+
+```{r setup, include=FALSE}
+library(ggplot2)
+library(dplyr)
+library(forcats)
+
+knitr::opts_chunk$set(
+ fig.asp = 1 / 1.6,
+ out.width = "75%",
+ fig.width = 5,
+ collapse = TRUE,
+ comment = "#>",
+ dpi = 96,
+ fig.retina = NULL
+)
+```
+
+I'm very pleased to announce ggplot2 2.2.0. It includes four major new features:
+
+* Subtitles and captions.
+* A large rewrite of the facetting system.
+* Improved theme options.
+* Better stacking.
+
+As well as numerous bug fixes and minor improvements, as described in the [release notes](http://github.com/hadley/ggplot2/releases/tag/v2.2.0).
+
+The majority of this work was carried out by [Thomas Pederson](https://github.com/thomasp85), who I was lucky to have as my "ggplot2 intern" this summer. Make sure to check out his other visualisation packages: [ggraph](https://github.com/thomasp85/ggraph), [ggforce](https://github.com/thomasp85/ggforce), and [tweenr](https://github.com/thomasp85/tweenr).
+
+Install ggplot2 with:
+
+```{r, eval = FALSE}
+install.packages("ggplot2")
+```
+
+## Subtitles and captions
+
+Thanks to [Bob Rudis](https://rud.is), you can now add subtitles and captions to your plots:
+
+```{r subtitle}
+ggplot(mpg, aes(displ, hwy)) +
+ geom_point(aes(color = class)) +
+ geom_smooth(se = FALSE, method = "loess") +
+ labs(
+ title = "Fuel efficiency generally decreases with engine size",
+ subtitle = "Two seaters (sports cars) are an exception because of their light weight",
+ caption = "Data from fueleconomy.gov"
+ )
+```
+
+These are controlled by the theme settings `plot.subtitle` and `plot.caption`.
+
+The plot title is now aligned to the left by default. To return to the previous centered alignment, use `theme(plot.title = element_text(hjust = 0.5))`.
+
+## Facets
+
+The facet and layout implementation has been moved to ggproto and received a large rewrite and refactoring. This will allow others to create their own facetting systems, as descrbied in the `vignette("extending-ggplot2")`. Along with the rewrite a number of features and improvements has been added, most notably:
+
+* ou can now use functions in facetting formulas, thanks to
+ [Dan Ruderman](https://github.com/DanRuderman).
+
+ ```{r facet-1}
+ ggplot(diamonds, aes(carat, price)) +
+ geom_hex(bins = 20) +
+ facet_wrap(~cut_number(depth, 6))
+ ```
+
+* Axes are now drawn under the panels in `facet_wrap()` when the
+ rentangle is not completely filled.
+
+ ```{r facet-2}
+ ggplot(mpg, aes(displ, hwy)) +
+ geom_point() +
+ facet_wrap(~class)
+ ```
+
+* You can set the position of the axes with the `position` argument.
+
+ ```{r facet-3}
+ ggplot(mpg, aes(displ, hwy)) +
+ geom_point() +
+ scale_x_continuous(position = "top") +
+ scale_y_continuous(position = "right")
+ ```
+
+* You can display a secondary axis that is a one-to-one transformation of
+ the primary axis with `sec.axis`.
+
+ ```{r facet-4}
+ ggplot(mpg, aes(displ, hwy)) +
+ geom_point() +
+ scale_y_continuous(
+ "mpg (US)",
+ sec.axis = sec_axis(~ . * 1.20, name = "mpg (UK)")
+ )
+ ```
+
+* Strips can be placed on any side, and the placement with respect to axes
+ can be controlled with the `strip.placement` theme option.
+
+ ```{r facet-5}
+ ggplot(mpg, aes(displ, hwy)) +
+ geom_point() +
+ facet_wrap(~ drv, strip.position = "bottom") +
+ theme(
+ strip.placement = "outside",
+ strip.background = element_blank(),
+ strip.text = element_text(face = "bold")
+ ) +
+ xlab(NULL)
+ ```
+
+## Theming
+
+* Blank elements can now be overridden again so you get the expected
+ behavior when setting e.g. `axis.line.x`.
+
+* `element_line()` gets an `arrow` argument that lets you put arrows on axes.
+
+ ```{r theme-1}
+ arrow <- arrow(length = unit(0.4, "cm"), type = "closed")
+
+ ggplot(mpg, aes(displ, hwy)) +
+ geom_point() +
+ theme_minimal() +
+ theme(
+ axis.line = element_line(arrow = arrow)
+ )
+ ```
+
+* Control of legend styling has been improved. The whole legend area can be
+ aligned according to the plot area and a box can be drawn around all legends:
+
+ ```{r theme-2}
+ ggplot(mpg, aes(displ, hwy, shape = drv, colour = fl)) +
+ geom_point() +
+ theme(
+ legend.justification = "top",
+ legend.box = "horizontal",
+ legend.box.margin = margin(3, 3, 3, 3, "mm"),
+ legend.margin = margin(),
+ legend.box.background = element_rect(colour = "grey50")
+ )
+ ```
+
+* `panel.margin` and `legend.margin` have been renamed to `panel.spacing`
+ and `legend.spacing` respectively, as this better indicates their roles.
+ A new `legend.margin` actually controls the margin around each legend.
+
+* When computing the height of titles, ggplot2 now inclues the height of the
+ descenders (i.e. the bits `g` and `y` that hang underneath). This improves
+ the margins around titles, particularly the y axis label. I have also very
+ slightly increased the inner margins of axis titles, and removed the outer
+ margins.
+
+* The default themes has been tweaked by
+ [Jean-Olivier Irisson](http://www.obs-vlfr.fr/~irisson/) making them better
+ match `theme_grey()`.
+
+* Lastly, the `theme()` function now has named arguments so autocomplete
+ and documentation suggestions are vastly improved.
+
+## Stacking bars
+
+`position_stack()` and `position_fill()` now stack values in the reverse
+order of the grouping, which makes the default stack order match the legend.
+
+```{r stack-1}
+avg_price <- diamonds %>%
+ group_by(cut, color) %>%
+ summarise(price = mean(price)) %>%
+ ungroup() %>%
+ mutate(price_rel = price - mean(price))
+
+ggplot(avg_price) +
+ geom_col(aes(x = cut, y = price, fill = color))
+```
+
+(Note also the new `geom_col()` which is short-hand for
+`geom_bar(stat = "identity")`, contributed by Bob Rudis.)
+
+If you want to stack in the opposite order, try
+[`forcats::fct_rev()`](http://forcats.tidyverse.org/reference/fct_rev.html):
+
+```{r stack-2}
+ggplot(avg_price) +
+ geom_col(aes(x = cut, y = price, fill = fct_rev(color)))
+```
+
+Additionally, you can now stack negative values:
+
+```{r stack-3}
+ggplot(avg_price) +
+ geom_col(aes(x = cut, y = price_rel, fill = color))
+```
+
+The overall ordering cannot necessarily be matched in the presence of
+negative values, but the ordering on either side of the x-axis will match.
+
+Labels can also be stacked, but the default position is suboptimal:
+
+```{r stack-4}
+series <- data.frame(
+ time = c(rep(1, 4),rep(2, 4), rep(3, 4), rep(4, 4)),
+ type = rep(c('a', 'b', 'c', 'd'), 4),
+ value = rpois(16, 10)
+)
+
+ggplot(series, aes(time, value, group = type)) +
+ geom_area(aes(fill = type)) +
+ geom_text(aes(label = type), position = "stack")
+```
+
+You can improve the position with the `vjust` parameter. A `vjust` of 0.5 will center the labels inside the corresponding area:
+
+```{r stack-5}
+ggplot(series, aes(time, value, group = type)) +
+ geom_area(aes(fill = type)) +
+ geom_text(aes(label = type), position = position_stack(vjust = 0.5))
+```
+
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-med/r-cran-ggplot2.git
More information about the debian-med-commit
mailing list