Mapping aesthetics with formulas

George G. Vega Yon

2026-07-23

One of the most convenient features of nplot() is that many aesthetics can be mapped directly from graph attributes using a one-sided formula, instead of building the vector of colors, shapes, or sizes by hand. This vignette walks through the different formula interfaces available in netplot.

At a glance, nplot() understands two flavors of formula:

Aesthetic Formula What it does
vertex.color ~ attr Colors vertices by a vertex attribute (categorical, numeric, logical).
vertex.nsides ~ attr Maps each unique value of a vertex attribute to a distinct shape.
vertex.size ~ attr Scales vertex sizes from a numeric vertex attribute.
edge.width ~ attr Scales edge widths from a numeric edge attribute.
edge.color ~ ego(...) + alter(...) Blends edge colors from the two endpoints (see the last section).
library(netplot)
library(igraph)

A working example

We will use the UKfaculty network from the igraphdata package, a friendship network among faculty at a UK university. It already carries a Group vertex attribute (the school/department each person belongs to).

data("UKfaculty", package = "igraphdata")

set.seed(225)
l <- layout_with_fr(UKfaculty)

# A couple of extra attributes to play with. We qualify igraph::degree()
# explicitly because other packages (e.g. sna) also define a degree().
V(UKfaculty)$indeg  <- igraph::degree(UKfaculty, mode = "in")
V(UKfaculty)$is_hub <- V(UKfaculty)$indeg > stats::median(V(UKfaculty)$indeg)

Coloring vertices: vertex.color = ~ attr

Passing vertex.color = ~ Group colors each vertex according to its Group attribute. netplot detects the type of the attribute and picks a sensible scale automatically:

When you print a plot built this way, netplot also draws a matching legend.

nplot(UKfaculty, layout = l, vertex.color = ~ Group)
Vertices colored by the categorical Group attribute, with an automatic legend.
Vertices colored by the categorical Group attribute, with an automatic legend.

The same syntax works for a numeric attribute, in which case a continuous color gradient is used. netplot detects that the attribute is continuous and draws a color bar (rather than a set of discrete keys) as the legend:

nplot(UKfaculty, layout = l, vertex.color = ~ indeg)
Vertices colored by in-degree (a numeric attribute) using a continuous gradient, with a color-bar legend.
Vertices colored by in-degree (a numeric attribute) using a continuous gradient, with a color-bar legend.

And for a logical attribute, mapping the two values to two colors:

nplot(UKfaculty, layout = l, vertex.color = ~ is_hub)
Vertices colored by a logical attribute (hub vs. non-hub).
Vertices colored by a logical attribute (hub vs. non-hub).

Shaping vertices: vertex.nsides = ~ attr

vertex.nsides controls the number of sides of each vertex polygon (3 = a triangle, 4 = a square, and larger numbers approximate a circle). Passing a formula maps every unique value of the attribute to a distinct shape, which is handy for encoding a second categorical variable alongside color:

nplot(
  UKfaculty,
  layout        = l,
  vertex.color  = ~ Group,
  vertex.nsides = ~ Group
)
Vertex color and shape mapped from the same Group attribute.
Vertex color and shape mapped from the same Group attribute.

Because each group gets both a color and a shape, the figure stays readable even when printed in grayscale.

Sizing vertices: vertex.size = ~ attr

vertex.size accepts a formula naming a numeric vertex attribute. Values are rescaled to the range given by vertex.size.range, so more central actors show up as larger nodes:

nplot(
  UKfaculty,
  layout            = l,
  vertex.size       = ~ indeg,
  vertex.size.range = c(.01, .04, 4)
)
Vertex size mapped from in-degree.
Vertex size mapped from in-degree.

Putting it together

The formula interfaces compose, so a single nplot() call can encode several variables at once — color, shape, and size — with very little code:

nplot(
  UKfaculty,
  layout            = l,
  vertex.color      = ~ Group,   # color by department
  vertex.nsides     = ~ Group,   # distinct shape per department
  vertex.size       = ~ indeg,   # size by popularity (in-degree)
  vertex.size.range = c(.01, .04, 4)
)
Color, shape, and size all mapped from graph attributes in a single call.
Color, shape, and size all mapped from graph attributes in a single call.

Scaling edges: edge.width = ~ attr

Edges have their own numeric attributes too. edge.width = ~ attr scales edge widths from a numeric edge attribute; the values are normalized and mapped to edge.width.range. Here we use the friendship weight:

nplot(
  UKfaculty,
  layout           = l,
  edge.width       = ~ weight,
  edge.width.range = c(1, 4, 4),
  skip.arrows      = TRUE
)
Edge width mapped from the numeric weight edge attribute.
Edge width mapped from the numeric weight edge attribute.

For vertex.nsides, vertex.size, and edge.width, the right-hand side of the formula is evaluated with the graph’s attributes in scope, so you are not limited to bare attribute names — expressions work too, e.g. edge.width = ~ log1p(weight) or vertex.size = ~ degree ^ 2.

Coloring edges: edge.color = ~ ego(...) + alter(...)

Edge colors use a richer, dedicated grammar built from two special terms:

Each term borrows its color from the corresponding endpoint’s vertex.color (unless you pass an explicit col), and each accepts three tweaks:

The default, ~ ego(alpha = .1, col = "gray") + alter, fades each edge from a faint gray at the source to the target’s color. The panels below vary mix to shift the blend from alter only to ego only:

gridExtra::grid.arrange(
  nplot(UKfaculty, layout = l, vertex.color = ~ Group,
        edge.color = ~ ego(mix = 0, alpha = .1) + alter(mix = 1)),
  nplot(UKfaculty, layout = l, vertex.color = ~ Group,
        edge.color = ~ ego(mix = .5, alpha = .1) + alter(mix = .5)),
  nplot(UKfaculty, layout = l, vertex.color = ~ Group,
        edge.color = ~ ego(mix = 1, alpha = .1) + alter(mix = 0)),
  ncol = 3
)
Varying the mix between ego and alter in the edge color formula. Left: alter only. Middle: an even blend. Right: ego only.
Varying the mix between ego and alter in the edge color formula. Left: alter only. Middle: an even blend. Right: ego only.

Applying formulas after the fact

The attribute-mapping formulas for vertex color also work with set_vertex_gpar(), so you can recolor an existing plot without rebuilding it:

np <- nplot(UKfaculty, layout = l)

set_vertex_gpar(np, element = "core", fill = ~ Group)
Recoloring an existing plot by attribute using set_vertex_gpar().
Recoloring an existing plot by attribute using set_vertex_gpar().

Summary