{"id":15502333,"url":"https://github.com/hughjonesd/ggmagnify","last_synced_at":"2025-04-07T17:07:21.793Z","repository":{"id":156225174,"uuid":"632907971","full_name":"hughjonesd/ggmagnify","owner":"hughjonesd","description":"Create a magnified inset of part of a ggplot object","archived":false,"fork":false,"pushed_at":"2025-03-02T07:56:02.000Z","size":107528,"stargazers_count":287,"open_issues_count":6,"forks_count":3,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-03-31T16:13:46.318Z","etag":null,"topics":["ggplot","graphics","r"],"latest_commit_sha":null,"homepage":"https://hughjonesd.github.io/ggmagnify/","language":"R","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hughjonesd.png","metadata":{"files":{"readme":"README.Rmd","changelog":"NEWS.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-04-26T11:29:10.000Z","updated_at":"2025-03-24T08:42:24.000Z","dependencies_parsed_at":"2024-03-23T14:27:37.415Z","dependency_job_id":"fe3bfead-084a-4d34-ba2e-271aa282e438","html_url":"https://github.com/hughjonesd/ggmagnify","commit_stats":{"total_commits":193,"total_committers":1,"mean_commits":193.0,"dds":0.0,"last_synced_commit":"972edb2a0a60c3d256dd26c3a9f83f98df9a6c57"},"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hughjonesd%2Fggmagnify","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hughjonesd%2Fggmagnify/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hughjonesd%2Fggmagnify/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hughjonesd%2Fggmagnify/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hughjonesd","download_url":"https://codeload.github.com/hughjonesd/ggmagnify/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247694875,"owners_count":20980733,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ggplot","graphics","r"],"created_at":"2024-10-02T09:09:21.131Z","updated_at":"2025-04-07T17:07:21.743Z","avatar_url":"https://github.com/hughjonesd.png","language":"R","funding_links":[],"categories":["Presentation, composition and scales"],"sub_categories":[],"readme":"---\noutput: github_document\n---\n\n\u003c!-- README.md is generated from README.Rmd. Please edit that file --\u003e\n\n```{r, include = FALSE}\nlibrary(dplyr) # I have this twice to avoid the startup message below\nknitr::opts_chunk$set(\n  collapse = TRUE,\n  comment = \"#\u003e\",\n  fig.path = \"man/figures/README-\",\n  out.width = \"100%\",\n  dev = \"ragg_png\"\n)\n\nlatest \u003c- gh::gh(\"/repos/hughjonesd/ggmagnify/releases/latest\")$name\n\nset.seed(103121)\ndv \u003c- vroom::vroom(\"Davies_NC_2018/Davies2018_subset.txt\")\n\ndv$NegLogP \u003c- -log10(dv$`P-value`)\n\n```\n\n# ggmagnify \u003ca href=\"https://hughjonesd.github.io/ggmagnify/\"\u003e\u003cimg src=\"man/figures/logo.png\" align=\"right\" height=\"139\" /\u003e\u003c/a\u003e\n\u003c!-- badges: start --\u003e\n[![r-universe](https://hughjonesd.r-universe.dev/badges/ggmagnify)](https://hughjonesd.r-universe.dev/ggmagnify)\n[![R-CMD-check](https://github.com/hughjonesd/ggmagnify/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/hughjonesd/ggmagnify/actions/workflows/R-CMD-check.yaml)\n[![Lifecycle: experimental](https://img.shields.io/badge/lifecycle-experimental-orange.svg)](https://lifecycle.r-lib.org/articles/stages.html#experimental)\n\u003c!-- badges: end --\u003e\n\nggmagnify creates a magnified inset of part of a\n[ggplot](https://ggplot2.tidyverse.org/) object. The magnified area can be a\n(rounded) rectangle, an ellipse, a convex hull of points, or an arbitrary shape.\nBorders can be drawn around the target area and the inset, along with projection\nlines and/or shading between the two. The inset can have a drop shadow.\n\nYou can install ggmagnify from r-universe:\n\n``` r\ninstall.packages(\"ggmagnify\", repos = c(\"https://hughjonesd.r-universe.dev\", \n                 \"https://cloud.r-project.org\"))\n```\n\nThis will install the latest github release (currently `r latest`).\n\nOr install the development version from [GitHub](https://github.com/) with:\n\n``` r\n# install.packages(\"remotes\")\nremotes::install_github(\"hughjonesd/ggmagnify\")\n```\n\n\n## Basic inset\n\nTo create an inset, use `geom_magnify(from, to)`. `from` can\nbe a vector giving the four corners of the area to magnify:\n`from = c(xmin, xmax, ymin, ymax)`.\n\nSimilarly, `to` specifies where the magnified inset should go: `to = c(xmin, xmax, ymin, ymax)`.\n\n```{r example-basic}\nlibrary(ggplot2)\nlibrary(ggmagnify)\n\nggp \u003c- ggplot(dv, aes(Position, NegLogP)) + \n  geom_point(color = \"darkblue\", alpha = 0.8, size = 0.8) +\n  labs(title = \"GWAS p-values for cognitive function\",\n       subtitle = \"Davies et al. (2018).\", y = \"-log(p)\")\n\nfrom \u003c- c(xmin = 9.75e7, xmax = 9.95e7, ymin = 16, ymax = 28)\n# Names xmin, xmax, ymin, ymax are optional:\nto \u003c- c(2e8 - 2e7, 2e8 + 2e7,10, 26)\n\nggp + geom_magnify(from = from, to = to)\n```\n\n\n## Inset with shadow\n\n```{r example-shadow}\n\nloadNamespace(\"ggfx\")\n\nggp + geom_magnify(from = from, to = to, \n                   shadow = TRUE)\n```\n\n## Rounded corners\n\nNew in version 0.3.0, use `corners` to give a proportional radius for rounded\ncorners.\n\n```{r example-corners}\nggp + geom_magnify(from = from, to = to, \n                   corners = 0.1, shadow = TRUE)\n```\n\n## Ellipse\n\nThis requires R 4.1 or higher, and an appropriate graphics device.\n\n```{r example-ellipse}\n\nggp + geom_magnify(from = from, to = to, \n                   shape = \"ellipse\", shadow = TRUE)\n```\n\n## Pick points to magnify\n\nTo choose points to magnify, map `from` in an `aes()`:\n\n```{r example-logical-from}\nggpi \u003c- ggplot(iris, aes(Sepal.Width, Sepal.Length, colour = Species)) +\n              geom_point() + xlim(c(1.5, 6))\n\nggpi + geom_magnify(aes(from = Species == \"setosa\" \u0026 Sepal.Length \u003c 5), \n                    to = c(4, 6, 6, 7.5))\n```\n\n\n## Faceting\n\n```{r example-faceting}\n\nggpi +\n  facet_wrap(vars(Species)) +\n  geom_magnify(aes(from = Sepal.Length \u003e 5 \u0026 Sepal.Length \u003c 6.5), \n                    to = c(4.5, 6, 6, 7.5),\n                    shadow = TRUE)\n\n```\n\n\n## Magnify an arbitrary region (experimental)\n\n\nUse `shape = \"outline\"` to magnify the convex hull of a set of points:\n\n```{r example-outline}\n\nlibrary(dplyr)\n\nstarwars_plot \u003c- starwars |\u003e \n  mutate(Human = species == \"Human\") |\u003e \n  select(mass, height, Human) |\u003e \n  na.omit() |\u003e \n  ggplot(aes(mass, height, color = Human)) + \n    geom_point() + xlim(0, 220) + ylim(0, 250) + \n    theme_dark() +\n    theme(panel.grid = element_line(linetype = 2, colour = \"yellow\"), \n          axis.line = element_blank(), \n          panel.background = element_rect(fill = \"black\"),\n          legend.key = element_rect(fill= \"black\"),\n          rect = element_rect(fill = \"black\"),\n          text = element_text(colour = \"white\")) +\n    scale_colour_manual(values = c(\"TRUE\" = \"red\", \"FALSE\" = \"lightblue\")) +\n    ggtitle(\"Mass and height of Star Wars characters\",\n            subtitle = \"Humans magnified\")\n\nstarwars_plot +\n  geom_magnify(aes(from = Human), to = c(30, 200, 0, 120), shadow = TRUE,\n               shadow.args = list(colour = \"yellow\", sigma = 10,\n                                  x_offset = 2, y_offset = 5),\n               alpha = 0.8, colour = \"yellow\", linewidth = 0.6, \n               shape = \"outline\", expand = 0.2)\n\n```\n\n\nUse a grob or data frame to magnify any shape:\n\n```{r example-grob}\n\ns \u003c- seq(0, 2*pi, length = 7)\nhex \u003c- data.frame(x = 3 + sin(s)/2, y = 6 + cos(s)/2) \n\nggpi + geom_magnify(from = hex, \n                    to = c(4, 6, 5, 7), shadow = TRUE, aspect = \"fixed\")\n\n```\n\n\n## Maps (experimental)\n\nWith maps, `shape = \"outline\"` magnifies just the selected map polygons:\n\n```{r example-map}\n\nusa \u003c- sf::st_as_sf(maps::map(\"state\", fill=TRUE, plot =FALSE))\n\nggpm \u003c- ggplot(usa) +\n          geom_sf(aes(fill = ID == \"texas\"), colour = \"grey20\") +\n          coord_sf(default_crs = sf::st_crs(4326), ylim = c(10, 50)) + \n          theme(legend.position = \"none\") +\n          scale_fill_manual(values = c(\"TRUE\" = \"red\", \"FALSE\" = \"steelblue4\"))\n\n\nggpm + geom_magnify(aes(from = ID == \"texas\"),\n                    to = c(-125, -98, 10, 30), \n                    shadow = TRUE, linewidth = 1, colour = \"orange2\",\n                    shape = \"outline\", \n                    aspect = \"fixed\", \n                    expand = 0) \n```\n\n\n## Axes\n\n```{r example-axes}\n\nggp + \n  scale_x_continuous(labels = NULL) + \n  geom_magnify(from = from, to = to, \n               axes = \"xy\")\n```\n\n\n## Projection lines and borders\n\n### Colour and linetype\n\n```{r example-colours}\n\nggp + \n  geom_magnify(from = from, to = to,\n               colour = \"darkgreen\", linewidth = 0.5, proj.linetype = 3)\n```\n\n\n### Projection line styles\n\n```{r example-proj}\n\nggpi \u003c- ggplot(iris, aes(Sepal.Width, Sepal.Length, colour = Species)) +\n              geom_point()\nfrom2 \u003c- c(3, 3.5, 6.5, 7)\nto2 \u003c- c(2.75, 3.75, 5, 6)\n\nggpi + \n  geom_magnify(from = from2, to = to2,\n               proj = \"facing\") # the default\n\nggpi + \n  geom_magnify(from = from2, to = to2,\n               proj = \"corresponding\") # always project corner to corner\n\nggpi + \n  geom_magnify(from = from2, to = to2,\n               proj = \"single\") # just one line\n```\n\n### Projection fill\n\n```{r example-proj-fill}\n\nggpi + \n  geom_magnify(from = from2, to = to2,\n               proj.fill = alpha(\"yellow\", 0.2)) # fill between the lines\n\nggpi + \n  geom_magnify(from = from2, to = to2, shape = \"ellipse\",\n               proj.fill = alpha(\"orange\", 0.2)) # works with any shape\n\n\n```\n\n## Tips and tricks\n\n### Graphics devices\n\n`geom_magnify()` uses masks. This requires R version 4.1.0 or higher, and a\ngraphics device that supports masking. If you are using knitr, you may have\nluck with the `ragg_png` device (which was used to create this README). If your\ndevice doesn't support masks, only `shape = \"rect\"` will work, and the plot\ninset will not be clipped to the panel area.\n\n\n### Adding layers to the inset\n\n`geom_magnify()` stores the plot when it is added to it. So, order matters:\n\n```{r example-order}\n\nggpi \u003c- ggplot(iris, aes(Sepal.Width, Sepal.Length, colour = Species)) +\n              geom_point() + xlim(2, 6)\n\nfrom3 \u003c-  c(2.5, 3.5, 6, 7)\nto3 \u003c- c(4.7, 6.1, 4.3, 5.7)\nggpi + \n  geom_smooth() + \n  geom_magnify(from = from3, to = to3)\n# Print the inset without the smooth:\nggpi +\n  geom_magnify(from = from3, to = to3) +\n  geom_smooth()\n\n```\n\nFor complex modifications to the inset, set `plot` explicitly:\n\n```{r example-advanced-2}\n\nbooms \u003c- ggplot(faithfuld, aes(waiting, eruptions)) +\n         geom_contour_filled(aes(z = density), bins = 50) +\n         scale_fill_viridis_d(option = \"B\") + \n         theme(legend.position = \"none\")\n\nbooms_inset \u003c- booms + \n  geom_point(data = faithful, color = \"red\", fill = \"white\", alpha = 0.7, \n             size = 2, shape = \"circle filled\") + \n  coord_cartesian(expand = FALSE)\n\nshadow.args \u003c- list(\n  colour = alpha(\"grey80\", 0.8),\n  x_offset = 0,\n  y_offset = 0,\n  sigma = 10\n)\n\nbooms + geom_magnify(from = c(78, 90, 4.0, 4.8), to = c(70, 90, 1.7, 3.3),\n                     colour = \"white\", shape = \"ellipse\",\n                     shadow = TRUE, shadow.args = shadow.args,\n                     plot = booms_inset)\n\n\n```\n\n### Draw an inset outside the plot region\n\n```{r example-noclip}\n\nggp + \n  coord_cartesian(clip = \"off\") + \n  theme(plot.margin = ggplot2::margin(10, 60, 10, 10)) +\n  geom_magnify(from = from, to = to + c(0.5e8, 0.5e8, 0, 0), \n               shadow = TRUE)\n\n```\n\n### Keep grid lines the same\n\nTo make sure the inset uses the same grid lines as the main graph,\nset `breaks` in `scale_x` and `scale_y`:\n\n```{r example-gridlines}\n\nggp2 \u003c- ggplot(iris, aes(Sepal.Width, Sepal.Length, color = Species)) + \n        geom_point() +\n        theme_classic() + \n        theme(panel.grid.major = element_line(\"grey80\"),\n              panel.grid.minor = element_line(\"grey90\"))\n\n# different grid lines:\nggp2 + \n  geom_magnify(from = c(2.45, 3.05, 5.9, 6.6), to = c(3.4, 4.4, 5.5, 6.6),\n               shadow = TRUE) \n\n# fix the grid lines:\nggp2 +\n  scale_x_continuous(breaks = seq(2, 5, 0.5)) + \n  scale_y_continuous(breaks = seq(5, 8, 0.5)) + \n  geom_magnify(from = c(2.45, 3.05, 5.9, 6.6), to = c(3.4, 4.4, 5.5, 6.6),\n               shadow = TRUE) \n\n```\n\n### Recomputing data\n\nUse `recompute` if you want to recompute smoothers, densities, etc. in the inset.\n\n```{r example-recompute}\n \ndf \u003c- data.frame(x = seq(-5, 5, length = 500), y = 0)\ndf$y[abs(df$x) \u003c 1] \u003c- sin(df$x[abs(df$x) \u003c 1])\ndf$y \u003c- df$y + rnorm(500, mean = 0, sd = 0.25)\n\nggp2 \u003c- ggplot(df, aes(x, y)) + \n  geom_point() + \n  geom_smooth(method = \"loess\", formula = y ~ x) + \n  ylim(-5, 5)\n\n# The default:\nggp2 + geom_magnify(from = c(-1.25, 1.25, -1, 1),\n                    to = c(2, 5, 1, 5))\n\n# Recomputing recalculates the smooth for the inset:\nggp2 + geom_magnify(from = c(-1.25, 1.25, -1, 1),\n                    to = c(2, 5, 1, 5),\n                    recompute = TRUE)\n```\n\n### Magnify twice\n\n```{r example-magnify-twice}\n\ndata \u003c- data.frame(\n  x = runif(4000), \n  y = runif(4000)\n)\nggm_unif \u003c- ggplot(data, aes(x, y)) +\n            coord_cartesian(expand = FALSE) +\n            geom_density2d_filled(bins = 50, linewidth = 0, n = 200) +\n            geom_point(color='white', alpha = .5, size = .5) + \n            theme(legend.position = \"none\")\n\n\nggm_unif + \n  geom_magnify(from = c(0.05, 0.15, 0.05, 0.15), to = c(0.2, 0.4, 0.2, 0.4), \n               colour = \"white\", proj.linetype = 1, linewidth = 0.6) +\n  geom_magnify(from = c(0.25, 0.35, 0.25, 0.35), to = c(0.45, 0.85, 0.45, 0.85), \n               expand = 0, colour =\"white\", proj.linetype = 1)\n\n```\n\nAn inset *within* an inset is a bit more complex, but also doable:\n\n```{r example-and-so-ad-infinitum}\n\nggp \u003c- data.frame(x = rnorm(1e5), y = rnorm(1e5), \n                  colour = sample(8L, 1e5, replace = TRUE)) |\u003e \n  ggplot(aes(x = x, y = y, colour = factor(colour))) + \n  scale_color_brewer(type = \"qual\", palette = 2) +\n  geom_point(alpha = 0.12, size = 0.7) + \n  lims(x = c(-3,3), y = c(-3,3)) +\n  theme_classic() + theme(panel.grid = element_blank(), \n                          axis.line = element_blank(), \n                          plot.background = element_rect(fill = \"black\"),\n                          panel.background = element_rect(fill = \"black\"),\n                          title = element_text(colour = \"white\"),\n                          legend.position = \"none\")\n\nggpm \u003c- ggp + \n  lims(x = c(-0.3, 0.3), y = c(-0.3, 0.3)) + \n  geom_magnify(from = c(-0.03, 0.03, -0.03, 0.03),\n               to = c(-0.3, -0.1, -0.3, -0.1),\n               expand = FALSE, colour = \"white\")\n\nggp + \n  geom_magnify(plot = ggpm, \n               from = c(-0.3, 0.3, -0.3, 0.3),\n               to = c(-3, -1, -3, -1),\n               expand = FALSE, colour = \"white\") +\n  labs(title = \"Normal data\", \n       subtitle = \"The distribution gets more uniform as you zoom in\")\n\n```\n\n\n\n## Acknowledgements\n\nggmagnify was inspired by [this post](https://stackoverflow.com/a/66409862/10522567)\nand motivated by making [these plots](https://github.com/hughjonesd/academic-bias).\n\nData for the GWAS plots comes from:\n\nDavies et al. (2018)\n'Study of 300,486 individuals identifies 148 independent genetic loci influencing general cognitive function.' *Nature Communications*.\n\nData was trimmed to remove overlapping points.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhughjonesd%2Fggmagnify","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhughjonesd%2Fggmagnify","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhughjonesd%2Fggmagnify/lists"}