Data

While many of the articles touch data topic in some specific way, we've created this one to give a broader view of the data concepts in amCharts 4.

Structure of data

Array of data items

Absolutely all data in amCharts 4 takes the same shape - it's an array of objects.

Each object in the array represents a single data point.

Each key in the object (data point) holds a value which can be used for series, axes, or simply displayed in labels or tooltips.

Here's a basic data (taken from our XY Chart article):

[{
  "country": "Lithuania",
  "research": 501.9,
  "marketing": 250,
  "sales": 199
}, {
  "country": "Czech Republic",
  "research": 301.9,
  "marketing": 222,
  "sales": 251
}, {
  "country": "Ireland",
  "research": 201.1,
  "marketing": 170,
  "sales": 199
}, {
  "country": "Germany",
  "research": 165.8,
  "marketing": 122,
  "sales": 90
}, {
  "country": "Australia",
  "research": 139.9,
  "marketing": 99,
  "sales": 252
}, {
  "country": "Austria",
  "research": 128.3,
  "marketing": 85,
  "sales": 84
}, {
  "country": "UK",
  "research": 99,
  "marketing": 93,
  "sales": 142
}, {
  "country": "Belgium",
  "research": 60,
  "marketing": 50,
  "sales": 55
}, {
  "country": "The Netherlands",
  "research": 50,
  "marketing": 42,
  "sales": 25
}]

For example, a Category axis will build a category out of each data point, taking category labels out of "country" field.

A "Research" series, will draw values for its data points from "research" fields.

How do they know which fields to use? We'll explain shortly.

Order of data items

In most cases the order of data items in data array is important, because it will affect actual plot, and in some cases might even lead to experience-breaking anomalies if the order is not right.

Let's look at some of the common setups.

Date-based data

The order of data items is extremely important when plotting date/time-based data.

Points must always come in ascending data.

IMPORTANT Points must always come in ascending order.

Having them out of order might lead anomalies when zooming chart, grouping data points, or an outright messy plot, e.g.:

Data items are in correct place, but the line seems buggy.

If fixing the order in source data is not an option, you will need to pre-sort your data before using in chart.

To do that, define a custom function, executed on some event, like beforedatavalidated which kicks in, well, before data is validated. The following will fix the issue:

chart.events.on("beforedatavalidated", function(ev) {
  chart.data.sort(function(a, b) {
    return (new Date(a.date)) - (new Date(b.date));
  });
});
chart.events.on("beforedatavalidated", function(ev) {
  chart.data.sort(function(a, b) {
    return (new Date(a.date)) - (new Date(b.date));
  });
});
{
  // ...
  "events": {
    "beforedatavalidated": function(ev) {
      ev.target.data.sort(function(a, b) {
        return (new Date(a.date)) - (new Date(b.date));
      });
    }
  }
}

That'll fix our chart:

See the Pen amCharts 4: date axis with out-of-order items by amCharts (@amcharts) on CodePen.24419

NOTE The actual code will depend per type and structure of your data. Update accordingly.

Category-based data

Categories on CategoryAxis will be shown, and related Series will be plotted as they appear in source data.

Make sure that your categories are ordered the way you want them to appear in chart.

XY or scatter data

In scenarios where both X and Y dimensions are governed by a ValueAxis, having data points in any order is OK.

Though, if you plan on connecting your data points with a line, as opposed to just showing the bullets, you might need to consider data order as well, since lines will connect data points that are adjacent in data, not by proximity of their coordinates.

Binding data

As we already mentioned, there's a way to bind specific values in data to the elements that will use them, such as Category axis, or Column series.

Let's explore ways on how data can be accessed and used from different places.

Data fields

Some specific data users such as, say, Column series need specific data to be able to make sense.

For a Pie chart series, it might be numeric value and a title to name its slices.

For a series in an XY Chart, it needs to know at least two dimensional values, so it can plot data points on a two-dimensional plot.

Therefore each Series has a pre-defined list of "data fields", as defined in its dataFields property.

Look up each specific series in our class reference to find out the type of the data fields available for each specific series.

Here's how dataFields looks like for Pie series.

Pie series data fields

You can see that dataFields for Pie series is defined by IPieSeriesDataFields interface. Go ahead, click on it.

Pie series data fields in class reference

Now we know precisely which data fields are available for Pie series, and can set up dataFields:

pieSeries.dataFields.value = "research";
pieSeries.dataFields.category = "country";
pieSeries.dataFields.value = "research";
pieSeries.dataFields.category = "country";
{
  // ...
  "series": [{
    "type": "PieSeries",
    "dataFields": {
      "value": "research",
      "category": "country"
    }
  }]
}

There you go, we just told our Pie series to get its "category" (slice title) from "country" fields in data, and its values for slices from "research".

MORE INFO For more chart-specific Series configurations, data fields details, and examples, visit our other articles in "Chart types" section.

SUMMING UP To sum it up, data fields hold data that is needed by chart elements. Those fields hold data in specific format. They are pre-processed by the chart, aggregated, calculated, and otherwise given great care.

Property fields

We already know data fields, that hold chart data in specific format.

Now, let's look at their cousins - property fields.

Property fields are a way to tie chart element's properties to data.

Those fields are not pre-processed like data fields. They're just a way to bind a property to a field in data.

For example, we may bind a column fill color in a Column series to a color field in data, so we set individual colors for each column through data.

Here's how it would look code-wise:

series.columns.template.propertyFields.fill = "color";
series.columns.template.propertyFields.stroke = "color";
series.columns.template.propertyFields.fill = "color";
series.columns.template.propertyFields.stroke = "color";
{
  // ...
  "series": [{
    "type": "ColumnSeries",
    // ...
    "columns": {
      "propertyFields": {
        "fill": "color",
        "stroke": "color"
      }
    }
  }]
}

Here's a live example, that shows the use of both data and property fields:

See the Pen amCharts V4: Data (1) by amCharts (@amcharts) on CodePen.24419

SUMMING UP Property fields are used to bind raw values in data to various properties in the chart elements.

Just to drive point home, here's how we would use property fields to color sections of a Line series:

series.propertyFields.fill = "color";
series.propertyFields.stroke = "color";
series.propertyFields.fill = "color";
series.propertyFields.stroke = "color";
{
  // ...
  "series": [{
    "type": "LineSeries",
    // ...
    "propertyFields": {
      "fill": "color",
      "stroke": "color"
    }
  }]
}

NOTE Notice how we are setting propertyFields directly on series. Line series is a bit different from other types of series in that it will automatically create segments based on the need. Not necessarily for each data point, which is the case with Column series.

See the Pen amCharts 4: Line series with alternating fills by amCharts (@amcharts) on CodePen.24419

Config field

Both data fields and property fields we just learned about are good to binding a single value to a single property.

But what if you would like to bind a whole lot of values, including ones for sub-elements?

For that we have a configField setting.

It means basically this: "this field in data may hold a config object that needs to be applied for the particular element tied to the data point".

Config fields can be multi-level, meaning that we can set not just element's properties, but also those of its sub-elements.

Take a look here:

chart.data = [{
  "country": "Lithuania",
  "litres": 501.9,
  "config": {
    "isActive": true,
    "stroke": am4core.color("#3787ac"),
    "filters": [{
      "type": "DropShadowFilter"
    }]
  }
}, {
  "country": "Czech Republic",
  "litres": 301.9
}, {
  "country": "Ireland",
  "litres": 201.1
}, {
  "country": "Germany",
  "litres": 165.8
}, {
  "country": "Australia",
  "litres": 139.9
}, {
  "country": "Austria",
  "litres": 128.3
}];

// ...

pieSeries.slices.template.configField = "config";
chart.data = [{
  "country": "Lithuania",
  "litres": 501.9,
  "config": {
    "isActive": true,
    "stroke": am4core.color("#3787ac"),
    "filters": [{
      "type": "DropShadowFilter"
    }]
  }
}, {
  "country": "Czech Republic",
  "litres": 301.9
}, {
  "country": "Ireland",
  "litres": 201.1
}, {
  "country": "Germany",
  "litres": 165.8
}, {
  "country": "Australia",
  "litres": 139.9
}, {
  "country": "Austria",
  "litres": 128.3
}];

// ...

pieSeries.slices.template.configField = "config";
{
  // ...
  "data": [{
    "country": "Lithuania",
    "litres": 501.9,
    "config": {
      "isActive": true,
      "stroke": "#3787ac",
      "filters": [{
        "type": "DropShadowFilter"
      }]
    }
  }, {
    "country": "Czech Republic",
    "litres": 301.9
  }, {
    "country": "Ireland",
    "litres": 201.1
  }, {
    "country": "Germany",
    "litres": 165.8
  }, {
    "country": "Australia",
    "litres": 139.9
  }, {
    "country": "Austria",
    "litres": 128.3
  }],
  // ...
  "series": [{
    "type": "PieSeries",
    // ...
    "slices": {
      "configField": "config"
    }
  }]
}

The above will not only set stroke for the first slice in our Pie chart, but will also set its isActive to make it pre-pulled as well as add Drop Shadow filter.

Here's how this looks in a live demo:

See the Pen amCharts 4: Using configField by amCharts (@amcharts) on CodePen.24419

Text placeholders

Placeholders in on-screen text

Various labels and tooltips in amCharts 4 can also directly refer to data values, using text placeholders.

For example, a tooltipText property of the columns in Column series, can refer directly to any value in data either by its data field name or by how its called in data:

series.columns.template.tooltipText = "{category}:\n[bold]{value}[/]";
series.columns.template.tooltipText = "{category}:\n[bold]{value}[/]";
{
  // ...
  "series": [{
    "type": "ColumnSeries",
    // ...
    "columns": {
      // ...
      "tooltipText": "{category}:\n[bold]{value}[/]"
    }
  }]
}

Go ahead, hover over any of the columns in the below chart and see how our code transformed into information in tooltip:

See the Pen amCharts V4: Data (2) by amCharts (@amcharts) on CodePen.24419

MORE INFO Please refer to our article "Formatting Strings" on more info and details on how these work.

Placeholders in URLs

Labels and tooltips are not the only place where you can use placeholders to bind to data. You can also use them in element's url property.

For example, if we a column series and want to run a Google search on the category of the clicked column, we could do something like this:

series.columns.template.url = "https://www.google.com/search?q={category.urlEncode()}";
series.columns.template.url = "https://www.google.com/search?q={category.urlEncode()}";
{
  // ...
  "series": [{
    // ...
    "columns": {
      "url": "https://www.google.com/search?q={category.urlEncode()}"
    }
  }]
}

IMPORTANT Notice the urlEncode() call next to the category reference? It's important, because it will make sure our strings are encoded properly to be included in the URLs.

Setting data

There are a number of ways to set data to a chart. Let's look at them.

Chart-wide data

Probably the most common way is to simply set chart's data property.

That's it. You don't need to do anything else. Just set your data as any other chart's property:

chart.data = [ ... ];
chart.data = [ ... ];
{
  // ...
  "data": [ ... ]
}

Series-specific data

Each Series on a chart can draw data from chart, or they can have own data.

To set data directly to some series, you set series' data property:

series.data = [ ... ];
series.data = [ ... ];
{
  // ...
  "series": [{
    "type": "ColumnSeries",
    "data": [ ... ]
  }]
}

This might come in handy of you have a lot of series, with seemingly different data, and do not want to plop it all together. Or, you have separate data sources for each series.

Here's a Radar chart example that has three series, all with their own data sets:

See the Pen amCharts V4: Radar chart (Scatter plot) by amCharts (@amcharts) on CodePen.24419

Dynamic updates

Replacing whole data set

To replace the whole data with a new set you simply assign it to chart's data property.

No further actions are necessary. The chart will automatically take in the new data and update everything.

Below is a demo that demonstrates how you can use custom dropdown selector to dynamically replace whole data set of the chart.

See the Pen amCharts 4: Dynamically updating chart data by amCharts team (@amcharts) on CodePen.0

Incremental updates

Sometimes replacing a the whole data set just because you added a new data point does not make a sense - it would be a tremendous waste of computer resources.

For that amCharts 4 implements incremental loading.

To add new data point, or several data points, to already existing data set, you use chart's addData(datapoint(s), remove) method.

As the first parameter, you pass in a data point (or an array of data points) to add to your existing data set.

Optional second parameter tells how many data points to remove from the begining of the data set.

This allows creating live-data charts, without going through the motion of updating whole data sets.

Here's a cool one:

See the Pen amCharts V4: Live data by amCharts (@amcharts) on CodePen.24419

Manipulating existing data points

You can also manipulate and change data in data array.

However, the chart itself won't detect such direct changes.

To take in the changes, you will need to call chart's method invalidateData() or invalidateRawData().

chart.data[1]["research"] = 123;
chart.invalidateData();
chart.data[1]["research"] = 123;
chart.invalidateData();

Now, let's explain the difference between the two, seemingly similar functions that update the data.

If we use invalidateData() the chart will re-evaluate all of the data. It will completely disregard current data set and treat the updated data as a new set of data. This will trigger chart redraw.

invalidateRawData() is more subtle. It will assume that data items remain the same, with only their numeric values changed. This will not trigger complete re-parsing of data and redraw, but the chart will rather transition to new values of existing data items. Depending on whether you have animations enabled, this transition will be either instantaneous or will smoothly animate to new values.

Below is a small chart that demonstrates the power of invalidateRawData():

See the Pen amCharts 4: Animating data by amCharts team (@amcharts) on CodePen.0

Restructuring chart data

You can use chart's or series' "beforedatavalidated" event to completely change how chart data looks, even before the chart starts reading it.

Please refer to "Manipulating chart data" tutorial for more information and examples.

Parsing dates in data

Ideally, dates for your date-based charts should come as JavaScript Date objects, or as timestamps.

However, we're not living in the ideal world, and data may come as strings, formatted in any number of unpredictable formats.

Take this data for example:

[{
  "date": "03/15/2019",
  "value": 450
}, {
  "date": "03/16/2019",
  "value": 320
}, {
  "date": "03/17/2019",
  "value": 411
}, {
  "date": "03/18/2019",
  "value": 451
}, {
  "date": "03/19/2019",
  "value": 390
}]

While some browsers, like Chrome, will do their best and figure out dates out of those strings, other - less clever - browsers might stumble on them.

Just to be on the safe side, we need to somehow instruct the chart what format our string dates are in so that it can parse them out correctly.

That's where chart's "number formatter" (accessible via numberFormatter property) comes in.

Number formatter is used for a lot of things, but the functionality of our focus for this chapter is its use for parsing strings for dates.

To set a format for incoming dates, we are using formatter's inputDateFormat property.

So, to make the chart correctly parse above dates, we're going to do this:

chart.dateFormatter.inputDateFormat = "MM/dd/yyyy";
chart.dateFormatter.inputDateFormat = "MM/dd/yyyy";
{
  // ...
  "dateFormatter": [{
    "inputDateFormat": "MM/dd/yyyy"
  }]
}

The "MM" in the format represents double-digit month numbers. "dd" is a double-digit month day. Finally, "yyyy" is a year.

For a complete list of available codes, please visit our "Formatting Date & Time" article.

Here's a live chart, utilizing string-based dates and inputDateFormat:

See the Pen KJLQdo by amCharts team (@amcharts) on CodePen.0

Using "dummy data"

Each and every element in amCharts 4 has a property dummyData.

Think of it as a storage container for any arbitrary data you want to associate with that object.

Technically, in JavaScript you would be able to create any properties on any object. That would not fly in TypeScript applications though, since TypeScript enforces type check. Hence a built-in property to store your data.

dummyData is not used by the charts. It can be used by any custom code you create, such as events, adapters, and even referenced to by text formatters.

Using in adapters

Whenever an adapter is applied to some value, its handler function receives reference to the object itself. This means that you will also be able to access dummyData for it as well, which might be used in the functionality of the function.

The following example shows how you can set arbitrary data to chart's series, which later on is used to modify series' name using name adapter:

let series = chart.series.push(new am4charts.ColumnSeries());
// ...
series.dummyData = {
  name: "First series"
};
series.adapter.add("name", function(name, target) {
  return target.dummyData.name;
});
var series = chart.series.push(new am4charts.ColumnSeries());
// ...
series.dummyData = {
  name: "First series"
};
series.adapter.add("name", function(name, target) {
  return target.dummyData.name;
});
{
  // ...
  "series": [{
    "type": "ColumnSeries",
    // ...
    "dummyData": {
      "name": "First series
    },
    "adapter": {
      "name": function(name, target) {
        return target.dummyData.name;
      }
    }
  }]
}

Here's an example in action:

See the Pen amCharts 4: dummyData (1) by amCharts (@amcharts) on CodePen.24419

Referring to dummy data in text

We can refer to dummy data in text as well.

For example, in a tooltipText template:

series.tooltipText = "{dummyData.name} -- {dummyData.note}";
series.tooltipText = "{dummyData.name} -- {dummyData.note}";
{
  // ...
  "series": [{
    "type": "ColumnSeries",
    // ...
    "tooltipText": "{dummyData.name} -- {dummyData.note}"
  }]
}

We can even use references within text formatting blocks, e.g. "Quick [bold brown]brown {dummyData.perp}[/] jumps over lazy [bold {dummyData.victim_color}]{dummyData.victim}[/].";

BTW, here's a working example with the above:

See the Pen amCharts 4: text formatting by amCharts (@amcharts) on CodePen.24419

Dummy data in data points

Finally, you may want to include your dummy data in your real data.

That's where "property fields" may come in handy. Remember we've learned about them earlier in this article.

chart.data = [{
  "category": "Research",
  "value": 450,
  "breakdown": {
    "personnel": 250,
    "equipment": 150,
    "other": 50
  }
}, {
  "category": "Marketing",
  "value": 1200,
  "breakdown": {
    "personnel": 600,
    "equipment": 400,
    "other": 200
  }
}, {
  "category": "Distribution",
  "value": 1850,
  "breakdown": {
    "personnel": 1200,
    "equipment": 600,
    "other": 50
  }
}];

// ...

let series = chart.series.push(new am4charts.ColumnSeries());
series.dataFields.valueY = "value";
series.dataFields.categoryX = "category";
series.columns.template.propertyFields.dummyData = "breakdown";
series.columns.template.tooltipText = "[bold]{category}[/]\nPersonnel: {dummyData.personnel}\nEquipment: {dummyData.equipment}\nOther: {dummyData.other}";
chart.data = [{
  "category": "Research",
  "value": 450,
  "breakdown": {
    "personnel": 250,
    "equipment": 150,
    "other": 50
  }
}, {
  "category": "Marketing",
  "value": 1200,
  "breakdown": {
    "personnel": 600,
    "equipment": 400,
    "other": 200
  }
}, {
  "category": "Distribution",
  "value": 1850,
  "breakdown": {
    "personnel": 1200,
    "equipment": 600,
    "other": 50
  }
}];

// ...

var series = chart.series.push(new am4charts.ColumnSeries());
series.dataFields.valueY = "value";
series.dataFields.categoryX = "category";
series.columns.template.propertyFields.dummyData = "breakdown";
series.columns.template.tooltipText = "[bold]{category}[/]\nPersonnel: {dummyData.personnel}\nEquipment: {dummyData.equipment}\nOther: {dummyData.other}";
{
  // ...
  "data": [{
    "category": "Research",
    "value": 450,
    "breakdown": {
      "personnel": 250,
      "equipment": 150,
      "other": 50
    }
  }, {
    "category": "Marketing",
    "value": 1200,
    "breakdown": {
      "personnel": 600,
      "equipment": 400,
      "other": 200
    }
  }, {
    "category": "Distribution",
    "value": 1850,
    "breakdown": {
      "personnel": 1200,
      "equipment": 600,
      "other": 50
    }
  }],
  // ...
  "series": [{
    "type": "ColumnSeries",
    // ...
    "columns": {
      "propertyFields": {
        "dummyData": "breakdown"
      },
      "tooltipText": "[bold]{category}[/]\nPersonnel: {dummyData.personnel}\nEquipment: {dummyData.equipment}\nOther: {dummyData.other}"
    }
  }]
}

See the Pen amCharts 4: using dummyData (3) by amCharts (@amcharts) on CodePen.24419

Loading external data

A chart, besides numerous ways to set in-line data, has also built-in facilities to load data from external resources in CSV or JSON formats.

Please refer to "Loading external data" article for further details.

Related demos