Skip to main content
A volcano plot visualizes differential expression results by plotting log₂ fold change (x-axis) against −log₁₀(p-value) (y-axis). Points that pass both the fold-change cutoff and p-value threshold are colored as up-regulated (right) or down-regulated (left); all others are shown as not-significant (gray).

When to Use

  • Differential gene expression: Standard plot for RNA-seq/microarray DEG analysis
  • Proteomics: Display differential protein abundance
  • Metabolomics: Show metabolite changes between conditions
  • CRISPR screens: Visualize gene-level enrichment/depletion
  • Any differential analysis: When fold change and significance both matter
  • Quality control: Identify outliers and assess overall signal strength

Basic Example

use kuva::plot::VolcanoPlot;
use kuva::backend::svg::SvgBackend;
use kuva::render::render::render_multiple;
use kuva::render::layout::Layout;
use kuva::render::plots::Plot;

let vp = VolcanoPlot::new()
    .with_points(vec![
        ("EGFR",   3.2_f64, 1e-4_f64),
        ("AKT1",   3.5,     5e-5   ),
        ("VHL",   -3.0,     5e-4   ),
        ("GAPDH",  0.3,     0.5    ),
    ])
    .with_label_top(3)
    .with_legend("DEG status");

let plots = vec![Plot::Volcano(vp)];
let layout = Layout::auto_from_plots(&plots)
    .with_title("Tumour vs. Normal")
    .with_x_label("log₂ fold change")
    .with_y_label("−log₁₀(p-value)");

let svg = SvgBackend.render_scene(&render_multiple(plots, layout));
std::fs::write("volcano.svg", svg).unwrap();

Key Methods

Data Input

with_point(name, log2fc, pvalue)

Add a single point. Useful for incremental building.
let vp = VolcanoPlot::new()
    .with_point("EGFR", 3.2_f64, 1e-4_f64)
    .with_point("GAPDH", 0.3, 0.5);

with_points(iter)

Add multiple points from an iterator of (name, log2fc, pvalue) tuples. Primary input method.
let results = vec![
    ("EGFR",  3.2_f64, 1e-4_f64),
    ("AKT1",  3.5,     5e-5    ),
    ("VHL",  -3.0,     5e-4    ),
    ("GAPDH", 0.3,     0.5     ),
];
let vp = VolcanoPlot::new().with_points(results);
Accepts any types implementing Into<String> / Into<f64>.

Thresholds

with_fc_cutoff(cutoff: f64)

Set absolute log₂FC threshold for up/down classification (default 1.0 — 2× fold change).
let vp = VolcanoPlot::new()
    .with_fc_cutoff(1.5);  // 2^1.5 ≈ 2.8× fold change
Dashed vertical lines drawn at ±fc_cutoff.

with_p_cutoff(cutoff: f64)

Set p-value significance threshold (default 0.05).
let vp = VolcanoPlot::new()
    .with_p_cutoff(0.01);  // stricter threshold
Dashed horizontal line drawn at −log10(p_cutoff).

Colors

with_color_up(color: impl Into<String>)

Set color for up-regulated points: log2fc ≥ fc_cutoff and pvalue ≤ p_cutoff (default "firebrick").
let vp = VolcanoPlot::new()
    .with_color_up("#d62728");

with_color_down(color: impl Into<String>)

Set color for down-regulated points: log2fc ≤ −fc_cutoff and pvalue ≤ p_cutoff (default "steelblue").
let vp = VolcanoPlot::new()
    .with_color_down("#1f77b4");

with_color_ns(color: impl Into<String>)

Set color for not-significant points (default "#aaaaaa").
let vp = VolcanoPlot::new()
    .with_color_ns("#cccccc");

Gene Labels

with_label_top(n: usize)

Label the n most significant points (lowest p-values). Default 0 — no labels.
let vp = VolcanoPlot::new()
    .with_points(results)
    .with_label_top(10);  // label top 10 genes

with_label_style(style: LabelStyle)

Set label placement style (default LabelStyle::Nudge).
  • LabelStyle::Nudge (default) — Labels sorted by x and nudged vertically to reduce overlap
  • LabelStyle::Exact — Labels at exact point position; may overlap
  • LabelStyle::Arrow { offset_x, offset_y } — Labels offset by px with gray leader line
use kuva::plot::LabelStyle;

let vp = VolcanoPlot::new()
    .with_label_top(10)
    .with_label_style(LabelStyle::Arrow { offset_x: 14.0, offset_y: 16.0 });

Other Options

with_point_size(size: f64)

Set circle radius in pixels (default 3.0).
let vp = VolcanoPlot::new()
    .with_point_size(4.0);

with_pvalue_floor(floor: f64)

Set explicit p-value floor for −log10 transform. Points with pvalue == 0.0 are clamped to this value.
let vp = VolcanoPlot::new()
    .with_pvalue_floor(1e-10);  // y-axis ceiling = 10
When not set, floor is inferred as minimum non-zero p-value in data.

with_legend(label: impl Into<String>)

Enable legend showing Up, Down, and NS entries.
let vp = VolcanoPlot::new()
    .with_legend("DEG status");

Examples

Standard Differential Expression

let vp = VolcanoPlot::new()
    .with_points(vec![
        ("EGFR",   3.2_f64, 1e-4_f64),
        ("AKT1",   3.5,     5e-5   ),
        ("TP53",   2.8,     2e-4   ),
        ("VHL",   -3.0,     5e-4   ),
        ("PTEN",  -2.5,     8e-4   ),
        ("GAPDH",  0.3,     0.5    ),
        ("ACTB",  -0.2,     0.7    ),
    ])
    .with_fc_cutoff(1.0)
    .with_p_cutoff(0.05)
    .with_label_top(5)
    .with_legend("DEG status");

let plots = vec![Plot::Volcano(vp)];
let layout = Layout::auto_from_plots(&plots)
    .with_title("Tumour vs. Normal — RNA-seq")
    .with_x_label("log₂ fold change")
    .with_y_label("−log₁₀(p-value)");
Standard thresholds: |log2FC| ≥ 1.0, p ≤ 0.05. Top 5 genes labeled.

Custom Thresholds

let vp = VolcanoPlot::new()
    .with_points(results)
    .with_fc_cutoff(1.5)     // stricter: 2.8× fold change
    .with_p_cutoff(0.01)     // p < 0.01
    .with_label_top(10);

let plots = vec![Plot::Volcano(vp)];
let layout = Layout::auto_from_plots(&plots)
    .with_title("Strict Thresholds: |log₂FC| ≥ 1.5, p < 0.01")
    .with_x_label("log₂ fold change")
    .with_y_label("−log₁₀(p-value)");
Higher thresholds for more stringent selection.

Custom Colors

let vp = VolcanoPlot::new()
    .with_points(results)
    .with_color_up("#d62728")      // red for up
    .with_color_down("#1f77b4")    // blue for down
    .with_color_ns("#cccccc")      // light gray for NS
    .with_label_top(8);

let plots = vec![Plot::Volcano(vp)];
let layout = Layout::auto_from_plots(&plots)
    .with_title("Custom Color Scheme")
    .with_x_label("log₂ fold change")
    .with_y_label("−log₁₀(p-value)");
Match your publication color scheme.

Arrow-style Labels

use kuva::plot::LabelStyle;

let vp = VolcanoPlot::new()
    .with_points(results)
    .with_label_top(10)
    .with_label_style(LabelStyle::Arrow { offset_x: 12.0, offset_y: 14.0 });

let plots = vec![Plot::Volcano(vp)];
let layout = Layout::auto_from_plots(&plots)
    .with_title("Arrow-style Gene Labels")
    .with_x_label("log₂ fold change")
    .with_y_label("−log₁₀(p-value)");
Labels offset with leader lines for dense plots.

Zero p-values

p-values of exactly 0.0 cannot be log-transformed. They are automatically capped at the smallest non-zero p-value in the data (or at pvalue_floor if set). This prevents infinite y positions.

Threshold Lines

Dashed threshold lines are drawn automatically:
  • Vertical lines at ±fc_cutoff
  • Horizontal line at −log10(p_cutoff)

See Also

  • ManhattanPlot — For GWAS p-values across the genome
  • Scatter — For general scatter plots without differential expression logic

Build docs developers (and LLMs) love