{"id":28269701,"url":"https://github.com/m-jahn/fluctuator","last_synced_at":"2025-07-24T07:08:11.384Z","repository":{"id":157987433,"uuid":"369254476","full_name":"m-jahn/fluctuator","owner":"m-jahn","description":"R package to Import and Modify SVG (XML) Graphic Files","archived":false,"fork":false,"pushed_at":"2024-11-01T15:28:54.000Z","size":3488,"stargazers_count":24,"open_issues_count":0,"forks_count":2,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-06-17T02:41:35.459Z","etag":null,"topics":["metabolic-models","metabolism","network","omics-data","r-package","svg","svg-images"],"latest_commit_sha":null,"homepage":"","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/m-jahn.png","metadata":{"files":{"readme":"README.md","changelog":null,"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":"2021-05-20T15:26:46.000Z","updated_at":"2025-05-25T19:19:19.000Z","dependencies_parsed_at":null,"dependency_job_id":"ac760832-586b-4b3b-9623-a9ace57c3f3d","html_url":"https://github.com/m-jahn/fluctuator","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/m-jahn/fluctuator","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/m-jahn%2Ffluctuator","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/m-jahn%2Ffluctuator/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/m-jahn%2Ffluctuator/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/m-jahn%2Ffluctuator/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/m-jahn","download_url":"https://codeload.github.com/m-jahn/fluctuator/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/m-jahn%2Ffluctuator/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266807144,"owners_count":23987426,"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","status":"online","status_checked_at":"2025-07-24T02:00:09.469Z","response_time":99,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["metabolic-models","metabolism","network","omics-data","r-package","svg","svg-images"],"created_at":"2025-05-20T15:15:13.066Z","updated_at":"2025-07-24T07:08:11.375Z","avatar_url":"https://github.com/m-jahn.png","language":"R","funding_links":[],"categories":[],"sub_categories":[],"readme":"fluctuator\n================\nMichael Jahn\n2024-11-01\n\n\u003c!-- badges start --\u003e\n\n[![R build\nstatus](https://github.com/m-jahn/fluctuator/workflows/R-CMD-check/badge.svg)](https://github.com/m-jahn/fluctuator/actions)\n[![Maintenance](https://img.shields.io/badge/Maintained%3F-yes-green.svg)](https://github.com/m-jahn)\n![GitHub issues](https://img.shields.io/github/issues/m-jahn/fluctuator)\n![GitHub last\ncommit](https://img.shields.io/github/last-commit/m-jahn/fluctuator)\n![Platform](https://img.shields.io/badge/platform-all-green)\n\u003c!-- badges end --\u003e\n\n\u003c!-- include logo--\u003e\n\n\u003cimg src=\"inst/extdata/logo.png\" align=\"right\" /\u003e\n\n------------------------------------------------------------------------\n\nAn Interface to Import and Modify SVG (XML) Graphic Files in R.\n\n## Description\n\nSVG is the primary choice for scalable, open-source graphic files. This\npackages provides a simple interface to import SVG graphic files in R,\nmodify these in a programmatic way, and export the files again. The\npurpose of this package is to overlay scientific data on medium or large\nscale network representations, which is too laborious and time-consuming\nto do manually. SVG Graphics have to be drawn beforehand, for example\nusing [Inkscape](https://inkscape.org/). Objects (“nodes”) are than\nidentified and modified using unique IDs/label in R. The fantastic\n[Escher](https://escher.github.io/#/) app follows a similar approach,\nwhere a metabolic network is first drawn on a canvas, and then used as a\ntemplate to overlay metabolic, flux, gene expression or other data.\nOptions to customize the metabolic maps are too restricted.\n\nPackage info:\n\n- Maintainer: Michael Jahn, Science for Life Lab, Stockholm\n- License: GPL-3\n- Depends: R (\\\u003e= 3.5.0)\n- Imports: `methods`, `XML`, `dplyr`\n\n## Installation\n\nTo install the package directly from github, use the following function\nfrom the `devtools` package in your R session:\n\n``` r\ndevtools::install_github(\"m-jahn/fluctuator\")\n```\n\n## Usage\n\n### Make an SVG template (with Inkscape)\n\nThe first step is to create an SVG file depicting e.g. a metabolic\nnetwork. Inkscape is a free, open source software for vector images and\nnatively supports SVG. Every item (node) that is created gets a cryptic\nlabel in Inkscape, such as `path-123-456-0-1`. All we need to do is to\nopen the object dialog and change labels to more human readable names\n(see picture). These are later used to identify objects in R. I\nrecommend using different prefixes for different types of objects, like\n`node_XYZ` for the nodes of a network, and `reaction_XYZ` for\nconnections between nodes.\n\n\u003c!-- include logo--\u003e\n\n\u003cimg src=\"inst/extdata/make_template.png\" align=\"right\" /\u003e\n\n### Read SVG\n\nWe can then import the SVG file in R using `read_svg()`. The resulting\nobject of class `XMLsvg` has two slots, the original XML structure and a\nfeature table with all graphical objects (nodes) and their attributes.\n\n``` r\nlibrary(dplyr)\nlibrary(fluctuator)\n\n# import example map\nSVG \u003c- read_svg(\"inst/extdata/example_network.svg\")\n\n# show class\nclass(SVG)\n```\n\n    ## [1] \"XMLsvg\"\n    ## attr(,\"package\")\n    ## [1] \"fluctuator\"\n\n``` r\n# access summary table of objects/nodes\nhead(SVG@summary)\n```\n\n    ## # A tibble: 6 × 23\n    ##   node_no id    d     style transform `connector-curvature` node_set label cx   \n    ##   \u003cchr\u003e   \u003cchr\u003e \u003cchr\u003e \u003cchr\u003e \u003cchr\u003e     \u003cchr\u003e                 \u003cchr\u003e    \u003cchr\u003e \u003cchr\u003e\n    ## 1 1       path… M 5.… fill… scale(0.… 0                     path     \u003cNA\u003e  \u003cNA\u003e \n    ## 2 2       path… M 5.… fill… scale(0.… 0                     path     \u003cNA\u003e  \u003cNA\u003e \n    ## 3 3       path… M 52… fill… \u003cNA\u003e      0                     path     ABC   \u003cNA\u003e \n    ## 4 4       path… M 60… fill… \u003cNA\u003e      0                     path     DEF   \u003cNA\u003e \n    ## 5 5       path… \u003cNA\u003e  colo… \u003cNA\u003e      \u003cNA\u003e                  circle   node… 56.4…\n    ## 6 6       path… \u003cNA\u003e  colo… \u003cNA\u003e      \u003cNA\u003e                  circle   node… 34.3…\n    ## # ℹ 14 more variables: cy \u003cchr\u003e, r \u003cchr\u003e, stockid \u003cchr\u003e, orient \u003cchr\u003e,\n    ## #   refY \u003cchr\u003e, refX \u003cchr\u003e, isstock \u003cchr\u003e, space \u003cchr\u003e, x \u003cchr\u003e, y \u003cchr\u003e,\n    ## #   role \u003cchr\u003e, groupmode \u003cchr\u003e, width \u003cchr\u003e, height \u003cchr\u003e\n\n### Get attributes of SVG\n\nWe can search for certain nodes in the SVG and display their attributes.\nNote that nodes are objects, not to be confused with single points of a\npath.\n\n``` r\nget_attributes(SVG, node = \"node_1\", attr = c(\"label\", \"style\"))\n```\n\n    ## # A tibble: 1 × 2\n    ##   label  style                                                                  \n    ##   \u003cchr\u003e  \u003cchr\u003e                                                                  \n    ## 1 node_1 color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibi…\n\n``` r\nget_attributes(SVG, node = \"ABC\", attr = c(\"label\", \"style\"))\n```\n\n    ## # A tibble: 1 × 2\n    ##   label style                                                                   \n    ##   \u003cchr\u003e \u003cchr\u003e                                                                   \n    ## 1 ABC   fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-wid…\n\n### Change attributes of SVG\n\nThe most important feature of this package is to change SVG attributes\nusing the `set_attributes()` function. The function takes four important\narguments: the name (`label`) of the `node` whose attributes should be\nchanged, and which corresponds to Inkscape object names. The `attribute`\nthat is supposed to be changed (e.g. `style`). And finally a `pattern`\nas well as a `replacement` that modifies the character value of the\nattribute. If `pattern` is an empty string (`\"\"`), the entire value of\nthe attribute will be overwritten with the replacement.\n\nIn this example we change the fill color of `node_1` to red. Then we\nalso change the thickness of the two arrows (reactions) `ABC` and `DEF`.\n\n``` r\nSVG \u003c- set_attributes(SVG, node = \"node_1\", attr = \"style\",\n  pattern = \"fill:#808080\", replacement = \"fill:#FF0000\")\n```\n\n    ## New attributes were set for 1 node(s).\n\n``` r\nSVG \u003c- set_attributes(SVG, node = c(\"ABC\", \"DEF\"), attr = \"style\",\n  pattern = \"stroke-width:1.32291663\", replacement = c(\"stroke-width:2.5\", \"stroke-width:0.5\"))\n```\n\n    ## New attributes were set for 2 node(s).\n\n### Export modified SVG file\n\nModified SVG files can be saved to disk using `write_svg()`.\n\n``` r\nwrite_svg(SVG, file = \"inst/extdata/example_network_mod.svg\")\n```\n\n|               Original SVG               |                 Modified SVG                 |\n|:----------------------------------------:|:--------------------------------------------:|\n| ![](inst/extdata/example_network.png) | ![](inst/extdata/example_network_mod.png) |\n\n### Real world example\n\nLet’s import a reduced network of the central carbon metabolism of the\nbacterium *Cupriavidus necator*. We can inspect the Inkscape names of\nall reactions in the summary table (column `label`).\n\n``` r\nSVG2 \u003c- read_svg(\"inst/extdata/central_metabolism.svg\")\nhead(SVG2@summary)\n```\n\n    ## # A tibble: 6 × 25\n    ##   node_no id      d     style transform `connector-curvature` node_set nodetypes\n    ##   \u003cchr\u003e   \u003cchr\u003e   \u003cchr\u003e \u003cchr\u003e \u003cchr\u003e     \u003cchr\u003e                 \u003cchr\u003e    \u003cchr\u003e    \n    ## 1 1       path14… M 5.… fill… scale(0.… 0                     path     \u003cNA\u003e     \n    ## 2 2       path14… M 5.… fill… scale(-0… 0                     path     \u003cNA\u003e     \n    ## 3 3       path12… M 5.… fill… scale(0.… 0                     path     \u003cNA\u003e     \n    ## 4 4       path12… M 5.… fill… scale(0.… 0                     path     \u003cNA\u003e     \n    ## 5 5       path82… M 5.… fill… scale(0.… 0                     path     \u003cNA\u003e     \n    ## 6 6       path80… M 5.… fill… scale(0.… 0                     path     \u003cNA\u003e     \n    ## # ℹ 17 more variables: label \u003cchr\u003e, y \u003cchr\u003e, x \u003cchr\u003e, role \u003cchr\u003e, space \u003cchr\u003e,\n    ## #   stockid \u003cchr\u003e, orient \u003cchr\u003e, refY \u003cchr\u003e, refX \u003cchr\u003e, isstock \u003cchr\u003e,\n    ## #   collect \u003cchr\u003e, cx \u003cchr\u003e, cy \u003cchr\u003e, r \u003cchr\u003e, groupmode \u003cchr\u003e, width \u003cchr\u003e,\n    ## #   height \u003cchr\u003e\n\nThe network has objects for metabolites, reactions, and reaction text\nlabels. We want to modify the thickness of arrows representing flux\nthrough reactions. We import a table with flux data matching the\nreactions in the SVG file.\n\n``` r\ndata(metabolic_flux)\nhead(metabolic_flux)\n```\n\n    ## # A tibble: 6 × 3\n    ##   substrate reaction flux_mmol_gDCW_h\n    ##   \u003cchr\u003e     \u003cchr\u003e               \u003cdbl\u003e\n    ## 1 formate   ACONT               0.366\n    ## 2 formate   AKGDH               0    \n    ## 3 formate   CS                  0.366\n    ## 4 formate   EDA                 0    \n    ## 5 formate   EDD                 0    \n    ## 6 formate   ENO                 3.98\n\nThen we inspect the style of the nodes that we want to change, and find\nthe text bit for `stroke-width`.\n\n``` r\nget_attributes(SVG2, node = \"GAPDH\") %\u003e% pull(style)\n```\n\n    ## [1] \"opacity:1;vector-effect:none;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#808080;stroke-width:0.34395832;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker-start:url(#marker11347);marker-end:url(#marker66490);enable-background:new\"\n\nAs some reactions have very high flux and others no flux at all, we\napply a square root transformation to the fluxes so that stroke width is\nmore balanced.\n\n``` r\nmetabolic_flux \u003c- metabolic_flux %\u003e%\n  mutate(stroke_width = 0.5 + 0.2*sqrt(abs(flux_mmol_gDCW_h)))\n\nSVG2 \u003c- set_attributes(SVG2,\n  node = metabolic_flux$reaction, attr = \"style\",\n  pattern = \"stroke-width:[0-9]+\\\\.[0-9]+\",\n  replacement = paste0(\"stroke-width:\", metabolic_flux$stroke_width))\n```\n\n    ## New attributes were set for 48 node(s).\n\nWe can also change the color according to metabolic flux.\n\n``` r\n# make color palette\npal \u003c- colorRampPalette(c(\"#ABABAB\", \"#009419\", \"#F0B000\", \"#FF4800\"))(10)\n\nmetabolic_flux \u003c- metabolic_flux %\u003e%\n  mutate(\n    stroke_color = stroke_width %\u003e% {1+(./max(.))*9} %\u003e% round,\n    stroke_color_rgb = pal[stroke_color])\n\nSVG2 \u003c- set_attributes(SVG2,\n  node = metabolic_flux$reaction, attr = \"style\",\n  pattern = \"stroke:#808080\",\n  replacement = paste0(\"stroke:\", metabolic_flux$stroke_color_rgb))\n```\n\n    ## New attributes were set for 48 node(s).\n\nAnd for better looks, we can reduce the size of the arrow heads. Arrow\nheads are called `marker` in Inkscape SVGs.\n\n**Important**: Note that the node attribute that is used to filter\nmatching objects changes to `id` now. This can be a different attribute\nfor different types of SVG files or editors (Inkscape, Illustrator, …).\n\nTo modify the size, we change the `transform` field of the `marker`\nnodes. This needs to be done separately for start and end arrows.\n\n``` r\nSVG2 \u003c- set_attributes(SVG2, \n  node = grep(\"marker\", SVG2@summary$id, value = TRUE),\n  node_attr = \"id\",\n  attr = \"transform\",\n  pattern = \"scale\\\\(0.2\\\\)\",\n  replacement = \"scale(0.15)\")\n```\n\n    ## New attributes were set for 37 node(s).\n\n``` r\nSVG2 \u003c- set_attributes(SVG2, \n  node = grep(\"marker\", SVG2@summary$id, value = TRUE),\n  node_attr = \"id\",\n  attr = \"transform\",\n  pattern = \"scale\\\\(-0.2\\\\)\",\n  replacement = \"scale(-0.15)\")\n```\n\n    ## New attributes were set for 37 node(s).\n\nExport the modified SVG file.\n\n``` r\nwrite_svg(SVG2, file = \"inst/extdata/central_metabolism_mod.svg\")\n```\n\n    ## [1] \"inst/extdata/central_metabolism_mod.svg\"\n\n|                Original SVG                 |            SVG with overlaid fluxes             |\n|:-------------------------------------------:|:-----------------------------------------------:|\n| ![](inst/extdata/central_metabolism.png) | ![](inst/extdata/central_metabolism_mod.png) |\n\n### Advanced modifications\n\nPractically every object of the SVG files can be modified in R. Here is\nan (incomplete) list of modifications that can be useful when working\nwith metabolic maps, or other scientific images that profit from\noverlaying numerical data.\n\n#### Change directionality of arrows\n\nSome reactions in the previous example are reversible, hence they have\narrows at the start and at the end. However, the flux in one particular\ncondition can only go in one direction. To visualize *only forward* or\n*only backward* flux, we can remove arrow heads from selected reactions.\nIn the example below the `marker-end:...` string is removed for all\nreactions with negative flux and the `marker-start:...` string for all\nreactions with positive flux.\n\n``` r\nSVG2 \u003c- set_attributes(SVG2,\n  node = filter(metabolic_flux, flux_mmol_gDCW_h \u003c 0)$reaction,\n  attr = \"style\",\n  pattern = \"marker-end:url\\\\(#marker[0-9]*\\\\);\",\n  replacement = \"\")\n```\n\n    ## New attributes were set for 13 node(s).\n\n``` r\nSVG2 \u003c- set_attributes(SVG2,\n  node = filter(metabolic_flux, flux_mmol_gDCW_h \u003e= 0)$reaction,\n  attr = \"style\",\n  pattern = \"marker-start:url\\\\(#marker[0-9]*\\\\);\",\n  replacement = \"\")\n```\n\n    ## New attributes were set for 35 node(s).\n\n``` r\nwrite_svg(SVG2, file = \"inst/extdata/central_metabolism_direction.svg\")\n```\n\n    ## [1] \"inst/extdata/central_metabolism_direction.svg\"\n\n|            SVG with overlaid fluxes             |            SVG with correct directionality            |\n|:-----------------------------------------------:|:-----------------------------------------------------:|\n| ![](inst/extdata/central_metabolism_mod.png) | ![](inst/extdata/central_metabolism_direction.png) |\n\n#### Add numeric flux data\n\nWe can also change text fields in the SVG file and by these means add\nnumeric flux data. All we need is a template that has predefined text\nfields, whose values are then changed using `set_values()`. Values are\ndifferent “fields” in XML files and therefore require an own\nmodification function. We can load a template map that has text fields\nnamed `value_REACTION`. First we can inspect the current values using\n`get_values()` analogously to `get_attributes()`.\n\n``` r\nSVG3 \u003c- read_svg(\"inst/extdata/central_metabolism_values.svg\")\nget_values(SVG3, node = c(\"value_ACONT\", \"value_AKGDH\", \"value_CS\"))\n```\n\n    ## value_ACONT value_AKGDH    value_CS \n    ##       \"0.0\"       \"0.0\"       \"0.0\"\n\nThen we set new values.\n\n``` r\nSVG3 \u003c- set_values(SVG3,\n  node = paste0(\"value_\", metabolic_flux$reaction),\n  value = round(metabolic_flux$flux_mmol_gDCW_h, 3)\n)\n\nwrite_svg(SVG3, file = \"inst/extdata/central_metabolism_values_filled.svg\")\n```\n\n    ## [1] \"inst/extdata/central_metabolism_values_filled.svg\"\n\n|              SVG template for values               |                SVG with added flux values                 |\n|:--------------------------------------------------:|:---------------------------------------------------------:|\n| ![](inst/extdata/central_metabolism_values.png) | ![](inst/extdata/central_metabolism_values_filled.png) |\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fm-jahn%2Ffluctuator","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fm-jahn%2Ffluctuator","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fm-jahn%2Ffluctuator/lists"}