A gallery of Euler and Venn diagrams

This page is a gallery of Euler and Venn diagrams and a showcase of the customization options available through eunoiaplot. It is the Julia counterpart to the eulerr gallery and the eunoia-py gallery.

Rendering lives in a Makie extension that loads once a backend is imported. Here we use CairoMakie, a software backend that needs no display.

using Eunoia, CairoMakie
CairoMakie.activate!(type = "png") # raster output for the gallery

The styling keywords (colors, fills, edges, labels, quantities, legend, complement) mirror the Python API, but their values are Makie attributes. A matplotlib style dict like {"linestyle": "--"} becomes Dict(:linestyle => :dash), a fill's "facecolor" becomes :color, and so on. A uniform style is a Dict with Symbol keys; a per-set or per-region map is a Dict keyed by name whose values are style collections (a Dict or a (; key = value) named tuple).

Euler diagrams

Uniform intersections

Here we use squares instead of the default circles.

fit = euler(
    Dict(
        "A" => 10,
        "B" => 10,
        "C" => 10,
        "A&B" => 4,
        "A&C" => 4,
        "B&C" => 4,
        "A&B&C" => 2,
    );
    shape = "square",
)
eunoiaplot(fit)
Example block output

Disjoint sets

Rename the sets simply by choosing the dictionary keys. A flat edges style is applied to every outline.

fit = euler(Dict("Tom" => 1, "Greg" => 1, "Alberta" => 1))
eunoiaplot(fit; edges = Dict(:linestyle => :dash))
Example block output

To style each outline independently, pass edges as a Dict keyed by set name (each value a collection of lines! keywords) or as a vector of such collections, one per set in shape order — mirroring how colors accepts a per-set Dict.

fit = euler(Dict("Tom" => 1, "Greg" => 1, "Alberta" => 1))
eunoiaplot(
    fit;
    edges = Dict(
        "Tom" => (; linestyle = :dash, linewidth = 2),
        "Greg" => (; linestyle = :dot),
    ),
)
Example block output

A set contained in the intersection of two sets

fit = euler(
    Dict(
        "A" => 15,
        "B" => 15,
        "C" => 0,
        "A&B" => 3,
        "A&C" => 0,
        "B&C" => 0,
        "A&B&C" => 3,
    ),
)
eunoiaplot(
    fit;
    colors = ["black", "cyan", "orange"],
    edges = Dict(:color => :white, :linewidth => 2),
)
Example block output

Two sets intersecting inside a third

Here we override individual region fills with per-region fills styles.

fit = euler(
    Dict(
        "A" => 15,
        "B" => 0,
        "C" => 0,
        "A&B" => 3,
        "A&C" => 3,
        "B&C" => 0,
        "A&B&C" => 2,
    ),
)
eunoiaplot(
    fit;
    fills = Dict(
        "A&B&C" => (; color = :orange),
        "A&B" => (; color = :lightblue),
        "A&C" => (; color = :lightsalmon),
    ),
)
Example block output

Difficult set (for circles!)

This relationship cannot be drawn exactly with circles but works well with ellipses.

fit = euler(
    Dict("A" => 7, "B" => 6, "C" => 0, "A&B" => 0, "A&C" => 1, "B&C" => 1, "A&B&C" => 2);
    shape = "ellipse",
)
eunoiaplot(fit; quantities = true)
Example block output

Russian doll

Sets intersecting inside other sets.

fit = euler(
    Dict(
        "A" => 15,
        "B" => 0,
        "C" => 0,
        "A&B" => 10,
        "A&C" => 0,
        "B&C" => 0,
        "A&B&C" => 5,
    ),
)
eunoiaplot(fit)
Example block output

Wilkinson set relationship

This set relationship is taken from Wilkinson et al. It works best with ellipses. We show the fitted values rather than the requested ones.

fit = euler(
    Dict(
        "A" => 4,
        "B" => 6,
        "C" => 3,
        "D" => 2,
        "E" => 7,
        "F" => 3,
        "A&B" => 2,
        "A&F" => 2,
        "B&C" => 2,
        "B&D" => 1,
        "B&F" => 2,
        "C&D" => 1,
        "D&E" => 1,
        "E&F" => 1,
        "A&B&F" => 1,
        "B&C&D" => 1,
    );
    shape = "ellipse",
)
eunoiaplot(fit; quantities = "fitted", edges = Dict(:linestyle => :dot))
Example block output

Gene set

fit = euler(
    Dict(
        "SE" => 13,
        "Treat" => 28,
        "Anti-CCP" => 101,
        "DAS28" => 91,
        "SE&Treat" => 1,
        "SE&DAS28" => 14,
        "Treat&Anti-CCP" => 6,
        "SE&Anti-CCP&DAS28" => 1,
    ),
)
eunoiaplot(fit; quantities = true)
Example block output

Three sets intersecting inside a fourth

fit = euler(
    Dict(
        "A" => 30,
        "A&B" => 3,
        "A&C" => 3,
        "A&D" => 3,
        "A&B&C" => 0.4,
        "A&B&D" => 0.4,
        "A&C&D" => 0.4,
        "A&B&C&D" => 1,
    ),
)
eunoiaplot(fit)
Example block output

eulerAPE combination

A combination taken from the eulerAPE article, fit with ellipses.

fit = euler(
    Dict(
        "a" => 3491,
        "b" => 3409,
        "c" => 3503,
        "a&b" => 120,
        "a&c" => 114,
        "b&c" => 132,
        "a&b&c" => 126,
    );
    shape = "ellipse",
)
eunoiaplot(fit)
Example block output

Four uniform interactions

This time we fit the diagram using rectangles.

fit = euler(
    Dict(
        "Frodo" => 10,
        "Sam" => 10,
        "Merry" => 10,
        "Pippin" => 10,
        "Frodo&Sam" => 3,
        "Frodo&Merry" => 3,
        "Frodo&Pippin" => 0,
        "Sam&Merry" => 0,
        "Sam&Pippin" => 3,
        "Merry&Pippin" => 3,
        "Frodo&Sam&Merry" => 1,
        "Frodo&Sam&Pippin" => 1,
        "Frodo&Merry&Pippin" => 1,
        "Sam&Merry&Pippin" => 1,
        "Frodo&Sam&Merry&Pippin" => 1,
    );
    shape = "rectangle",
)
eunoiaplot(fit)
Example block output

Two circles intersecting completely

fit = euler(Dict("A" => 0, "B" => 0, "A&B" => 10))
eunoiaplot(fit)
Example block output

Styling and the complement

A universe (complement) box

Pass complement to euler to reserve area outside every set; it comes back as a container that eunoiaplot draws behind everything. The complement plot keyword styles that box.

fit = euler(Dict("A" => 10, "B" => 8, "A&B" => 4); complement = 20)
eunoiaplot(fit; complement = Dict(:color => "#eeeeee"), quantities = true)
Example block output

Colors as a sequence or a mapping

colors accepts either a vector (one per set, in fit order) or a mapping from set name to color.

fit = euler(
    Dict("A" => 10, "B" => 7, "C" => 8, "A&B" => 3, "A&C" => 4, "B&C" => 2, "A&B&C" => 1),
)
eunoiaplot(fit; colors = Dict("A" => "#e41a1c", "B" => "#377eb8", "C" => "#4daf4a"))
Example block output

A legend instead of inline labels

When sets are small or crowded their inline labels can overlap. Passing legend = true moves the set names into a color-keyed legend and turns the inline labels off by default (pass labels = true to keep both). A Dict or named tuple is forwarded to Makie's Legend, so you can control its title and appearance.

fit = euler(
    Dict("A" => 10, "B" => 7, "C" => 8, "A&B" => 3, "A&C" => 4, "B&C" => 2, "A&B&C" => 1),
)
eunoiaplot(fit; legend = Dict(:title => "Sets"))
Example block output

Custom label text and style

labels can be a Dict for per-set control. Map a set name to a replacement string, to a collection of text! keywords (with an optional :text key for custom text), or to nothing/false to hide that set's label. A style Dict whose keys are not set names — e.g. Dict(:fontsize => 14) — is a uniform style applied to every label instead.

fit = euler(Dict("alpha" => 10, "beta" => 7, "alpha&beta" => 3))
eunoiaplot(
    fit;
    labels = Dict(
        "alpha" => (; text = "α", fontsize = 22),
        "beta" => (; text = "β", fontsize = 22, color = :crimson),
    ),
)
Example block output

Original versus fitted quantities

Drawing into axes we create ourselves lets us place two diagrams side by side to compare the requested values against what the layout achieved. Each panel is an Axis with DataAspect(); eunoiaplot! draws into it without touching decorations.

fit = euler(
    Dict("A" => 7, "B" => 6, "A&C" => 1, "B&C" => 1, "A&B&C" => 2, "C" => 0);
    shape = "ellipse",
)

f = Figure(size = (900, 400))
left = Axis(f[1, 1]; aspect = DataAspect(), title = "original")
right = Axis(f[1, 2]; aspect = DataAspect(), title = "fitted")
hidedecorations!(left); hidespines!(left)
hidedecorations!(right); hidespines!(right)
eunoiaplot!(left, fit; quantities = "original")
eunoiaplot!(right, fit; quantities = "fitted")
f
Example block output

Counts and percentages

quantities also chooses how to display each region. A string picks the display type—"counts" (the raw value, the default) or "percent" (the region's share of the total)—while a Dict or named tuple combines a type, a value source (:source), and text styling. Passing both types stacks the count above its percentage.

fit = euler(
    Dict(
        "SE" => 13,
        "Treat" => 28,
        "Anti-CCP" => 101,
        "DAS28" => 91,
        "SE&Treat" => 1,
        "SE&DAS28" => 14,
        "Treat&Anti-CCP" => 6,
        "SE&Anti-CCP&DAS28" => 1,
    ),
)
eunoiaplot(fit; quantities = Dict(:type => ["counts", "percent"], :fontsize => 8))
Example block output

Venn diagrams

venn draws topological (non-proportional) Venn diagrams. It accepts an integer count, a vector of names, or a mapping.

Note

Circle Venn diagrams are unsupported in the pinned Eunoia core and raise an error. Use ellipses (1–5 sets) or squares/rectangles (1–3 sets); the examples below pass shape explicitly.

Two and three sets

f = Figure(size = (900, 400))
left = Axis(f[1, 1]; aspect = DataAspect())
right = Axis(f[1, 2]; aspect = DataAspect())
hidedecorations!(left); hidespines!(left)
hidedecorations!(right); hidespines!(right)
eunoiaplot!(left, venn(2; shape = "ellipse"))
eunoiaplot!(right, venn(["A", "B", "C"]; shape = "ellipse"))
f
Example block output

Four and five sets

f = Figure(size = (900, 400))
left = Axis(f[1, 1]; aspect = DataAspect())
right = Axis(f[1, 2]; aspect = DataAspect())
hidedecorations!(left); hidespines!(left)
hidedecorations!(right); hidespines!(right)
eunoiaplot!(left, venn(4; shape = "ellipse"))
eunoiaplot!(right, venn(5; shape = "ellipse"))
f
Example block output

Squares

Venn diagrams of up to three sets can be drawn with square shapes instead of the default ellipse. Here the two sit side by side for the same three sets.

f = Figure(size = (900, 400))
left = Axis(f[1, 1]; aspect = DataAspect(), title = "ellipse")
right = Axis(f[1, 2]; aspect = DataAspect(), title = "square")
hidedecorations!(left); hidespines!(left)
hidedecorations!(right); hidespines!(right)
eunoiaplot!(left, venn(3; shape = "ellipse"))
eunoiaplot!(right, venn(3; shape = "square"))
f
Example block output

"rectangle" is accepted too (up to three sets), but the symmetric Venn layout renders it identically to a square — rectangles only differ from squares in area-proportional Euler diagrams.

From membership lists

venn accepts the same membership-list input as euler; it only needs the set names.

eunoiaplot(
    venn(Dict("A" => ["x", "y", "z"], "B" => ["y", "z", "w"], "C" => ["z", "w", "q"]);
         shape = "ellipse"),
)
Example block output