In this section we will describe, at overview-level, the key concepts that are used to create charts using chart-parts. These concepts will likely be familiar to people with a background in data visualization, especially if they have used mark-based visualization systems before.
Scales are a construct that allow us to translate values from a source domain into a target domain. Practically, what this means is that we can translate values from our data into values that are shown to users, namely screen coordinates, colors, etc...
In chart-parts, our scales are based on d3 scales, and assume basic familiarity with how they operate.
Here are some resources regarding d3 scales that may be helpful:
These are the basic scale types supported by the system. More details about using and creating them can be found in the Client APIs documentation section.
In chart-parts, each created scale has a name. This allows us to reference the scale when we are defining mark encodings.
// React API
<LinearScale name="y" domain="data.amount" range={Dimension.Height} nice zero />
// SceneBuilder API
linear('y')
.domain('data.amount')
.range(Dimension.Height)
.nice()
.zero(),
Marks are the fundamental building block, and visual unit of chart-parts, and any grammar-of-graphics.
Marks often have multiple parameters to describe how they appear. For example, to describe a rectangle, one needs at a minimum: (height, width, (x,y) position, and color).
In our system, when we bind these parameters to our data, this is called an encoding. When we encode a parameter, we create an instance of the mark for each row in our data table, and parameterize the mark against that data context.
// React API
<Rect
table="my-data"
x={({ d, x }) => x(d.category)}
height={({ d, y }) => y(d.amount)}
y={0}
width={30}
fill="blue"
/>
// SceneBuilder API
rect()
.table('my-data')
.encode({
x: ({ d, x }) => x(d.category),
height: ({ d, y }) => y(d.amount),
y: 0,
width: 30,
fill: 'blue',
})
In the above examples we specify an object where the keys are parameter names (e.g x, y, height, width, and fill) and the values are encoding functions.
Encoding functions have the following signature:
encode(ctx: MarkEncodingContext)
MarkEncodingContext Properties:
Axes are a common feature of charts where we can understand the encoding of a dimension in the view-space. Our system provides the ability to create Axes with a chart orientation (Top, Bottom, Left, Right) that are bound to a scale. For more information on creating scales, see the Client API section.
Axes can be constructed with a variety of options, including tick and label parameterization.
// React API
<Axis orient={AxisOrientation.Bottom} scale="x" />
// SceneBuilder API
axis('x', AxisOrientation.Bottom),
A common task in charting is to divide the view-space into separate sub-charts. There are a variety of reasons we may want to do this.
To support this, our view model supports the idea of recursively splitting view areas based on data-faceting or manual definition using the Group mark.
Group type marks are how we split views into sub-views. Each group instance provides a localized view-space to marks nested within it. Groups, like any other mark type, can be bound rows in a table or defined as singleton instances. When groups are data-bound they may optionally define a "facet" parameter to facet the source data table.
Any mark be specified as a singleton by not providing a table or faceting option, in which case it is unbound to a data-table and emits only a single item.
group() /* no tabledefined */
Groups may specify a facet value that describes how to facet incoming data. In this case, the data is partitioned into a set of sub-tables, each representing a view of the source table, and one group item is emitted per each data partition. When faceting is used, the data partition is given a name and provided to child marks so that they can render based on the faceted view of the data the group provides.
// React API
<Group table="my-data" facet={{ name: 'my-data-facet', groupBy: 'category' }}>
{/* ...children */}
</Group>
// SceneBuilder API
group()
.table('my-data')
.facet({
name: 'my-data-facet',
groupBy: 'category',
})