Data
This tutorial will look at how data is loaded, updated, and used in amCharts 5.
The data object
Section titled “The data object”All amCharts 5 components that use data have a property named data.
It’s not a simple storage for data but rather object based on class ListData.
Any data manipulation - setting data, inserting, removing, or updating values for data points - is done via many of the ListData methods. The object will take care of the rest - no need to let the chart know we’ve updated something - it will just happen.
In this tutorial, we will cover the most common of ListData’s methods, but make sure you visit its full class reference for more options.
Setting data
Section titled “Setting data”Data set set via ListData object of the component that is direct user of the data.
For example, in XY Chart the users of data are its series, not the chart itself.
Chart’s legend is another example of a data user.
To set initial data or replace all of the current data, we use setAll() method:
series.data.setAll([{ category: "Research", value: 1000}, { category: "Marketing", value: 1200}, { category: "Sales", value: 850}]);series.data.setAll([{ category: "Research", value: 1000}, { category: "Marketing", value: 1200}, { category: "Sales", value: 850}]);Using setAll() will overwrite any data that may have been set previously. The series will be immediately redrawn to reflect the new data.
Data order
Section titled “Data order”Incremental updates
Section titled “Incremental updates”Adding single data point
Section titled “Adding single data point”To add a single data point to the end of the data, we use push() method:
series.data.push({ category: "HR", value: 500});series.data.push({ category: "HR", value: 500});Should we want to add data points to the beginning of data, we would use unshift() instead:
series.data.unshift({ category: "HR", value: 500});series.data.unshift({ category: "HR", value: 500});We can insert new data point at specific index, too. For that we’d use insertIndex(). The function takes index (zero-based) and value to insert into data:
series.data.insertIndex(2, { category: "HR", value: 500});series.data.insertIndex(2, { category: "HR", value: 500});Adding multiple data points
Section titled “Adding multiple data points”Similarly, to add multiple data points to the end of the data, we can use pushAll() method:
series.data.pushAll([{ category: "HR", value: 500}, { category: "Logistics", value: 450}]);series.data.pushAll([{ category: "HR", value: 500}, { category: "Logistics", value: 450}]);Updating existing data
Section titled “Updating existing data”To update existing data points, we use method setIndex().
The method takes index (zero-based) and new data as parameters. For example, let’s update values of the second data point:
series.data.setIndex(1, { category: "Marketing", value: 1000});series.data.setIndex(1, { category: "Marketing", value: 1000});Calling setIndex() will take care of all the recalculation and redrawing of the required elements. It will even animate from one value to another, if we have animations enabled.
Removing data items
Section titled “Removing data items”If we need to remove data items, we can use removeIndex() method:
series.data.removeIndex(0);series.data.removeIndex(0);The above will remove the first data item from series.
Pre-processing data
Section titled “Pre-processing data”ListData comes with a capability to pre-process the data before it is passed onto a chart.
For that it has a property: processor. It can be set to an instance of DataProcessor, which can do the following:
- Convert string-based dates or
Dateobjects into timestamps, which is expected format for time-based data in amCharts 5. - Convert string-based numbers into proper numeric values.
- Replace empty values with something else.
Creating a processor
Section titled “Creating a processor”Like with any object in amCharts 5, data processor is created using its new() static method:
series.data.processor = am5.DataProcessor.new(root, {});series.data.processor = am5.DataProcessor.new(root, {});The above will create the processor, which will do nothing, until we set some of its properties.
Data mutation
Section titled “Data mutation”Data processor will manipulate values in the source array!
If you need your source data to remain intact, you may need to make a hard copy of the data and pass it into series instead.
This is also why you don’t need to define data processors for multiple series if they use the same data.
// Source datalet data = [{ date: "2021-01-01", value: "1000"}, { date: "2021-01-02", value: "800"}];
// Creating processor only for the first seriesseries1.data.processor = am5.DataProcessor.new(root, { numericFields: ["value"]});
// Setting dataseries1.data.setAll(data);series2.data.setAll(data);series3.data.setAll(data);// Source datavar data = [{ date: "2021-01-01", value: "1000"}, { date: "2021-01-02", value: "800"}];
// Creating processor only for the first seriesseries1.data.processor = am5.DataProcessor.new(root, { numericFields: ["value"]});
// Setting dataseries1.data.setAll(data);series2.data.setAll(data);series3.data.setAll(data);Parsing numeric values
Section titled “Parsing numeric values”Chart expects numbers in data where numeric data fields are used. If we have values supplied as strings in our data, the chart will fail.
We can use data processor to automatically convert those strings into numeric values, by setting numericFields property:
// Processor needs to be set before dataseries.data.processor = am5.DataProcessor.new(root, { numericFields: ["value"]});
series.data.setAll([{ date: "2021-01-01", value: "1000"}, { date: "2021-01-02", value: "800"}]);// Processor needs to be set before dataseries.data.processor = am5.DataProcessor.new(root, { numericFields: ["value"]});
series.data.setAll([{ date: "2021-01-01", value: "1000"}, { date: "2021-01-02", value: "800"}]);Parsing dates
Section titled “Parsing dates”If we use a date axis in our chart, the values that specify date/time will need to come in JavaScript timestamp format, which is number of milliseconds since “UNIX epoch”.
If our data is in any other way we may need to tell data processor to convert those dates into timestamp.
To specify what fields hold dates we use dateFields:
// Processor needs to be set before dataseries.data.processor = am5.DataProcessor.new(root, { numericFields: ["value"], dateFields: ["date"]});
series.data.setAll([{ date: new Date(2021, 0, 1), value: "1000"}, { date: new Date(2021, 0, 2), value: "800"}]);// Processor needs to be set before dataseries.data.processor = am5.DataProcessor.new(root, { numericFields: ["value"], dateFields: ["date"]});
series.data.setAll([{ date: new Date(2021, 0, 1), value: "1000"}, { date: new Date(2021, 0, 2), value: "800"}]);The above example uses Date objects to specify dates.
If our data holds dates as strings, we will additionally have to specify dateFormat so that processor knows how to parse it into a timestamp:
// Processor needs to be set before dataseries.data.processor = am5.DataProcessor.new(root, { numericFields: ["value"], dateFields: ["date"], dateFormat: "yyyy-MM-dd"});
series.data.setAll([{ date: "2021-01-01", value: "1000"}, { date: "2021-01-02", value: "800"}]);// Processor needs to be set before dataseries.data.processor = am5.DataProcessor.new(root, { numericFields: ["value"], dateFields: ["date"], dateFormat: "yyyy-MM-dd"});
series.data.setAll([{ date: "2021-01-01", value: "1000"}, { date: "2021-01-02", value: "800"}]);The following format codes are supported when parsing dates:
| Period | Code | No. | Example | Comment |
|---|---|---|---|---|
| era | G | 1 | AD | “AD” or “BC” |
| year | y | 1..n | 1996 | Calendar year. |
| Y | 1..n | 1997 | Week year. | |
| month | M | 1..2 | 09 | One or two digit month number. |
| 3 | Sep | Short month name. | ||
| 4 | September | Full month name. | ||
| week | w | 1..2 | 27 | Week of the year. |
| W | 1 | 3 | Week of the month. | |
| day | d | 1..2 | 1 | Day of the month. |
| D | 1..3 | 345 | Day of the year. | |
| am/pm | a | 1 | AM | “AM” or “PM” |
| 2 | A.M. | “A.M.” or “P.M.” | ||
| 3 | A | “A” or “P” | ||
| hour | h | 1..2 | 11 | Hour [1-12]. |
| H | 1..2 | 13 | Hour [0-23]. | |
| K | 1..2 | 0 | Hour [0-11]. | |
| k | 1..2 | 24 | Hour [1-24]. | |
| minute | m | 1..2 | 59 | Minute. |
| second | s | 1..2 | 12 | Second. |
| S | 1..2 | 3456 | Fractional Second. | |
| x | 1..n | 1507908460868 | Timestamp. | |
| n | 1..3 | 029 | Milliseconds. | |
| zone | Z | 1 | GMT-08:00 | Time zone in GMT format. |
| 2 | -0800 | Time zone in RFC 822 format. | ||
| other | i | 1 | 2017-10-14T05:24:17.872Z | Date/time formatted according to ISO8601 format. |
Parsing colors
Section titled “Parsing colors”Colors in amCharts 4 are represented by Color objects. If you need data processor to automatically convert string-based or hex color codes to Color objects, you can use colorFields.
Data processor recognizes the following formats:
- Hex strings:
"#ff0000" - RGBA notation:
"rgb(255, 0, 0)"or"rgba(255, 0, 0, 1)" - Hex numbers:
0xff0000or16711680
// Processor needs to be set before dataseries.data.processor = am5.DataProcessor.new(root, { numericFields: ["value"], colorFields: ["color"]});
series.data.setAll([{ date: "2021-01-01", value: "1000", color: "#ff0000"}, { date: "2021-01-02", value: "800", color: "#ff0000"}]);// Processor needs to be set before dataseries.data.processor = am5.DataProcessor.new(root, { numericFields: ["value"], colorFields: ["color"]});
series.data.setAll([{ date: "2021-01-01", value: "1000", color: "#ff0000"}, { date: "2021-01-02", value: "800", color: "#ff0000"}]);Deep processing
Section titled “Deep processing”Processor can convert input values from deeper objects, too.
numericFields, dateFields, and colorFields accept strings using dot notation to identify hierarchy.
For example consider data like this:
[{ date: new Date(2021, 0, 1), value: 1000, columnSettings: { fill: "#d6e681" }}, { date: new Date(2021, 0, 2), value: 800, columnSettings: { fill: "#babf95" }}, { date: new Date(2021, 0, 3), value: 700, columnSettings: { fill: "#c4ad83" }}, { date: new Date(2021, 0, 4), value: 1200, columnSettings: { fill: "#c6b677" }}, { date: new Date(2021, 0, 5), value: 740, columnSettings: { fill: "#dbb957" }}]We can use "columnSettings.fill" to allow processor to get to resolving fill color:
series.data.processor = am5.DataProcessor.new(root, { numericFields: ["value"], dateFields: ["date"], colorFields: ["columnSettings.fill"]});series.data.processor = am5.DataProcessor.new(root, { numericFields: ["value"], dateFields: ["date"], colorFields: ["columnSettings.fill"]});Replacing empty values
Section titled “Replacing empty values”In some cases we need to replace empty values (e.g. null or empty strings) with something else. That’s where processors emptyAs setting comes in:
// Processor needs to be set before dataseries.data.processor = am5.DataProcessor.new(root, { emptyAs: 0});
series.data.setAll([{ category: "Research", value: 1000}, { category: "Marketing", value: null}, { category: "Sales", value: 850}]);// Processor needs to be set before dataseries.data.processor = am5.DataProcessor.new(root, { emptyAs: 0});
series.data.setAll([{ category: "Research", value: 1000}, { category: "Marketing", value: null}, { category: "Sales", value: 850}]);In the above, without the processor, a data point for “Marketing” would be not plotted. With data processor, it will be replaced with a zero, which is a proper numeric value and thus will plot the data point.
External data
Section titled “External data”Loading
Section titled “Loading”amCharts 5 comes with a helper utility am5.net.load().
It takes at least one argument (a URL of the request) and returns a Promise object with net load result (INetLoadResult).
am5.net.load("/data/mydata.json").then(function(result) { // This gets executed when data finishes loading // ... do something console.log(result.response);}).catch(function(result) { // This gets executed if there was an error loading URL // ... handle error console.log("Error loading " + result.xhr.responseURL);});am5.net.load("/data/mydata.json").then(function(result) { // This gets executed when data finishes loading // ... do something console.log(result.response);}).catch(function(result) { // This gets executed if there was an error loading URL // ... handle error console.log("Error loading " + result.xhr.responseURL);});The result passed to handler functions is an object that implements INetLoadResult interface. It contains a bunch of useful references, but the most important one is response, which contains the actual output of the requested URL.
Parsing
Section titled “Parsing”Now that we’ve set up loading of data, we need to parse it before it can be used by chart.
amCharts 5 comes with two build-in parsers: CSV and JSON.
Those are represented by classes CSVParser and JSONParser respetively, both of which have a static method parse() so they do not need to be instantiated as objects.
parse() method takes two parameters:
data- input data as a string.options(optional) - format-specific options.
It returns parsed data, which can be set directly to its intended user, e.g. series:
am5.net.load("/data/mydata.json").then(function(result) { // This gets executed when data finishes loading series.data.setAll(am5.JSONParser.parse(result.response));}).catch(function(result) { // This gets executed if there was an error loading URL // ... handle error console.log("Error loading " + result.xhr.responseURL);});am5.net.load("/data/mydata.json").then(function(result) { // This gets executed when data finishes loading series.data.setAll(am5.JSONParser.parse(result.response));}).catch(function(result) { // This gets executed if there was an error loading URL // ... handle error console.log("Error loading " + result.xhr.responseURL);});CSV format can have a lot of configuration options, such as column delimiters, column names, etc.
am5.net.load("/data/mydata.csv").then(function(result) { // This gets executed when data finishes loading series.data.setAll(am5.CSVParser.parse(result.response, { delimiter: ";", reverse: true, skipEmpty: true, useColumnNames: true }));}).catch(function(result) { // This gets executed if there was an error loading URL // ... handle error console.log("Error loading " + result.xhr.responseURL);});am5.net.load("/data/mydata.csv").then(function(result) { // This gets executed when data finishes loading series.data.setAll(am5.CSVParser.parse(result.response, { delimiter: ";", reverse: true, skipEmpty: true, useColumnNames: true }));}).catch(function(result) { // This gets executed if there was an error loading URL // ... handle error console.log("Error loading " + result.xhr.responseURL);});We can also use standalone data parser to parse date/numeric/color fields in data after it is parsed:
am5.net.load("/data/mydata.csv").then(function(result) {
// Parse data let data = am5.CSVParser.parse(result.response, { delimiter: ";", reverse: true, skipEmpty: true, useColumnNames: true });
// Process data let processor = am5.DataProcessor.new(root, { dateFields: ["date"], dateFormat: "yyyy-MM-dd", numericFields: ["value", "volume"] }); processor.processMany(data);
// Use parsed/processed data series.data.setAll(data);
});am5.net.load("/data/mydata.csv").then(function(result) {
// Parse data var data = am5.CSVParser.parse(result.response, { delimiter: ";", reverse: true, skipEmpty: true, useColumnNames: true });
// Process data var processor = am5.DataProcessor.new(root, { dateFields: ["date"], dateFormat: "yyyy-MM-dd", numericFields: ["value", "volume"] }); processor.processMany(data);
// Use parsed/processed data series.data.setAll(data);
});External data example
Section titled “External data example”The following Gantt chart example combines a few techniques described above, like loading and parsing external data, as well as using data processor to parse date and color fields.
User data
Section titled “User data”Each element in amCharts 5 can have any arbitrary data attached to it using its userData setting.
This setting is not used by chart in any way, and acts purely as custom data storage for later retrieval/use from the object.
It can be set using set() or setAll() methods:
chart.set("userData", { foo: "bar"});chart.set("userData", { foo: "bar"});Or via new() syntax:
let yAxis = chart.yAxes.push(am5xy.ValueAxis.new(root, { extraTooltipPrecision: 1, userData: { foo: "bar", someArbitraryValue: 100 }, renderer: am5xy.AxisRendererY.new(root, { minGridDistance: 30 })}));var yAxis = chart.yAxes.push(am5xy.ValueAxis.new(root, { extraTooltipPrecision: 1, userData: { foo: "bar", someArbitraryValue: 100 }, renderer: am5xy.AxisRendererY.new(root, { minGridDistance: 30 })}));As with any other setting, it can be retrieved using get() method:
chart.get("userData");chart.get("userData");