Changing Plot Type

Muze has support for a number of atomic plot types. Plot types in Muze are called mark. Though Muze automatically figures out the mark based on the input fields, you can override that by mentioning which mark to use.

Muze currently supports the following marks:

  • Bar
  • Line
  • Area
  • Point
  • Tick
  • Text

Muze takes inspiration from Layered Grammar of Graphics which has direct impact on the API, terminology and philosophy of Muze.

In this document we will see how to achieve the most common chart type we have seen in visualization. This tutorial is a precursor to layer composition where you can create composite charts on your own using the same mental model without having to depend on vendor.

Data

We will be using cars.json data for illustration purpose.

Lets create a simple bar chart

As you have seen before, following is a simple vertical bar chart.

main
run-button
run-button
reset-button
// DataModel instance is created from https://www.charts.com/static/cars.json data,
// https://www.charts.com/static/cars-schema.json schema and assigned to variable dm.

const env = muze();
const canvas = env.canvas();

canvas
    .data(dm)
    .width(600)
    .height(400)
    .rows(['Miles_per_Gallon']) /* Gets plotted on y-axis */
    .columns(['Origin'])  /* Gets plotted on x-axis */
    .mount('#chart-container');

As you can see, we have not mentioned what chart to render in the code. Muze understands what kind of field is getting assigned to which axis and based on that it decides which mark is appropriate for plotting. Here a dimension (Origin) is getting assigned to x-axis and a measure (Miles_per_Gallon) is getting assigned to y-axis. Hence it creates a chart with bar mark. This table shows the mapping between variables assignment to encoding channels and mark type.

Making a horizontal bar chart

We can convert a vertical bar chart to a horizontal bar chart by just substituting the rows and columns.

main
run-button
run-button
reset-button
// DataModel instance is created from https://www.charts.com/static/cars.json data,
// https://www.charts.com/static/cars-schema.json schema and assigned to variable dm.

const env = muze();
const canvas = env.canvas();

canvas
    .data(dm)
    .width(600)
    .height(400)
    .columns(['Miles_per_Gallon']) /* Gets plotted on y-axis */
    .rows(['Origin'])  /* Gets plotted on x-axis */
    .mount('#chart-container');

Now with this example, y-axis gets a dimensional field and x-axis gets a measure field. Since a dimension is mapped to y-axis, a band scale is created for y-axis and a continuous axis gets created for x-axis which houses a measure. Hence the orientation of bar chart gets changed from vertical to horizontal.

Making a stacked bar chart

Adding a color encoding to the first chart will transform it into a stacked bar chart. The bars are stacked based on the value of the field assigned to color encoding channel. But we wont be doing that directly, as the previous data is not appropriate for stack column. We have calculated a new variable which displays the count of cars per region (Origin) by Cylinders using DataModel's operator. We will be using this data to render the stacked bar chart.

main
run-button
run-button
reset-button
// DataModel instance is created from https://www.charts.com/static/cars.json data,
// https://www.charts.com/static/cars-schema.json schema and assigned to variable dm.

const carCountDM = dm.calculateVariable({
    name: 'carCount',
    type: 'measure'
}, ['Name', () => 1]) /* Here we create a new variable with an initial value 1. When we assign fields
	to encoding channels, Muze internally perfroms groupBy. This new variable gets grouped with
    the default aggregation function 'sum' which gives us the count of cars */

const env = muze();
const canvas = env.canvas();

canvas
    .data(carCountDM)
    .width(600)
    .height(400)
    .color('Cylinders')
    .rows(['carCount']) /* Gets plotted on y-axis */
    .columns(['Origin'])  /* Gets plotted on x-axis */
    .mount('#chart-container');
logo

Not all data are suitable for stacking

Not all kind of data are suitable for stacking. In the above we had to create a new field so that stacking makes sense. If your data already have a field for which stacking makes sense, there is no need of transformation.

You can remove the code responsible for creating the new variable and render a stacked chart.

canvas.data(dm);
canvas.color('Cylinders');
canvas.rows(['Acceleration']);
canvas.columns(['Origin']);

If you read the chart you will realize that it does not makes sense.

Making a grouped bar chart

When we created a stacked chart, we just added a field to color encoding channel. Internally Muze took some decisions and rendered a stacked bar chart. stack is a transformation Muze choose by default to render a chart when a field is assigned to color, shape or size.

We have to be explicit when we need a group (or dodge) transformed chart

main
run-button
run-button
reset-button
// DataModel instance is created from https://www.charts.com/static/cars.json data,
// https://www.charts.com/static/cars-schema.json schema and assigned to variable dm.

const env = muze();
const canvas = env.canvas();

canvas
    .data(dm)
    .width(600)
    .height(400)
    .columns(['Miles_per_Gallon']) /* Gets plotted on y-axis */
    .rows(['Origin'])  /* Gets plotted on x-axis */
  	.color('Cylinders')
	.layers([{
    	mark: 'bar',
      	transform: { type: 'group' } // By default stack transform is selected
    }])
    .mount('#chart-container');

In the stacked bar example we had not called the layer method which allowed Muze to push the default layer configuration

.layers([{
    mark: 'bar',
    transform: { type: 'stack' } // By default stack transform is selected
}])

Layer is a powerful concept which we are going to explore later in this page.

Making a Scatter Chart

Let's move on from bar charts and make a scatter chart. In order to create a scatter chart, we supply measures in both rows and columns.

main
run-button
run-button
reset-button
// DataModel instance is created from https://www.charts.com/static/cars.json data,
// https://www.charts.com/static/cars-schema.json schema and assigned to variable dm.

const env = muze();
const canvas = env.canvas();

canvas
    .data(dm)
    .width(600)
    .height(400)
    .columns(['Miles_per_Gallon']) /* Gets plotted on y-axis */
    .rows(['Horsepower'])  /* Gets plotted on x-axis */
  	.detail(['Name'])
    .mount('#chart-container');
logo

Controlling granularity of data

If you do not provide the fields in the detail method, it will plot a single point with aggregated information. With detail method you are essentially controlling the granularity of data to be displayed in the scatter plot.

Creating a Scatter Plot with Shapes

We can change the shape of the points by assigning a field to shape encoding channel.

main
run-button
run-button
reset-button
// DataModel instance is created from https://www.charts.com/static/cars.json data,
// https://www.charts.com/static/cars-schema.json schema and assigned to variable dm.

const env = muze();
const canvas = env.canvas();

canvas
    .data(dm)
    .width(600)
    .height(400)
    .columns(['Miles_per_Gallon']) /* Gets plotted on y-axis */
    .rows(['Horsepower'])  /* Gets plotted on x-axis */
  	.shape('Origin')
  	.detail(['Name'])
    .mount('#chart-container');

color, shape and size are called retinal encoding. Learn more about retinal encoding here.

Creating A Bubble Chart

We can also choose to create a bubble chart instead of a scatter plot with shapes by adding a field to the size encoding channel.

main
run-button
run-button
reset-button
// DataModel instance is created from https://www.charts.com/static/cars.json data,
// https://www.charts.com/static/cars-schema.json schema and assigned to variable dm.

const env = muze();
const canvas = env.canvas();

canvas
    .data(dm)
    .width(600)
    .height(400)
    .columns(['Cylinders'])
  	.size('Weight_in_lbs')
  	.color('Weight_in_lbs')
    .rows(['Horsepower'])
  	.detail(['Maker'])
  	.layers([{ mark: 'point' }])
    .mount('#chart-container');

Creating a line chart

In order to create a line chart, map a temporal field to any of the axes. Temporal field is a special type of dimension which represents datetime. A Line chart shows trend over time, hence a temporal variable is needed to show a line chart

main
run-button
run-button
reset-button
// DataModel instance is created from https://www.charts.com/static/cars.json data,
// https://www.charts.com/static/cars-schema.json schema and assigned to variable dm.

const env = muze();
const canvas = env.canvas();

canvas
    .data(dm)
    .width(600)
    .height(400)
    .rows(['Acceleration'])
    .columns(['Year'])
    .mount('#chart-container');
logo

Try it

Line and area charts are very similar. While line shows trends over time, area shows change of volume. Both are measured across time.

Try adding the following line at the end to change line chart to area chart

canvas.layers([{ mark: 'area' }])

You can also change the mark to bar, point etc... to get a sense of how intuitive it is to have a visualization driven by data.

Creating a pie chart

We can create a pie chart using Muze and it is slightly different from the normal charts (since they dont have x and y axes). We do not need to specify Rows and Columns here and a pie chart only needs the angle or radius and color encodings to render.

main
run-button
run-button
reset-button
// DataModel instance is created from https://www.charts.com/static/cars.json data,
// https://www.charts.com/static/cars-schema.json schema and assigned to variable dm.

const carCountDM = dm.calculateVariable({
    name: 'carCount',
    type: 'measure'
}, ['Name', () => 1]);

const env = muze();
const canvas = env.canvas();

canvas
    .data(carCountDM)
    .width(600)
    .height(400)
  	.layers([{
    	mark: 'arc',
      	encoding:{
        	angle: 'carCount'
        }
    }])
	.color('Origin')
	.rows([])
	.columns([])
    .mount('#chart-container');
logo

Not all data are suitable for pie

Just like stacked bar, not all data are suitable for pie. Here in cars.json data there is no field which we can use to form a meaningful pie chart. Hence we have calculated count of cars per country (Origin) which could be plotted in a pie chart.

Had carCount variable been present in data, we wouldn't have calculated a new variable.

Wrapping up

In this tutorial we learnt how we can achieve various chart types using a consistent mental model. In the next tutorial we will see how we scale this idea of mark to create trendline in a chart.