Skip to content

Fix tick misalignment and revert transformation inheritance in sec.axis (#2978) #3040

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 24 commits into from
Apr 11, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
2064059
revert 2796 and 2805; adjust sec axis breaks init to respect non-iden…
dpseidel Nov 13, 2018
6fe5cf5
add rescaling to secondary axis
dpseidel Nov 14, 2018
0c258c8
update vdiffr svgs
dpseidel Nov 14, 2018
e211ffe
add positioning and visual tests for sec.axis
dpseidel Nov 15, 2018
b7ac60c
use data_frame() in tests
dpseidel Nov 15, 2018
c036488
Merge remote-tracking branch 'upstream/master' into sec_scale
dpseidel Nov 19, 2018
6f35473
Merge remote-tracking branch 'upstream/master' into revert2796
dpseidel Nov 29, 2018
3c3994f
Add a temporary patch for date time axes
dpseidel Nov 29, 2018
06dae1e
merge tests from sec_scale branch
dpseidel Nov 29, 2018
cfa1086
clean up
dpseidel Nov 30, 2018
b7a85be
Merge remote-tracking branch 'upstream/master' into revert2796
dpseidel Dec 13, 2018
19b13c2
Merge remote-tracking branch 'upstream/master' into revert2796
dpseidel Dec 15, 2018
fded720
update sec.axis test cases
dpseidel Dec 15, 2018
225eb36
apply monotonicity test on unexpanded range
dpseidel Jan 4, 2019
64d6cbc
updated visual tests
dpseidel Jan 4, 2019
3a36f26
update docs for sec axis
dpseidel Jan 19, 2019
6fde6f3
Merge remote-tracking branch 'upstream/master' into revert2796
dpseidel Jan 19, 2019
d89c7e9
add news bullet for bug fix
dpseidel Jan 19, 2019
4625f88
Merge remote-tracking branch 'upstream/master' into revert2796
dpseidel Jan 20, 2019
62d6a46
Merge remote-tracking branch 'upstream/master' into revert2796
dpseidel Jan 21, 2019
6643849
fix data.frame in example
dpseidel Jan 21, 2019
2b29405
test transformation by subtraction
dpseidel Feb 1, 2019
a656a08
Merge branch 'master' into revert2796
dpseidel Feb 1, 2019
2f8b35d
Merge branch 'master' into revert2796
thomasp85 Apr 11, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ core developer team.

* `stat_bin()` will now error when the number of bins exceeds 1e6 to avoid
accidentally freezing the user session (@thomasp85).

* `sec_axis()` now places ticks accurately when using nonlinear transformations (@dpseidel, #2978).

* `facet_wrap()` and `facet_grid()` now automatically remove NULL from facet
specs, and accept empty specs (@yutannihilation, #3070, #2986).
Expand Down
73 changes: 56 additions & 17 deletions R/axis-secondary.R
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,15 @@
#' `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.
#'
#' As of v3.1, date and datetime scales have limited secondary axis capabilities.
#' Unlike other continuous scales, secondary axis transformations for date and datetime scales
#' must respect their primary POSIX data structure.
#' This means they may only be transformed via addition or subtraction, e.g.
#' `~. + hms::hms(days = 8)`, or
#' `~.- 8*60*60`. Nonlinear transformations will return an error.
#' To produce a time-since-event secondary axis in this context, users
#' may consider adapting secondary axis labels.
#'
#' @examples
#' p <- ggplot(mtcars, aes(cyl, mpg)) +
#' geom_point()
Expand Down Expand Up @@ -56,7 +65,7 @@
#' price = seq(20, 200000, length.out = 10)
#' )
#'
#' # useful for labelling different time scales in the same plot
#' # This may useful for labelling different time scales in the same plot
#' ggplot(df, aes(x = dx, y = price)) + geom_line() +
#' scale_x_datetime("Date", date_labels = "%b %d",
#' date_breaks = "6 hour",
Expand Down Expand Up @@ -136,6 +145,7 @@ AxisSecondary <- ggproto("AxisSecondary", NULL,
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.waive(self$breaks)) self$breaks <- scale$trans$breaks
if (is.derived(self$labels)) self$labels <- scale$labels
},

Expand All @@ -148,37 +158,66 @@ AxisSecondary <- ggproto("AxisSecondary", NULL,
)
},

break_info = function(self, range, scale) {
if (self$empty()) return()

# Get original range before transformation
inv_range <- scale$trans$inverse(range)
mono_test = function(self, scale){
range <- scale$range$range
along_range <- seq(range[1], range[2], length.out = self$detail)
old_range <- scale$trans$inverse(along_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 monotonicity
if (length(unique(sign(diff(full_range)))) != 1)
stop("transformation for secondary axes must be monotonic")
},

break_info = function(self, range, scale) {
if (self$empty()) return()

# Test for monotonicity on unexpanded range
self$mono_test(scale)

# Get scale's original range before transformation
along_range <- seq(range[1], range[2], length.out = self$detail)
old_range <- scale$trans$inverse(along_range)

# Create mapping between primary and secondary range
full_range <- self$transform_range(old_range)

# Get break info for the secondary axis
new_range <- range(scale$transform(full_range), na.rm = TRUE)
sec_scale <- self$create_scale(new_range, scale)
range_info <- sec_scale$break_info()
new_range <- range(full_range, na.rm = TRUE)

# patch for date and datetime scales just to maintain functionality
# works only for linear secondary transforms that respect the time or date transform
if (scale$trans$name %in% c("date", "time")){
temp_scale <- self$create_scale(new_range, trans = scale$trans)
range_info <- temp_scale$break_info()
names(range_info) <- paste0("sec.", names(range_info))
return(range_info)
}

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, primary) {
create_scale = function(self, range, trans = identity_trans()) {
scale <- ggproto(NULL, ScaleContinuousPosition,
name = self$name,
breaks = self$breaks,
labels = self$labels,
limits = range,
expand = c(0, 0),
trans = primary$trans
name = self$name,
breaks = self$breaks,
labels = self$labels,
limits = range,
expand = c(0, 0),
trans = trans
)
scale$train(range)
scale
Expand Down
11 changes: 10 additions & 1 deletion man/sec_axis.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

104 changes: 104 additions & 0 deletions tests/figs/sec-axis/sec-axis-custom-transform.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading