2  draw high-level API

library(rtemis.draw)
.:rtemis.draw 0.1.0 🖌 aarch64-apple-darwin20

Attaching package: 'rtemis.draw'
The following object is masked from 'package:graphics':

    Axis

Important Note that this vignette has been built using the light theme. The page theme toggle will work partly to switch the chart theme, but the result is not the same as using the dark theme directly. When working with an IDE like VS Code, if you don’t define a theme, the function will auto-detect your system setting and apply the appropriate theme.

2.1 Data

We’ll use the built-in penguins dataset. Let’s take a look at the variables:

str(penguins)
'data.frame':   344 obs. of  8 variables:
 $ species    : Factor w/ 3 levels "Adelie","Chinstrap",..: 1 1 1 1 1 1 1 1 1 1 ...
 $ island     : Factor w/ 3 levels "Biscoe","Dream",..: 3 3 3 3 3 3 3 3 3 3 ...
 $ bill_len   : num  39.1 39.5 40.3 NA 36.7 39.3 38.9 39.2 34.1 42 ...
 $ bill_dep   : num  18.7 17.4 18 NA 19.3 20.6 17.8 19.6 18.1 20.2 ...
 $ flipper_len: int  181 186 195 NA 193 190 181 195 193 190 ...
 $ body_mass  : int  3750 3800 3250 NA 3450 3650 3625 4675 3475 4250 ...
 $ sex        : Factor w/ 2 levels "female","male": 2 1 1 NA 1 2 1 2 NA NA ...
 $ year       : int  2007 2007 2007 2007 2007 2007 2007 2007 2007 2007 ...

2.2 Boxplots

2.2.1 Single or Multiple Numeric Vectors

Input: Single numeric vector

draw_boxplot(
  penguins$body_mass
)
2026-04-28 05:16:09 Removed 2 NA values [FUN]

Note:

  • The function automatically detects the presence of NAs, prints a message, and excludes them from the plot
  • The x-axis label is automatically set to the name of the variable after cleaning

You can define a custom label either by passing a named list/data.frame or by using the labels argument:

draw_boxplot(
  list(`Body Mass` = penguins$body_mass)
)
2026-04-28 05:16:09 Removed 2 NA values [FUN]
draw_boxplot(
  penguins$body_mass,
  labels = "Body Mass"
)
2026-04-28 05:16:09 Removed 2 NA values [FUN]

Input can be a list of any number of numeric vectors:

draw_boxplot(
  list(
    `Bill Length` = penguins$bill_len,
    `Flipper Length` = penguins$flipper_len
  )
)
2026-04-28 05:16:09 Removed 2 NA values [FUN]
2026-04-28 05:16:09 Removed 2 NA values [FUN]

or a data.frame:

draw_boxplot(
  penguins[, c("bill_len", "flipper_len")],
  labels = c("Bill Length", "Flipper Length")
)
2026-04-28 05:16:09 Removed 2 NA values [FUN]
2026-04-28 05:16:09 Removed 2 NA values [FUN]
draw_boxplot(
  penguins[, c("bill_len", "flipper_len")],
  labels = c("Bill Length", "Flipper Length"),
  color = rtemis_colors[2]
)
2026-04-28 05:16:10 Removed 2 NA values [FUN]
2026-04-28 05:16:10 Removed 2 NA values [FUN]

2.2.2 Grouped Single or Multiple Numeric Vectors

draw_boxplot(
  penguins$body_mass,
  group = penguins$species
)
2026-04-28 05:16:10 Removed 2 NA values from data [draw_boxplot]
draw_boxplot(
  list(
    `Bill Length` = penguins$bill_len,
    `Flipper Length` = penguins$flipper_len
  ),
  group = penguins$species
)

2.3 Histograms

2.3.1 Single Numeric Vector

draw_histogram(penguins$body_mass)

Use the breaks argument to control binning — it accepts any value accepted by graphics::hist(): a number of bins, a character algorithm name, or an explicit vector of break points:

draw_histogram(penguins$body_mass, breaks = 20)

2.3.2 Grouped

draw_histogram(
  penguins$body_mass,
  group = penguins$species
)

2.4 Density Plots

2.4.1 Single or Multiple Numeric Vectors

draw_density(
  x = penguins$body_mass
)
2026-04-28 05:16:10 Removed 2 NA values from x [draw_density]
draw_density(
  list(
    `Flipper Length` = penguins$flipper_len,
    `Bill Length` = penguins$bill_len
  )
)
2026-04-28 05:16:10 Removed 2 NA values from Flipper Length [FUN]
2026-04-28 05:16:10 Removed 2 NA values from Bill Length [FUN]

2.4.2 Grouped Single or Multiple Numeric Vectors

draw_density(
  x = penguins$body_mass,
  group = penguins$species
)
2026-04-28 05:16:10 Removed 2 NA values from x [draw_density]
draw_density(
  list(`Body Mass` = penguins$body_mass),
  group = penguins$species
)
2026-04-28 05:16:10 Removed 2 NA values from Body Mass [FUN]
draw_density(
  list(
    `Bill Length` = penguins$bill_len,
    `Bill Depth` = penguins$bill_dep
  ),
  group = penguins$sex
)

2.5 Barplots

draw_bar(
  names(table(penguins$species)),
  table(penguins$species)
)
dat <- table(interaction(penguins$sex, penguins$species))
draw_bar(
  names(dat),
  dat,
  horizontal = TRUE,
  color = rtemis_colors[c(2, 1)]
)

2.6 Scatterplots

2.6.1 Simple

draw_scatter(penguins$bill_len, penguins$flipper_len)

2.6.2 With Fitted Line

Pass fit = "gam" (or "glm") to overlay a fitted line with a 95% confidence band:

draw_scatter(
  penguins$bill_len,
  penguins$flipper_len,
  fit = "gam"
)

2.6.3 Grouped

draw_scatter(
  penguins$bill_len,
  penguins$flipper_len,
  group = penguins$species
)

2.6.4 Grouped with Fitted Lines

Fit lines and confidence bands are drawn per group and share the group’s color. Clicking a legend entry toggles the scatter points, fit line, and band together:

draw_scatter(
  penguins$bill_len,
  penguins$flipper_len,
  group = penguins$species,
  fit = "gam"
)

2.7 Line Plots

2.7.1 Single Series

year_counts <- as.integer(table(penguins$year))
draw_line(
  sort(unique(penguins$year)),
  year_counts,
  title = "Penguins Observed per Year"
)

2.7.2 Multiple Series

Pass a named list to y to draw one line per element:

year_species <- table(penguins$species, penguins$year)
years <- as.integer(colnames(year_species))
draw_line(
  years,
  setNames(
    lapply(rownames(year_species), function(sp) as.integer(year_species[sp, ])),
    rownames(year_species)
  )
)

2.7.3 Smoothed Lines

draw_line(
  sort(unique(penguins$year)),
  year_counts,
  smooth = TRUE
)

2.7.4 Area Chart

draw_line(
  years,
  setNames(
    lapply(rownames(year_species), function(sp) as.integer(year_species[sp, ])),
    rownames(year_species)
  ),
  area = TRUE
)

2.8 Pie Charts

2.8.1 Simple

species_counts <- table(penguins$species)
draw_pie(as.integer(species_counts), names(species_counts))

2.8.2 Nightingale / Rose Chart

Set rose_type = "radius" to encode value as radius instead of arc angle:

draw_pie(
  as.integer(species_counts),
  names(species_counts),
  rose_type = "radius"
)

2.9 Heatmaps

2.9.1 Correlation Matrix

draw_heatmap() accepts any numeric matrix. For square matrices, square_cells is enabled automatically so every cell is perfectly square.

Set zlim = c(-1, 1) to fix the color scale to the full correlation range:

num_vars <- c("bill_len", "bill_dep", "flipper_len", "body_mass")
m <- cor(na.omit(penguins[, num_vars]))
draw_heatmap(m, zlim = c(-1, 1), title = "Penguin Correlations")

2.9.2 Lower Triangle

For symmetric matrices it is common to show only one triangle. Use triangle = "lower" to keep the lower triangle and diagonal, masking the upper triangle:

draw_heatmap(m, triangle = "lower", zlim = c(-1, 1))

2.9.3 With Cell Values

Set show_values = TRUE to print each correlation coefficient inside its cell. value_digits controls the number of decimal places:

draw_heatmap(
  m,
  triangle = "lower",
  zlim = c(-1, 1),
  show_values = TRUE,
  value_digits = 2
)

2.9.4 Hierarchical Clustering

cluster_rows and cluster_cols reorder the matrix using hclust(), grouping similar rows and columns together:

draw_heatmap(
  m,
  cluster_rows = TRUE,
  cluster_cols = TRUE,
  zlim = c(-1, 1)
)

2.9.5 General (Non-square) Heatmap

For rectangular matrices, set square_cells = FALSE. Here we compute the mean of each trait per species and z-score the columns so traits on different scales are directly comparable:

traits <- c("bill_len", "bill_dep", "flipper_len", "body_mass")
means <- sapply(
  traits,
  function(tr) tapply(penguins[[tr]], penguins$species, mean, na.rm = TRUE)
)
colnames(means) <- c("Bill Length", "Bill Depth", "Flipper Length", "Body Mass")

draw_heatmap(
  scale(means),
  square_cells = FALSE,
  show_values = TRUE,
  value_digits = 2,
  title = "Mean Traits by Species (z-scored)"
)

2.10 Spectrograms

draw_spectrogram() renders an interactive time–frequency heatmap. Pass a raw numeric signal vector together with sample_rate; the function computes the STFT internally via signal::specgram(). Alternatively pass a pre-computed spectrogram matrix directly.

All examples below use synthetic signals so no external data is required.

2.10.1 Chirp (Frequency Sweep)

A chirp sweeps linearly from a low to a high frequency. The spectrogram makes the sweep immediately visible as a diagonal ridge:

Fs  <- 8000L                          # 8 kHz sample rate
t   <- seq(0, 2, by = 1 / Fs)
sig <- signal::chirp(t, f0 = 100, t1 = 2, f1 = 3800)

draw_spectrogram(sig, sample_rate = Fs, title = "Linear Chirp (100 – 3800 Hz)")

2.10.2 Multiple Pure Tones

Three simultaneous sine waves appear as three horizontal bands — one per frequency:

t   <- seq(0, 2, by = 1 / Fs)
sig <- sin(2 * pi * 440  * t) +
       sin(2 * pi * 1100 * t) +
       sin(2 * pi * 2400 * t)

draw_spectrogram(sig, sample_rate = Fs, title = "440 Hz + 1100 Hz + 2400 Hz")

2.10.3 Log Frequency Scale

Use freq_scale = "log" to expand the low-frequency region — useful when the signal of interest spans several octaves. freq_unit = "kHz" and time_unit = "ms" rescale the axis labels:

t   <- seq(0, 2, by = 1 / Fs)
sig <- signal::chirp(t, f0 = 100, t1 = 2, f1 = 3800)

draw_spectrogram(
  sig,
  sample_rate = Fs,
  freq_scale  = "log",
  freq_unit   = "kHz",
  time_unit   = "ms",
  title       = "Chirp — log frequency scale"
)

2.10.4 Diverging Palette (EEG / ERSP)

For signed data — such as an Event-Related Spectral Perturbation (ERSP) matrix from an EEG experiment — use palette = "diverging". The midpoint colour maps exactly to zero. Set db = FALSE because the values are already on a meaningful signed scale:

set.seed(1)
n_freq <- 60
n_time <- 120
freq   <- seq(4, 80, length.out = n_freq)   # 4 – 80 Hz
time   <- seq(-0.5, 1.5, length.out = n_time) # −500 ms to +1500 ms

# Background: small random fluctuations
ersp <- matrix(rnorm(n_freq * n_time, sd = 0.4), nrow = n_freq)

# Alpha suppression (8–13 Hz, 200–800 ms post-stimulus)
alpha_f <- freq >= 8  & freq <= 13
alpha_t <- time >= 0.2 & time <= 0.8
ersp[alpha_f, alpha_t] <- ersp[alpha_f, alpha_t] - 2.5

# Gamma increase (40–60 Hz, 100–400 ms post-stimulus)
gamma_f <- freq >= 40 & freq <= 60
gamma_t <- time >= 0.1 & time <= 0.4
ersp[gamma_f, gamma_t] <- ersp[gamma_f, gamma_t] + 2

draw_spectrogram(
  ersp,
  frequency  = freq,
  time       = time,
  db         = FALSE,
  power      = FALSE,
  palette    = "diverging",
  title      = "Simulated ERSP"
)

2.10.5 Real-world Data

The seewave package includes several bird song recordings as Wave objects ready to pass to draw_spectrogram():

library(seewave)
data(tico)   # Tiaris olivaceus song, 22050 Hz

draw_spectrogram(
  tico@left,
  sample_rate = tico@samp.rate,
  n_fft       = 512L,
  freq_range  = c(0, 8000),
  title       = "Tiaris olivaceus (tico)"
)

For EEG and physiological signals, PhysioNet (physionet.org) hosts thousands of freely downloadable recordings. The EDF/EDF+ format can be read into R with the edfReader package.

© 2026 E.D. Gennatas