├── _pkgdown.yml ├── docs ├── .nojekyll ├── reference │ ├── tag_facets-1.png │ ├── tag_facets-2.png │ ├── tag_facets-3.png │ ├── tag_facets-4.png │ ├── tag_facets-5.png │ ├── tag_facets-6.png │ ├── tag_facets-7.png │ ├── tag_facets-8.png │ ├── figures │ │ ├── README-example-1.png │ │ └── README-unnamed-chunk-2-1.png │ ├── index.html │ └── tag_facets.html ├── pkgdown.yml ├── link.svg ├── bootstrap-toc.css ├── docsearch.js ├── pkgdown.js ├── 404.html ├── authors.html ├── bootstrap-toc.js ├── pkgdown.css ├── index.html └── docsearch.css ├── .Rbuildignore ├── man ├── figures │ ├── README-example-1.png │ └── README-unnamed-chunk-2-1.png ├── get_layout.Rd └── tag_facets.Rd ├── NAMESPACE ├── tagger.Rproj ├── R ├── zzzz.R ├── utils.R └── tag-facet.R ├── DESCRIPTION ├── .gitignore ├── README.md └── README.Rmd /_pkgdown.yml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/.nojekyll: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^.*\.Rproj$ 2 | ^\.Rproj\.user$ 3 | ^README\.Rmd$ 4 | ^_pkgdown\.yml$ 5 | ^docs$ 6 | ^pkgdown$ 7 | -------------------------------------------------------------------------------- /docs/reference/tag_facets-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eliocamp/tagger/HEAD/docs/reference/tag_facets-1.png -------------------------------------------------------------------------------- /docs/reference/tag_facets-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eliocamp/tagger/HEAD/docs/reference/tag_facets-2.png -------------------------------------------------------------------------------- /docs/reference/tag_facets-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eliocamp/tagger/HEAD/docs/reference/tag_facets-3.png -------------------------------------------------------------------------------- /docs/reference/tag_facets-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eliocamp/tagger/HEAD/docs/reference/tag_facets-4.png -------------------------------------------------------------------------------- /docs/reference/tag_facets-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eliocamp/tagger/HEAD/docs/reference/tag_facets-5.png -------------------------------------------------------------------------------- /docs/reference/tag_facets-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eliocamp/tagger/HEAD/docs/reference/tag_facets-6.png -------------------------------------------------------------------------------- /docs/reference/tag_facets-7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eliocamp/tagger/HEAD/docs/reference/tag_facets-7.png -------------------------------------------------------------------------------- /docs/reference/tag_facets-8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eliocamp/tagger/HEAD/docs/reference/tag_facets-8.png -------------------------------------------------------------------------------- /man/figures/README-example-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eliocamp/tagger/HEAD/man/figures/README-example-1.png -------------------------------------------------------------------------------- /docs/pkgdown.yml: -------------------------------------------------------------------------------- 1 | pandoc: 2.7.3 2 | pkgdown: 1.5.1 3 | pkgdown_sha: ~ 4 | articles: [] 5 | last_built: 2020-07-18T02:31Z 6 | 7 | -------------------------------------------------------------------------------- /man/figures/README-unnamed-chunk-2-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eliocamp/tagger/HEAD/man/figures/README-unnamed-chunk-2-1.png -------------------------------------------------------------------------------- /docs/reference/figures/README-example-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eliocamp/tagger/HEAD/docs/reference/figures/README-example-1.png -------------------------------------------------------------------------------- /docs/reference/figures/README-unnamed-chunk-2-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eliocamp/tagger/HEAD/docs/reference/figures/README-unnamed-chunk-2-1.png -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | S3method(ggplot_add,tagger) 4 | S3method(ggplot_build,ggtagged) 5 | S3method(ggplot_gtable,ggplot_build_ggtagged) 6 | export(get_col) 7 | export(get_layout) 8 | export(get_row) 9 | export(get_tag) 10 | export(tag_facets) 11 | import(checkmate) 12 | importFrom(ggplot2,ggplot_add) 13 | importFrom(ggplot2,ggplot_build) 14 | importFrom(ggplot2,ggplot_gtable) 15 | -------------------------------------------------------------------------------- /tagger.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: Default 4 | SaveWorkspace: Default 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 3 10 | Encoding: UTF-8 11 | 12 | RnwWeave: Sweave 13 | LaTeX: pdfLaTeX 14 | 15 | AutoAppendNewline: Yes 16 | StripTrailingWhitespace: Yes 17 | 18 | BuildType: Package 19 | PackageUseDevtools: Yes 20 | PackageInstallArgs: --no-multiarch --with-keep.source 21 | PackageRoxygenize: rd,collate,namespace 22 | -------------------------------------------------------------------------------- /R/zzzz.R: -------------------------------------------------------------------------------- 1 | .onLoad <- function(libname, pkgname) { 2 | ggplot2::register_theme_elements(tagger.panel.tag.text = ggplot2::element_text(), 3 | tagger.panel.tag.background = ggplot2::element_rect(), 4 | element_tree = list(tagger.panel.tag.text = ggplot2::el_def("element_text", c("strip.text", "text")), 5 | tagger.panel.tag.background = ggplot2::el_def("element_rect", c("strip.background", "rect")))) 6 | } 7 | 8 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: tagger 2 | Title: Adds tags to 'ggpot2' facets 3 | Version: 0.0.0.9000 4 | Authors@R: 5 | person(given = "Elio", 6 | family = "Campitelli", 7 | role = c("cre", "aut"), 8 | email = "elio.campitelli@cima.fcen.uba.ar", 9 | comment = c(ORCID = "0000-0002-7742-9230")) 10 | Description: Adds tags to 'ggplot2' facets with a single function and with native 'ggplot2' syntax. 11 | License: GPL-3 12 | Encoding: UTF-8 13 | Language: en 14 | LazyData: true 15 | Roxygen: list(markdown = TRUE) 16 | RoxygenNote: 7.2.3 17 | Imports: 18 | grDevices, 19 | gridtext, 20 | grid, 21 | ggplot2, 22 | gtable, 23 | checkmate 24 | Suggests: 25 | memoise 26 | -------------------------------------------------------------------------------- /docs/link.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 12 | 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.toptal.com/developers/gitignore/api/r 3 | # Edit at https://www.toptal.com/developers/gitignore?templates=r 4 | 5 | ### R ### 6 | # History files 7 | .Rhistory 8 | .Rapp.history 9 | 10 | # Session Data files 11 | .RData 12 | 13 | # User-specific files 14 | .Ruserdata 15 | 16 | # Example code in package build process 17 | *-Ex.R 18 | 19 | # Output files from R CMD build 20 | /*.tar.gz 21 | 22 | # Output files from R CMD check 23 | /*.Rcheck/ 24 | 25 | # RStudio files 26 | .Rproj.user/ 27 | 28 | # produced vignettes 29 | vignettes/*.html 30 | vignettes/*.pdf 31 | 32 | # OAuth2 token, see https://github.com/hadley/httr/releases/tag/v0.3 33 | .httr-oauth 34 | 35 | # knitr and R markdown default cache directories 36 | *_cache/ 37 | /cache/ 38 | 39 | # Temporary files created by R markdown 40 | *.utf8.md 41 | *.knit.md 42 | 43 | # R Environment Variables 44 | .Renviron 45 | 46 | ### R.Bookdown Stack ### 47 | # R package: bookdown caching files 48 | /*_files/ 49 | 50 | # End of https://www.toptal.com/developers/gitignore/api/r 51 | .Rproj.user 52 | 53 | -------------------------------------------------------------------------------- /R/utils.R: -------------------------------------------------------------------------------- 1 | # # from ggplot2 2 | # plot_theme <- function(x, default = ggplot2::theme_get()) { 3 | # theme <- x$theme 4 | # 5 | # # apply theme defaults appropriately if needed 6 | # if (ggplot2:::is_theme_complete(theme)) { 7 | # # for complete themes, we fill in missing elements but don't do any element merging 8 | # # can't use `defaults()` because it strips attributes 9 | # missing <- setdiff(names(default), names(theme)) 10 | # theme[missing] <- default[missing] 11 | # } else { 12 | # # otherwise, we can just add the theme to the default theme 13 | # theme <- default + theme 14 | # } 15 | # 16 | # # if we're still missing elements relative to fallback default, fill in those 17 | # missing <- setdiff(names(ggplot2:::ggplot_global$theme_default), names(theme)) 18 | # theme[missing] <- ggplot2:::ggplot_global$theme_default[missing] 19 | # 20 | # # Check that all elements have the correct class (element_text, unit, etc) 21 | # if (ggplot2:::is_theme_validate(theme)) { 22 | # mapply( 23 | # ggplot2:::validate_element, theme, names(theme), 24 | # MoreArgs = list(element_tree = ggplot2::get_element_tree()) 25 | # ) 26 | # } 27 | # 28 | # theme 29 | # } 30 | # 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # tagger 5 | 6 | 7 | 8 | 9 | 10 | The goal of tagger is to easily add tags to facetted plots in order to 11 | identify panles. 12 | 13 | ## Installation 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | You can install the development version from 24 | [GitHub](https://github.com/) with: 25 | 26 | ``` r 27 | # install.packages("devtools") 28 | devtools::install_github("eliocamp/tagger") 29 | ``` 30 | 31 | ## Example 32 | 33 | In the default usage, you can simply add `tag_facets()` to add a tag to 34 | each panel of a ggplo2 plot. 35 | 36 | ``` r 37 | library(tagger) 38 | library(ggplot2) 39 | 40 | ggplot(mtcars, aes(hp, mpg)) + 41 | geom_point() + 42 | facet_grid(cyl ~ vs) + 43 | tag_facets() 44 | ``` 45 | 46 | ![](man/figures/README-example-1.png) If you want to tag rows 47 | and columns instead of each panel indivdually, you can set it with the 48 | `tag` argument. 49 | 50 | ``` r 51 | ggplot(mtcars, aes(hp, mpg)) + 52 | geom_point() + 53 | facet_grid(cyl ~ vs) + 54 | tag_facets(tag = "rc") 55 | ``` 56 | 57 | ![](man/figures/README-unnamed-chunk-2-1.png) 58 | -------------------------------------------------------------------------------- /README.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | output: github_document 3 | --- 4 | 5 | 6 | 7 | ```{r, include = FALSE} 8 | knitr::opts_chunk$set( 9 | collapse = TRUE, 10 | comment = "#>", 11 | fig.path = "man/figures/README-" 12 | ) 13 | ``` 14 | 15 | # tagger 16 | 17 | 18 | 19 | 20 | The goal of tagger is to easily add tags to facetted plots in order to identify panles. 21 | 22 | ## Installation 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | You can install the development version from [GitHub](https://github.com/) with: 31 | 32 | ``` r 33 | # install.packages("devtools") 34 | devtools::install_github("eliocamp/tagger") 35 | ``` 36 | ## Example 37 | 38 | In the default usage, you can simply add `tag_facets()` to add a tag to each panel of a ggplo2 plot. 39 | 40 | ```{r example} 41 | library(tagger) 42 | library(ggplot2) 43 | 44 | ggplot(mtcars, aes(hp, mpg)) + 45 | geom_point() + 46 | facet_grid(cyl ~ vs) + 47 | tag_facets() 48 | ``` 49 | If you want to tag rows and columns instead of each panel indivdually, you can set it with the `tag` argument. 50 | 51 | ```{r} 52 | ggplot(mtcars, aes(hp, mpg)) + 53 | geom_point() + 54 | facet_grid(cyl ~ vs) + 55 | tag_facets(tag = "rc") 56 | ``` 57 | -------------------------------------------------------------------------------- /man/get_layout.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/tag-facet.R 3 | \name{get_layout} 4 | \alias{get_layout} 5 | \alias{get_tag} 6 | \alias{get_row} 7 | \alias{get_col} 8 | \title{Get labels asigned to each panel of a tagged plot} 9 | \usage{ 10 | get_layout(plot = ggplot2::last_plot()) 11 | 12 | get_tag(filter = TRUE, plot = ggplot2::last_plot(), n = 1) 13 | 14 | get_row(filter = TRUE, plot = ggplot2::last_plot(), n = 1) 15 | 16 | get_col(filter = TRUE, plot = ggplot2::last_plot(), n = 1) 17 | } 18 | \arguments{ 19 | \item{plot}{a plot object} 20 | 21 | \item{filter}{an expression that, evaluated within the data used 22 | to generate the facets of the plot, evaluates to a logical vector 23 | or a sequence of rows.} 24 | 25 | \item{n}{number of expected panels.} 26 | } 27 | \value{ 28 | \code{get_tags()}, \code{get_row()} and \code{get_col()} return a character vector 29 | of length \code{n} with the tags, unique rows or unique columns that meet 30 | the \code{filter} condition. 31 | 32 | \code{get_layout()} returns the full data.frame describing the panel layout. 33 | } 34 | \description{ 35 | Get labels asigned to each panel of a tagged plot 36 | } 37 | \examples{ 38 | library(ggplot2) 39 | g <- ggplot(mtcars, aes(hp, mpg)) + 40 | geom_point() + 41 | facet_grid(cyl ~ vs) + 42 | tag_facets("rc") 43 | 44 | # By default these functions retrieve tags from 45 | # the result of ggplo2::last_plot(). 46 | 47 | # Get all tags 48 | get_layout() 49 | 50 | # Get one tag 51 | get_tag(cyl == 4 & vs == 0) 52 | 53 | # Get more than one tag 54 | get_tag(cyl == 4, n = 2) 55 | 56 | 57 | get_row(cyl == 4) 58 | get_col(vs == 0) 59 | 60 | # Use it with inline markdown to refer always to the correct panel: 61 | # "As you can see in panel `r get_tag(cyl == 4 & vs == 0)` ..." 62 | 63 | } 64 | -------------------------------------------------------------------------------- /docs/bootstrap-toc.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Table of Contents v0.4.1 (http://afeld.github.io/bootstrap-toc/) 3 | * Copyright 2015 Aidan Feldman 4 | * Licensed under MIT (https://github.com/afeld/bootstrap-toc/blob/gh-pages/LICENSE.md) */ 5 | 6 | /* modified from https://github.com/twbs/bootstrap/blob/94b4076dd2efba9af71f0b18d4ee4b163aa9e0dd/docs/assets/css/src/docs.css#L548-L601 */ 7 | 8 | /* All levels of nav */ 9 | nav[data-toggle='toc'] .nav > li > a { 10 | display: block; 11 | padding: 4px 20px; 12 | font-size: 13px; 13 | font-weight: 500; 14 | color: #767676; 15 | } 16 | nav[data-toggle='toc'] .nav > li > a:hover, 17 | nav[data-toggle='toc'] .nav > li > a:focus { 18 | padding-left: 19px; 19 | color: #563d7c; 20 | text-decoration: none; 21 | background-color: transparent; 22 | border-left: 1px solid #563d7c; 23 | } 24 | nav[data-toggle='toc'] .nav > .active > a, 25 | nav[data-toggle='toc'] .nav > .active:hover > a, 26 | nav[data-toggle='toc'] .nav > .active:focus > a { 27 | padding-left: 18px; 28 | font-weight: bold; 29 | color: #563d7c; 30 | background-color: transparent; 31 | border-left: 2px solid #563d7c; 32 | } 33 | 34 | /* Nav: second level (shown on .active) */ 35 | nav[data-toggle='toc'] .nav .nav { 36 | display: none; /* Hide by default, but at >768px, show it */ 37 | padding-bottom: 10px; 38 | } 39 | nav[data-toggle='toc'] .nav .nav > li > a { 40 | padding-top: 1px; 41 | padding-bottom: 1px; 42 | padding-left: 30px; 43 | font-size: 12px; 44 | font-weight: normal; 45 | } 46 | nav[data-toggle='toc'] .nav .nav > li > a:hover, 47 | nav[data-toggle='toc'] .nav .nav > li > a:focus { 48 | padding-left: 29px; 49 | } 50 | nav[data-toggle='toc'] .nav .nav > .active > a, 51 | nav[data-toggle='toc'] .nav .nav > .active:hover > a, 52 | nav[data-toggle='toc'] .nav .nav > .active:focus > a { 53 | padding-left: 28px; 54 | font-weight: 500; 55 | } 56 | 57 | /* from https://github.com/twbs/bootstrap/blob/e38f066d8c203c3e032da0ff23cd2d6098ee2dd6/docs/assets/css/src/docs.css#L631-L634 */ 58 | nav[data-toggle='toc'] .nav > .active > ul { 59 | display: block; 60 | } 61 | -------------------------------------------------------------------------------- /docs/docsearch.js: -------------------------------------------------------------------------------- 1 | $(function() { 2 | 3 | // register a handler to move the focus to the search bar 4 | // upon pressing shift + "/" (i.e. "?") 5 | $(document).on('keydown', function(e) { 6 | if (e.shiftKey && e.keyCode == 191) { 7 | e.preventDefault(); 8 | $("#search-input").focus(); 9 | } 10 | }); 11 | 12 | $(document).ready(function() { 13 | // do keyword highlighting 14 | /* modified from https://jsfiddle.net/julmot/bL6bb5oo/ */ 15 | var mark = function() { 16 | 17 | var referrer = document.URL ; 18 | var paramKey = "q" ; 19 | 20 | if (referrer.indexOf("?") !== -1) { 21 | var qs = referrer.substr(referrer.indexOf('?') + 1); 22 | var qs_noanchor = qs.split('#')[0]; 23 | var qsa = qs_noanchor.split('&'); 24 | var keyword = ""; 25 | 26 | for (var i = 0; i < qsa.length; i++) { 27 | var currentParam = qsa[i].split('='); 28 | 29 | if (currentParam.length !== 2) { 30 | continue; 31 | } 32 | 33 | if (currentParam[0] == paramKey) { 34 | keyword = decodeURIComponent(currentParam[1].replace(/\+/g, "%20")); 35 | } 36 | } 37 | 38 | if (keyword !== "") { 39 | $(".contents").unmark({ 40 | done: function() { 41 | $(".contents").mark(keyword); 42 | } 43 | }); 44 | } 45 | } 46 | }; 47 | 48 | mark(); 49 | }); 50 | }); 51 | 52 | /* Search term highlighting ------------------------------*/ 53 | 54 | function matchedWords(hit) { 55 | var words = []; 56 | 57 | var hierarchy = hit._highlightResult.hierarchy; 58 | // loop to fetch from lvl0, lvl1, etc. 59 | for (var idx in hierarchy) { 60 | words = words.concat(hierarchy[idx].matchedWords); 61 | } 62 | 63 | var content = hit._highlightResult.content; 64 | if (content) { 65 | words = words.concat(content.matchedWords); 66 | } 67 | 68 | // return unique words 69 | var words_uniq = [...new Set(words)]; 70 | return words_uniq; 71 | } 72 | 73 | function updateHitURL(hit) { 74 | 75 | var words = matchedWords(hit); 76 | var url = ""; 77 | 78 | if (hit.anchor) { 79 | url = hit.url_without_anchor + '?q=' + escape(words.join(" ")) + '#' + hit.anchor; 80 | } else { 81 | url = hit.url + '?q=' + escape(words.join(" ")); 82 | } 83 | 84 | return url; 85 | } 86 | -------------------------------------------------------------------------------- /man/tag_facets.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/tag-facet.R 3 | \name{tag_facets} 4 | \alias{tag_facets} 5 | \title{Adds tags to facets} 6 | \usage{ 7 | tag_facets( 8 | tag = c("panel", "rc", "cr"), 9 | position = "tl", 10 | tag_levels = c("a", "1"), 11 | tag_pool = NULL, 12 | tag_prefix = "", 13 | tag_suffix = ")", 14 | tag_sep = "." 15 | ) 16 | } 17 | \arguments{ 18 | \item{tag}{Character indicating the element to tag. Possible values are 19 | \itemize{ 20 | \item "panel": each single panels is individually tag. 21 | \item "rc": facets are tagged by row and then column. 22 | \item "cr": factes are tagged by columns and then row. 23 | }} 24 | 25 | \item{position}{Character indicating the position of the tag. Options are "tl" 26 | (top-left), "tr" (top-right), "br" (bottom-right) and "bl" (bottom-left). 27 | Alternatively, for finer control, it can be a list of elements "x", "u", 28 | "hjust" and "vjust" that define the position of the tag within each panel.} 29 | 30 | \item{tag_levels}{A character vector defining the enumeration format to use 31 | at each level. Possible values are \code{'a'} for lowercase letters, \code{'A'} for 32 | uppercase letters, \code{'1'} for numbers, \code{'i'} for lowercase Roman numerals, and 33 | \code{'I'} for uppercase Roman numerals. It can also be a list containing 34 | character vectors defining arbitrary tag sequences. If any element in the 35 | list is a scalar and one of \code{'a'}, \code{'A'}, \code{'1'}, \verb{'i}, or \code{'I'}, this level 36 | will be expanded to the expected sequence.} 37 | 38 | \item{tag_pool}{An optional character vector of user-defined "pool" of tags. 39 | If not \code{NULL}, it will be used instead of \code{tag_levels}. Should be at least 40 | as long as the total number of panels.} 41 | 42 | \item{tag_prefix, tag_suffix}{Strings that should appear before or after the 43 | tag.} 44 | 45 | \item{tag_sep}{A separator between different tag levels} 46 | } 47 | \description{ 48 | Adds tags to facets 49 | } 50 | \details{ 51 | Tags inherit aesthetic properties (size, font, colour, etc...) from strip.text and 52 | strip.background defined by \code{\link[ggplot2:theme]{ggplot2::theme()}}. For fine-grained detail, these can be 53 | overriden by setting tagger.panel.tag.text and tagger.panel.tag.background. 54 | } 55 | \examples{ 56 | 57 | library(ggplot2) 58 | # Base plot 59 | g <- ggplot(mtcars, aes(hp, mpg)) + 60 | geom_point() + 61 | facet_grid(cyl ~ vs) 62 | 63 | g + tag_facets() 64 | g + tag_facets("rc") 65 | g + tag_facets("cr") 66 | g + tag_facets(position = "br") 67 | g + tag_facets("rc", tag_levels = c("A", "I")) 68 | 69 | # You can get finer control over position. 70 | g + tag_facets(position = list(x = 0.5, y = 0.5)) 71 | 72 | g + tag_facets(tag_pool = c("one", "two", "three", "four", "five", "six"), tag_suffix = "") 73 | # Thanks to theme inheritance, tags should look aceptable 74 | # out of the box in any theme. 75 | g + tag_facets() + 76 | theme_dark() 77 | 78 | # But you can control their appearance and create your own atrocities 79 | g + tag_facets() + 80 | theme_dark() + 81 | theme(tagger.panel.tag.text = element_text(color = "red", size = 16), 82 | tagger.panel.tag.background = element_rect(fill = "purple")) 83 | 84 | } 85 | -------------------------------------------------------------------------------- /docs/pkgdown.js: -------------------------------------------------------------------------------- 1 | /* http://gregfranko.com/blog/jquery-best-practices/ */ 2 | (function($) { 3 | $(function() { 4 | 5 | $('.navbar-fixed-top').headroom(); 6 | 7 | $('body').css('padding-top', $('.navbar').height() + 10); 8 | $(window).resize(function(){ 9 | $('body').css('padding-top', $('.navbar').height() + 10); 10 | }); 11 | 12 | $('[data-toggle="tooltip"]').tooltip(); 13 | 14 | var cur_path = paths(location.pathname); 15 | var links = $("#navbar ul li a"); 16 | var max_length = -1; 17 | var pos = -1; 18 | for (var i = 0; i < links.length; i++) { 19 | if (links[i].getAttribute("href") === "#") 20 | continue; 21 | // Ignore external links 22 | if (links[i].host !== location.host) 23 | continue; 24 | 25 | var nav_path = paths(links[i].pathname); 26 | 27 | var length = prefix_length(nav_path, cur_path); 28 | if (length > max_length) { 29 | max_length = length; 30 | pos = i; 31 | } 32 | } 33 | 34 | // Add class to parent
  • , and enclosing
  • if in dropdown 35 | if (pos >= 0) { 36 | var menu_anchor = $(links[pos]); 37 | menu_anchor.parent().addClass("active"); 38 | menu_anchor.closest("li.dropdown").addClass("active"); 39 | } 40 | }); 41 | 42 | function paths(pathname) { 43 | var pieces = pathname.split("/"); 44 | pieces.shift(); // always starts with / 45 | 46 | var end = pieces[pieces.length - 1]; 47 | if (end === "index.html" || end === "") 48 | pieces.pop(); 49 | return(pieces); 50 | } 51 | 52 | // Returns -1 if not found 53 | function prefix_length(needle, haystack) { 54 | if (needle.length > haystack.length) 55 | return(-1); 56 | 57 | // Special case for length-0 haystack, since for loop won't run 58 | if (haystack.length === 0) { 59 | return(needle.length === 0 ? 0 : -1); 60 | } 61 | 62 | for (var i = 0; i < haystack.length; i++) { 63 | if (needle[i] != haystack[i]) 64 | return(i); 65 | } 66 | 67 | return(haystack.length); 68 | } 69 | 70 | /* Clipboard --------------------------*/ 71 | 72 | function changeTooltipMessage(element, msg) { 73 | var tooltipOriginalTitle=element.getAttribute('data-original-title'); 74 | element.setAttribute('data-original-title', msg); 75 | $(element).tooltip('show'); 76 | element.setAttribute('data-original-title', tooltipOriginalTitle); 77 | } 78 | 79 | if(ClipboardJS.isSupported()) { 80 | $(document).ready(function() { 81 | var copyButton = ""; 82 | 83 | $(".examples, div.sourceCode").addClass("hasCopyButton"); 84 | 85 | // Insert copy buttons: 86 | $(copyButton).prependTo(".hasCopyButton"); 87 | 88 | // Initialize tooltips: 89 | $('.btn-copy-ex').tooltip({container: 'body'}); 90 | 91 | // Initialize clipboard: 92 | var clipboardBtnCopies = new ClipboardJS('[data-clipboard-copy]', { 93 | text: function(trigger) { 94 | return trigger.parentNode.textContent; 95 | } 96 | }); 97 | 98 | clipboardBtnCopies.on('success', function(e) { 99 | changeTooltipMessage(e.trigger, 'Copied!'); 100 | e.clearSelection(); 101 | }); 102 | 103 | clipboardBtnCopies.on('error', function() { 104 | changeTooltipMessage(e.trigger,'Press Ctrl+C or Command+C to copy'); 105 | }); 106 | }); 107 | } 108 | })(window.jQuery || window.$) 109 | -------------------------------------------------------------------------------- /docs/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Page not found (404) • tagger 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 55 | 56 | 57 | 58 | 59 | 60 | 61 |
    62 |
    63 | 97 | 98 | 99 | 100 |
    101 | 102 |
    103 |
    104 | 107 | 108 | Content not found. Please use links in the navbar. 109 | 110 |
    111 | 112 | 117 | 118 |
    119 | 120 | 121 | 122 |
    123 | 126 | 127 |
    128 |

    Site built with pkgdown 1.5.1.

    129 |
    130 | 131 |
    132 |
    133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | -------------------------------------------------------------------------------- /docs/authors.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Authors • tagger 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 55 | 56 | 57 | 58 | 59 | 60 | 61 |
    62 |
    63 | 97 | 98 | 99 | 100 |
    101 | 102 |
    103 |
    104 | 107 | 108 |
      109 |
    • 110 |

      Elio Campitelli. Maintainer, author. 111 |

      112 |
    • 113 |
    114 | 115 |
    116 | 117 |
    118 | 119 | 120 | 121 |
    122 | 125 | 126 |
    127 |

    Site built with pkgdown 1.5.1.

    128 |
    129 | 130 |
    131 |
    132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | -------------------------------------------------------------------------------- /docs/bootstrap-toc.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Table of Contents v0.4.1 (http://afeld.github.io/bootstrap-toc/) 3 | * Copyright 2015 Aidan Feldman 4 | * Licensed under MIT (https://github.com/afeld/bootstrap-toc/blob/gh-pages/LICENSE.md) */ 5 | (function() { 6 | 'use strict'; 7 | 8 | window.Toc = { 9 | helpers: { 10 | // return all matching elements in the set, or their descendants 11 | findOrFilter: function($el, selector) { 12 | // http://danielnouri.org/notes/2011/03/14/a-jquery-find-that-also-finds-the-root-element/ 13 | // http://stackoverflow.com/a/12731439/358804 14 | var $descendants = $el.find(selector); 15 | return $el.filter(selector).add($descendants).filter(':not([data-toc-skip])'); 16 | }, 17 | 18 | generateUniqueIdBase: function(el) { 19 | var text = $(el).text(); 20 | var anchor = text.trim().toLowerCase().replace(/[^A-Za-z0-9]+/g, '-'); 21 | return anchor || el.tagName.toLowerCase(); 22 | }, 23 | 24 | generateUniqueId: function(el) { 25 | var anchorBase = this.generateUniqueIdBase(el); 26 | for (var i = 0; ; i++) { 27 | var anchor = anchorBase; 28 | if (i > 0) { 29 | // add suffix 30 | anchor += '-' + i; 31 | } 32 | // check if ID already exists 33 | if (!document.getElementById(anchor)) { 34 | return anchor; 35 | } 36 | } 37 | }, 38 | 39 | generateAnchor: function(el) { 40 | if (el.id) { 41 | return el.id; 42 | } else { 43 | var anchor = this.generateUniqueId(el); 44 | el.id = anchor; 45 | return anchor; 46 | } 47 | }, 48 | 49 | createNavList: function() { 50 | return $(''); 51 | }, 52 | 53 | createChildNavList: function($parent) { 54 | var $childList = this.createNavList(); 55 | $parent.append($childList); 56 | return $childList; 57 | }, 58 | 59 | generateNavEl: function(anchor, text) { 60 | var $a = $(''); 61 | $a.attr('href', '#' + anchor); 62 | $a.text(text); 63 | var $li = $('
  • '); 64 | $li.append($a); 65 | return $li; 66 | }, 67 | 68 | generateNavItem: function(headingEl) { 69 | var anchor = this.generateAnchor(headingEl); 70 | var $heading = $(headingEl); 71 | var text = $heading.data('toc-text') || $heading.text(); 72 | return this.generateNavEl(anchor, text); 73 | }, 74 | 75 | // Find the first heading level (`

    `, then `

    `, etc.) that has more than one element. Defaults to 1 (for `

    `). 76 | getTopLevel: function($scope) { 77 | for (var i = 1; i <= 6; i++) { 78 | var $headings = this.findOrFilter($scope, 'h' + i); 79 | if ($headings.length > 1) { 80 | return i; 81 | } 82 | } 83 | 84 | return 1; 85 | }, 86 | 87 | // returns the elements for the top level, and the next below it 88 | getHeadings: function($scope, topLevel) { 89 | var topSelector = 'h' + topLevel; 90 | 91 | var secondaryLevel = topLevel + 1; 92 | var secondarySelector = 'h' + secondaryLevel; 93 | 94 | return this.findOrFilter($scope, topSelector + ',' + secondarySelector); 95 | }, 96 | 97 | getNavLevel: function(el) { 98 | return parseInt(el.tagName.charAt(1), 10); 99 | }, 100 | 101 | populateNav: function($topContext, topLevel, $headings) { 102 | var $context = $topContext; 103 | var $prevNav; 104 | 105 | var helpers = this; 106 | $headings.each(function(i, el) { 107 | var $newNav = helpers.generateNavItem(el); 108 | var navLevel = helpers.getNavLevel(el); 109 | 110 | // determine the proper $context 111 | if (navLevel === topLevel) { 112 | // use top level 113 | $context = $topContext; 114 | } else if ($prevNav && $context === $topContext) { 115 | // create a new level of the tree and switch to it 116 | $context = helpers.createChildNavList($prevNav); 117 | } // else use the current $context 118 | 119 | $context.append($newNav); 120 | 121 | $prevNav = $newNav; 122 | }); 123 | }, 124 | 125 | parseOps: function(arg) { 126 | var opts; 127 | if (arg.jquery) { 128 | opts = { 129 | $nav: arg 130 | }; 131 | } else { 132 | opts = arg; 133 | } 134 | opts.$scope = opts.$scope || $(document.body); 135 | return opts; 136 | } 137 | }, 138 | 139 | // accepts a jQuery object, or an options object 140 | init: function(opts) { 141 | opts = this.helpers.parseOps(opts); 142 | 143 | // ensure that the data attribute is in place for styling 144 | opts.$nav.attr('data-toggle', 'toc'); 145 | 146 | var $topContext = this.helpers.createChildNavList(opts.$nav); 147 | var topLevel = this.helpers.getTopLevel(opts.$scope); 148 | var $headings = this.helpers.getHeadings(opts.$scope, topLevel); 149 | this.helpers.populateNav($topContext, topLevel, $headings); 150 | } 151 | }; 152 | 153 | $(function() { 154 | $('nav[data-toggle="toc"]').each(function(i, el) { 155 | var $nav = $(el); 156 | Toc.init($nav); 157 | }); 158 | }); 159 | })(); 160 | -------------------------------------------------------------------------------- /docs/reference/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Function reference • tagger 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 55 | 56 | 57 | 58 | 59 | 60 | 61 |
    62 |
    63 | 97 | 98 | 99 | 100 |
    101 | 102 |
    103 |
    104 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 133 | 134 | 135 | 136 |
    119 |

    All functions

    120 |

    121 |
    131 |

    tag_facets()

    132 |

    Adds tags to facets

    137 |
    138 | 139 | 144 |
    145 | 146 | 147 |
    148 | 151 | 152 |
    153 |

    Site built with pkgdown 1.5.1.

    154 |
    155 | 156 |
    157 |
    158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | -------------------------------------------------------------------------------- /docs/pkgdown.css: -------------------------------------------------------------------------------- 1 | /* Sticky footer */ 2 | 3 | /** 4 | * Basic idea: https://philipwalton.github.io/solved-by-flexbox/demos/sticky-footer/ 5 | * Details: https://github.com/philipwalton/solved-by-flexbox/blob/master/assets/css/components/site.css 6 | * 7 | * .Site -> body > .container 8 | * .Site-content -> body > .container .row 9 | * .footer -> footer 10 | * 11 | * Key idea seems to be to ensure that .container and __all its parents__ 12 | * have height set to 100% 13 | * 14 | */ 15 | 16 | html, body { 17 | height: 100%; 18 | } 19 | 20 | body { 21 | position: relative; 22 | } 23 | 24 | body > .container { 25 | display: flex; 26 | height: 100%; 27 | flex-direction: column; 28 | } 29 | 30 | body > .container .row { 31 | flex: 1 0 auto; 32 | } 33 | 34 | footer { 35 | margin-top: 45px; 36 | padding: 35px 0 36px; 37 | border-top: 1px solid #e5e5e5; 38 | color: #666; 39 | display: flex; 40 | flex-shrink: 0; 41 | } 42 | footer p { 43 | margin-bottom: 0; 44 | } 45 | footer div { 46 | flex: 1; 47 | } 48 | footer .pkgdown { 49 | text-align: right; 50 | } 51 | footer p { 52 | margin-bottom: 0; 53 | } 54 | 55 | img.icon { 56 | float: right; 57 | } 58 | 59 | img { 60 | max-width: 100%; 61 | } 62 | 63 | /* Fix bug in bootstrap (only seen in firefox) */ 64 | summary { 65 | display: list-item; 66 | } 67 | 68 | /* Typographic tweaking ---------------------------------*/ 69 | 70 | .contents .page-header { 71 | margin-top: calc(-60px + 1em); 72 | } 73 | 74 | dd { 75 | margin-left: 3em; 76 | } 77 | 78 | /* Section anchors ---------------------------------*/ 79 | 80 | a.anchor { 81 | margin-left: -30px; 82 | display:inline-block; 83 | width: 30px; 84 | height: 30px; 85 | visibility: hidden; 86 | 87 | background-image: url(./link.svg); 88 | background-repeat: no-repeat; 89 | background-size: 20px 20px; 90 | background-position: center center; 91 | } 92 | 93 | .hasAnchor:hover a.anchor { 94 | visibility: visible; 95 | } 96 | 97 | @media (max-width: 767px) { 98 | .hasAnchor:hover a.anchor { 99 | visibility: hidden; 100 | } 101 | } 102 | 103 | 104 | /* Fixes for fixed navbar --------------------------*/ 105 | 106 | .contents h1, .contents h2, .contents h3, .contents h4 { 107 | padding-top: 60px; 108 | margin-top: -40px; 109 | } 110 | 111 | /* Navbar submenu --------------------------*/ 112 | 113 | .dropdown-submenu { 114 | position: relative; 115 | } 116 | 117 | .dropdown-submenu>.dropdown-menu { 118 | top: 0; 119 | left: 100%; 120 | margin-top: -6px; 121 | margin-left: -1px; 122 | border-radius: 0 6px 6px 6px; 123 | } 124 | 125 | .dropdown-submenu:hover>.dropdown-menu { 126 | display: block; 127 | } 128 | 129 | .dropdown-submenu>a:after { 130 | display: block; 131 | content: " "; 132 | float: right; 133 | width: 0; 134 | height: 0; 135 | border-color: transparent; 136 | border-style: solid; 137 | border-width: 5px 0 5px 5px; 138 | border-left-color: #cccccc; 139 | margin-top: 5px; 140 | margin-right: -10px; 141 | } 142 | 143 | .dropdown-submenu:hover>a:after { 144 | border-left-color: #ffffff; 145 | } 146 | 147 | .dropdown-submenu.pull-left { 148 | float: none; 149 | } 150 | 151 | .dropdown-submenu.pull-left>.dropdown-menu { 152 | left: -100%; 153 | margin-left: 10px; 154 | border-radius: 6px 0 6px 6px; 155 | } 156 | 157 | /* Sidebar --------------------------*/ 158 | 159 | #pkgdown-sidebar { 160 | margin-top: 30px; 161 | position: -webkit-sticky; 162 | position: sticky; 163 | top: 70px; 164 | } 165 | 166 | #pkgdown-sidebar h2 { 167 | font-size: 1.5em; 168 | margin-top: 1em; 169 | } 170 | 171 | #pkgdown-sidebar h2:first-child { 172 | margin-top: 0; 173 | } 174 | 175 | #pkgdown-sidebar .list-unstyled li { 176 | margin-bottom: 0.5em; 177 | } 178 | 179 | /* bootstrap-toc tweaks ------------------------------------------------------*/ 180 | 181 | /* All levels of nav */ 182 | 183 | nav[data-toggle='toc'] .nav > li > a { 184 | padding: 4px 20px 4px 6px; 185 | font-size: 1.5rem; 186 | font-weight: 400; 187 | color: inherit; 188 | } 189 | 190 | nav[data-toggle='toc'] .nav > li > a:hover, 191 | nav[data-toggle='toc'] .nav > li > a:focus { 192 | padding-left: 5px; 193 | color: inherit; 194 | border-left: 1px solid #878787; 195 | } 196 | 197 | nav[data-toggle='toc'] .nav > .active > a, 198 | nav[data-toggle='toc'] .nav > .active:hover > a, 199 | nav[data-toggle='toc'] .nav > .active:focus > a { 200 | padding-left: 5px; 201 | font-size: 1.5rem; 202 | font-weight: 400; 203 | color: inherit; 204 | border-left: 2px solid #878787; 205 | } 206 | 207 | /* Nav: second level (shown on .active) */ 208 | 209 | nav[data-toggle='toc'] .nav .nav { 210 | display: none; /* Hide by default, but at >768px, show it */ 211 | padding-bottom: 10px; 212 | } 213 | 214 | nav[data-toggle='toc'] .nav .nav > li > a { 215 | padding-left: 16px; 216 | font-size: 1.35rem; 217 | } 218 | 219 | nav[data-toggle='toc'] .nav .nav > li > a:hover, 220 | nav[data-toggle='toc'] .nav .nav > li > a:focus { 221 | padding-left: 15px; 222 | } 223 | 224 | nav[data-toggle='toc'] .nav .nav > .active > a, 225 | nav[data-toggle='toc'] .nav .nav > .active:hover > a, 226 | nav[data-toggle='toc'] .nav .nav > .active:focus > a { 227 | padding-left: 15px; 228 | font-weight: 500; 229 | font-size: 1.35rem; 230 | } 231 | 232 | /* orcid ------------------------------------------------------------------- */ 233 | 234 | .orcid { 235 | font-size: 16px; 236 | color: #A6CE39; 237 | /* margins are required by official ORCID trademark and display guidelines */ 238 | margin-left:4px; 239 | margin-right:4px; 240 | vertical-align: middle; 241 | } 242 | 243 | /* Reference index & topics ----------------------------------------------- */ 244 | 245 | .ref-index th {font-weight: normal;} 246 | 247 | .ref-index td {vertical-align: top;} 248 | .ref-index .icon {width: 40px;} 249 | .ref-index .alias {width: 40%;} 250 | .ref-index-icons .alias {width: calc(40% - 40px);} 251 | .ref-index .title {width: 60%;} 252 | 253 | .ref-arguments th {text-align: right; padding-right: 10px;} 254 | .ref-arguments th, .ref-arguments td {vertical-align: top;} 255 | .ref-arguments .name {width: 20%;} 256 | .ref-arguments .desc {width: 80%;} 257 | 258 | /* Nice scrolling for wide elements --------------------------------------- */ 259 | 260 | table { 261 | display: block; 262 | overflow: auto; 263 | } 264 | 265 | /* Syntax highlighting ---------------------------------------------------- */ 266 | 267 | pre { 268 | word-wrap: normal; 269 | word-break: normal; 270 | border: 1px solid #eee; 271 | } 272 | 273 | pre, code { 274 | background-color: #f8f8f8; 275 | color: #333; 276 | } 277 | 278 | pre code { 279 | overflow: auto; 280 | word-wrap: normal; 281 | white-space: pre; 282 | } 283 | 284 | pre .img { 285 | margin: 5px 0; 286 | } 287 | 288 | pre .img img { 289 | background-color: #fff; 290 | display: block; 291 | height: auto; 292 | } 293 | 294 | code a, pre a { 295 | color: #375f84; 296 | } 297 | 298 | a.sourceLine:hover { 299 | text-decoration: none; 300 | } 301 | 302 | .fl {color: #1514b5;} 303 | .fu {color: #000000;} /* function */ 304 | .ch,.st {color: #036a07;} /* string */ 305 | .kw {color: #264D66;} /* keyword */ 306 | .co {color: #888888;} /* comment */ 307 | 308 | .message { color: black; font-weight: bolder;} 309 | .error { color: orange; font-weight: bolder;} 310 | .warning { color: #6A0366; font-weight: bolder;} 311 | 312 | /* Clipboard --------------------------*/ 313 | 314 | .hasCopyButton { 315 | position: relative; 316 | } 317 | 318 | .btn-copy-ex { 319 | position: absolute; 320 | right: 0; 321 | top: 0; 322 | visibility: hidden; 323 | } 324 | 325 | .hasCopyButton:hover button.btn-copy-ex { 326 | visibility: visible; 327 | } 328 | 329 | /* headroom.js ------------------------ */ 330 | 331 | .headroom { 332 | will-change: transform; 333 | transition: transform 200ms linear; 334 | } 335 | .headroom--pinned { 336 | transform: translateY(0%); 337 | } 338 | .headroom--unpinned { 339 | transform: translateY(-100%); 340 | } 341 | 342 | /* mark.js ----------------------------*/ 343 | 344 | mark { 345 | background-color: rgba(255, 255, 51, 0.5); 346 | border-bottom: 2px solid rgba(255, 153, 51, 0.3); 347 | padding: 1px; 348 | } 349 | 350 | /* vertical spacing after htmlwidgets */ 351 | .html-widget { 352 | margin-bottom: 10px; 353 | } 354 | 355 | /* fontawesome ------------------------ */ 356 | 357 | .fab { 358 | font-family: "Font Awesome 5 Brands" !important; 359 | } 360 | 361 | /* don't display links in code chunks when printing */ 362 | /* source: https://stackoverflow.com/a/10781533 */ 363 | @media print { 364 | code a:link:after, code a:visited:after { 365 | content: ""; 366 | } 367 | } 368 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Adds tags to ggpot2 facets • tagger 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 20 | 21 | 22 |
    23 |
    56 | 57 | 58 | 59 | 60 |
    61 |
    62 |
    63 | 65 | 66 | 67 |

    The goal of tagger is to easily add tags to facetted plots in order to identify panles.

    68 |
    69 |

    70 | Installation

    71 | 72 | 73 | 74 | 75 |

    You can install the development version from GitHub with:

    76 |
    # install.packages("devtools")
     77 | devtools::install_github("eliocamp/tagger")
    78 |
    79 |
    80 |

    81 | Example

    82 |

    In the default usage, you can simply add tag_facets() to add a tag to each panel of a ggplo2 plot.

    83 |
    library(tagger)
     84 | library(ggplot2)
     85 | 
     86 | ggplot(mtcars, aes(hp, mpg)) +
     87 |    geom_point() +
     88 |    facet_grid(cyl ~ vs) +
     89 |    tag_facets()
    90 |

    If you want to tag rows and columns instead of each panel indivdually, you can set it with the tag argument.

    91 |
    ggplot(mtcars, aes(hp, mpg)) +
     92 |    geom_point() +
     93 |    facet_grid(cyl ~ vs) +
     94 |    tag_facets(tag = "rc")
    95 |

    96 |
    97 |
    98 |
    99 | 100 | 115 |
    116 | 117 | 118 |
    121 | 122 |
    123 |

    Site built with pkgdown 1.5.1.

    124 |
    125 | 126 |
    127 |
    128 | 129 | 130 | 131 | 132 | 133 | 134 | -------------------------------------------------------------------------------- /docs/docsearch.css: -------------------------------------------------------------------------------- 1 | /* Docsearch -------------------------------------------------------------- */ 2 | /* 3 | Source: https://github.com/algolia/docsearch/ 4 | License: MIT 5 | */ 6 | 7 | .algolia-autocomplete { 8 | display: block; 9 | -webkit-box-flex: 1; 10 | -ms-flex: 1; 11 | flex: 1 12 | } 13 | 14 | .algolia-autocomplete .ds-dropdown-menu { 15 | width: 100%; 16 | min-width: none; 17 | max-width: none; 18 | padding: .75rem 0; 19 | background-color: #fff; 20 | background-clip: padding-box; 21 | border: 1px solid rgba(0, 0, 0, .1); 22 | box-shadow: 0 .5rem 1rem rgba(0, 0, 0, .175); 23 | } 24 | 25 | @media (min-width:768px) { 26 | .algolia-autocomplete .ds-dropdown-menu { 27 | width: 175% 28 | } 29 | } 30 | 31 | .algolia-autocomplete .ds-dropdown-menu::before { 32 | display: none 33 | } 34 | 35 | .algolia-autocomplete .ds-dropdown-menu [class^=ds-dataset-] { 36 | padding: 0; 37 | background-color: rgb(255,255,255); 38 | border: 0; 39 | max-height: 80vh; 40 | } 41 | 42 | .algolia-autocomplete .ds-dropdown-menu .ds-suggestions { 43 | margin-top: 0 44 | } 45 | 46 | .algolia-autocomplete .algolia-docsearch-suggestion { 47 | padding: 0; 48 | overflow: visible 49 | } 50 | 51 | .algolia-autocomplete .algolia-docsearch-suggestion--category-header { 52 | padding: .125rem 1rem; 53 | margin-top: 0; 54 | font-size: 1.3em; 55 | font-weight: 500; 56 | color: #00008B; 57 | border-bottom: 0 58 | } 59 | 60 | .algolia-autocomplete .algolia-docsearch-suggestion--wrapper { 61 | float: none; 62 | padding-top: 0 63 | } 64 | 65 | .algolia-autocomplete .algolia-docsearch-suggestion--subcategory-column { 66 | float: none; 67 | width: auto; 68 | padding: 0; 69 | text-align: left 70 | } 71 | 72 | .algolia-autocomplete .algolia-docsearch-suggestion--content { 73 | float: none; 74 | width: auto; 75 | padding: 0 76 | } 77 | 78 | .algolia-autocomplete .algolia-docsearch-suggestion--content::before { 79 | display: none 80 | } 81 | 82 | .algolia-autocomplete .ds-suggestion:not(:first-child) .algolia-docsearch-suggestion--category-header { 83 | padding-top: .75rem; 84 | margin-top: .75rem; 85 | border-top: 1px solid rgba(0, 0, 0, .1) 86 | } 87 | 88 | .algolia-autocomplete .ds-suggestion .algolia-docsearch-suggestion--subcategory-column { 89 | display: block; 90 | padding: .1rem 1rem; 91 | margin-bottom: 0.1; 92 | font-size: 1.0em; 93 | font-weight: 400 94 | /* display: none */ 95 | } 96 | 97 | .algolia-autocomplete .algolia-docsearch-suggestion--title { 98 | display: block; 99 | padding: .25rem 1rem; 100 | margin-bottom: 0; 101 | font-size: 0.9em; 102 | font-weight: 400 103 | } 104 | 105 | .algolia-autocomplete .algolia-docsearch-suggestion--text { 106 | padding: 0 1rem .5rem; 107 | margin-top: -.25rem; 108 | font-size: 0.8em; 109 | font-weight: 400; 110 | line-height: 1.25 111 | } 112 | 113 | .algolia-autocomplete .algolia-docsearch-footer { 114 | width: 110px; 115 | height: 20px; 116 | z-index: 3; 117 | margin-top: 10.66667px; 118 | float: right; 119 | font-size: 0; 120 | line-height: 0; 121 | } 122 | 123 | .algolia-autocomplete .algolia-docsearch-footer--logo { 124 | background-image: url("data:image/svg+xml;utf8,"); 125 | background-repeat: no-repeat; 126 | background-position: 50%; 127 | background-size: 100%; 128 | overflow: hidden; 129 | text-indent: -9000px; 130 | width: 100%; 131 | height: 100%; 132 | display: block; 133 | transform: translate(-8px); 134 | } 135 | 136 | .algolia-autocomplete .algolia-docsearch-suggestion--highlight { 137 | color: #FF8C00; 138 | background: rgba(232, 189, 54, 0.1) 139 | } 140 | 141 | 142 | .algolia-autocomplete .algolia-docsearch-suggestion--text .algolia-docsearch-suggestion--highlight { 143 | box-shadow: inset 0 -2px 0 0 rgba(105, 105, 105, .5) 144 | } 145 | 146 | .algolia-autocomplete .ds-suggestion.ds-cursor .algolia-docsearch-suggestion--content { 147 | background-color: rgba(192, 192, 192, .15) 148 | } 149 | -------------------------------------------------------------------------------- /R/tag-facet.R: -------------------------------------------------------------------------------- 1 | #' Adds tags to facets 2 | #' 3 | #' @param tag Character indicating the element to tag. Possible values are 4 | #' * "panel": each single panels is individually tag. 5 | #' * "rc": facets are tagged by row and then column. 6 | #' * "cr": factes are tagged by columns and then row. 7 | #' 8 | #' @param position Character indicating the position of the tag. Options are "tl" 9 | #' (top-left), "tr" (top-right), "br" (bottom-right) and "bl" (bottom-left). 10 | #' Alternatively, for finer control, it can be a list of elements "x", "u", 11 | #' "hjust" and "vjust" that define the position of the tag within each panel. 12 | #' 13 | #' @inheritParams patchwork::plot_annotation 14 | #' 15 | #' @param tag_pool An optional character vector of user-defined "pool" of tags. 16 | #' If not `NULL`, it will be used instead of `tag_levels`. Should be at least 17 | #' as long as the total number of panels. 18 | #' 19 | #' 20 | #' @details 21 | #' 22 | #' Tags inherit aesthetic properties (size, font, colour, etc...) from strip.text and 23 | #' strip.background defined by [ggplot2::theme()]. For fine-grained detail, these can be 24 | #' overriden by setting tagger.panel.tag.text and tagger.panel.tag.background. 25 | #' 26 | #' @examples 27 | #' 28 | #' library(ggplot2) 29 | #' # Base plot 30 | #' g <- ggplot(mtcars, aes(hp, mpg)) + 31 | #' geom_point() + 32 | #' facet_grid(cyl ~ vs) 33 | #' 34 | #' g + tag_facets() 35 | #' g + tag_facets("rc") 36 | #' g + tag_facets("cr") 37 | #' g + tag_facets(position = "br") 38 | #' g + tag_facets("rc", tag_levels = c("A", "I")) 39 | #' 40 | #' # You can get finer control over position. 41 | #' g + tag_facets(position = list(x = 0.5, y = 0.5)) 42 | #' 43 | #' g + tag_facets(tag_pool = c("one", "two", "three", "four", "five", "six"), tag_suffix = "") 44 | #' # Thanks to theme inheritance, tags should look aceptable 45 | #' # out of the box in any theme. 46 | #' g + tag_facets() + 47 | #' theme_dark() 48 | #' 49 | #' # But you can control their appearance and create your own atrocities 50 | #' g + tag_facets() + 51 | #' theme_dark() + 52 | #' theme(tagger.panel.tag.text = element_text(color = "red", size = 16), 53 | #' tagger.panel.tag.background = element_rect(fill = "purple")) 54 | #' 55 | #' @export 56 | #' @import checkmate 57 | tag_facets <- function(tag = c("panel", "rc", "cr"), position = "tl", 58 | tag_levels = c("a", "1"), tag_pool = NULL, tag_prefix = "", 59 | tag_suffix = ")", tag_sep = ".") { 60 | 61 | if (!is.list(position)) { 62 | assert_choice(position, c("tl", "tr", "bl", "br")) 63 | 64 | position <- switch(position, 65 | tl = list(x = 0, y = 1, hjust = 0, vjust = 1), 66 | tr = list(x = 1, y = 1, hjust = 1, vjust = 1), 67 | bl = list(x = 0, y = 0, hjust = 0, vjust = 0), 68 | br = list(x = 1, y = 0, hjust = 1, vjust = 0), 69 | position) 70 | } 71 | 72 | position$hjust <- if (!is.null(position$hjust)) position$hjust else 0.5 73 | position$vjust <- if (!is.null(position$vjust)) position$vjust else 0.5 74 | 75 | checks <- makeAssertCollection() 76 | 77 | 78 | assert_list(position, types = "numeric", len = 4, add = checks) 79 | assert_names(names(position), must.include = c("x", "y", "hjust", "vjust"), 80 | .var.name = "position", add = checks) 81 | 82 | assert_choice(tag[1], c("panel", "rc", "cr"), add = checks) 83 | assert_character(tag_prefix, any.missing = FALSE, len = 1, add = checks) 84 | assert_character(tag_suffix, any.missing = FALSE, len = 1, add = checks) 85 | assert_character(tag_sep, any.missing = FALSE, len = 1, add = checks) 86 | 87 | tag <- switch(tag[1], 88 | rc = c("ROW", "COL"), 89 | cr = c("COL", "ROW"), 90 | panel = "PANEL") 91 | 92 | assert_character(tag_levels, any.missing = FALSE, min.len = length(tag), add = checks) 93 | reportAssertions(checks) 94 | 95 | 96 | structure(list(tag = tag, tag_pool = tag_pool, tag_levels = tag_levels, open = tag_prefix, 97 | close = tag_suffix, tag_sep = tag_sep, position = position), 98 | class = "tagger") 99 | } 100 | 101 | #' @export 102 | #' @importFrom ggplot2 ggplot_add 103 | ggplot_add.tagger <- function(object, plot, object_name) { 104 | class(plot) <- c("ggtagged", class(plot)) 105 | plot$tag_options <- object 106 | plot 107 | } 108 | 109 | `%||%` <- function(a, b) { 110 | if (length(a) == 0) return(a) 111 | nulls <- lengths(a) == 0 112 | if (all(nulls[-length(nulls)])) return(b) 113 | a[nulls] <- b[nulls] 114 | a 115 | } 116 | 117 | as.element_text <- function(x) { 118 | if (length(x) == 0) return(ggplot2::element_text()) 119 | return(x) 120 | } 121 | 122 | 123 | as.element_rect <- function(x) { 124 | if (length(x) == 0) return(ggplot2::element_rect()) 125 | return(x) 126 | } 127 | 128 | 129 | #' @export 130 | #' @importFrom ggplot2 ggplot_build 131 | ggplot_build.ggtagged <- function(plot) { 132 | gb <- NextMethod("ggplot_build") 133 | gb <- add_class(gb, "ggplot_build_ggtagged") 134 | gb 135 | } 136 | 137 | add_class <- function(object, class) { 138 | class(object) <- c(class, setdiff(class(object), class)) 139 | return(object) 140 | } 141 | 142 | 143 | #' @export 144 | #' @importFrom ggplot2 ggplot_gtable 145 | ggplot_gtable.ggplot_build_ggtagged <- function(data) { 146 | gt <- NextMethod("ggplot_gtable") 147 | lay <- assign_tags(data) 148 | facet_tags <- lay$TAG_PANEL 149 | 150 | tag_options <- data$plot$tag_options 151 | 152 | facet_tags <- paste0(tag_options$open, facet_tags, tag_options$close) 153 | 154 | theme <- ggplot2:::plot_theme(data$plot) 155 | 156 | tag_style <- ggplot2::calc_element("tagger.panel.tag.text", theme, verbose = FALSE, skip_blank = FALSE) 157 | tag_gpar <- list(col = tag_style$colour, 158 | family = tag_style$family, 159 | fontface = tag_style$face, 160 | fontsize = tag_style$size, 161 | lineheight = tag_style$lineheight) 162 | tag_gpar <- tag_gpar[lengths(tag_gpar) != 0] 163 | tag_gpar <- do.call(grid::gpar, tag_gpar) 164 | 165 | box <- ggplot2::calc_element("tagger.panel.tag.background", theme, verbose = FALSE, skip_blank = FALSE) 166 | box_gp <- grid::gpar(col = box$colour, fill = box$fill, lty = box$linetype, lwd = box$size) 167 | 168 | x <- tag_options$position$x 169 | y <- tag_options$position$y 170 | 171 | hjust <- tag_options$position$hjust 172 | vjust <- tag_options$position$vjust 173 | 174 | panels <- which(startsWith(gt$layout$name, "panel")) 175 | 176 | # Panels are sorted top to bottom, left to right 177 | panels <- c(matrix(panels, ncol = max(lay$ROW), nrow = max(lay$COL), byrow = TRUE)) 178 | 179 | # Remove empty panels 180 | empty <- vapply(panels, function(p) inherits(gt$grobs[p][[1]], "zeroGrob"), 181 | FUN.VALUE = logical(1)) 182 | panels <- panels[!empty] 183 | 184 | grob_args <- list(x = x, y = y, hjust = hjust, vjust = vjust, 185 | rot = tag_style$angle, 186 | gp = tag_gpar, 187 | box_gp = box_gp, 188 | # r = grid::unit(c(0.1, .1, .1, .1), "lines"), 189 | padding = grid::unit(c(0.2, .2, .2, .2), "lines"), 190 | use_markdown = FALSE) 191 | grob_args <- grob_args[lengths(grob_args) != 0] 192 | grob <- gridtext::richtext_grob 193 | 194 | for (p in seq_along(facet_tags)) { 195 | grob_args$text <- facet_tags[p] 196 | tagGrob <- do.call(grob, grob_args) 197 | this_panel <- gt$layout[panels[p], ] 198 | gt <- gtable::gtable_add_grob(gt, tagGrob, t = this_panel$t, l = this_panel$l, clip = "on", z = 2) 199 | } 200 | 201 | return(gt) 202 | } 203 | 204 | 205 | assign_tags <- function(plot) { 206 | tag_options <- plot$plot$tag_options 207 | 208 | lay <- plot$layout$layout 209 | o <- order(lay$ROW, lay$COL) 210 | lay <- lay[o, ] 211 | facet_vars <- lay[toupper(tag_options$tag)] 212 | facet_vars <- lapply(facet_vars, function(x) as.numeric(as.factor(x))) 213 | tag_options$tag_levels <- rep_len(tag_options$tag_levels, length.out = length(facet_vars)) 214 | facet_tags <- lapply(seq_along(facet_vars), function(i) { 215 | i_panels <- facet_vars[[i]] 216 | if (is.null(tag_options$tag_pool[[i]])) { 217 | tag_pool <- switch(tag_options$tag_levels[i], 218 | a = letters[i_panels], 219 | A = LETTERS[i_panels], 220 | "1" = i_panels, 221 | "i" = tolower(utils::as.roman(i_panels)), 222 | "I" = utils::as.roman(i_panels), 223 | stop("tag_levels is not valid") 224 | ) 225 | 226 | } else { 227 | tag_pool <- tag_options$tag_pool[i_panels] 228 | } 229 | return(tag_pool) 230 | }) 231 | 232 | if (!identical(tag_options$tag, "PANEL")) { 233 | lay[paste0("TAG_", tag_options$tag)] <- facet_tags 234 | } 235 | 236 | facet_tags <- Reduce(function(a, b) paste(a, b, sep = tag_options$tag_sep), facet_tags) 237 | 238 | lay[["TAG_PANEL"]] <- facet_tags 239 | 240 | lay[order(o), ] 241 | return(lay) 242 | } 243 | 244 | #' Get labels asigned to each panel of a tagged plot 245 | #' 246 | #' @param filter an expression that, evaluated within the data used 247 | #' to generate the facets of the plot, evaluates to a logical vector 248 | #' or a sequence of rows. 249 | #' @param plot a plot object 250 | #' @param n number of expected panels. 251 | #' 252 | #' @return 253 | #' `get_tags()`, `get_row()` and `get_col()` return a character vector 254 | #' of length `n` with the tags, unique rows or unique columns that meet 255 | #' the `filter` condition. 256 | #' 257 | #' `get_layout()` returns the full data.frame describing the panel layout. 258 | #' 259 | #' 260 | #' @examples 261 | #' library(ggplot2) 262 | #' g <- ggplot(mtcars, aes(hp, mpg)) + 263 | #' geom_point() + 264 | #' facet_grid(cyl ~ vs) + 265 | #' tag_facets("rc") 266 | #' 267 | #' # By default these functions retrieve tags from 268 | #' # the result of ggplo2::last_plot(). 269 | #' 270 | #' # Get all tags 271 | #' get_layout() 272 | #' 273 | #' # Get one tag 274 | #' get_tag(cyl == 4 & vs == 0) 275 | #' 276 | #' # Get more than one tag 277 | #' get_tag(cyl == 4, n = 2) 278 | #' 279 | #' 280 | #' get_row(cyl == 4) 281 | #' get_col(vs == 0) 282 | #' 283 | #' # Use it with inline markdown to refer always to the correct panel: 284 | #' # "As you can see in panel `r get_tag(cyl == 4 & vs == 0)` ..." 285 | #' 286 | #' @export 287 | get_layout <- function(plot = ggplot2::last_plot()) { 288 | if (!inherits(plot, "ggtagged")) { 289 | stop("The plot has no tags.") 290 | } 291 | plot <- ggplot_build_memoised(plot) 292 | lay <- assign_tags(plot) 293 | lay <- lay[, setdiff(colnames(lay), c("SCALE_X", "SCALE_Y"))] 294 | 295 | return(lay) 296 | } 297 | 298 | #' @export 299 | #' @rdname get_layout 300 | get_tag <- function(filter = TRUE, plot = ggplot2::last_plot(), n = 1) { 301 | lay <- get_layout(plot = plot) 302 | filter <- eval(substitute(filter), envir = lay) 303 | lay <- lay[filter, ] 304 | 305 | if (nrow(lay) != n) { 306 | stop("Returned ", nrow(lay), ifelse(n == 1, " panel", " panels"), 307 | " (expected ", n, ").") 308 | } 309 | lay$TAG_PANEL 310 | } 311 | 312 | if (requireNamespace("memoise", quietly = TRUE)) { 313 | ggplot_build_memoised <- memoise::memoise(ggplot2::ggplot_build) 314 | } else { 315 | ggplot_build_memoised <- ggplot2::ggplot_build 316 | } 317 | 318 | #' @export 319 | #' @rdname get_layout 320 | get_row <- function(filter = TRUE, plot = ggplot2::last_plot(), n = 1) { 321 | lay <- get_layout(plot = plot) 322 | filter <- eval(substitute(filter), envir = lay) 323 | lay <- lay[filter, ] 324 | 325 | row <- unique(lay$TAG_ROW) 326 | if (length(row) != n) { 327 | stop("Returned ", length(row), ifelse(n == 1, " panel", " panels"), 328 | " (expected ", n, ").") 329 | } 330 | row 331 | } 332 | 333 | #' @export 334 | #' @rdname get_layout 335 | get_col <- function(filter = TRUE, plot = ggplot2::last_plot(), n = 1) { 336 | lay <- get_layout(plot = plot) 337 | filter <- eval(substitute(filter), envir = lay) 338 | lay <- lay[filter, ] 339 | 340 | col <- unique(lay$TAG_COL) 341 | if (length(col) != n) { 342 | stop("Returned ", length(col), ifelse(n == 1, " panel", " panels"), 343 | " (expected ", n, ").") 344 | } 345 | col 346 | } 347 | -------------------------------------------------------------------------------- /docs/reference/tag_facets.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Adds tags to facets — tag_facets • tagger 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 56 | 57 | 58 | 59 | 60 | 61 | 62 |
    63 |
    64 | 98 | 99 | 100 | 101 |
    102 | 103 |
    104 |
    105 | 110 | 111 |
    112 |

    Adds tags to facets

    113 |
    114 | 115 |
    tag_facets(
    116 |   tag = c("panel", "rc", "cr"),
    117 |   position = "tl",
    118 |   tag_levels = c("a", "1"),
    119 |   tag_pool = NULL,
    120 |   tag_prefix = "",
    121 |   tag_suffix = ")",
    122 |   tag_sep = "."
    123 | )
    124 | 125 |

    Arguments

    126 | 127 | 128 | 129 | 130 | 135 | 136 | 137 | 138 | 142 | 143 | 144 | 145 | 149 | 150 | 151 | 152 | 155 | 156 | 157 | 158 | 160 | 161 | 162 | 163 | 165 | 166 | 167 | 168 | 169 | 170 |
    tag

    Character indicating the element to tag. Possible values are

      131 |
    • "panel": each single panels is individually tag.

    • 132 |
    • "rc": facets are tagged by row and then column.

    • 133 |
    • "cr": factes are tagged by columns and then row.

    • 134 |
    position

    Character indicating the position of the tag. Options are "tl" 139 | (top-left), "tr" (top-right), "br" (bottom-right) and "bl" (bottom-left). 140 | Alternatively, for finer control, it can be a list of elements "x", "u", 141 | "hjust" and "vjust" that define the position of the tag within each panel.

    tag_levels

    A character vector defining the enumeration format to use 146 | at each level. Possible values are 'a' for lowercase letters, 'A' for 147 | uppercase letters, '1' for numbers, 'i' for lowercase Roman numerals, and 148 | 'I' for uppercase Roman numerals.

    tag_pool

    An optional character vector of user-defined "pool" of tags. 153 | If not NULL, it will be used instead of tag_levels. Should be at least 154 | as long as the total number of panels.

    tag_prefix

    Strings that should appear before or after the 159 | tag.

    tag_suffix

    Strings that should appear before or after the 164 | tag.

    tag_sep

    A separator between different tag levels

    171 | 172 |

    Details

    173 | 174 |

    Tags inherit aesthetic properties (size, font, colour, etc...) from strip.text and 175 | strip.background defined by ggplot2::theme(). For fine-grained detail, these can be 176 | overriden by setting tagger.panel.tag.text and tagger.panel.tag.background.

    177 | 178 |

    Examples

    179 |
    180 | library(ggplot2) 181 | # Base plot 182 | g <- ggplot(mtcars, aes(hp, mpg)) + 183 | geom_point() + 184 | facet_grid(cyl ~ vs) 185 | 186 | g + tag_facets()
    g + tag_facets("rc")
    g + tag_facets(position = "br")
    g + tag_facets("rc", tag_levels = c("A", "I"))
    187 | # You can get finer control over position. 188 | g + tag_facets(position = list(x = 0.5, y = 0.5, hjust = 0, vjust = 0))
    189 | g + tag_facets(tag_pool = c("one", "two", "three", "four", "five", "six"), tag_suffix = "")
    # Thanks to theme inheritance, tags should look aceptable 190 | # out of the box in any theme. 191 | g + tag_facets() + 192 | theme_dark()
    193 | # But you can control their appearance and create your own atrocities 194 | g + tag_facets() + 195 | theme_dark() + 196 | theme(tagger.panel.tag.text = element_text(color = "red", size = 16), 197 | tagger.panel.tag.background = element_rect(fill = "purple"))
    198 |
    199 |
    200 | 205 |
    206 | 207 | 208 | 218 |
    219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | --------------------------------------------------------------------------------