I'm using Google's histogram functionality in JavaScript to graph the output of a stochastic model, and it's overlaying extra bars on top of an otherwise nice chart.
The chart is generated by running the calculate_display_coverage() function hundreds of times with different inputs. I assure you the inner workings of the function aren't a problem here (I can't really post my whole Fiddle, but I show what the data look like in my code snippet).
gender = {
gender1: "Male",
gender2: "Female"
};
device = {
device1: "Tablet",
device2: "Mobile",
device3: "Desktop"
};
var paramstring = "";
resultArray = [];
resultArray[0] = ['Run','Coverage'];
var q = 1;
for (var x = 0; x < 100; x++) {
for (var y in gender) {
for (var w in device) {
for (var z = 1950; z < 1980; z++) {
paramstring = "09/21/";
paramstring += z.toString();
resultArray[q] = [q,calculate_display_coverage('SIW', y, paramstring, w).displayed];
q++
}
}
}
}
// dataset looks like [['Run','Coverage'],[1,80000],[2,42000],...]
google.charts.load("current", {packages:["corechart"]});
google.charts.setOnLoadCallback(drawChart);
function drawChart() {
var data = google.visualization.arrayToDataTable(resultArray);
var options = {
title: 'Model output by coverage',
legend: { position: 'none' },
};
var chart = new google.visualization.Histogram(document.getElementById('chart_div'));
chart.draw(data, options);
}
With HTML:
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js">
</script>
<body>
<div id="chart_div" style="width: 900px; height: 500px;"></div>
</body>
In this case, where x = 100, the histogram includes these extra bars
Somewhat different formatting but same issue for more runs, where x = 2000.
Anyone seen this before?
The problem is that both columns of your data are numeric. Currently it has the format
[['Run','Count'],[1,80000],[2,42000],...]
So the histogram is including the index values as datapoints. You need to cast them as strings, so in the above code, the statement should be
resultArray[q] = [q.toString(),calculate_display_coverage('SIW', y, paramstring, w).displayed];
// [['Run','Count'],['1',80000],['2',42000],...]
That will remove the extra bars.
Related
I am trying to create a google chart that has a list of all fixtures from a certain week and how many posts each one has.
This is the code I am running:
<script type="text/javascript">
google.charts.load('current', {'packages':['bar']});
google.charts.setOnLoadCallback(drawChart);
function drawChart() {
var stringArray = [];
<c:forEach items="${posts}" var="post" varStatus="count">
stringArray.push("<c:out value='${post.fixture.home.teamName}v${post.fixture.away.teamName}'/>", 1);
</c:forEach>
var newArray = [["Games", "Amount of Posts"]];
var postArray = [1,2,3,4,5,6,7,8];
var x = 1;
while(x <= stringArray.length){
var games = stringArray[x];
var numbers = postArray[x];
console.log("Game " + games);
newArray.push([games, numbers]);
x++;
}
console.log(newArray[1]);
var data = google.visualization.arrayToDataTable(newArray);
var options = {
chart: {
title: 'Referee Performance',
subtitle: 'Season 20-21',
},
bars: 'horizontal'
};
var chart = new google.charts.Bar(document.getElementById('barchart_material'));
chart.draw(data, google.charts.Bar.convertOptions(options));
}
</script>
Lets take a closer look at this section
var newArray = [["Games", "Amount of Posts"]];
var postArray = [1,2,3,4,5,6,7,8];
var x = 1;
while(x <= stringArray.length){
var games = stringArray[x];
var numbers = postArray[x];
console.log("Game " + games);
newArray.push([games, numbers]);
x++;
}
So the newArray is what will be used to draw the table because it has the headers, I then have an array with all the fixtures in (stringArray) and another array of how many posts each one has (postArray). I am trying to add the first index of string array and postArray to the next index in newArray. This isn't working and is doing something weird. I've added console.log and this is the output I am getting.
Game 1
2:50 Game Crystal PalacevSouthampton
2:50 Game 1
2:50 Game Crystal PalacevSouthampton
2:50 Game 1
2:50 Game undefined
2:54 (2) [1, 2]
So there is 2 problems with this. For some reason x isn't incrementing and it isn't saving the team Names its saving a 1 for some reason. I'm not sure if I'm doing something stupid or not?
So how can I fix the error above?
Thank you for your time
I have google chart (line chart), need to customize legend with some extra information. Please refer this http://plnkr.co/edit/ysZwYaAQpMhHarcA2UHq?p=preview [Plunker][1] to know more in detail. I want legend information like this below snap
So if you see in below image, and my fiddle I have R1, R2 , R3 and so on as releases to select, and each release has its own score, so I want that score to be shown as R1 100 just like below image.
Note that graph data is different, and the dropdown for selection of releases data is different. but both have releaseId R1 , R2. So on selection of dropdown value say R1,R2 or R3 I am pushing only that row from graphdata to drawgraph.
So basically, I need to show selected R1 score in legend.
Please give me some idea or link where this is done earlier. Thanks in advance.
to add the score to the legend,
you can add the score to the column label before drawing the chart
replace the drawChart function with the following...
function drawChart() {
var chartColors = [];
var chartColumns = [0];
var checks = document.getElementsByTagName('input');
for (var i = 0; i < $scope.selectedNewRelease.length; i++) {
var seriesColumn = getColumnIndex(x, $scope.selectedNewRelease[i].releaseId);
chartColumns.push(seriesColumn);
x.setColumnLabel(seriesColumn, x.getColumnLabel(seriesColumn) + ' ' + $scope.selectedNewRelease[i].score);
}
var view = new google.visualization.DataView(x);
view.setColumns(chartColumns);
chart.draw(view, options);
if ($scope.selectedNewRelease.length>0) {
$scope.Grtgraph=true;
}else{
$scope.Grtgraph=false;
}
}
function getColumnIndex(data, columnLabel) {
for (var i = 0; i < data.getNumberOfColumns(); i++) {
if (data.getColumnLabel(i) === columnLabel) {
return i;
}
}
}
I am making a doughnut chart using canvas js. I am using a custom js code where I am taking a response from XML and then pointing the data points
Here when I am changing the type of chart to "line" the graph is behaving as it should but when I am changing the chart type to “pie” or “doughnut” so now instead of one chart it is giving me two charts. How it working could someone please through a light?
My code is
$scope.loadChartValue = function (data, scopes) {
scopes.data_id = [];
scopes.legend_text = "";
scopes.inner_chart_data = [];
for (var i = 0; i <= data.length; i++) {
var arrayvalue = data[0].data[i]._attr;
if (existsInArray(scopes.data_id, arrayvalue.label._value) == false) {
scopes.data_id.push(arrayvalue.label._value);
}
}
for (var i = 0; i < scopes.data_id.length; i++) {
scopes.inner_chart_data = [];
for (var j = 0; j <= data.length; j++) {
if (data[0].data[j]._attr.label._value == scopes.data_id[i]) {
scopes.inner_chart_data.push({ label: data[0].data[j]._attr.label._value, y: data[0].data[j]._attr.value._value });
scopes.legend_text = data[0].data[j]._attr.label._value;
}
}
scopes.dataset.push(
{
type: "doughnut",
markerType: "circle",
markerSize: scopes.markersize,
color: scopes.chart_color_value[i],
showInLegend: true,
name: scopes.legend_text,
legendText: scopes.legend_text,
dataPoints: scopes.inner_chart_data
}
);
}
scopes.data_length = data.length / scopes.data_id.length;
}
Line, Column and other chart-types supports multi-series whereas pie/doughnut is single series charts.
You are creating multiple data-series instead of 1 data-series with multiple dataPoints. Creating single series with multiple dataPoints instead of multiple dataSeries will work fine.
I'm drawing a graph using jqplot but it's plotting against the wrong date. see the code snippet and image below.
else if(rtype === "DATE AXES"){
$scope.pl = [[]];
for (var i = 0; i < plotVal.length; i++) {
console.log(plotVal[i].x+", "+plotVal[i].y);
$scope.pl.push([new Date(plotVal[i].x), plotVal[i].y]);
}
var data = $scope.pl;
console.log("data: ");
console.log(data)
jQuery.jqplot("chartdiv", [data],
{
axes:{
xaxis:{
renderer:$.jqplot.DateAxisRenderer,
}
},
series:[{color:'#5FAB78', markerOptions:{style:'square'}}],
});
}
Any idea what's wrong?
Found the problem. It's the pl array in the code; passed an empty value into it first, so it distorted the arrangement
$scope.pl = [[]];
was suppoed to be:
$scope.pl = [];
I am creating a scattered Graph using NVD3 using the code they have provided in their limited documentation. I have created a Scatter graph function that loops over a JSON and pushes the values to the data array.
Now I have 2 values for x axis in my Json , x and run Number. What i want is that the graph should be plotted for the value "x" (which have equal gaps) but it should display values of Run Number on the x axis (which have unequal gaps). We want to do this to make the graph more symmetric, as it is not important for us to display the gaps in graph accurately.
What i did was create a new Array xAxisValue and push the Run Numbers onto it while we loop the JSON to get values. values for x are pushed onto the data array ,
Then using
chart.xAxis.axisLabel('Run No.').tickFormat(d3.format('0d')).tickValues(
xAxisValue);
I set the Tick Values to the xAxisValue (run Number) and then pass the data variable to the draw chart function
d3.select('#chart svg').datum(myData).call(chart);
But this does not seem to work. My Axis is blank and hovering over a value displays the tool tip displays values of x instead of run number.
Because we are dynamically updating the graph i have separated the add Graph and upgrade graph function
Here is the code
function addGraph() {
var jsons = [];
chart = nv.models.scatterChart().showDistX(true).showDistY(true)
.transitionDuration(350).color(d3.scale.category10().range());
chart.tooltipContent(function(key) {
return '<h3>' + key + '</h3>';
});
chart.scatter.onlyCircles(false);
var myData = scatterData(2, 11, jsons);
d3.select('#chart svg').datum(myData).call(chart);
// add zoom handler
nv.utils.windowResize(chart.update);
return chart;
}
Upgrade Graph Function
function upgradeGraph() {
minValue = 1000000, maxValue = 0, minValueY = 100000000, maxValueY = 0;
var jsons = [];
d3.select('svg').text('');
if ($("#check2").is(':checked')) {
jsons.push("charge_ONTk_Barrel_L2_mpv.json");
}
if ($("#check1").is(':checked')) {
jsons.push("charge_ONTk_Barrel_L1_mpv.json");
}
if ($("#check3").is(':checked')) {
jsons.push("charge_ONTk_Barrel_L3_mpv.json");
}
var myData = scatterData(2, 11, jsons);
chart.xAxis.axisLabel('Run No.').tickFormat(d3.format('0d')).tickValues(
xAxisValue);
chart.yAxis.axisLabel('S/N (mpv)').tickFormat(d3.format('.04f'));
for (var i = 0; i < xAxisValue.length; i++) {
console.log("Run Number: " + xAxisValue[i]);
}
console.log("Min Y: " + minValueY + " Max Y " + maxValueY);
chart.forceX([ minValue - 2, maxValue + 2 ]);
chart.forceY([ minValueY - 3, maxValueY + 3 ]);
d3.select('#chart svg').datum(myData).call(chart);
// add zoom
addZoom({
xAxis : chart.xAxis,
yAxis : chart.yAxis,
yDomain : chart.yDomain,
xDomain : chart.xDomain,
redraw : function() {
chart.update();
},
svg : chart.svg
});
nv.utils.windowResize(chart.update);
return chart;
}
And the ScatterData Function
function scatterData(groups, points, jsons) {
var data = [];
data.push({
key : 'Error',
values : [],
color : '#FBEC5D'
});
data.push({
key : 'Bin Content ',
values : [],
color : '#0D4F8B'
});
for (var i = 0; i < jsons.length; i++) {
xAxisValue = [];
var jsonURL = jsons[i];
var xmlhttp = new XMLHttpRequest();
var url = "alljsons/" + jsons[i];
var parameters = location.search;
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
var myArr = JSON.parse(xmlhttp.responseText);
var jsonName = jsonURL.split(".");
var temp = jsonName[0];
var value = myArr[temp];
// $(".title" + loop).html(temp);
for ( var i in value) {
if (value[i].run > maxValue) {
maxValue = value[i].x;
}
if (value[i].run < minValue) {
minValue = value[i].x;
}
if (value[i].y > maxValueY) {
maxValueY = value[i].y;
}
if (value[i].y < minValueY) {
minValueY = value[i].y;
}
xAxisValue.push(value[i].run);
data[1].values.push({
x : value[i].x,
y : value[i].y,
size : 6 // Configure the size of each scatter point
,
shape : "circle"
});
var err = value[i].y - value[i].yErr;
if (err < 0) {
err = 0;
console.log("error: " + err);
}
data[0].values.push({
x : value[i].x,
y : err,
size : 6 // Configure the size of each scatter point
,
shape : "triangle-down"
});
}
}
};
xmlhttp.open("GET", url, false);
xmlhttp.send();
}
return data;
}
Here is the Output i am getting
If I understand your question correctly:
For the x-axis ticks, I would use D3's axis.tickformat function. You could create a function mapXToRunNumber(x) that takes an x value and returns a run number (you seem to be close to having this already). Then, you would use: chart.xAxis.tickFormat(mapXtoRunNumber);
For the tooltip to also show the same value as the x-axis, you would use the nvD3 function chart.interactiveLayer.tooltip.headerFormatter(mapXToRunNumber).