Skip to content

Commit

Permalink
First version
Browse files Browse the repository at this point in the history
  • Loading branch information
eliocamp committed Dec 15, 2018
1 parent 82e1bfb commit e3fee01
Show file tree
Hide file tree
Showing 11 changed files with 272 additions and 79 deletions.
5 changes: 5 additions & 0 deletions .Rbuildignore
@@ -0,0 +1,5 @@
^README\.Rmd$
^\.travis\.yml$
^LICENSE\.md$
^.*\.Rproj$
^\.Rproj\.user$
4 changes: 4 additions & 0 deletions .gitignore
@@ -0,0 +1,4 @@
.Rproj.user
.Rhistory
.RData
.Ruserdata
5 changes: 5 additions & 0 deletions .travis.yml
@@ -0,0 +1,5 @@
# R for travis: see documentation at https://docs.travis-ci.com/user/languages/r

language: R
sudo: false
cache: packages
6 changes: 4 additions & 2 deletions DESCRIPTION
Expand Up @@ -7,9 +7,11 @@ Authors@R:
role = c("cre", "aut"),
email = "elio.campitelli@cima.fcen.uba.ar",
comment = c(ORCID = "0000-0002-7742-9230"))
Description: What the package does (one paragraph).
Description: Use multiple fill and color scales in `ggplot2`.
License: GPL-3
Encoding: UTF-8
LazyData: true
Imports:
ggplot2
ggplot2 (>= 3.0.0),
stringi
RoxygenNote: 6.1.1
9 changes: 8 additions & 1 deletion NAMESPACE
@@ -1 +1,8 @@
exportPattern("^[[:alpha:]]+")
# Generated by roxygen2: do not edit by hand

S3method(ggplot_add,new_aes)
export(new_scale)
export(new_scale_color)
export(new_scale_colour)
export(new_scale_fill)
importFrom(ggplot2,ggplot_add)
143 changes: 67 additions & 76 deletions R/new_scale_pkg.R → R/new-scale.R
@@ -1,32 +1,38 @@
#' Adds a new scale to a plot
#'
#'
#' Creates a new scale "slot". Geoms added to a plot after this function will
#' use a new scale definition.
#'
#' use a new scale definition.
#'
#' @param new_aes A string with the name of the aesthetic for which a new scale
#' swill be created.
#'
#' @examples
#'
#' @details
#' `new_scale_color()`, `new_scale_colour()` and `new_scale_fill()` are just
#' aliases to `new_scale("color")`, etc...
#'
#' @examples
#' library(ggplot2)
#'
#'
#' # Equivalent to melt(volcano), but we don't want to depend on reshape2
#' topography <- expand.grid(x = 1:nrow(volcano), y = 1:ncol(volcano))
#' topography <- expand.grid(x = 1:nrow(volcano),
#' y = 1:ncol(volcano))
#' topography$z <- c(volcano)
#'
#'
#' # point measurements of something at a few locations
#' measurements <- data.frame(x = runif(30, 1, 80), y = runif(30, 1, 60),
#' measurements <- data.frame(x = runif(30, 1, 80),
#' y = runif(30, 1, 60),
#' thing = rnorm(30))
#'
#'
#' ggplot(mapping = aes(x, y)) +
#' geom_contour(data = topography, aes(z = z, color = ..level..)) +
#' scale_color_viridis_c(option = "D") + # Color scale for topography
#'
#' new_scale_color() + # geoms below will use another color scale
#'
#' geom_contour(data = topography, aes(z = z, color = stat(level))) +
#' # Color scale for topography
#' scale_color_viridis_c(option = "D") +
#' # geoms below will use another color scale
#' new_scale_color() +
#' geom_point(data = measurements, size = 3, aes(color = thing)) +
#' scale_color_viridis_c(option = "A") # Color scale applied to geoms added
#' # after new_scale_color()
#'
#' # Color scale applied to geoms added after new_scale_color()
#' scale_color_viridis_c(option = "A")
#'
#' @export
new_scale <- function(new_aes) {
structure(ggplot2::standardise_aes_names(new_aes), class = "new_aes")
Expand All @@ -50,88 +56,92 @@ new_scale_colour <- function() {
new_scale("colour")
}

#' Special behaviour of the "+" for adding a `new_aes` object
#' It changes the name of the aesthethic for the previous layers, appending
#' "_new" to them.
#' @export
#' @importFrom ggplot2 ggplot_add
ggplot_add.new_aes <- function(object, plot, object_name) {
plot$layers <- lapply(plot$layers, bump_aes, new_aes = object)
plot$scales$scales <- lapply(plot$scales$scales, bump_aes, new_aes = object)
plot$labels <- bump_aes(plot$labels, new_aes = object)
plot$layers <- bump_aes_layers(plot$layers, new_aes = object)
plot$scales$scales <- bump_aes_scales(plot$scales$scales, new_aes = object)
plot$labels <- bump_aes_labels(plot$labels, new_aes = object)
plot
}

bump_aes_layers <- function(layers, new_aes) {
lapply(layers, bump_aes_layer, new_aes = new_aes)

bump_aes <- function(layer, new_aes) {
UseMethod("bump_aes")
}

bump_aes.Scale <- function(layer, new_aes) {
old_aes <- layer$aesthetics[remove_new(layer$aesthetics) %in% new_aes]
new_aes <- paste0(old_aes, "_new")

layer$aesthetics[layer$aesthetics %in% old_aes] <- new_aes

if (is.character(layer$guide)) {
layer$guide <- match.fun(paste("guide_", layer$guide, sep = ""))()
}
layer$guide$available_aes[layer$guide$available_aes %in% old_aes] <- new_aes
layer
}

bump_aes.Layer <- function(layer, new_aes) {
bump_aes_layer <- function(layer, new_aes) {
original_aes <- new_aes

old_aes <- names(layer$mapping)[remove_new(names(layer$mapping)) %in% new_aes]
new_aes <- paste0(old_aes, "_new")

old_geom <- layer$geom

old_setup <- old_geom$handle_na
new_setup <- function(self, data, params) {
colnames(data)[colnames(data) %in% new_aes] <- original_aes
old_setup(data, params)
}

new_geom <- ggplot2::ggproto(paste0("New", class(old_geom)[1]), old_geom,
handle_na = new_setup)

new_geom$default_aes <- change_name(new_geom$default_aes, old_aes, new_aes)
new_geom$non_missing_aes <- change_name(new_geom$non_missing_aes, old_aes, new_aes)
new_geom$required_aes <- change_name(new_geom$required_aes, old_aes, new_aes)
new_geom$optional_aes <- change_name(new_geom$optional_aes, old_aes, new_aes)

layer$geom <- new_geom

old_stat <- layer$stat

old_setup2 <- old_stat$handle_na
new_setup <- function(self, data, params) {
colnames(data)[colnames(data) %in% new_aes] <- original_aes
old_setup2(data, params)
}

new_stat <- ggplot2::ggproto(paste0("New", class(old_stat)[1]), old_stat,
handle_na = new_setup)

new_stat$default_aes <- change_name(new_stat$default_aes, old_aes, new_aes)
new_stat$non_missing_aes <- change_name(new_stat$non_missing_aes, old_aes, new_aes)
new_stat$required_aes <- change_name(new_stat$required_aes, old_aes, new_aes)
new_stat$optional_aes <- change_name(new_stat$optional_aes, old_aes, new_aes)

layer$stat <- new_stat

layer$mapping <- change_name(layer$mapping, old_aes, new_aes)
layer
}

bump_aes.list <- function(layer, new_aes) {
old_aes <- names(layer)[remove_new(names(layer)) %in% new_aes]
bump_aes_scales <- function(scales, new_aes) {
lapply(scales, bump_aes_scale, new_aes = new_aes)
}

bump_aes_scale <- function(scale, new_aes) {
old_aes <- scale$aesthetics[remove_new(scale$aesthetics) %in% new_aes]
new_aes <- paste0(old_aes, "_new")

names(layer)[names(layer) %in% old_aes] <- new_aes
layer

scale$aesthetics[scale$aesthetics %in% old_aes] <- new_aes

if (is.character(scale$guide)) {
scale$guide <- match.fun(paste("guide_", scale$guide, sep = ""))()
}
scale$guide$available_aes[scale$guide$available_aes %in% old_aes] <- new_aes
scale
}

bump_aes_labels <- function(labels, new_aes) {
old_aes <- names(labels)[remove_new(names(labels)) %in% new_aes]
new_aes <- paste0(old_aes, "_new")

names(labels)[names(labels) %in% old_aes] <- new_aes
labels
}


change_name <- function(list, old, new) {
UseMethod("change_name")
}
Expand All @@ -156,22 +166,3 @@ change_name.NULL <- function(list, old, new) {
remove_new <- function(aes) {
stringi::stri_replace_all(aes, "", regex = "(_new)*")
}



# Example

library(ggplot2)

vd <- reshape2::melt(volcano)
names(vd) <- c("x", "y", "z")

# point measurements of something (abund) at a few locations
d <- data.frame(x=runif(30, 1, 80), y = runif(30, 1, 60), abund=rnorm(30))

ggplot(mapping = aes(x, y)) +
geom_contour(aes(z = z, color = ..level..), data = vd) +
scale_color_viridis_c(option = "D") +
new_scale_color() + # geoms below can use another color scale!
geom_point(data = d, size = 3, aes(color = abund)) +
scale_color_viridis_c(option = "A")
66 changes: 66 additions & 0 deletions README.Rmd
@@ -0,0 +1,66 @@
---
output: github_document
---

<!-- README.md is generated from README.Rmd. Please edit that file -->

```{r setup, include = FALSE}
knitr::opts_chunk$set(
collapse = TRUE,
comment = "#>",
fig.path = "man/figures/README-",
out.width = "100%"
)
```
# ggnewscale

[![Travis build status](https://travis-ci.org/eliocamp/ggnewscale.svg?branch=master)](https://travis-ci.org/eliocamp/ggnewscale)

`ggnewscale` tries to make it painless to use multiple color and fill scales in `ggplot2`. It's very experimental, so use at your own risk!

For another way of defining multiple scales, you can also try [relayer](https://github.com/clauswilke/relayer).

## Installation

Or you can install the development version from
[GitHub](https://github.com/) with:

``` r
# install.packages("devtools")
devtools::install_github("eliocamp/ggnewscale")
```

## Usage

The main function is `new_scale()` and its aliases `new_scale_color()` and `new_scale_fill()`. When added to a plot, every geom added after them will use a different scale.

As an example, lets overlay some measurements over a contour map of topography using the beloed `volcano`.

```{r}
library(ggplot2)
library(ggnewscale)
# Equivalent to melt(volcano)
topography <- expand.grid(x = 1:nrow(volcano),
y = 1:ncol(volcano))
topography$z <- c(volcano)
# point measurements of something at a few locations
set.seed(42)
measurements <- data.frame(x = runif(30, 1, 80),
y = runif(30, 1, 60),
thing = rnorm(30))
ggplot(mapping = aes(x, y)) +
geom_contour(data = topography, aes(z = z, color = stat(level))) +
# Color scale for topography
scale_color_viridis_c(option = "D") +
# geoms below will use another color scale
new_scale_color() +
geom_point(data = measurements, size = 3, aes(color = thing)) +
# Color scale applied to geoms added after new_scale_color()
scale_color_viridis_c(option = "A")
```




59 changes: 59 additions & 0 deletions README.md
@@ -0,0 +1,59 @@

<!-- README.md is generated from README.Rmd. Please edit that file -->

# ggnewscale

[![Travis build
status](https://travis-ci.org/eliocamp/ggnewscale.svg?branch=master)](https://travis-ci.org/eliocamp/ggnewscale)

`ggnewscale` tries to make it painless to use multiple color and fill
scales in `ggplot2`. It’s very experimental, so use at your own risk\!

For another way of defining multiple scales, you can also try
[relayer](https://github.com/clauswilke/relayer).

## Installation

Or you can install the development version from
[GitHub](https://github.com/) with:

``` r
# install.packages("devtools")
devtools::install_github("eliocamp/ggnewscale")
```

## Usage

The main function is `new_scale()` and its aliases `new_scale_color()`
and `new_scale_fill()`. When added to a plot, every geom added after
them will use a different scale.

As an example, lets overlay some measurements over a contour map of
topography using the beloed `volcano`.

``` r
library(ggplot2)
library(ggnewscale)
# Equivalent to melt(volcano)
topography <- expand.grid(x = 1:nrow(volcano),
y = 1:ncol(volcano))
topography$z <- c(volcano)

# point measurements of something at a few locations
set.seed(42)
measurements <- data.frame(x = runif(30, 1, 80),
y = runif(30, 1, 60),
thing = rnorm(30))

ggplot(mapping = aes(x, y)) +
geom_contour(data = topography, aes(z = z, color = stat(level))) +
# Color scale for topography
scale_color_viridis_c(option = "D") +
# geoms below will use another color scale
new_scale_color() +
geom_point(data = measurements, size = 3, aes(color = thing)) +
# Color scale applied to geoms added after new_scale_color()
scale_color_viridis_c(option = "A")
```

<img src="man/figures/README-unnamed-chunk-1-1.png" width="100%" />
1 change: 1 addition & 0 deletions ggnewscale.Rproj
Expand Up @@ -17,3 +17,4 @@ StripTrailingWhitespace: Yes
BuildType: Package
PackageUseDevtools: Yes
PackageInstallArgs: --no-multiarch --with-keep.source
PackageRoxygenize: rd,collate,namespace
Binary file added man/figures/README-unnamed-chunk-1-1.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit e3fee01

Please sign in to comment.