Fitting an ERGM and Viewing the Output with tabulergm

Overview

This vignette demonstrates how to:

  1. Fit an ERGM using the ergm package.
  2. Produce a publication-ready Markdown table with tabulergm_table(format = "markdown").
  3. Render the table inline in a Quarto or R Markdown document using results: asis.
  4. Replace the shipped term titles, descriptions, and citations for a single table.
  5. Interactively preview the table in the RStudio viewer (or a browser) with tabulergm_view().

Setup

library(ergm)
library(tabulergm)

Fitting the model

We use the Florentine marriage network, which ships with ergm, and fit a simple model with an edges term and a nodematch term for wealth quartile.

data(florentine)

model <- ergm(
  flomarriage ~ edges + nodematch("wealth"),
  control = control.ergm(seed = 42)
)
summary(model)
#> Call:
#> ergm(formula = flomarriage ~ edges + nodematch("wealth"), control = control.ergm(seed = 42))
#> 
#> Maximum Likelihood Results:
#> 
#>                  Estimate Std. Error MCMC % z value Pr(>|z|)    
#> edges             -1.5892     0.2454      0  -6.477   <1e-04 ***
#> nodematch.wealth     -Inf     0.0000      0    -Inf   <1e-04 ***
#> ---
#> Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#> 
#>      Null Deviance: 166.4  on 120  degrees of freedom
#>  Residual Deviance: 107.4  on 118  degrees of freedom
#>  
#> AIC: 109.4  BIC: 112.2  (Smaller is better. MC Std. Err. = 0)
#> Warnings:
#> 
#>  * The following terms have infinite coefficient estimates due to an
#>    extreme sufficient statistic:
#> 
#>    nodematch.wealth

Creating a Markdown table

Calling tabulergm_table() with format = "markdown" returns a knitr_kable object. Adding the chunk option results: asis (or results = "asis" in R Markdown) causes knitr to emit the table verbatim, so the Markdown renderer (Quarto, Pandoc, GitHub, etc.) formats it properly.

Math notation in the math column is automatically wrapped in $...$ so that Pandoc can render it reliably in table cells across output formats, including Word. Network figures in the figure column are emitted with Markdown image syntax.

Quarto tip: use #| results: asis (or results = "asis" in R Markdown) on the chunk so that knitr emits the table verbatim instead of quoting it.

tabulergm_table(
  model,
  include_math        = TRUE,
  include_description = TRUE,
  format              = "markdown"
)
term figure estimate se pvalue description math
edges -1.589235 0.2453649 0 Counts the ties present in the network. Acts as the baseline density term, playing the role an intercept plays in a regression model. \(\sum_{i<j} y_{ij}\)
nodematch -Inf 0.0000000 0 Counts the ties joining nodes that share the same value of a categorical attribute, the standard measure of homophily. Setting diff = TRUE adds one statistic per attribute value (differential homophily). (mcpherson2001) \(\sum_{i<j} y_{ij} \mathbf{1}(x_i = x_j)\)

Note: Orange nodes indicate nodes with a focal attribute.

\[mcpherson2001\] doi:10.1146/annurev.soc.27.1.415

Customizing titles, descriptions, and citations

Each term carries a short title and a plain-language description, taken from tabulergm’s term dictionary and falling back to the ergm term database for terms the dictionary does not cover. Add the title column with include_title = TRUE, and replace either field for a single table with the override.* arguments:

tabulergm_table(
  model,
  include_title       = TRUE,
  include_description = TRUE,
  override.title      = c(edges = "Density"),
  override.desc       = c(edges = "Baseline propensity to form ties."),
  format              = "markdown"
)
term title figure estimate se pvalue description
edges Density -1.589235 0.2453649 0 Baseline propensity to form ties.
nodematch Uniform homophily -Inf 0.0000000 0 Counts the ties joining nodes that share the same value of a categorical attribute, the standard measure of homophily. Setting diff = TRUE adds one statistic per attribute value (differential homophily). (mcpherson2001)

Note: Orange nodes indicate nodes with a focal attribute.

\[mcpherson2001\] doi:10.1146/annurev.soc.27.1.415

override.math, override.figure, and override.citation work the same way, and the single override argument sets several fields at once:

tabulergm_table(
  model,
  override = list(
    edges     = list(title = "Density", desc = "Baseline tie propensity."),
    nodematch = list(citation = "doi:10.1146/annurev.soc.27.1.415")
  )
)

Override names are matched against the term name first and the coefficient name second, so an expanded coefficient such as nodematch.wealth.3 can be targeted on its own.

Terms with a citation show a (key) marker next to their description, and the matching [key] identifier line is appended below the table. Citations are stored as a DOI, arXiv id, PubMed id, or URL rather than a formatted reference, so readers can import them into their own bibliography software:

tabulergm_table(
  flomarriage ~ gwesp(0.5, fixed = TRUE) + gwdegree(0.5, fixed = TRUE),
  format = "markdown"
)
term figure math description
gwesp \(\exp{(\tau)} \sum_{i=1}^{n-2} \left[1 - \left(1 - \exp{(-\tau)}\right)^i\right] EP_i(y)\) Summarizes how many partners tied nodes share, weighting each additional shared partner geometrically less than the last. Provides a better-behaved measure of transitive closure than a raw triangle count; the decay parameter controls how fast the weights fall off. (hunter2007)
gwdegree \(\exp{(\tau)} \sum_{i=1}^{n-1} \left[1 - \left(1 - \exp{(-\tau)}\right)^i\right] D_i(y)\) Summarizes the degree distribution with geometrically decreasing weights. Captures whether ties concentrate on a few high-degree nodes or spread evenly, and stabilizes models that would otherwise degenerate. (snijders2006; hunter2007)

\[hunter2007\] doi:10.1016/j.socnet.2006.08.005
\[snijders2006\] doi:10.1111/j.1467-9531.2006.00176.x

Inspecting a formula without a fitted model

You can also pass a bare formula to inspect term metadata before fitting:

tabulergm_table(
  flomarriage ~ edges + nodematch("wealth") + triangle,
  format = "markdown"
)
term figure math description
edges \(\sum_{i<j} y_{ij}\) Counts the ties present in the network. Acts as the baseline density term, playing the role an intercept plays in a regression model.
nodematch \(\sum_{i<j} y_{ij} \mathbf{1}(x_i = x_j)\) Counts the ties joining nodes that share the same value of a categorical attribute, the standard measure of homophily. Setting diff = TRUE adds one statistic per attribute value (differential homophily). (mcpherson2001)
triangle \(\sum_{i<j<k} y_{ij} y_{jk} y_{ik}\) Counts the sets of three mutually connected nodes, the basic measure of local clustering in an undirected network. (frank1986)

Note: Orange nodes indicate nodes with a focal attribute.

\[mcpherson2001\] doi:10.1146/annurev.soc.27.1.415
\[frank1986\] doi:10.1080/01621459.1986.10478342

The term dictionary

tabulergm ships math and network drawings for commonly used ERGM terms, including directed variants and mode-specific bipartite terms (b1* terms summarize the first mode, and b2* terms summarize the second mode). The table below covers every term currently included in the dictionary; terms with both directed and undirected definitions (edges, gwesp, gwdsp) display the undirected version:

dictionary_terms <- network ~
  edges + mutual + triangle +
  gwesp(0.5, fixed = TRUE) + gwdsp(0.5, fixed = TRUE) +
  gwdegree(0.5, fixed = TRUE) + altkstar(2, fixed = TRUE) +
  nodematch("attr") + nodefactor("attr") + nodemix("attr") +
  nodecov("attr") + absdiff("attr") + edgecov("cov") +
  transitiveties + cyclicalties +
  nodeicov("attr") + nodeocov("attr") +
  gwb1dsp(0.5, fixed = TRUE) + gwb2dsp(0.5, fixed = TRUE) +
  b1factor("type") + b2factor("group") +
  b1nodematch("type") + b2nodematch("group") +
  b1starmix(2, "type") + b2starmix(2, "group")

tabulergm_table(dictionary_terms, format = "markdown")
term figure math description
edges \(\sum_{i<j} y_{ij}\) Counts the ties present in the network. Acts as the baseline density term, playing the role an intercept plays in a regression model.
mutual \(\sum_{i<j} y_{ij} y_{ji}\) Counts the dyads in which both directed ties are present, capturing the tendency for ties to be returned. (holland1981)
triangle \(\sum_{i<j<k} y_{ij} y_{jk} y_{ik}\) Counts the sets of three mutually connected nodes, the basic measure of local clustering in an undirected network. (frank1986)
gwesp \(\exp{(\tau)} \sum_{i=1}^{n-2} \left[1 - \left(1 - \exp{(-\tau)}\right)^i\right] EP_i(y)\) Summarizes how many partners tied nodes share, weighting each additional shared partner geometrically less than the last. Provides a better-behaved measure of transitive closure than a raw triangle count; the decay parameter controls how fast the weights fall off. (hunter2007)
gwdsp \(\exp{(\tau)} \sum_{i=1}^{n-2} \left[1 - \left(1 - \exp{(-\tau)}\right)^i\right] DP_i(y)\) Summarizes shared partners over every dyad, tied or not, with geometrically decreasing weights. Commonly paired with gwesp to separate shared partnership from closure itself. (hunter2007)
gwdegree \(\exp{(\tau)} \sum_{i=1}^{n-1} \left[1 - \left(1 - \exp{(-\tau)}\right)^i\right] D_i(y)\) Summarizes the degree distribution with geometrically decreasing weights. Captures whether ties concentrate on a few high-degree nodes or spread evenly, and stabilizes models that would otherwise degenerate. (snijders2006; hunter2007)
altkstar \(\sum_{k=2}^{n-1} (-1)^k \frac{S_k(y)}{\lambda^{k-2}}\) Alternating sum of the k-star counts, an equivalent parameterization of the geometrically weighted degree distribution used to model degree heterogeneity. (snijders2006; hunter2007)
nodematch \(\sum_{i<j} y_{ij} \mathbf{1}(x_i = x_j)\) Counts the ties joining nodes that share the same value of a categorical attribute, the standard measure of homophily. Setting diff = TRUE adds one statistic per attribute value (differential homophily). (mcpherson2001)
nodefactor \(\sum_{i<j} y_{ij} \left[\mathbf{1}(x_i = k) + \mathbf{1}(x_j = k)\right]\) Counts the tie endpoints belonging to each level of a categorical attribute, measuring how active nodes with that value are in forming ties.
nodemix \(\sum_{i<j} y_{ij} \mathbf{1}(\{x_i, x_j\} = \{k, l\})\) Counts the ties for every pairing of attribute values, reproducing the full mixing matrix of a categorical attribute.
nodecov \(\sum_{i<j} y_{ij} (x_i + x_j)\) Sums a quantitative attribute over both ends of each tie, measuring how strongly that attribute drives tie formation.
absdiff \(\sum_{i<j} y_{ij} \left\lvert{}x_i - x_j\right\rvert{}\) Sums the absolute difference in a quantitative attribute across tied nodes. Negative estimates indicate homophily, since similar nodes contribute less.
edgecov \(\sum_{i<j} y_{ij} x_{ij}\) Sums a fixed dyad-level covariate over the observed ties, letting an external matrix such as distance or a previously observed network predict tie formation.
transitiveties \(\sum_{i \neq j} y_{ij} \mathbf{1}\left(\exists k : y_{ik} y_{kj} = 1\right)\) Counts the ties closed by at least one two-path. Unlike a triple count, a tie contributes once no matter how many shared partners it has.
cyclicalties \(\sum_{i \neq j} y_{ij} \mathbf{1}\left(\exists k : y_{jk} y_{ki} = 1\right)\) Counts the ties that take part in at least one cycle, capturing generalized exchange rather than hierarchy.
nodeicov \(\sum_{i \neq j} y_{ij} x_j\) Sums the receiving node’s attribute value over all ties, measuring how a quantitative attribute drives incoming ties (popularity).
nodeocov \(\sum_{i \neq j} y_{ij} x_i\) Sums the sending node’s attribute value over all ties, measuring how a quantitative attribute drives outgoing ties (activity).
gwb1dsp \(\exp{(\tau)} \sum_{i=1}^{n_{B_2}} \left[1 - \left(1 - \exp{(-\tau)}\right)^i\right] DP_i(y)\) Summarizes how many second-mode nodes each pair of first-mode nodes has in common, weighting additional shared partners geometrically less.
gwb2dsp \(\exp{(\tau)} \sum_{i=1}^{n_{B_1}} \left[1 - \left(1 - \exp{(-\tau)}\right)^i\right] DP_i(y)\) Summarizes how many first-mode nodes each pair of second-mode nodes has in common, weighting additional shared partners geometrically less.
b1factor \(\sum_{i \in B_1} \sum_{j \in B_2} y_{ij} \mathbf{1}(x_i = k)\) Counts the ties incident on first-mode nodes at each level of a categorical attribute, measuring how active those nodes are.
b2factor \(\sum_{i \in B_1} \sum_{j \in B_2} y_{ij} \mathbf{1}(x_j = k)\) Counts the ties incident on second-mode nodes at each level of a categorical attribute, measuring how active those nodes are.
b1nodematch \(\sum_{k\in B_2} \sum_{i<j \in B_1} \mathbf{1}(x_i = x_j) y_{ik} y_{jk}\) Counts the pairs of first-mode nodes that share an attribute value and are both tied to the same second-mode node. The alpha and beta discount parameters temper the count when nodes share many partners. (bomiriya2014)
b2nodematch \(\sum_{k\in B_1} \sum_{i<j \in B_2} \mathbf{1}(x_i = x_j) y_{ik} y_{jk}\) Counts the pairs of second-mode nodes that share an attribute value and are both tied to the same first-mode node. The alpha and beta discount parameters temper the count when nodes share many partners. (bomiriya2014)
b1starmix \(\sum_{i \in B_1} \mathbf{1}(x_i = p) \sum_{j_1 < \cdots < j_k \in B_2} \prod_{l=1}^{k} y_{i j_l} \mathbf{1}(x_{j_l} = q)\) Counts the k-stars centered on a first-mode node with one attribute value whose second-mode neighbors all share another, capturing mixing and degree together.
b2starmix \(\sum_{j \in B_2} \mathbf{1}(x_j = p) \sum_{i_1 < \cdots < i_k \in B_1} \prod_{l=1}^{k} y_{i_l j} \mathbf{1}(x_{i_l} = q)\) Counts the k-stars centered on a second-mode node with one attribute value whose first-mode neighbors all share another, capturing mixing and degree together.

Note: Orange nodes indicate nodes with a focal attribute. Orange and teal nodes represent nodes with different values of the focal attribute. Square nodes represent nodes in the first mode and circle nodes in the second mode.

\[holland1981\] doi:10.1080/01621459.1981.10477598
\[frank1986\] doi:10.1080/01621459.1986.10478342
\[hunter2007\] doi:10.1016/j.socnet.2006.08.005
\[snijders2006\] doi:10.1111/j.1467-9531.2006.00176.x
\[mcpherson2001\] doi:10.1146/annurev.soc.27.1.415
\[bomiriya2014\] doi:10.48550/arXiv.2312.05673

Interactive preview with tabulergm_view()

During an interactive session you can call tabulergm_view() to open the table in the RStudio viewer pane or the system browser:

tabulergm_view(model, include_math = TRUE, include_description = TRUE)

tabulergm_view() builds a self-contained HTML page that loads MathJax from a CDN, so LaTeX math and embedded network figures render immediately without any additional setup.