Concepts

edit

Marks

A mark draws rows of data with shapes. Each mark is a layer of the Plot, and the marks are drawn in the order they are added:

MarkDraws
BarMark<T>a bar per row, in the band of a category
RectMark<T>a rectangle per row: bands, intervals or bins
LineMark<T>a line per series
AreaMark<T>the area between a line and a baseline, per series
DotMark<T>a dot per row: a circle or another symbol
RuleXMark<T>, RuleYMark<T>vertical and horizontal lines
TickMark<T>a short line per row, across a band
TextMark<T>a text per row
AnnotationMark<T>a text per row, with an arrow pointing at a point
ArcMark<T>a slice per row: pies, donuts, polar areas
BoxMark<T>a box plot per category
CandlestickMark<T>a candlestick per row
FunnelMark<T>a funnel, a bar per stage
WaterfallMark<T>running totals as floating bars
ViolinMark<T>a violin per category: the mirrored density of its values
RidgelineMark<T>a ridgeline plot: the density of the values of each category, as overlapping areas
HexbinMark<T>hexagons colored by the number of points in them
ContourMark<T>the contours of the density of points
ParallelCoordinatesMark<T>parallel coordinates: an axis per dimension, each with its scale, and a line per row
PackMark<T>, TreemapMark<T>, PartitionMark<T>a tree, as packed circles, a treemap, or a sunburst and an icicle
TreeMark<T>a tree as a node-link diagram: a tidy tree or a dendrogram
SankeyMark<T>the flows between nodes, a row per link
ChordMark<T>the flows between nodes around a circle, a row per flow
NetworkMark<T>a network graph, a row per link, with a force-directed layout
GeoMark<T>a geometry per row on a map: regions, lines and points, such as the features of a GeoJSON document

RuleXMark and RuleYMark take values directly: new RuleYMark(0) draws a line at zero.

Channels

A channel is a function of a row: the x and y positions, the fill and stroke colors, the size, the label and the tooltip. Its values are numbers (any numeric type), times (DateTime, DateTimeOffset, DateOnly, in UTC) or categories (strings, and anything else, formatted with the invariant culture). null and double.NaN are missing values: the row is not drawn, and a line breaks around it.

A constant color is a property: FillColor and StrokeColor take a ChartColor, which converts from #rrggbb or a CSS color name such as "steelblue". A channel such as Fill goes through the color scale instead.

Scales

The scales map the values of the channels: the x and y scales to positions, the color scale to colors, the size scale to radii. Unless the plot sets it, a scale is inferred from the values: a band scale for the categories of bars, a point scale for other categories, a time scale for times, and a linear scale for numbers. The domain of a linear scale is extended to round values, and includes zero when a mark has a baseline, such as bars and areas.

ScaleUse
LinearScalenumbers
LogScale, SymlogScalevalues that span several orders of magnitude; symlog accepts zero and negative values
PowScale, SqrtScalenumbers, raised to a power
TimeScaletimes, with ticks on calendar boundaries
BandScale, PointScalecategories, as bands or as points
OrdinalColorScalea color per category
SequentialColorScale, DivergingColorScalenumbers, on a ramp of colors interpolated in OKLab
SizeScalenumbers, as radii whose areas are proportional to the values

A value that a scale cannot map is missing too, and gets no tick: zero and negative values on a log scale, and values that the transform of the scale makes infinite, such as zero on a power scale with a negative exponent.

Set a scale to change its domain, its ticks or its format. For instance, a percentage axis that starts at zero:

var plot = new Plot
{
    Title = "Share of each year",
    YScale = new LinearScale { TickFormat = NumberFormats.Percent() },
};
plot.Add(new BarMark<Sale>(SampleData.Sales, x: sale => sale.Region, y: sale => sale.Amount)
{
    Fill = sale => sale.Year,
    Transform = new StackTransform { Offset = StackOffset.Expand },
});

NumberFormats has common formats: SI prefixes (1.5k), a fixed number of decimals, and percentages.

Coordinates

The coordinates turn the positions of the x and y scales into points:

  • CartesianCoordinates: x is horizontal and y is vertical. With Transposed, x is vertical: bars become horizontal.

  • PolarCoordinates: x is the angle and y is the radius. Set the start and end angles for a gauge, and an inner radius for a donut. Transposed, y is the angle: bars become radial bars.

  • GeoCoordinates: x is the longitude and y is the latitude, in degrees, and a GeoProjection places them on a map, scaled to fit the plot area. The map has no axes; it can have a graticule and the outline of the globe.

Plots that have an ArcMark<T> use polar coordinates by default, and plots that have a GeoMark<T> use geographic coordinates: see the maps.

Transforms

A transform derives the rows a mark draws from its data:

The series of a stack are given by the Series channel of the mark, else by its fill or stroke channel.

Facets and the secondary axis

The FacetX and FacetY channels of the marks split a chart into a grid of small charts that share the scales: see Small multiples. Plot.Facets sets the number of columns, the gap between the charts and the order of the facets.

A mark that sets UseSecondaryYScale maps its y channels with the secondary y scale, whose axis is on the right in Cartesian coordinates: see the secondary axis example.

Axes, legends and margins

The x and y scales get an axis, and the color and size scales get a legend, unless the chart does not use them as measures (pies, hierarchies). Plot.XAxis and Plot.YAxis set the label, the grid, the rotation of the tick labels and the visibility of an axis; Plot.Legend sets the title and the position of the legend, and Plot.SizeLegend the title and the values of the legend of the sizes, which is drawn next to it. The margins are computed from the measured texts: when the tick labels of the horizontal axis overlap, they are rotated by -45 degrees, and only some of them are drawn when they still overlap. Plot.Margin sets the margins instead.

The scene

CreateScene lays the plot out into a ChartScene: rectangles, circles, lines, paths, texts and groups, in pixels. The SVG renderer and the image renderer both draw this scene, so they share the layout. You can also read it to draw the chart with your own renderer:

var plot = new Plot { Title = "Sales" };
plot.Add(new BarMark<Sale>(SampleData.Sales.Where(sale => sale.Year == 2024), x: sale => sale.Region, y: sale => sale.Amount));

// The layout, as drawing primitives: rectangles, paths, lines, texts and groups, in pixels
var scene = plot.CreateScene();
var bars = scene.Root.Children.OfType<SceneGroup>().Single(group => group.CssClass == "marks");
Assert.HasCount(4, bars.Children.OfType<SceneGroup>().Single().Children.OfType<SceneRectangle>());