Marks (Geometry Layers)

library(gglite)

gglite can automatically infer the mark type from your data and aesthetic mappings in many cases — for example, numeric × numeric data defaults to mark_point(), and categorical × unique numeric data defaults to mark_interval(). This means you can often omit the mark_*() call entirely. All marks are specified explicitly on this page because the purpose here is to document each mark type individually.

1 Point (Scatter Plot)

g2(mtcars, hp ~ mpg) |> mark_point()

1.1 Color by a variable

g2(mtcars, hp ~ mpg, color = ~ wt) |>
  mark_point()

1.2 Size by a variable

g2(mtcars, hp ~ mpg, size = ~ wt) |> mark_point()

1.3 Shape by a variable

g2(iris, Sepal.Length ~ Sepal.Width, shape = ~ Species) |>
  mark_point()

1.4 Custom style

g2(mtcars, hp ~ mpg, size = 10) |>
  mark_point() |>
  style_mark(fill = 'lightyellow', stroke = 'steelblue', lineWidth = 2)

2 Line

df = data.frame(x = 1:10, y = cumsum(rnorm(10)))
g2(df, y ~ x) |> mark_line()

2.1 Multi-series line

df = data.frame(
  x = rep(1:5, 2), y = c(3, 1, 4, 1, 5, 2, 7, 1, 8, 3),
  group = rep(c('A', 'B'), each = 5)
)
g2(df, y ~ x, color = ~ group) |> mark_line()

3 Interval (Bar Chart)

df = data.frame(x = c('A', 'B', 'C', 'D'), y = c(3, 7, 2, 5))
g2(df, y ~ x) |> mark_interval()

3.1 Stacked bar

df = data.frame(
  x = rep(c('A', 'B', 'C'), each = 2), y = c(3, 2, 5, 4, 1, 6),
  color = rep(c('a', 'b'), 3)
)
g2(df, y ~ x, color = ~ color) |>
  mark_interval() |> transform('stackY')

3.2 Grouped (dodged) bar

g2(df, y ~ x, color = ~ color) |>
  mark_interval() |> transform('dodgeX')

4 Area

df = data.frame(x = 1:10, y = c(3, 1, 4, 1, 5, 9, 2, 6, 5, 3))
g2(df, y ~ x) |> mark_area()

4.1 Stacked area

df = data.frame(
  x = rep(1:5, 2), y = c(3, 1, 4, 1, 5, 2, 7, 1, 8, 3),
  group = rep(c('A', 'B'), each = 5)
)
g2(df, y ~ x, color = ~ group) |>
  mark_area() |> transform('stackY')

5 Rect

g2(mtcars, hp ~ mpg) |>
  mark_rect() |>
  transform('bin', thresholdsX = 10, thresholdsY = 10)

6 Cell

df = expand.grid(x = LETTERS[1:5], y = LETTERS[1:5])
df$value = seq_len(nrow(df))
g2(df, y ~ x, color = ~ value) |> mark_cell()

7 Text

df = data.frame(x = c('A', 'B', 'C'), y = c(3, 7, 2))
g2(df, y ~ x) |>
  mark_interval() |>
  mark_text(encode = list(text = 'y'))

8 Path

The path mark in G2 uses an SVG-like d channel to draw arbitrary shapes. It does not use x/y data coordinates like mark_line(). Use mark_line() to connect data points in order.

g2(df, y ~ x) |> mark_path()

A line mark connecting data points in order:

n = 100
t = seq(0, 4 * pi, length.out = n)
df = data.frame(x = t * cos(t), y = t * sin(t))
g2(df, y ~ x) |> mark_line()

9 Polygon

The polygon mark draws filled polygons. Use list columns to store vertex coordinates for each polygon.

df = data.frame(group = c('triangle', 'square'))
df$x = list(c(0, 1, 0.5), c(2, 3, 3, 2))
df$y = list(c(0, 0, 1), c(0, 0, 1, 1))
g2(df, color = ~ group) |>
  mark_polygon(encode = list(x = 'x', y = 'y'))
df = data.frame(
  x = c('A', 'B', 'C'), y = c(1, 3, 2),
  x1 = c('B', 'C', 'D'), y1 = c(3, 1, 4)
)
g2(df) |>
  mark_link(encode = list(x = c('x', 'x1'), y = c('y', 'y1'))) |>
  style_mark(stroke = 'steelblue', lineWidth = 2)

11 Reference Lines

11.1 Vertical reference line (lineX)

g2(mtcars, hp ~ mpg) |>
  mark_point() |>
  mark_line_x(
    data = list(list(x = mean(mtcars$mpg))),
    encode = list(x = 'x')
  ) |>
  style_mark(stroke = 'red', lineDash = c(4, 4))

11.2 Horizontal reference line (lineY)

g2(mtcars, hp ~ mpg) |>
  mark_point() |>
  mark_line_y(
    data = list(list(y = mean(mtcars$hp))),
    encode = list(y = 'y')
  ) |>
  style_mark(stroke = 'red', lineDash = c(4, 4))

12 Range

12.1 Full range

g2(mtcars, hp ~ mpg) |>
  mark_point() |>
  mark_range(
    data = list(list(x = c(15, 25), y = c(100, 200))),
    encode = list(x = 'x', y = 'y')
  ) |>
  style_mark(fill = 'steelblue', fillOpacity = 0.15)

12.2 Horizontal range (rangeX)

g2(mtcars, hp ~ mpg) |>
  mark_point() |>
  mark_range_x(
    data = list(list(x = c(15, 25))),
    encode = list(x = 'x')
  ) |>
  style_mark(fill = 'steelblue', fillOpacity = 0.15)

12.3 Vertical range (rangeY)

g2(mtcars, hp ~ mpg) |>
  mark_point() |>
  mark_range_y(
    data = list(list(y = c(100, 200))),
    encode = list(y = 'y')
  ) |>
  style_mark(fill = 'orange', fillOpacity = 0.15)

13 Connector

df = data.frame(x = c('A', 'B'), y = c(3, 7))
g2(df, y ~ x) |>
  mark_interval() |>
  mark_connector(
    data = list(list(x = 'A', x1 = 'B')),
    encode = list(x = 'x', x1 = 'x1')
  ) |>
  labels(text = '+133%')

14 Box Plot

g2(iris, Sepal.Width ~ Species) |> mark_boxplot()

15 Beeswarm

The beeswarm mark uses force simulation to arrange individual data points without overlap, creating a beeswarm layout useful for visualizing distributions within categories.

g2(iris, Sepal.Width ~ Species) |> mark_beeswarm()

15.1 Beeswarm with color

g2(iris, Sepal.Width ~ Species, color = ~ Species) |>
  mark_beeswarm()

15.2 Beeswarm + box plot overlay

g2(iris, Sepal.Width ~ Species) |>
  mark_beeswarm() |> mark_boxplot() |> style_mark(boxFillOpacity = 0.15)

16 Liquid

g2() |>
  mark_liquid(data = list(value = 0.6)) |>
  style_mark(contentText = '60%', contentFill = 'white', contentFontSize = 40)

17 Heatmap

g2(iris, Sepal.Length ~ Sepal.Width, color = ~ Petal.Length) |>
  mark_heatmap()

18 Image

The image mark places images at data coordinates.

df = data.frame(
  x = c(1, 2, 3), y = c(3, 5, 2),
  url = rep(
    'https://cdn.jsdelivr.net/npm/@browser-logos/chrome/chrome_64x64.png', 3
  )
)
g2(df, y ~ x) |>
  mark_image(encode = list(src = 'url')) |>
  style_mark(width = 30, height = 30)

19 Word Cloud

df = data.frame(
  text = c('Hello', 'Data', 'Science', 'R', 'G2', 'Chart', 'Vis', 'Stats'),
  value = c(30, 25, 20, 18, 15, 12, 10, 8)
)
g2(df) |>
  mark_word_cloud(
    encode = list(text = 'text', value = 'value', color = 'text')
  )

20 Sankey

df = data.frame(
  source = c('Coal', 'Coal', 'Oil', 'Oil', 'Gas', 'Gas', 'Solar'),
  target = c('Electricity', 'Heat', 'Electricity', 'Transport',
    'Electricity', 'Heat', 'Electricity'),
  value = c(25, 10, 15, 20, 20, 5, 10)
)
g2(df) |>
  mark_sankey(
    encode = list(source = 'source', target = 'target', value = 'value'),
    layout = list(nodeAlign = 'center', nodePadding = 0.03)
  )

21 Chord

df = data.frame(
  source = c('A', 'A', 'A', 'B', 'B', 'C'),
  target = c('B', 'C', 'D', 'C', 'D', 'D'),
  value = c(5, 3, 2, 4, 1, 3)
)
g2(df) |>
  mark_chord(
    encode = list(source = 'source', target = 'target', value = 'value')
  )

22 Treemap

tree_data = list(
  name = 'root', children = list(
    list(name = 'Frontend', children = list(
      list(name = 'React', value = 180),
      list(name = 'Vue', value = 120),
      list(name = 'Angular', value = 80)
    )),
    list(name = 'Backend', children = list(
      list(name = 'Node.js', value = 150),
      list(name = 'Python', value = 130),
      list(name = 'Go', value = 90)
    )),
    list(name = 'Data', children = list(
      list(name = 'R', value = 100),
      list(name = 'Julia', value = 40)
    ))
  )
)
g2() |>
  mark_treemap(
    data = list(type = 'inline', value = tree_data),
    encode = list(value = 'value')
  )

23 Pack (Circle Packing)

g2() |>
  mark_pack(
    data = list(type = 'inline', value = tree_data),
    encode = list(value = 'value', color = 'depth'),
    legend = FALSE
  ) |>
  style_mark(labelText = js(
    '(d) => d.r >= 10 && d.height === 0 ? d.data.name : ""'
  ))

24 Gauge

g2() |>
  mark_gauge(
    data = list(
      type = 'inline',
      value = list(target = 120, total = 400, name = 'score')
    )
  )

25 Force Graph

g2() |>
  mark_force_graph(
    data = list(type = 'inline', value = list(
      nodes = list(
        list(id = 'CEO'), list(id = 'CTO'), list(id = 'CFO'),
        list(id = 'VP Eng'), list(id = 'VP Sales'),
        list(id = 'Dev Lead'), list(id = 'Backend'),
        list(id = 'Frontend'), list(id = 'Accounting'),
        list(id = 'Payroll'), list(id = 'Sales Rep 1'),
        list(id = 'Sales Rep 2')
      ),
      links = list(
        list(source = 'CEO', target = 'CTO'),
        list(source = 'CEO', target = 'CFO'),
        list(source = 'CEO', target = 'VP Sales'),
        list(source = 'CTO', target = 'VP Eng'),
        list(source = 'VP Eng', target = 'Dev Lead'),
        list(source = 'Dev Lead', target = 'Backend'),
        list(source = 'Dev Lead', target = 'Frontend'),
        list(source = 'CFO', target = 'Accounting'),
        list(source = 'CFO', target = 'Payroll'),
        list(source = 'VP Sales', target = 'Sales Rep 1'),
        list(source = 'VP Sales', target = 'Sales Rep 2'),
        list(source = 'Backend', target = 'Frontend')
      )
    )),
    nodeLabels = list(list(text = 'id'))
  )

26 Tree

g2() |>
  mark_tree(
    data = list(type = 'inline', value = list(
      name = 'Company', children = list(
        list(name = 'Engineering', children = list(
          list(name = 'Frontend'),
          list(name = 'Backend'),
          list(name = 'DevOps')
        )),
        list(name = 'Marketing', children = list(
          list(name = 'SEO'),
          list(name = 'Content')
        )),
        list(name = 'Finance', children = list(
          list(name = 'Accounting'),
          list(name = 'Payroll')
        ))
      )
    )),
    nodeLabels = list(list(text = 'name'))
  )

27 Density

The density mark computes kernel density estimation (KDE) automatically from the x aesthetic. Map color for grouped density curves.

g2(iris, ~ Sepal.Width, color = ~ Species) |> mark_density()

28 Sunburst

tree_data = list(
  name = 'root', children = list(
    list(name = 'Frontend', children = list(
      list(name = 'React', value = 180),
      list(name = 'Vue', value = 120),
      list(name = 'Angular', value = 80)
    )),
    list(name = 'Backend', children = list(
      list(name = 'Node.js', value = 150),
      list(name = 'Python', value = 130),
      list(name = 'Go', value = 90)
    )),
    list(name = 'Data', children = list(
      list(name = 'R', value = 100),
      list(name = 'Julia', value = 40)
    ))
  )
)
g2() |>
  mark_sunburst(
    data = list(type = 'inline', value = list(tree_data)),
    encode = list(value = 'value')
  ) |>
  style_mark(inset = 0.5)