I want to display some data with high variety in amcharts4, so I like to use the logarithmic scale on the value axis. Unfortunately the data contain some zero values, which of course can't be displayed with logarithmic scale.
I tried to change the zero values to 1 before rendering the chart, which would work, but now the values are not correct any more.
data.forEach(item => {
for (const key in item) {
if (item[key] === 0) {
item[key] = 1;
}
}
});
Is there any better way to handle zero values with logarithmic value axis, that I can display the correct data?
Here is a code pen, which shows my current solution.
Edit
As of version 4.9.34, treatZeroAs
is officially supported. Just set it on the value axis to the desired value to remap your zero values to:
valueAxis.treatZeroAs = 0.1;
The below workaround isn't needed anymore, but you may find the value axis adapter snippet helpful in changing the first label when using treatZeroAs
.
Old method - pre 4.9.34
There doesn't appear to be a direct equivalent to v3's treatZeroAs
property, which automatically handled this sort of thing. Pre-processing the data is one step, but you can also copy the original value into a separate object property and use a series tooltip adapter to dynamically show your actual value:
data.forEach(item => {
for (const key in item) {
if (item[key] <= 0) {
item[key+"_actual"] = item[key]; //copy the original value into a different property
item[key] = 1;
}
}
});
// ...
//display actual data that was re-mapped if it exists
chart.series.each((series) => {
series.adapter.add("tooltipText", (text, target) => {
if (target.dataFields) {
let valueField = target.dataFields.valueY;
let tooltipData = target.tooltipDataItem;
if (tooltipData.dataContext[valueField + "_actual"] !== undefined) {
return '{' + valueField + '_actual}';
}
else {
return text;
}
}
else {
return text;
}
})
});
If you want to fake a zero label, you can use an adapter for that as well since your smallest value in this case will be 1:
//fake the zero axis label
valueAxis.renderer.labels.template.adapter.add("text", (text) => {
if (text === "1") {
return "0"
}
else {
return text;
}
})