{"id":14068253,"url":"https://github.com/eliocamp/metamer","last_synced_at":"2025-06-21T22:05:24.779Z","repository":{"id":38953330,"uuid":"159563811","full_name":"eliocamp/metamer","owner":"eliocamp","description":"Create data sets with identical statistics.","archived":false,"fork":false,"pushed_at":"2022-06-23T20:46:56.000Z","size":10187,"stargazers_count":25,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-05-26T10:57:55.402Z","etag":null,"topics":["r","r-package","rstats"],"latest_commit_sha":null,"homepage":"https://eliocamp.github.io/metamer","language":"R","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/eliocamp.png","metadata":{"files":{"readme":"README.Rmd","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-11-28T20:56:36.000Z","updated_at":"2024-08-22T14:38:39.000Z","dependencies_parsed_at":"2022-08-21T01:10:29.088Z","dependency_job_id":null,"html_url":"https://github.com/eliocamp/metamer","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/eliocamp/metamer","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eliocamp%2Fmetamer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eliocamp%2Fmetamer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eliocamp%2Fmetamer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eliocamp%2Fmetamer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/eliocamp","download_url":"https://codeload.github.com/eliocamp/metamer/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eliocamp%2Fmetamer/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":261200392,"owners_count":23123948,"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":["r","r-package","rstats"],"created_at":"2024-08-13T07:06:03.131Z","updated_at":"2025-06-21T22:05:19.766Z","avatar_url":"https://github.com/eliocamp.png","language":"R","readme":"---\noutput: github_document\n---\n\n\u003c!-- README.md is generated from README.Rmd. Please edit that file --\u003e\n\n```{r setup, include = FALSE}\nknitr::opts_chunk$set(\n  collapse = TRUE,\n  comment = \"#\u003e\",\n  fig.path = \"man/figures/README-\",\n  out.width = \"100%\",\n  cache = TRUE,\n  cache.extra = 42\n)\n\n```\n# metamer\n\u003c!-- badges: start --\u003e\n[![DOI](https://zenodo.org/badge/159563811.svg)](https://zenodo.org/badge/latestdoi/159563811)  [![Travis build status](https://travis-ci.org/eliocamp/metamer.svg?branch=master)](https://travis-ci.org/eliocamp/metamer) [![Codecov test coverage](https://codecov.io/gh/eliocamp/metamer/branch/master/graph/badge.svg)](https://app.codecov.io/gh/eliocamp/metamer?branch=master)\n\u003c!-- badges: end --\u003e\n\n\nImplements the algorithm proposed by [Matejka \u0026 Fitzmaurice (2017)](https://www.autodesk.com/research/publications/same-stats-different-graphs) to create metamers (datasets with identical statistical properties but very different graphs) with an annealing scheme derived from [de Vicente et al. (2003)](https://www.sciencedirect.com/science/article/abs/pii/S0375960103013653?via%3Dihub).\n\nIn colour theory, [metamers](https://en.wikipedia.org/wiki/Metamerism_(color)) are colours that have very different wavelength distribution but are perceived as equal by out visual system. This happens because out eyes essentially summarise a continuous distribution of wavelength by just 3 numbers: the amount that each type of cone cell is exited. Colour metamerism is how artists can reproduce so many colours with a few pigments, or how PC monitors use only 3 lights to show colourful pictures. \n\n![](man/figures/lemon.jpg)\n\n(from the excellent [Color: From Hexcodes to Eyeballs](http://jamie-wong.com/post/color/) by [Jamie Wong](https://github.com/jlfwong))\n\nStatistical transformations such as mean, standard deviation and correlation behave very similarly in that they summarise data with just a few numbers for the benefit of our limited cognitive capacity. Thus, statistical metamers are sets of data that share some statistical properties. \n\n[This article](https://eliocamp.github.io/codigo-r/en/2019/01/statistical-metamerism/) explores statistical metamerism in more detail. \n\n## Installation\n\nYou can install metamer with:\n\n```r\ninstall.packages(\"metamer\")\n```\n\nor install the development version with:\n\n``` r\n# install.packages(\"devtools\")\ndevtools::install_github(\"eliocamp/metamer\")\n```\n\n## Example\n\nYou can construct metamers from a starting dataset and a vector of statistical properties to remain constant (by default, up to 2 significant figures). \n\n```{r example}\nlibrary(metamer)\n# Start with the datasaurus\n# install.packages(\"datasauRus\")\ndino \u003c- subset(datasauRus::datasaurus_dozen, dataset == \"dino\")\ndino$dataset \u003c- NULL\n\n# And we want to preserve means and correlation\nmean_cor \u003c- delayed_with(mean(x), mean(y), cor(x, y)) \n\nset.seed(42) # To make results reproducible\nmetamers \u003c- metamerise(dino, preserve = mean_cor, \n                       stop_if = n_metamers(300), \n                       perturbation = 1,\n                       keep = 19)\nprint(metamers)\n```\n\nWe found `r length(metamers$metamers)` metamers. Let's see the final one, with the starting dataset as background. \n\n```{r}\nlibrary(ggplot2)\n\nggplot(tail(metamers), aes(x, y)) +\n  geom_point(data = dino, color = \"red\", alpha = 0.5, size = 0.4) +\n  geom_point()\n```\n\nWe can check that the statistical properties have been preserved up to 2 significant figures:\n\n```{r}\ncbind(dino = signif(mean_cor(dino), 2),\n      last = signif(mean_cor(tail(metamers)), 2))\n```\n\nHowever, a semi random cloud of points is not that interesting, so we can specify a minimizing function so that the result is similar to another dataset. `metamerise` will start from the last metamer of the previous run if the `data` argument is a list of metamers and append the result. \n\n```{r}\nx_shape \u003c- subset(datasauRus::datasaurus_dozen, dataset == \"x_shape\")\nx_shape$dataset \u003c- NULL\n```\n\n```{r}\nmetamers \u003c- metamerise(dino, \n                       preserve = mean_cor,\n                       minimize = mean_dist_to(x_shape),\n                       stop_if = minimize_ratio(0.02),\n                       keep = 99)\n```\n\nNow the result is a bit more impressive. \n\n```{r}\nggplot(tail(metamers), aes(x, y)) +\n  geom_point(data = dino, color = \"red\", alpha = 0.5, size = 0.4) +\n  geom_point()\n```\n\nWe can animate the whole thing. \n\n```{r, gganimate = list(fps = 30)}\nlibrary(gganimate)\n\nggplot(metamers, aes(x, y)) +\n  geom_point() +\n  transition_manual(.metamer)\n```\n\nYou can freehand your own starting or target data with the `draw_data()` utility, that will open a shiny interface. You might need to install `shiny` and `miniUI` with `install.packages(c(\"shiny\", \"miniUI\"))`.\n\nMetamerizing operations can be chained while changing the minimizing function. \n\n```{r}\n\nstar \u003c- subset(datasauRus::datasaurus_dozen, dataset == \"star\")\nstar$dataset \u003c- NULL\nset.seed(42)\nmetamers \u003c- metamerise(dino,\n                       preserve = mean_cor, \n                       minimize = mean_dist_to(x_shape),\n                       stop_if = minimize_ratio(0.05),\n                       keep = 29) |\u003e \n  set_minimize(mean_dist_to(star)) |\u003e \n  metamerise(stop_if =  minimize_ratio(0.05),\n             keep = 30) |\u003e \n  set_minimize(mean_dist_to(dino)) |\u003e \n  metamerise(stop_if =  minimize_ratio(0.05), \n             keep = 30) \n```\n\nAnd the full sequence \n\n```{r, gganimate = list(nframes = 30*3, fps = 30)}\nggplot(metamers, aes(x, y)) +\n  geom_point() +\n  transition_manual(.metamer)\n```\n\n","funding_links":[],"categories":["R"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feliocamp%2Fmetamer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Feliocamp%2Fmetamer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feliocamp%2Fmetamer/lists"}