Linear x axis for bar chart chartjs - charts

I am using chartjs 2.9.3. I want to have linear scale for x axis for bar plots. It should work just like any linear scale of line plots representing negative, decimal, positive values.
I have managed to create almost similar scale here using this options.
scales: {
xAxes: [
{
type: "time",
time: {
parser: "Y",
unit: "year",
displayFormats: {
year: "Y"
}
},
}
]
},
But it is not working for decimal values, when dataset has negative x values, it is just rounding off to integer and placing the bar at that position.
How can represent decimal values as well?
Chartjs >=3.0.0 has introduced linear scale for bar plots. But this version has some other bugs in it so I am stuck with 2.9.3 version

I don't recommend to change the axis type to time, as this will introduce several other issues given that there are not "fractional times". And adding support for it is not that easy.
The best bet is to create your own labels with a custom function. For example:
const f = () => {
let a = [];
for (let n = -30; n <= 30; n++) {
a.push(n / 100 + "");
}
return a;
};
To generate from -3.00 to +3.00 (zeros representing decimal places). The bigger the numbers 30 and 100 the bigger the "resolution" of your linear scale.
Demo: https://codesandbox.io/s/react-chartjs-2-example-forked-26bbx?file=/src/index.js

Related

How to disable EChart's auto-adjust which happens on disabling one of the series?

I have an echart with multiple serieses. If user clicks on one of the legends and enable/disable a series, chart auto adusts (zoom in or zoom out) x and y axis to show optimal axis values.
How do I disable this behaviour so that chart do not auto-adjust axis when enabling/disabling serieses? I would like both axis to stay at initial values all the time without any change.
My Reserch: I know we can set min and max values on X and Y axis to keep X and Y axis fixed. But in this case, I have to calculate min/max manually across serieses. Any built-in flag or option will be much simpler to use.
Here is my solution at last:
I ended up setting min and max for each axis. I had to go through every data series, figure out max and min possible values among all serieses and set it on axis. This avoides above behaviour. (Some of the graphs may have multiple axes, so take that in account).
Here is generic function that can be used on chartOptions object to add min and max values based on data. Data is assumed to be part of each series. It only works on first axis incase of multiple axis.
static fixBothAxis(chartOptions) {
console.log(chartOptions);
const series = chartOptions.series;
let maxXAxis = -Infinity,
minXAxis = Infinity,
maxYAxis = -Infinity,
minYAxis = Infinity;
series.forEach((seriesItem: any) => {
// dont consider serieses that are related to secondary axis
if (seriesItem && seriesItem.data) {
const xSeries = seriesItem.data.map(item => item[0]);
const ySeries = seriesItem.data.map(item => item[1]);
maxXAxis = Math.ceil(Math.max(maxXAxis, ...xSeries));
minXAxis = Math.floor(Math.min(minXAxis, ...xSeries));
if(seriesItem.yAxisIndex !== 1){
maxYAxis = Math.ceil(Math.max(maxYAxis, ...ySeries));
minYAxis = Math.floor(Math.min(minYAxis, ...ySeries));
}
}
});
/* Make both max and min values "stick to nearest sensible value" */
const xAxis = Array.isArray(chartOptions.xAxis)
? chartOptions.xAxis[0]
: chartOptions.xAxis;
const yAxis = Array.isArray(chartOptions.yAxis)
? chartOptions.yAxis[0]
: chartOptions.yAxis;
if (xAxis && !isNaN(minXAxis) && !isNaN(maxXAxis)) {
xAxis.min = minXAxis;
xAxis.max = maxXAxis;
}
if (yAxis && !isNaN(minYAxis) && !isNaN(maxYAxis)) {
yAxis.min = minYAxis;
yAxis.max = maxYAxis;
}
return chartOptions;
}

How to render dates on x axis prior to 1900 with d3 .js scatter plot

My d3 scatter plot uses historic date data in a range from 1600 to present. I can plot my dots successfully but can't display the dates prior to 1900 I the x axis.
I am using this example to make a scatterplot in d3 but my data has historic dates prior to 1900. I have tried to implement this solution but this returns a single date repeated for each tick mark
If I try to implement d3.axisBottom(x) this returns the dates from my data, but dates prior to 1900 are not formatted correctly.
I have made a plunker with full code
Here is my relevant scale and axis code (from the plunkr):
var x = d3.scaleTime().range([0, width]);
var y = d3.scaleLinear().range([height, 0]);
var xAxis = d3.axisBottom(x).ticks(10).tickFormat(function(d){return timeFormat(d);});
var yAxis = d3.axisLeft(y).ticks(10);
var x = d3.scaleTime()
.domain(d3.extent(data, function(d) {return (d.dates);}))
.range([ 0, width ]);
svg.append("g")
.attr("transform", "translate(0," + height + ")")
//.call(xAxis, function (d){return (d);});
//.call(xAxis, function (d){return (d.dates);}); // returns just a single date for all tick marks
.call(d3.axisBottom(x)); // partially correct dates but not formatting dates prior to 1900
My scatter plot is fine and the dots are as expected. What I want to see on the x axis is the dates prior to 1900, eg 1750.
Very grateful for help.
The referenced answer is correct, you have other issues in your code. I've also updated that answer a bit to clean the code and include a generic example with an axis from 1600-2000.
The first problem is that you define your x scale:
var x = d3.scaleTime().range([0, width]);
Then pretty much immediately define your axis:
var xAxis = d3.axisBottom(x)
.tickFormat(timeFormat);
Then you define the x domain, while redefining x as well with:
var x = d3.scaleTime()
.domain(d3.extent(data, function(d) {return (d.dates);}))
.range([ 0, width ]);
If we use svg.call(xAxis), this means that the axis is using the first x scales domain, which defaults to [January 1 2000, January 2 2000], which is why every tick will have the same year if you apply the axis with only a default domain.
Your code has .call(d3.axisBottom(x) rather than .call(xAxis), which creates a new axis again, but without the formatting needed to render pre 1900 dates
Instead, determine the scale's domain first, then create the axis:
var x = d3.scaleTime()
.range([0, width])
.domain(d3.extent(data, function(d) {return (d.dates);}))
var xAxis = d3.axisBottom(x)
.tickFormat(timeFormat);
And now you can just apply the axis:
selection
.attr("transform",...)
.call(xAxis);
Here's an updated plunkr

echarts yaxis splitArea interval

I want to color yaxis from value x to y with some color,
I used splitArea but is no use to me because is a splitArea is a repeater, repeats color based on given interval.
I want to obtain something similar to Chart.js:
Acceptable Range Highlighting of Background in Chart.js 2.0
echart
yAxis: {
splitArea:{
show:true,
areaStyle:{
color:["rgba(255,0,0,0.7)",'yellow','green','green','green']
}
}
},

XAxis entry count is greater than it should be in iOS Charts 3.0.1 in Swift 3

I have two BarChartDataSets. One of them is always size 3 and the other is either 2 or 3. I tested out the code in version 3.0.0 and everything was working fine. When 3.0.1 came out, it broke my chart. I have the correct number of bars always, but I have six labels instead of 5 when the second dataset is only size 2. It has nothing to do with the stringForValue Delegate function. I set the X values using int's that are associated linearly with the Bar I want represented at that index, so each bar is equally spaced when working properly, but none of them are equally spaced when I have 6 labels and 5 bars.
The one on the left shows the issue and the one on the right shows what it looks like when my BarChartDataSet is size 3. It is duplicating whatever the last value on the chart is and adding it as a 6th label on the left. In 3.0.0 the one on the left would have only had 5 labels.
I dug into their code and where they create the labels in XAxisRendererHorizontalBarChart.swift right before they call drawLabel() I callprint("xAxis entries: \(xAxis.entries.count)")which prints xAxis entries: 6to the console even though right before I call let chartData = BarChartData(dataSets: [chartDataSet1, chartDataSet2])I callprint("dataEntries1 count: \(dataEntries1.count),
dataEntries2 count: (dataEntries2.count)")which prints dataEntries1 count: 3, dataEntries2 count: 2
First, xAxis.entries and dataSet.entries are totally different.
xAxis.entries are the values that being displayed on the axis as labels, while dataSet.entries is the value displayed for the data, e.g. the dot value in line chart, or the bar value for bar chart.
for x axis, it calculate the label count by your data entries min/max value. In Chart 3.0, x axis behaves like y axis, so it calculates x axis entries like y axis, please take a look at computeAxisValues() for details.
So xAxis.entries and dataSet.entries don't have to be equal size.
If you want to set the x axis label count same as your bars count, you can call: setLabelCount(5(6), true):
open func setLabelCount(_ count: Int, force: Bool)
{
self.labelCount = count
forceLabelsEnabled = force
}
Note, don't call the labelCount setter as it's different:
/// the number of label entries the axis should have
/// max = 25,
/// min = 2,
/// default = 6,
/// be aware that this number is not fixed and can only be approximated
open var labelCount: Int
{
get
{
return _labelCount
}
set
{
_labelCount = newValue
if _labelCount > 25
{
_labelCount = 25
}
if _labelCount < 2
{
_labelCount = 2
}
forceLabelsEnabled = false
}
}

Highcharts - navigation by 3rd element in series 'hidden' in plot

I have some info which is in this format (speed, frequency, date). What happens is that I need to plot this chart with speed x frequency, but I want to allow the users to use the navigation filtering by the date, which is not appearing on the chart.
Also, I have some info which is not built dynamically, which is the limits of speed x frequency. This info will be fixed as reference points on the plot. So, when I filter the plot info (not the limits), it must always display these limit plots.
You can have an idea by this chart, the area plots show the limits for the points (speed, frequency). Then, I would add points of speed x frequency (x date), and filter then by date.
Can you guys give me some advice on this?
here is a JSFIDDLE
JSFIDDLE
data: [
[0, 20, here is a date], [10, 20,here is a date],
[50, 39.9994, here is a date], [100,49.7494, here is a date]
],
Guys, notice that every element of the array in the series has 3 elements [a, b, c], suppose the third one (c) is a DATE and not a random number as it is right now. I want to be able to use the commented the navigator code to filter this series by this C element, which doesn't in fact appear on the chart you see, it is a hidden element, just to filter the data.
There will be a little tricky, if you want to have a navigator in the same chart. Navigator works only with datetime data and it must be connected with the axis from the main chart.
So, you have data in that format:
var points = [
[5, 9, Date.UTC(2016, 1, 0)],
[65, 6, Date.UTC(2016, 1, 1)],
...
You need two x axes - one which represents the data and the other which is connected to the navigator. The second axis must be visible to work with the navigator and must be connected with the datetime data.
So now, except two x axes, you need two series - one with the actual data, and the other consists of [date, y] values from the first series. The additional data will be visible in the navigator - note, that in the navigator you cannot use scatter series - so it will be converted to line series - to happen it without errors, your data should be sorted by date.
series: [{
id: 'main-series',
data: points.map(function(point) {
return [point[0], point[1], point[2], point[1]]
}),
showInNavigator: false,
xAxis: 1,
keys: ['x', 'y', 'date', 'holdY'] //holdY is for easier hiding points
}, {
xAxis: 0,
data: points.map(function(point) {
return [point[2], point[1]];
}),
showInNavigator: true,
enableMouseTracking: false,
color: 'transparent',
showInLegend: false
}],
xAxis: [{
minRange: 1000 * 3600 * 24,
type: 'datetime',
tickLength: 0,
tickLength: 0,
labels: {
enabled: false
},
}, {
type: 'linear'
}],
The last thing you need a callback which will hide/show points after the extremes in the navigator are set. Hiding/showing depends on the third point's property which is date. There is no directly API to hide/show specific points (except pie), but it can be achieved by setting point's value to null (that is why I preserved the real y in holdY).
events: {
afterSetExtremes: function(e) {
var points = this.chart.get('main-series').points;
points.forEach(function(point) {
point.update({
y: e.min <= point.date && point.date <= e.max ? point.holdY : null
}, false, false);
});
this.chart.redraw();
}
}
example: https://jsfiddle.net/3wuwdonn/1/
I would consider using a navigator as a separate chart, then you wouldn't need the second x axis and series in the main chart and you wouldn't need to make them look invisible.
example with a navigator only chart here: http://jsfiddle.net/f7Y9p/