punKEGGer was created to make KEGG pathway data
accessible and useful for network science workflows.
Traditional approaches to KEGG data within Bioconductor often reduce
pathways to simple gene sets (e.g., for enrichment analysis),
custom-saved graphNEL objects, or static visualizations
with hardcoded layouts. While those are valid for many use cases, they
often lose the rich topological and visual information encoded in KEGG’s
own pathway representations.
punKEGGer provides a different approach: it parses the
original .kgml files and converts them into clean,
annotated igraph objects, with optional layouts and
metadata preserved. From there, you can analyze topological features,
integrate identifiers, combine multiple pathways, or run dynamic
simulations — whatever your network science questions demand. It is,
after all, a Pretty Useful Networks-from-KEGG Exploration Resource.
This first example shows the basic parsing workflow. We start from a KGML file, which is KEGG’s XML representation of a pathway, and convert it into a tidygraph/igraph object. The resulting graph preserves pathway relations and can be used directly with standard network analysis tools.
# usually you'll read from the official database like so
# kgml <- read_xml("https://rest.kegg.jp/get/hsa04210/kgml")
# or use the function fetch_kegg_kgml
# kgml <- fetch_kegg_kgml("hsa04210")
# but for reproducibility purposes you can use
kgml_file <- system.file("extdata", "hsa04210.xml", package = "punKEGGer")
kgml <- xml2::read_xml(kgml_file)
g <- combine_kegg_network(kgml)
g
#> # A tbl_graph: 108 nodes and 141 edges
#> #
#> # A directed acyclic simple graph with 5 components
#> #
#> # Node Data: 108 × 2 (active)
#> name meta_id
#> <chr> <chr>
#> 1 53 53
#> 2 47 47
#> 3 54 54
#> 4 48 48
#> 5 43 43
#> 6 152 152
#> 7 51 51
#> 8 46 46
#> 9 38 295
#> 10 156 156
#> # ℹ 98 more rows
#> #
#> # Edge Data: 141 × 4
#> from to type subtype
#> <int> <int> <chr> <chr>
#> 1 1 2 PPrel activation
#> 2 2 5 PPrel activation
#> 3 3 4 PPrel activation
#> # ℹ 138 more rowsThe printed object summarizes the parsed pathway as an tidygraph object. At this stage, nodes still are annotated using KEGG entry identifiers; annotation is added in the next step.
KEGG entries often group several molecules (genes, etc) identifiers into a single pathway node. For molecule-level analyses, punKEGGer can expand these metanodes and then attach external identifiers, such as HGNC symbols, using a user-provided dictionary; such dictionaries can be obtained from resources such as BioMart.
node_info <- extract_kegg_nodes(kgml)
dict_path <- system.file("extdata", "example_dict.hsa04210.csv", package = "punKEGGer")
kegg_dict <- read.csv(dict_path)
# creates the dictionary for the metanodes
meta_dict <- create_meta_dict(node_info, kegg_dict)
# expands metanodes and returns a new graph
g_exp <- expand_metagraph(g, node_info, node_types = "gene")
#annotates expanded graph
g_annot <- annotate_kegg_graph(g_exp, meta_dict, identifiers = "hgnc_symbol")
#show the expanded graph with annotated nodes
tidygraph::as_tibble(g_annot, active = "nodes") |>
dplyr::select(name, meta_id, type, hgnc_symbol) |>
head()
#> # A tibble: 6 × 4
#> name meta_id type hgnc_symbol
#> <chr> <chr> <chr> <chr>
#> 1 hsa:598 10 gene BCL2L1
#> 2 hsa:1676 11 gene DFFA
#> 3 hsa:472 12 gene ATM
#> 4 hsa:7157 13 gene TP53
#> 5 hsa:2021 14 gene ENDOG
#> 6 hsa:9131 15 gene AIFM1Here, we expanded metanodes into single nodes. Then, we add identifier columns to the node table. These columns can then be used for plotting, filtering, or downstream analyses.
KEGG pathways include manually curated coordinates that reproduce the familiar KEGG pathway layout. layout_kegg() extracts those coordinates, and plot_kegg_graph() uses them to create a KEGG-like network plot.
layout_tbl <- layout_kegg(kgml)
g_layout <- add_kegg_layout(g_annot, layout_tbl)
plot_kegg_graph(g_layout, id = hgnc_symbol)In some analyses, the curated KEGG layout is less important than the graph topology itself. Since the output is a standard graph object, it can also be visualized with any other ggraph-compatible layouts.
library(ggraph)
#> Loading required package: ggplot2
library(tidygraph)
#>
#> Attaching package: 'tidygraph'
#> The following object is masked from 'package:stats':
#>
#> filter
# let's calculate some simple centrality measures
g_annot <-
g_annot |>
mutate(degree = centrality_degree())
# now we visualize the network using a force-directed layout, and show node size and label size proportional to degree
ggraph(g_annot, layout = "fr") +
geom_edge_link() +
geom_node_label(aes(size = degree,
label = hgnc_symbol),
fill = "#9D4EDD",
color = "white",
label.r = unit(0.15,"lines"),
label.padding = unit(0.25,"lines"),
label.size = 0.2,
repel = TRUE
) +
theme_void()
#let's identify modules in the network using the Infomap algorithm
g_annot <-
as_tbl_graph(g_annot)
g_annot <-
g_annot |>
mutate(infomap = group_infomap())
#let's calculate degree centrality
g_annot <-
g_annot |>
mutate(degree = centrality_degree())
# now, let's identify the gene(s) with highest centrality per module
df <-
g_annot |>
as_tibble() |>
group_by(infomap) |>
filter(degree == max(degree)) |>
arrange(infomap)
print(df)
#> # A tibble: 14 × 6
#> # Groups: infomap [8]
#> name meta_id type hgnc_symbol degree infomap
#> <chr> <chr> <chr> <chr> <dbl> <int>
#> 1 hsa:10000 33 gene AKT3 7 1
#> 2 hsa:207 33 gene AKT1 7 1
#> 3 hsa:208 33 gene AKT2 7 1
#> 4 hsa:4790 23 gene NFKB1 5 2
#> 5 hsa:5970 23 gene RELA 5 2
#> 6 hsa:823 32 gene CAPN1 3 3
#> 7 hsa:9020 34 gene MAP3K14 3 4
#> 8 hsa:8795 47 gene TNFRSF10B 1 5
#> 9 hsa:8797 47 gene TNFRSF10A 1 5
#> 10 hsa:355 48 gene FAS 1 5
#> 11 hsa:3562 49 gene IL3 1 5
#> 12 hsa:842 19 gene CASP9 3 6
#> 13 hsa:9131 15 gene AIFM1 1 7
#> 14 hsa:8743 53 gene TNFSF10 1 8In this example, we take our previous KEGG graph, find modules using a community detection method, and identify the most connected gene in each module. This is just an example; From here, you can compute centralities, detect communities, or simulate diffusion – whatever your network science needs demand.
#> R version 4.6.1 (2026-06-24)
#> Platform: x86_64-pc-linux-gnu
#> Running under: Ubuntu 26.04.1 LTS
#>
#> Matrix products: default
#> BLAS: /usr/lib/x86_64-linux-gnu/openblas-pthread/libblas.so.3
#> LAPACK: /usr/lib/x86_64-linux-gnu/openblas-pthread/libopenblasp-r0.3.32.so; LAPACK version 3.12.0
#>
#> locale:
#> [1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C
#> [3] LC_TIME=en_US.UTF-8 LC_COLLATE=en_US.UTF-8
#> [5] LC_MONETARY=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8
#> [7] LC_PAPER=en_US.UTF-8 LC_NAME=C
#> [9] LC_ADDRESS=C LC_TELEPHONE=C
#> [11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C
#>
#> time zone: Etc/UTC
#> tzcode source: system (glibc)
#>
#> attached base packages:
#> [1] stats graphics grDevices utils datasets methods base
#>
#> other attached packages:
#> [1] tidygraph_1.3.1 ggraph_2.2.2 ggplot2_4.0.3 punKEGGer_0.99.65
#> [5] BiocStyle_2.41.0
#>
#> loaded via a namespace (and not attached):
#> [1] viridis_0.6.5 sass_0.4.10 utf8_1.2.6
#> [4] generics_0.1.4 tidyr_1.3.2 xml2_1.6.0
#> [7] stringi_1.8.9 digest_0.6.39 magrittr_2.0.5
#> [10] evaluate_1.0.5 grid_4.6.1 RColorBrewer_1.1-3
#> [13] fastmap_1.2.0 jsonlite_2.0.0 ggrepel_0.9.8
#> [16] gridExtra_2.3.1 BiocManager_1.30.27 purrr_1.2.2
#> [19] viridisLite_0.4.3 scales_1.4.0 tweenr_2.0.3
#> [22] jquerylib_0.1.4 cli_3.6.6 graphlayouts_1.2.5
#> [25] rlang_1.3.0 polyclip_1.10-7 withr_3.0.3
#> [28] cachem_1.1.0 yaml_2.3.12 otel_0.2.0
#> [31] tools_4.6.1 memoise_2.0.1 dplyr_1.2.1
#> [34] buildtools_1.0.0 vctrs_0.7.3 R6_2.6.1
#> [37] lifecycle_1.0.5 stringr_1.6.0 MASS_7.3-66
#> [40] pkgconfig_2.0.3 pillar_1.11.1 bslib_0.12.0
#> [43] gtable_0.3.6 Rcpp_1.1.2 glue_1.8.1
#> [46] ggforce_0.5.0 xfun_0.61 tibble_3.3.1
#> [49] tidyselect_1.2.1 sys_3.4.3 knitr_1.52
#> [52] farver_2.1.2 htmltools_0.5.9 igraph_2.3.4
#> [55] labeling_0.4.3 rmarkdown_2.32 maketools_1.3.2
#> [58] compiler_4.6.1 S7_0.2.2