I am using C3 bar charts now I am facing problem with X-Axis tick. I am taking value of X-Axis dynamically.
See below code:
chart = c3.generate({
bindto: '#port_range_to_port_range',
data: {
x: 'x',
columns: [
['x','Ho Chi Minh City', 'Tanjung Priok (Jakarta)', 'Chennai', 'Chennai', 'Chennai'],
['Your lowest rate', 530, 800, 900, 525, 190],
['Benchmark rate', 241, 890, 254, 100, 149],
['Lowest rate on route',200, 859, 256, 365, 410],
],
type: 'bar'
},
axis: {
x: {
type: 'category' // this needed to load string x value
},
y: {
tick: {
format: d3.format("$,")
}
}
},
legend: {
show: false
}
});
You can see the above I have used "Chennai" three times in X-axis. See below for more
columns: [
['x','Ho Chi Minh City', 'Tanjung Priok (Jakarta)', 'Chennai', 'Chennai', 'Chennai'],
So problem is, when graph will plot then these duplicate value is not displaying, This (Chennai) is displaying only one time and rest times its printing the numbers. See attached graph
As we figured out in comments above, since 0.4.11 c3.js starts to group same categories into one.
Fast workaround is either use previous version, or add extra spaces to category name to make them actually differ but stay visually identical:
columns: [['x',
'Ho Chi Minh City',
'Tanjung Priok (Jakarta)',
'Chennai',
'Chennai ', // <-- here
'Chennai ' // <-- and here
],
If you want to do it dynamically, more appropriate solution may be adding index to repeating categories:
columns: [['x', ... some dynamic data ]
.map(function(category, i, arr){
var prev = arr.slice(0, i);
var count = prev.reduce(function(sum, item) {
if (item === category) return sum + 1;
else return sum;
}, 0);
if (count != 0) return category + '-' + count;
else return category;
})
]
// will give you
// [['x', 'Ho Chi Minh City', 'Tanjung Priok (Jakarta)',
// 'Chennai', 'Chennai-1', 'Chennai-2']]
See updated fiddle
Related
I am creating a chart.js which has both positive and negative values
but how to make all values be on the top half
(ignore the if it's positive or negative when drawing but keep the label)
var tax_dash = new Chart(ctx_tax_dash, {
type: "bar",
data: {
labels: lable_set,
datasets: [{
label: "Tax in",
data: total_tax_in_t_data__year,
backgroundColor: '#0fd96d',
// borderColor: sales_t_data,
borderWidth: 1,
},
{
label: "Tax out",
data: total_tax_out_t_data__year,
backgroundColor: '#0f81d9',
// borderColor: sales_t_data,
borderWidth: 1,
},
{
label: "Net VAT",
data: total_tax_in_out_t_data__year,
backgroundColor: '#d96a0f',
// borderColor: sales_t_data,
borderWidth: 1,
},
],
},
options: {
legend: {
display: true,
}
},
});
EDIT
what I am trying to do
possible solution: is (multi-axis) dual y axis.multi-axis example
~ issue: how to flip the axis so that the -100 be to the top and 0 be on the bottom
~ issue: how to split the data set base on the (sign)
OR
possible solution 2 : make all variable positive
#Gkiokan> solution: use the popup modifier to the showing values with negative
~ ++ issue: how the function will know if the value is negative
~ issue: the user needs to know that this value is negative in the label
Solution 2
I did it this morning user what Math.abs from #Lawrence comment and "popup modifier" from #Gkiokan comment as well as this jsfiddle
Thank you very much for the help. Chatting with smarter people rubs off on you :)
total_tax_in_t_data_portal_month_year = [Math.abs(12),Math.abs(-234),Math.abs(234)];
total_tax_in_t_data_portal_month_year_sign = [12,-234,234];
var tax_dash_portal = new Chart(ctx_tax_dash_portal, {
type: "bar",
data: {
labels: lable_set,
datasets: [
{
label: "VAT In",
data: total_tax_in_t_data_portal_month_year,
sign: total_tax_in_t_data_portal_month_year_sign,
backgroundColor: "#0fd96d",
// borderColor: sales_t_data,
borderWidth: 1,
},
{
label: "VAT Out",
data: total_tax_out_t_data_portal_month_year,
sign: total_tax_out_t_data_portal_month_year_sign,
backgroundColor: "#0f81d9",
// borderColor: sales_t_data,
borderWidth: 1,
},
{
label: "Net VAT",
data: total_tax_t_data_portal_month_year,
sign: total_tax_t_data_portal_month_year_sign,
backgroundColor: "#d96a0f",
// borderColor: sales_t_data,
borderWidth: 1,
},
],
},
options: {
legend: {
display: true,
},
tooltips: {
enabled: true,
callbacks: {
label: function (tooltipItem, data) {
var label = data.labels[tooltipItem.index];
var val = data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];
var sign = data.datasets[tooltipItem.datasetIndex].sign[tooltipItem.index];
if(sign < 0){
return label + ": -" + val;
}else{
return label + ': ' + val;
}
}
}
}
},
});
total_tax_in_t_data_portal_month_year is an example as the values come from a function
Math.abs is used to remove the negative sign
then I added sign to the datasets for essay access
tooltips callbacks is called on every variable so I added the if statement there
to add - if sign < 0 and do nothing if not
In my opinion you can have a data Set to save the orginal Data and the modified
Data and then use the values as you need. You can not trust the value characters. My solution will work kind cross over as you have control over both values.
I've made a jsfiddle for you which demonstrates the orginal Data vs modified Data usage. Please click first on Modify Data which will then map the data, so you can see the work in progress. In your case you would modify the data before calling the charts.
Actually you will need just a couple of methods as followed:
updateItemValues to modify the negative values and put it to the other object
tooltipCallback callback for the tooltip to use the mapped orginal value
let data = {
modified: false,
orginalData : {
'tax_in' : [10, 20, -30, -40, -100, -50],
'tax_out' : [-10, 10, 20, 10, -40, -70],
'net_vat' : [-50, -9, -40, -20, -10, -90],
},
modifiedData : {
// this modified data will be calculated before putting it in the charts
// for demo purpose we will just copy the values for now.
'tax_in' : [10, 20, -30, -40, -100, -50],
'tax_out' : [-10, 10, 20, 10, -40, -70],
'net_vat' : [-50, -9, -40, -20, -10, -90],
},
updateModifiedData(){
// loop though the orginal Data
Object.keys(this.orginalData).forEach( (item, indexx) => {
console.log('modifying item chart data for: ', item)
this.updateItemValues(item)
})
this.modified = true
document.getElementById('status').innerHTML = 'modified'
},
updateItemValues(dataKey){
let temp = []
this.orginalData[dataKey].forEach( (value, index) => {
console.log('- validating ', dataKey, 'index: ', index, '; value: ', value)
// if we have a negative value, just multiply by -1 so get it positive
if(value <= 0){
value = value * -1
}
// add to the temporary variable
temp.push(value)
})
// put the modified data to some place to have it saved
this.modifiedData[dataKey] = temp
console.log('-- final data modded ', temp)
},
tooltipCallback(tooltipItem, chartData) {
// find reference values
let index = tooltipItem.index
let dataIndex = tooltipItem.datasetIndex
// find the name of dataset
let key = chartData.datasets[dataIndex].name
// validate or whatever with the orginal value
let orginalValueOfItem = data.orginalData[key][index]
let modifiedValueOfItem = data.modifiedData[key][index]
// Modify your final tooltip here
return 'Orginal Value: ' + orginalValueOfItem + ' ; Modified Value: ' + modifiedValueOfItem
}
}
How can you use this solution?
Pretty simple.
Copy that data Object in your code.
Fill the data.orginalData value with your orginal charts data based on key
example data.orginalData.tax_in = [...]
In your datasets add name property with the corresponding key
Extend the Charts options with the tooltipCallback
Call data.updateModifiedData() to get the modified data
Checkout the jsFiddle for reference if you need to.
Have fun.
I have an array of actual values, it should be just a line and an array of threshold values it should be the second line. When the actual value-line crossed through the threshold line the color of this part of the line should be changed.
var chart = c3.generate({
point: {
r: 5
},
data: {
x: 'x',
columns: [
['x', '2013-01-01', '2013-01-02', '2013-01-03', '2013-01-04'],
['actual value', 230, 120, 300, 50],
['threshold', 130, 280, 100, 250],
],
type: 'spline',
regions: {
'threshold': [{'style': 'dashed'}]
},
},
axis: {
x: {
type: 'timeseries',
tick: {
format: '%Y-%m-%d'
}
}
}
});
I'm not sure how it is done in c3, but as you mentioned d3 in your question, you would use a threshold scale in d3.
See this example from blocks:
var colorThresholdScale = d3.scaleThreshold()
.domain([0.11, 0.22, 0.33, 0.50])
.range(["#fee5d9", "#fcae91", "#fb6a4a", "#de2d26", "#a50f15"]);
The range in a threshold scale always has one more value than the domain.
If the given value is less than the first value in the domain, then the first value in the range is output:
colorThresholdScale(0.02); // "#fee5d9"
If the input value is greater than the nth value in the domain, and less than the n + 1th value in the domain, then the n + 1th value in the range is output:
colorThresholdScale(0.25); // "#fb6a4a"
If the input value is greater than the last value in the domain, then the last value in the range is output:
colorThresholdScale(0.6); // "#a50f15"
I have a sample AmCharts solid gauge charts and I want to render it using the values from the Category and Size textboxes. For example , the textarea will have this JSON data ,
[
{
"Country":"Malaysia",
"Size":260,
"State":"Selangor",
"Population":450
},
{
"Country":"England",
"Size":140,
"State":"Liverpool",
"Population":100
},
{
"Country":"China",
"Size":250,
"State":"Beijing",
"Population":200
},
{
"Country":"South Korea",
"Size":360,
"State":"Seoul",
"Population":300
}
]
Then I set Category to be Country , so it loads 4 axes with 4 labels.
Then I set Size to be Population , so it loads the endValue(startValue starts from 0 right?) to be that. And the percentage is derived from maxValue * 1.2.
My main question is , How do I render a gauge chart using AmCharts with custom data configuration? dataProvider does not seem to work here like it does in serial charts.
Codepen link : https://codepen.io/ariff20/pen/WaKJRV
Elaborating further on my comment - since dataProvider does not exist in a gauge chart, you can remap your data and create the appropriate properties/arrays in your config instead of trying to set them manually in your codepen. For instance, your bands can be generated like so:
var colors = ["#84b761", "#fdd400", "#cc4748", "#67b7dc"]; //assuming 4 bands/colors - adjust as needed
var bands = mappedData.reduce(function(acc, d, idx) {
acc.push({
color: "#eee",
startValue: 0,
endValue: d.target,
radius: 100 - 20 * idx + "%",
innerRadius: 85 - 20 * idx + "%"
});
acc.push({
color: colors[idx],
startValue: 0,
endValue: d.value,
radius: 100 - 20 * idx + "%",
innerRadius: 85 - 20 * idx + "%",
balloonText: parseFloat(d.value / d.target * 100).toFixed(2)
});
return acc;
}, []);
Labels can be generated in a similar manner:
var labels = mappedData.map(function(d, idx) {
return {
text: d.name,
x: "49%",
y: 6 + 9 * idx + "%",
size: 15,
bold: true,
color: colors[idx],
align: "right"
};
});
Once generated, simply fill in the blanks in your setting template and pass it into your makeChart call, clearing any previous instances first:
var chart; //global ref to clear the chart
function changeData() {
// ... omitted
var setting = {
type: "gauge",
theme: "light",
axes: [
{
axisAlpha: 1,
tickAlpha: 1,
labelsEnabled: true,
startValue: 0,
endValue: Math.max.apply(
Math,
mappedData.map(function(o) {
return o.target;
})
),
startAngle: 0,
endAngle: 270,
bands: bands
}
],
allLabels: labels,
export: {
enabled: true
}
};
if (chart) {
chart.clear();
chart = null;
}
chart = AmCharts.makeChart("chartdiv", setting);
}
Demo
I have been using Flot js library to create Pie charts. But i am not able to figure out how to arrange legends in flot charts in descending order according to a variable percentage that i am calculating. This is my code.
legend: {
show: true,
noColumns: 1,
labelFormatter: function(label, series) {
var percent = Math.round(series.percent);
var number= series.data[0][1];
return("<div class=servicePieLabel val="+percent+"><span class=labelTitle>"+label+"</span><span class=labelPercent>"+ percent + '%</span></div>');
}
},
flot does provide a sorted property for the legend. You can set it to a custom function for sorting but flot only passes the item's label and color to it (not the data), so it ends up not very useful for what you need.
Without that function specified, flot will plot in series item order. My recomendation for you would be to simply presort your data into the proper order.
Given data like:
var d_pie = [
{ label: "Series1", data: 10},
{ label: "Series2", data: 30},
{ label: "Series3", data: 110},
{ label: "Series4", data: 70},
{ label: "Series5", data: 80},
{ label: "Series6", data: 20}
];
Sort descending by data value:
d_pie.sort(function(a, b){
return a.data < b.data ? 1 : -1;
});
Here's an example fiddle.
I need assistance on selecting a highcharts pie slice onLoad using HighCharts sister plugin HighChartTables.
Not using <td class="data" data-graph-name="name" data-graph-item-highlight="1">XYZ</td>
As this only pull out the slice, not the selected data behind it...
Any help would be much appreciated.
If what you want is to have a slice of the pie selected on initial load you use the sliced property. See this demo here. To do this you add a property to a point you want to have selected:
series: [{
type: 'pie',
name: 'Browser share',
data: [
['Firefox', 45.0],
['IE', 26.8],
{
name: 'Chrome',
y: 12.8,
sliced: true,
selected: true
},
['Safari', 8.5],
['Opera', 6.2],
['Others', 0.7]
]
}]
I'm not sure how to achieve that in HighchartTable, but you can get this using chart.events.load callback, to find sliced point and just render report, see: http://jsfiddle.net/6mk3S/36/
And code:
highChartConfig.chart.events = {
load: function () {
var data = this.series[0].data,
dLen = data.length,
i = 0;
while (dLen > i) {
var point = data[i];
if (point.sliced) {
$report.html('<h2>' + point.name + '</h2><p>' + point.percentage.toFixed(1) + '%</p>');
i = dLen;
}
i++;
}
}
}