Giter Club home page Giter Club logo

ggmagnify's Introduction

ggmagnify

r-universe R-CMD-check Lifecycle: experimental

ggmagnify creates a magnified inset of part of a ggplot object. The magnified area can be a (rounded) rectangle, an ellipse, a convex hull of points, or an arbitrary shape. Borders can be drawn around the target area and the inset, along with projection lines and/or shading between the two. The inset can have a drop shadow.

You can install ggmagnify from r-universe:

install.packages("ggmagnify", repos = c("https://hughjonesd.r-universe.dev", 
                 "https://cloud.r-project.org"))

This will install the latest github release (currently ggmagnify 0.4.0).

Or install the development version from GitHub with:

# install.packages("remotes")
remotes::install_github("hughjonesd/ggmagnify")

Basic inset

To create an inset, use geom_magnify(from, to). from can be a vector giving the four corners of the area to magnify: from = c(xmin, xmax, ymin, ymax).

Similarly, to specifies where the magnified inset should go: to = c(xmin, xmax, ymin, ymax).

library(ggplot2)
library(ggmagnify)

ggp <- ggplot(dv, aes(Position, NegLogP)) + 
  geom_point(color = "darkblue", alpha = 0.8, size = 0.8) +
  labs(title = "GWAS p-values for cognitive function",
       subtitle = "Davies et al. (2018).", y = "-log(p)")

from <- c(xmin = 9.75e7, xmax = 9.95e7, ymin = 16, ymax = 28)
# Names xmin, xmax, ymin, ymax are optional:
to <- c(2e8 - 2e7, 2e8 + 2e7,10, 26)

ggp + geom_magnify(from = from, to = to)

Inset with shadow

loadNamespace("ggfx")
#> <environment: namespace:ggfx>

ggp + geom_magnify(from = from, to = to, 
                   shadow = TRUE)

Rounded corners

New in version 0.3.0, use corners to give a proportional radius for rounded corners.

ggp + geom_magnify(from = from, to = to, 
                   corners = 0.1, shadow = TRUE)

Ellipse

This requires R 4.1 or higher, and an appropriate graphics device.

ggp + geom_magnify(from = from, to = to, 
                   shape = "ellipse", shadow = TRUE)

Pick points to magnify

To choose points to magnify, map from in an aes():

ggpi <- ggplot(iris, aes(Sepal.Width, Sepal.Length, colour = Species)) +
              geom_point() + xlim(c(1.5, 6))

ggpi + geom_magnify(aes(from = Species == "setosa" & Sepal.Length < 5), 
                    to = c(4, 6, 6, 7.5))

Faceting

ggpi +
  facet_wrap(vars(Species)) +
  geom_magnify(aes(from = Sepal.Length > 5 & Sepal.Length < 6.5), 
                    to = c(4.5, 6, 6, 7.5),
                    shadow = TRUE)

Magnify an arbitrary region (experimental)

Use shape = "outline" to magnify the convex hull of a set of points:

library(dplyr)

starwars_plot <- starwars |> 
  mutate(Human = species == "Human") |> 
  select(mass, height, Human) |> 
  na.omit() |> 
  ggplot(aes(mass, height, color = Human)) + 
    geom_point() + xlim(0, 220) + ylim(0, 250) + 
    theme_dark() +
    theme(panel.grid = element_line(linetype = 2, colour = "yellow"), 
          axis.line = element_blank(), 
          panel.background = element_rect(fill = "black"),
          legend.key = element_rect(fill= "black"),
          rect = element_rect(fill = "black"),
          text = element_text(colour = "white")) +
    scale_colour_manual(values = c("TRUE" = "red", "FALSE" = "lightblue")) +
    ggtitle("Mass and height of Star Wars characters",
            subtitle = "Humans magnified")

starwars_plot +
  geom_magnify(aes(from = Human), to = c(30, 200, 0, 120), shadow = TRUE,
               shadow.args = list(colour = "yellow", sigma = 10,
                                  x_offset = 2, y_offset = 5),
               alpha = 0.8, colour = "yellow", linewidth = 0.6, 
               shape = "outline", expand = 0.2)
#> Warning: Removed 1 rows containing missing values (`geom_point()`).

Use a grob or data frame to magnify any shape:

s <- seq(0, 2*pi, length = 7)
hex <- data.frame(x = 3 + sin(s)/2, y = 6 + cos(s)/2) 

ggpi + geom_magnify(from = hex, 
                    to = c(4, 6, 5, 7), shadow = TRUE, aspect = "fixed")

Maps (experimental)

With maps, shape = "outline" magnifies just the selected map polygons:

usa <- sf::st_as_sf(maps::map("state", fill=TRUE, plot =FALSE))

ggpm <- ggplot(usa) +
          geom_sf(aes(fill = ID == "texas"), colour = "grey20") +
          coord_sf(default_crs = sf::st_crs(4326), ylim = c(10, 50)) + 
          theme(legend.position = "none") +
          scale_fill_manual(values = c("TRUE" = "red", "FALSE" = "steelblue4"))


ggpm + geom_magnify(aes(from = ID == "texas"),
                    to = c(-125, -98, 10, 30), 
                    shadow = TRUE, linewidth = 1, colour = "orange2",
                    shape = "outline", 
                    aspect = "fixed", 
                    expand = 0) 

Axes

ggp + 
  scale_x_continuous(labels = NULL) + 
  geom_magnify(from = from, to = to, 
               axes = "xy")

Projection lines and borders

Colour and linetype

ggp + 
  geom_magnify(from = from, to = to,
               colour = "darkgreen", linewidth = 0.5, proj.linetype = 3)

Projection line styles

ggpi <- ggplot(iris, aes(Sepal.Width, Sepal.Length, colour = Species)) +
              geom_point()
from2 <- c(3, 3.5, 6.5, 7)
to2 <- c(2.75, 3.75, 5, 6)

ggpi + 
  geom_magnify(from = from2, to = to2,
               proj = "facing") # the default

ggpi + 
  geom_magnify(from = from2, to = to2,
               proj = "corresponding") # always project corner to corner

ggpi + 
  geom_magnify(from = from2, to = to2,
               proj = "single") # just one line

Projection fill

ggpi + 
  geom_magnify(from = from2, to = to2,
               proj.fill = alpha("yellow", 0.2)) # fill between the lines

ggpi + 
  geom_magnify(from = from2, to = to2, shape = "ellipse",
               proj.fill = alpha("orange", 0.2)) # works with any shape

Tips and tricks

Graphics devices

geom_magnify() uses masks. This requires R version 4.1.0 or higher, and a graphics device that supports masking. If you are using knitr, you may have luck with the ragg_png device (which was used to create this README). If your device doesn’t support masks, only shape = "rect" will work, and the plot inset will not be clipped to the panel area.

Adding layers to the inset

geom_magnify() stores the plot when it is added to it. So, order matters:

ggpi <- ggplot(iris, aes(Sepal.Width, Sepal.Length, colour = Species)) +
              geom_point() + xlim(2, 6)

from3 <-  c(2.5, 3.5, 6, 7)
to3 <- c(4.7, 6.1, 4.3, 5.7)
ggpi + 
  geom_smooth() + 
  geom_magnify(from = from3, to = to3)
#> `geom_smooth()` using method = 'loess' and formula = 'y ~ x'
#> `geom_smooth()` using method = 'loess' and formula = 'y ~ x'

# Print the inset without the smooth:
ggpi +
  geom_magnify(from = from3, to = to3) +
  geom_smooth()
#> `geom_smooth()` using method = 'loess' and formula = 'y ~ x'

For complex modifications to the inset, set plot explicitly:

booms <- ggplot(faithfuld, aes(waiting, eruptions)) +
         geom_contour_filled(aes(z = density), bins = 50) +
         scale_fill_viridis_d(option = "B") + 
         theme(legend.position = "none")

booms_inset <- booms + 
  geom_point(data = faithful, color = "red", fill = "white", alpha = 0.7, 
             size = 2, shape = "circle filled") + 
  coord_cartesian(expand = FALSE)

shadow.args <- list(
  colour = alpha("grey80", 0.8),
  x_offset = 0,
  y_offset = 0,
  sigma = 10
)

booms + geom_magnify(from = c(78, 90, 4.0, 4.8), to = c(70, 90, 1.7, 3.3),
                     colour = "white", shape = "ellipse",
                     shadow = TRUE, shadow.args = shadow.args,
                     plot = booms_inset)

Draw an inset outside the plot region

ggp + 
  coord_cartesian(clip = "off") + 
  theme(plot.margin = ggplot2::margin(10, 60, 10, 10)) +
  geom_magnify(from = from, to = to + c(0.5e8, 0.5e8, 0, 0), 
               shadow = TRUE)

Keep grid lines the same

To make sure the inset uses the same grid lines as the main graph, set breaks in scale_x and scale_y:

ggp2 <- ggplot(iris, aes(Sepal.Width, Sepal.Length, color = Species)) + 
        geom_point() +
        theme_classic() + 
        theme(panel.grid.major = element_line("grey80"),
              panel.grid.minor = element_line("grey90"))

# different grid lines:
ggp2 + 
  geom_magnify(from = c(2.45, 3.05, 5.9, 6.6), to = c(3.4, 4.4, 5.5, 6.6),
               shadow = TRUE) 

# fix the grid lines:
ggp2 +
  scale_x_continuous(breaks = seq(2, 5, 0.5)) + 
  scale_y_continuous(breaks = seq(5, 8, 0.5)) + 
  geom_magnify(from = c(2.45, 3.05, 5.9, 6.6), to = c(3.4, 4.4, 5.5, 6.6),
               shadow = TRUE) 

Recomputing data

Use recompute if you want to recompute smoothers, densities, etc. in the inset.

 
df <- data.frame(x = seq(-5, 5, length = 500), y = 0)
df$y[abs(df$x) < 1] <- sin(df$x[abs(df$x) < 1])
df$y <- df$y + rnorm(500, mean = 0, sd = 0.25)

ggp2 <- ggplot(df, aes(x, y)) + 
  geom_point() + 
  geom_smooth(method = "loess", formula = y ~ x) + 
  ylim(-5, 5)

# The default:
ggp2 + geom_magnify(from = c(-1.25, 1.25, -1, 1),
                    to = c(2, 5, 1, 5))

# Recomputing recalculates the smooth for the inset:
ggp2 + geom_magnify(from = c(-1.25, 1.25, -1, 1),
                    to = c(2, 5, 1, 5),
                    recompute = TRUE)

Magnify twice

data <- data.frame(
  x = runif(4000), 
  y = runif(4000)
)
ggm_unif <- ggplot(data, aes(x, y)) +
            coord_cartesian(expand = FALSE) +
            geom_density2d_filled(bins = 50, linewidth = 0, n = 200) +
            geom_point(color='white', alpha = .5, size = .5) + 
            theme(legend.position = "none")


ggm_unif + 
  geom_magnify(from = c(0.05, 0.15, 0.05, 0.15), to = c(0.2, 0.4, 0.2, 0.4), 
               colour = "white", proj.linetype = 1, linewidth = 0.6) +
  geom_magnify(from = c(0.25, 0.35, 0.25, 0.35), to = c(0.45, 0.85, 0.45, 0.85), 
               expand = 0, colour ="white", proj.linetype = 1)

An inset within an inset is a bit more complex, but also doable:

ggp <- data.frame(x = rnorm(1e5), y = rnorm(1e5), 
                  colour = sample(8L, 1e5, replace = TRUE)) |> 
  ggplot(aes(x = x, y = y, colour = factor(colour))) + 
  scale_color_brewer(type = "qual", palette = 2) +
  geom_point(alpha = 0.12, size = 0.7) + 
  lims(x = c(-3,3), y = c(-3,3)) +
  theme_classic() + theme(panel.grid = element_blank(), 
                          axis.line = element_blank(), 
                          plot.background = element_rect(fill = "black"),
                          panel.background = element_rect(fill = "black"),
                          title = element_text(colour = "white"),
                          legend.position = "none")

ggpm <- ggp + 
  lims(x = c(-0.3, 0.3), y = c(-0.3, 0.3)) + 
  geom_magnify(from = c(-0.03, 0.03, -0.03, 0.03),
               to = c(-0.3, -0.1, -0.3, -0.1),
               expand = FALSE, colour = "white")
#> Scale for x is already present.
#> Adding another scale for x, which will replace the existing scale.
#> Scale for y is already present.
#> Adding another scale for y, which will replace the existing scale.

ggp + 
  geom_magnify(plot = ggpm, 
               from = c(-0.3, 0.3, -0.3, 0.3),
               to = c(-3, -1, -3, -1),
               expand = FALSE, colour = "white") +
  labs(title = "Normal data", 
       subtitle = "The distribution gets more uniform as you zoom in")
#> Warning: Removed 570 rows containing missing values (`geom_point()`).

Acknowledgements

ggmagnify was inspired by this post and motivated by making these plots.

Data for the GWAS plots comes from:

Davies et al. (2018) ‘Study of 300,486 individuals identifies 148 independent genetic loci influencing general cognitive function.’ Nature Communications.

Data was trimmed to remove overlapping points.

ggmagnify's People

Contributors

hughjonesd avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

ggmagnify's Issues

Fill area between lines of magnified inset

Hi, this is a very nice package! It would be cool if it was possible to fill the area between the lines so that we can get the red area like below:

image

Here's the code I have so far (coming from this SO answer):

library(ggplot2)
library(sf)
#> Linking to GEOS 3.11.2, GDAL 3.6.2, PROJ 9.2.0; sf_use_s2() is TRUE
library(rnaturalearth)
library(ggmagnify)

europe <- ne_countries(continent = "Europe", scale = 'medium',
                       type = 'map_units', returnclass = 'sf') 

your_map <- ggplot(europe) +
  geom_sf() +
  xlim(c(-20, 50)) +
  ylim(c(35, 70)) +
  theme_linedraw()

ggm <- your_map + 
  geom_magnify(
    from = c(9, 15, 42, 45),
    to = c(22, 36, 46, 52),
    colour = "red",
    linewidth = 1
  ) 

ggm


Add a 👍 on this post to show support for this feature.

Using lubridate dates on the x-axis

This is a fantastic package. I have, however, not been able to use ggmagnify when I am using a lubridate date on an axis. Any insight would be appreciated!

Magnified plot not filling the whole plot area

First of all, amazing package! Thank you so much!

Describe the bug
I cannot make the magnified plot to fill the whole area of the inset. I am using the option with a provided inset plot, but it seems that it is being constrained somehow and not filling the whole inset area declare in the to argument. When I tried with annotate_custom it works fine, but with geom_magnify I could not make it work. I've set limits, breaks, and still nothing.

image

To Reproduce

library(tidyverse)
library(ggmagnify)

pca <- read.table("https://raw.githubusercontent.com/paulobarros/assets/main/pcs.txt", col.names = c("ind","PC1","PC2","PC3","gg")) |>
    select(-ind) |>
    mutate(gg = factor(gg)) |>
    mutate(colors = case_when(
        gg == "BEL" ~ alpha("#124559",.75),
        gg == "BRO" ~ alpha("#e0aaff",.75),
        gg == "CC" ~ alpha("#00b4d8",.75),
        gg == "China" ~ alpha("#80ed99",.75),
        gg == "WEL" ~ alpha("#bb9457",.75),
        gg == "TT" ~ alpha("#ff006e",.75),
        gg == "Nigeria" ~ alpha("#fca311",.75),
        gg == "Indio" ~ alpha("#6c757d",.75),
        gg == "GG" ~ alpha("#f07167",.75)
    ),
    lines = case_when(
        gg == "BEL" ~ alpha("#124559",.95),
        gg == "BRO" ~ alpha("#e0aaff",.95),
        gg == "CC" ~ alpha("#00b4d8",.95),
        gg == "China" ~ alpha("#80ed99",.95),
        gg == "WEL" ~ alpha("#bb9457",.95),
        gg == "TT" ~ alpha("#ff006e",.95),
        gg == "Nigeria" ~ alpha("#fca311",.95),
        gg == "Indio" ~ alpha("#6c757d",.95),
        gg == "GG" ~ alpha("#f07167",.95)
    ))

ggColors <- pca$colors |> unique()
ggLines <- pca$lines |> unique()

from <- c(xmin = -0.015,
          xmax = 0.015,
          ymin = -0.065,
          ymax = -0.02)


to <- c(0.05, 0.2, -0.04,0.03)

inset <- pca |> 
filter(!gg %in% c("TT","China")) |>
ggplot(aes(x = PC1, y = PC2,)) +
geom_point(aes(fill = gg),
color = alpha("black",.75),
shape = 21,
size = 6,
show.legend = FALSE) +
scale_fill_manual(values = ggColors |> unique()) +
labs(x = element_blank(), y  = element_blank()) +
#scale_color_manual(values = ggLines |> unique()) +
theme_bw(base_family = "Barlow Condensed", base_size = 16) +
scale_x_continuous(breaks = (seq(-0.005, 0.001, 0.002)), limits = c(-0.005,0.0011)) +
scale_y_continuous(breaks = (seq(-0.06, -0.023, 0.015)), limits = c(-0.06,-0.023))

pca |> 
#filter(!gg %in% c("TT","China")) |>
ggplot(aes(x = PC1, y = PC2,)) +
geom_point(aes(fill = gg),
color = alpha("black",.75),
shape = 21,
size = 6) +
scale_fill_manual(values = ggColors |> unique()) +
labs(x = "PC1 (10.34%)",
     y = "PC2 (7.73%)",) +
#scale_color_manual(values = ggLines |> unique()) +
theme_bw(base_family = "Barlow Condensed", base_size = 16) +
geom_magnify(from = from,
            to = to,
            shadow = TRUE,
            plot = inset,
            axes = "xy"
            )

Expected behavior
I would like the result to be like this version I was able to make with annotate_custom

image

Other information

Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Pop!_OS 22.04 LTS

other attached packages:
 [1] ggmagnify_0.4.0.9000 here_1.0.1          
 [3] plotly_4.10.4        lubridate_1.9.3     
 [5] forcats_1.0.0        stringr_1.5.1       
 [7] dplyr_1.1.4          purrr_1.0.2         
 [9] readr_2.1.5          tidyr_1.3.1         
[11] tibble_3.2.1         ggplot2_3.5.0       
[13] tidyverse_2.0.0     

Separate features of main and inset plot?

I am trying to illustrate some basic logistic regression concepts using plots. gg_magnify is almost perfect for what I want to do with the plot of predicted odds vs a continuous covariate (see figure). There are two issues and I'm assuming it's not easy to avoid them as gg_magnify inherits whatever you specify with ggplot. You'll notice with the inset that I've specified a y axis (which works great for the inset showing odds of 1 - 5) but because I want to highlight the full range of odds - which goes to up to several thousand - those numbers are overlapped on the main plot. The second issue is that I'm wanting to highlight the predicted odds at two set values of the covariate. This again works well using geom_label in the inset but I notice that the labels remain in the main plot, which I don't really need.

Any way around these two problems?

The full code to generate the plots is below:

library(tidyverse)
library(cowplot)
library(ggpubr)
library(ggmagnify)
library(emmeans)

# Simulate data ----
n <- 1000                    # don't change this unless necessary (plots might be fragile)
set.seed(1234)
x  <-  rnorm(n, 486, 142)    # generate macular hole inner opening data with mean 486 and sd = 152
z  <-  10.89 - 0.016 * x     # generate variable that is linear combination of intercept = 10.89 and coefficient for macular hole -0.016 (logit scale)
pr  <-  1/(1 + exp(-z))      # generate probabilities from this
y  <-  rbinom(n, 1, pr)      # generate outcome variable as a function of those probabilities

# Create dataframe from these:
df <-  data.frame(y = y, x = x, z = z, pr = pr)
df <- df |> 
  filter(x > 100) # only include those with thickness > 100

# Logistic regression model ----
summary(mod_logistic <- glm(y ~ x, data = df, family = "binomial"))

# Predictions ----
# Create new df to predict on new values of x
new_dat <- data.frame(x = seq(from = 0, to = 1200, length.out = 100))
# Predict new fitted values and SE's on logodds scale
pred_logodds <- predict(mod_logistic, newdata = new_dat, type = "link", se = TRUE)
new_dat <- cbind(new_dat, pred_logodds)
# Create new df of predictions
predictions <- new_dat |> 
  rename(pred_logodds_est = fit) |> 
  mutate(pred_logodds_LL = pred_logodds_est - (1.96 * se.fit),
         pred_logodds_UL = pred_logodds_est + (1.96 * se.fit)) |> 
  select(-c(se.fit, residual.scale))
# Predict new fitted values and SE's on odds scale
predictions <- predictions |> 
  mutate(pred_odds_est = exp(pred_logodds_est),
         pred_odds_LL = exp(pred_logodds_LL),
         pred_odds_UL = exp(pred_logodds_UL))
# Predict new fitted values and SE's on probability scale
pred_probs <- predict(mod_logistic, newdata = new_dat, type = "response", se = TRUE)
new_dat <- cbind(new_dat[1], pred_probs)
new_dat <- new_dat |> 
  mutate(pred_probs_LL = fit - (1.96 * se.fit),
         pred_probs_UL = fit + (1.96 * se.fit))
# Add predicted probs and CIs to predictions df
predictions <- cbind(predictions, 
                     pred_probs_est = new_dat$fit, 
                     pred_probs_LL = new_dat$pred_probs_LL,
                     pred_probs_UL = new_dat$pred_probs_UL)

# Plots ----
p1 <- ggplot(predictions, aes(x = x, y = pred_logodds_est)) + 
  geom_ribbon(aes(ymin = pred_logodds_LL, ymax = pred_logodds_UL), alpha = 0.2) + 
  geom_line(color = "cornflowerblue", linewidth = 1) +
  geom_point(data = df, aes(x = x, y = y), size = 2, alpha = 0.1) +
  annotate("text", x = 1150, y = 6, label = "log-odds", size = 10, color = "#E7B800") +
  scale_x_continuous(limits = c(0, 1200), breaks = seq(0, 1200, by = 100)) +
  scale_y_continuous(limits = c(-100, 100), breaks = seq(-100, 100, by = 2)) +
  coord_cartesian(xlim = c(0, 1200), ylim = c(-8, 8)) +
  geom_vline(xintercept = 600, color = "red", linetype = "dotted", linewidth = 0.6) +
  geom_vline(xintercept = 700, color = "red", linetype = "dotted", linewidth = 0.6) +
  geom_label(aes(x = 630, y = 2), label = "1.323", color = "red", size = 3) +
  geom_segment(aes(x = 615, y = 1.72, xend = 600, yend = 1.323, color = "red"), 
               linewidth = 0.2, alpha = 0.05, arrow = arrow(length = unit(0.01, "npc"))) +
  geom_label(aes(x = 670, y = -1), label = "-0.181", color = "red", size = 3) +
  geom_segment(aes(x = 686, y = -0.7, xend = 700, yend = -0.181, color = "red"), 
               linewidth = 0.2, alpha = 0.05, arrow = arrow(length = unit(0.01, "npc"))) +
  guides(color = "none") + 
  theme_bw(base_size = 20) +
  ylab("") +
  theme(axis.title.x = element_blank(), axis.text.x = element_blank())
p2 <- ggplot(predictions, aes(x = x, y = pred_odds_est)) + 
  geom_ribbon(aes(ymin = pred_odds_LL, ymax = pred_odds_UL), alpha = 0.2) + 
  geom_line(color = "cornflowerblue", linewidth = 1) +
  geom_point(data = df, aes(x = x, y = y), size = 2, alpha = 0.1) +
  annotate("text", x = 1150, y = 6000, label = "odds", size = 10, color = "#E7B800") +
  scale_x_continuous(limits = c(0, 1200), breaks = seq(0, 1200, by = 100)) +
  scale_y_continuous(limits = c(-20, 1000000), breaks = c(0, 1 ,2, 3, 4, 5, seq(1000, 1000000, by = 1000))) +
  coord_cartesian(xlim = c(0, 1200), ylim = c(0, 7000)) +
  geom_vline(xintercept = 600, color = "red", linetype = "dotted", linewidth = 0.6) +
  geom_vline(xintercept = 700, color = "red", linetype = "dotted", linewidth = 0.6) +
  geom_label(aes(x = 630, y = 4), label = "3.755", color = "red", size = 3) +
  geom_segment(aes(x = 615, y = 3.85, xend = 600, yend = 3.755, color = "red"), 
               linewidth = 0.2, alpha = 0.05, arrow = arrow(length = unit(0.01, "npc"))) +
  geom_label(aes(x = 730, y = 1.5), label = "0.834", color = "red", size = 3) +
  geom_segment(aes(x = 715, y = 1.34, xend = 700, yend = 0.834, color = "red"), 
               linewidth = 0.2, alpha = 0.05, arrow = arrow(length = unit(0.01, "npc"))) +
  guides(color = "none") + 
  ylab("") +
  theme_bw(base_size = 20) +
  theme(axis.title.x = element_blank(), axis.text.x = element_blank()) +
  geom_magnify(from = c(xmin = 500, xmax = 1000, ymin = 0, ymax = 5), 
               to = c(xmin = 480, xmax = 1000, ymin = 1000, ymax = 5000), shadow = T, axes = "xy")
p3 <- ggplot(predictions, aes(x = x, y = pred_probs_est)) + 
  geom_ribbon(aes(ymin = pred_probs_LL, ymax = pred_probs_UL), alpha = 0.2) + 
  geom_line(color = "cornflowerblue", linewidth = 1) +
  geom_point(data = df, aes(x = x, y = y), size = 2, alpha = 0.1) +
  annotate("text", x = 1150, y = 0.8, label = "probability", size = 10, color = "#E7B800") +
  scale_x_continuous(limits = c(0, 1200), breaks = seq(0, 1200, by = 100)) +
  scale_y_continuous(limits = c(0, 1), breaks = seq(0, 1, by = 0.2)) +
  geom_vline(xintercept = 600, color = "red", linetype = "dotted", linewidth = 0.6) +
  geom_vline(xintercept = 700, color = "red", linetype = "dotted", linewidth = 0.6) +
  ylab("") + xlab("Macular hole thickness") +
  theme_bw(base_size = 20)
ggarrange(p1, p2, p3, align = "v", ncol = 1)
ggsave("combined.pdf", width = 20, height = 16)
[combined.pdf](https://github.com/hughjonesd/ggmagnify/files/13574906/combined.pdf)

combined.pdf

A problem with multiple `geom_magnify()` layers

IND_COLL_data_Rus.csv
Figure 1_example

When I try to add a second geom_magnify() layer to my plot (attached), an even more magnified fragment of the first added layer emerges in the top right corner of my map, and I have no idea how to fix this. Any help/advice appreciated. Code, data, and an example plot with a double-magnified layer are attached

Thanks in advance




# preliminary data transformation
[IND_COLL_data_Rus.csv](https://github.com/hughjonesd/ggmagnify/files/14462368/IND_COLL_data_Rus.csv)

# load data for plotting
df_classes <- 
![Figure 1_example](https://github.com/hughjonesd/ggmagnify/assets/93224567/5bd7efcd-a04d-4550-a19c-7d844771440e)
read.csv("IND_COLL_data_Rus.csv")

# obtain geoSpatial data for Russia
gadm <- raster::getData('GADM', country='RUS', level=1)
gadm1 <- raster::getData('GADM', country='UKR', level=1)
gadm1 <- gadm1[gadm1$NAME_1=="Crimea", ]
gadm <- rbind(gadm, gadm1)

for(i in 1:length(gadm@polygons)){
  for(j in 1:length(gadm@polygons[[i]]@Polygons)){
    gadm@polygons[[i]]@Polygons[[j]]@coords[,1]<- sapply(gadm@polygons[[i]]@Polygons[[j]]@coords[,1], function(x){
      if(x < 0){
        x<-359.999+x
      }
      else{x}
    })
  }
}


data_map <- tidy(gadm)
data_map$id <- as.numeric(data_map$id)
data_map1 <- left_join(data_map, df_classes, by =c("id" = "n_map"))
data_map1$na <- as.factor(ifelse(is.na(data_map1$IND_COLL), 1, 0))
table(data_map1$na)
tapply(data_map1$id, data_map1$oblast, mean) 
colnames(data_map1 )

 Define the location of a magnified map of St. Petersburg
from_SPb <- c(xmin = 28, xmax = 32, ymin = 58, ymax = 62)
# Names xmin, xmax, ymin, ymax are optional:
to_SPb <- c(20, 40, 72, 80)

# Define the location of a magnified map of Moscow
from_M <- c(xmin = 36, xmax = 39, ymin = 55, ymax = 58)
to_M <- c(70, 85, 41, 47)

# create the cultural map of Russia
map_base <- ggplot(data_map1, aes(x = long, y = lat, group = group, colour = IND_COLL)) + 
  geom_polygon(data = data_map1, colour='NA', linewidth = 0.01,
               aes(x=long, y=lat, fill=IND_COLL)) +
  #geom_polygon_pattern(data = data_map1, colour='black', size = 0.1,
   #                    aes(x=long, y=lat, fill=IND_COLL, pattern = na), 
    #                   pattern_color = "grey20",
    #                   pattern_fill = "grey20",
     #                  #pattern_density = 0.1,
     #                  pattern_linetype = 2,
      #                 pattern_spacing = 0.01,
      #                 pattern_size = 0.005) +
  #scale_pattern_manual(values = c('none', 'stripe'), guide = "none") +
  scale_fill_gradient(low = "gray1", high = "gray95", na.value = "white")+
  geom_polygon(data = subset(data_map1, id == 38), colour='white', fill = 'white')+
  theme(axis.line=element_blank(),axis.text.x=element_blank(),
        axis.text.y=element_blank(),axis.ticks=element_blank(),
        axis.title.x=element_blank(),
        axis.title.y=element_blank(),
        legend.position = c(0.85, 0.1),
        legend.direction = "horizontal",
        legend.key.size = unit(0.3, 'cm'),
        legend.text = element_text(colour = "black", #brewer.pal(11, 'RdYlGn')[4:9], 
                                      size = 4),
        legend.box.margin = margin(t = 0, b = 0),
        plot.margin = margin(t = 0, 
                             r = 0,  
                             b = 0,  
                             l = 0),
        panel.grid = element_blank(),
        panel.spacing = unit(0, "cm", data = NULL),
        panel.background = element_blank())+
  labs(x=NULL, y = NULL, fill = NULL)

map_base <- map_base+
  geom_magnify(from = from_SPb, to = to_SPb, linewidth = 0.15) +
  geom_magnify(from = from_M, to = to_M, linewidth = 0.15) 

  

tiff('Figure 1.tiff', units="cm", width=13.5, height=8, res=2000)
map_base # +
  #geom_magnify(from = from_M, to = to_M, linewidth = 0.15)  

# x coordinates for legend end labels have to be tuned manually which is extremely annoying
grid::grid.draw(grid::textGrob("К", x = 0.788, y = 0.13, 
                               gp = gpar(fontsize = 8, fontface = "bold")))
grid::grid.draw(grid::textGrob("И", x = 0.9315, y = 0.13, 
                               gp = gpar(fontsize = 8, fontface = "bold")))
dev.off()

Surprising failure with xlim

Example:

library(ggplot2)
ggp <- ggplot(iris, aes(Sepal.Width, Sepal.Length, color = Species, shape = Species))
ggp2 <- ggp + geom_point()

from <- c(2.5, 3.0, 5.5, 6.0)
to <- c(3.5, 4.5, 4.5, 5.5)
ggp2 + xlim(2,5) + geom_magnify(aes(from = Species=="versicolor" & Sepal.Length < 6), to = c(4, 4.8, 5, 6))

This doesn't print a magnified inset, and gives a warning about dropped cases.

The failure is because xmin becomes NA in the data, which then leads all cases to be dropped as incomplete.

Not sure why xmin is becoming NA. It's fine with xlim(1.9, 5) ; or with expand = 0.

ggmagnify always calls `inset_theme()`, even if `plot` is explicitly passed in

Thank you very much for this nice and, generally, easy-to-use package! For a beginner like myself, a life saver.
However, I seem to be confused about the way to adjust margins on the inset, when using inset axes. I couldn't find an answer to my problem in the - excellent - Readme and thought to have found the solution in the help file of the inset_theme() function, esp. margin = if (axes == "") 0 else 8. Still, I do not get it to work.

Describe the bug
I do not manage to set the margin of the inset to 0 when using axes on the inset plot. Instead the margins of the main plot are adjusted.

To Reproduce

ggp <- ggplot(iris, aes(Sepal.Width, Sepal.Length, color = Species)) + geom_point()
ggp


# Attempt 1: inset_theme() + plot()
axes <- "bla"  # without this line - Error: object 'axes' not found
Inset = ggp + inset_theme(
  blank = inset_blanks(axes = axes),
  axes,
  margin = if (axes == "") 50 else 0  # swapping '0' and '50' around shows changes in 'Inset'
)
Inset

ggm <- ggp + 
  geom_magnify(
    from = c(3, 3.5, 4.5, 5),
    to = c(3.5, 4.25, 6, 7),
    axes = "xy",
    plot = Inset)
ggm  # main plot and inset margins are not affected by if statement in inset_theme()


# Attempt 2: inset_theme() alone
ggm <- ggp + 
  geom_magnify(
    from = c(3, 3.5, 4.5, 5),
    to = c(3.5, 4.25, 6, 7),
    axes = "xy") +
  inset_theme(
    blank = inset_blanks(axes = axes),
    axes,
    margin = if (axes == "") 50 else 0
  )
ggm  # only main plot's margins are adjusted, inset plot margins remain unchanged


# Attempt 3: as Attempt 1, but just theme()
Inset = ggp + theme(
  plot.margin = unit(c(0, 0, 0, 0), "mm")) # changing the values from '0' changes the margins as expected
Inset

ggm <- ggp + 
  geom_magnify(
    from = c(3, 3.5, 4.5, 5),
    to = c(3.5, 4.25, 6, 7), axes = "xy", plot = Inset)
ggm # neither the main plot's nor the inset's margins are adjusted


# Attempt 4: as Attempt 2,  but just theme()
ggm <- ggp + 
  geom_magnify(
    from = c(3, 3.5, 4.5, 5),
    to = c(3.5, 4.25, 6, 7), axes = "xy") +
  theme(
    plot.margin = unit(c(20, 20, 20, 20), "mm"))
ggm # only the main plot's margins are adjusted

Expected behavior
I expected the theme() function applied to the inset and 'transferred' to the main plot via the plot argument in geom_magnify() to change the looks of the inset. This works for the background, for example. Using this logic, I assumed that the if statement inside the inset_themes() function would set the margins of the inset. However, it only affects the margins of the main plot; it doesn't matter whether inset_themes() is directly 'applied' to the main plot or 'transferred' via the plot argument in geom_magnify().

Other information
Versions
ggmagnify: 0.4.0
packageVersion("ggplot2"): 3.5.1
R: 4.3.3
Graphics device: AGG Backend

Data frame grobs are getting wrong projections

s <- seq(0, 2*pi, length = 7)
hex <- grid::polygonGrob(x = 3 + sin(s)/2, y = 6 + cos(s)/2, 
                         default.units = "native")  # works fine

# not so good
hex <- data.frame(x = 3 + sin(s)/2, 
                  y = 6 + cos(s)/2)
ggpi + geom_magnify(from = hex, 
                    to = c(4, 6, 5, 7), shadow = TRUE, aspect = "fixed")

Ggmagnify "axes = false" not working with complex plot theme()

When using complex theme() in original ggplot, ggmagnify()'s "axes = FALSE" does not work.

ggplot(data = dat, aes(x=x, y=y, color = z)) + theme(axis.text.x = element_text(angle = 315, hjust = 0, vjust = 1, size = 8), axis.text.y = element_text(size = 8), axis.title.x = element_text(size = 9), axis.title.y = element_text(size = 9, vjust = 2.5))

Background not removed on inset on Mac/PC

In our R club today we tried your great package on both Mac and PC and we noted that there is a difference with your images on github.
A quick Docker-run Linux version seems to clarify that on Mac/PC there is a lack of cutting-out the background on non-square shapes (e.g. Hexagone) and that is even clearer to see on the Texas Map where the blue background remains on the Mac./PC run command but it is not present on your illustration.

I append the results for the "Texas" plot:

texas

It seems that there may be specific graphics libraries present in Linux-based system that is lacking on macOS and Windows...

No plot shown if data is in layers

Example:

dfr <- data.frame(x = runif(100), y = runif(100))

# works
ggplot(dfr, aes(x, y)) + 
  geom_point() + 
  geom_magnify(from = c(.25, .35, .25, .35), to = c(0.2, .6, 0.5, .8))

# no magnify
ggplot() + 
  geom_point(data = dfr, mapping = aes(x,y)) +
  geom_magnify(from = c(.25, .35, .25, .35), to = c(0.2, .6, 0.5, .8))

There's no warning either.

background of inset

I have a graph with transparent background. I'd like to have the zoomed area use a plain color background (e.g. white) so that the original plot does not show through. is it possible to do that?

The problem emerges because the plot I want to magnify is extracted like this:

  gpanel <- ggpubr::as_ggplot(cowplot::get_legend(ggplot_object))
  gzoom <- ggmagnify(gpanel ,xlim = c(0.8, 1), ylim = c(0.8, 1),
                     inset_xlim = c(0, 0.5), inset_ylim = c(0, 0.5), zoom=2,
                     shadow = TRUE)

an issue with `shape = "ellipse"`

when i draw an ellipse annotation in geom_magnify, it just worked wrong

ggplot(diamonds[diamonds$carat>1.2,], aes(carat, depth, color = cut)) + 
  geom_point() + 
  geom_magnify(from=c(1.8,63,2.2,67),to=c(3.6,48,6,58),shadow = T,shape = "ellipse") +
  coord_cartesian(clip = "off") + 
  theme(legend.position = c(1.15,.75),plot.margin = margin(20,90,10,10))

image
the annotation still looks like a rectangle thogth circled in an ellipse ┗( T﹏T )┛

Multiple insets in one plot

It would be great to be able to add multiple insets in the same plot. Currently, as the ggmagnify() function outputs a GgMagnify object, it cannot be re-entered into ggmagnify().

Two possible (conceptual, I am not familiar with how the function works) options that you may have already considered?

  • Allow ggmagnify() to take multiple lists of inputs, one for each inset
  • Find a manner to allow ggmagnify() to accept a GgMagnify object as an input to which another inset can be added.

I have no idea how feasible either of these options are. But hopeful this could work as it would be a cool addition!

Screws up when multiple polygons are magnified

For a map:

library(ggplot2)
library(ggmagnify)
usa <- sf::st_as_sf(maps::map("state", fill=TRUE, plot =FALSE))

ggpm <- ggplot(usa) + geom_sf() +
         coord_sf(default_crs = sf::st_crs(4326), ylim = c(10, 50)) 


ggpm + geom_magnify(aes(from = ID %in% c("texas", "arizona")),
                    to = c(-125, -98, 10, 30), 
                    shape = "outline", 
                    aspect = "fixed", 
                    expand = 0) 

image

Format y axis

Hey!

Thanks for the awesome package. I was trying to format the y-axis labels, which works in the main plot but seems to be ignored in the magnified portion.
Is it possible to do this? If so what would be the right way?

Describe the bug
A clear and concise description of what the bug is.

To Reproduce

cost_table_ncbi <- readxl::read_excel('data/Sequencing_Cost_Data_Table_May2022.xls')

cost_table_ncbi$Date <- as.Date(cost_table_ncbi$Date)

# Define a custom formatter function
dollar_format <- function(x) {
  #ifelse(x >= 1e6, paste0("$", round(x / 1e6, 2), "mio"), paste0("$", comma(x)))
  paste0("$", comma(x))
}

ggplot(data = cost_table_ncbi, aes(x = Date)) +
  geom_line(aes(y = `Cost per Genome`), color = "blue") +
  coord_cartesian(clip = "off") + 
  ggtitle("Cost for sequencing a single human whole genome") +
  theme(axis.title.y = element_blank(),
        plot.title = element_text(face = "bold"))+
  scale_y_continuous(labels = dollar_format) +
  geom_magnify(from=list(ymd("2012-01-31"), ymd("2022-10-31"), 0 , 1e4),
               to=list(ymd("2008-05-31"), ymd("2023-5-31"),2.5e7, 8.5e7 ),
               shadow = TRUE, axes = "xy", recompute = TRUE, expand =0,
               after_scale = list(scale_y_continuous = dollar_format))

Sequencing_Cost_Data_Table_May2022.xls

Expected behavior
I would like the yaxis labels in the magnified plot to also have $ prepended (among other things)

Screenshots

Screenshot 2024-03-15 at 16 23 09

Other information
scales lubridate ggmagnify ggpubr ggplot2
"1.3.0" "1.9.3" "0.4.0" "0.6.0" "3.5.0"

Fails with non-continuous scales

Test case which works fine without the call to scale:

ggplot(mtcars, aes(mpg, hp)) + 
  geom_point() + 
  scale_y_binned(n.breaks = 5) + 
  geom_magnify(x = 15, width = 5, y = 200, height = 100, to_x = 30, to_y = 200, magnify = c(1, 1.5))

object 'dv' not found

hello, i have just met a problem when i use

ggp <- ggplot(dv, aes(Position, NegLogP, color = cut)) + 
  geom_point(color = "darkblue", alpha = 0.8, size = 0.8) +
  labs(title = "GWAS p-values for cognitive function",
       subtitle = "Davies et al. (2018).", y = "-log(p)")

it says "object 'dv' not found".
how can i get the dv variant?

ggmagnify can magnify irregular areas

Thanks for developing such a great tool. I found out that ggmagnify can only magnify the graph in regular areas. But in the visualization of single-cell transcriptome sequencing, people are more inclined to zoom in on a specific cell subpopulation. However, the shape of cell subpopulations is irregular. Some R packages ggunchull and ggforce have advantages in drawing irregular graphics, but they cannot magnify the graphics and are not as beautiful and natural as ggmagnify. Looking forward to the new version of ggmagnify that can magnify irregular areas. I will continue to follow your work! Thank you very much!

ggmagnify appears to magnify more than just the polygon in maps

I am trying to magnify just a small area of the UK (local authority) but using the ggmagnify it copies over areas outside the polygon. Whilst in the readme example with Texas it only had the Texas polygon.

map_issue

I recreated the Texas example in your readme and the same thing happened.

My code:

map + geom_magnify(aes(from = LAD22NM == "Powys"),
to = c(0, 2, 54, 56),
shadow = TRUE, linewidth = 1, colour = "orange2",
shape = "outline",
aspect = "fixed",
expand = 0)

magnify linewidths and other aestetics

I'm magnifying a section of a ggraph object. It works ok, but the linewidths do not get magnified, which looks odd.

Is there a way to magnify aesthetics too?

Saving ggmagnify plot with ggsave()

Hello,
great package, thanks for your work!

Describe the bug
When I am trying to save one of the example plots with ggsave() to either pdf or eps format there seems to be an issue with the content of the magnified inset. It is just not displayed.

To Reproduce

ggpi <- ggplot(iris, aes(Sepal.Width, Sepal.Length, colour = Species)) +
    geom_point() + xlim(c(1.5, 6))

p <- ggpi + geom_magnify(aes(from = Species == "setosa" & Sepal.Length < 5), 
                    to = c(4, 6, 6, 7.5))

ggsave("filename.eps", p, device = cairo_pdf)

Screenshots
image

Other information

packageVersion("ggplot2")
[1] ‘3.4.4’
packageVersion("ggmagnify")
[1] ‘0.2.0.9000’

Thanks in advance.

geom_text_repel interaction

Hi, I am wondering if there is a way of restricting the geom_text_repel package to plot labels within the magnified region?
Many thanks!

conflict with purrr::compose()

At the moment, loading {ggmagnify} masks the compose() function from {purrr}. Can you change the name of that function to avoid the conflict name?

pdf() device transparency problems with shape = "outline"

I just tested the "Texas" commands and I see that it works for PDF but not PNG Device.

However, while the transparency of the backbround of the PDF version is achieved, the Texas state itself is not colored red. With shadow = TRUE there is a gray coloring inside. With shadow = FALSE there is only the orange outline. I colored the rest of the states "green4" with "yellow" lines to make the image different. Here are the PDF (Device) output using pdf(file="Texasplot2.pdf") and dev.off():

texas2

(Saving to PNG looks the same as the one on the first comment, with a red Texas but blue neighbours.)

Originally posted by @jsgro in #22 (comment)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.