issue with different type of axes for storyboard example of dimplejs - dimple.js

I'm trying to create a storyboard very similar to the one in dimpleJS examples. My data looks like below.
[{"Date":"2014-05-05T19:03:23Z","Liters":0.23,"Soda":"Coke","Day":"2014-05-05","Time of Day":"15:03","Day-Time":"2014-05-05 15:03"},{"Date":"2014-05-05T19:37:27Z","Liters":0.35,"Soda":"Coke","Day":"2014-05-05","Time of Day":"15:37","Day-Time":"2014-05-05 15:37"},{"Date":"2014-05-05T20:33:33Z","Liters":0.21,"Soda":"Coke","Day":"2014-05-05","Time of Day":"16:33","Day-Time":"2014-05-05 16:33"},{"Date":"2014-05-05T21:11:37Z","Liters":0.21,"Soda":"Coke","Day":"2014-05-05","Time of Day":"17:11","Day-Time":"2014-05-05 17:11"},{"Date":"2014-05-06T13:55:09Z","Liters":0.22,"Soda":"Coke","Day":"2014-05-06","Time of Day":"9:55","Day-Time":"2014-05-06 9:55"},{"Date":"2014-05-06T14:27:13Z","Liters":0.27,"Soda":"Coke","Day":"2014-05-06","Time of Day":"10:27","Day-Time":"2014-05-06 10:27"},{"Date":"2014-05-06T15:42:21Z","Liters":0.14,"Soda":"Coke","Day":"2014-05-06","Time of Day":"11:42","Day-Time":"2014-05-06 11:42"},{"Date":"2014-05-06T16:16:24Z","Liters":0.24,"Soda":"Coke","Day":"2014-05-06","Time of Day":"12:16","Day-Time":"2014-05-06 12:16"},{"Date":"2014-05-05T19:13:24Z","Liters":0.59,"Soda":"Diet Coke","Day":"2014-05-05","Time of Day":"15:13","Day-Time":"2014-05-05 15:13"},{"Date":"2014-05-05T20:33:33Z","Liters":0.01,"Soda":"Diet Coke","Day":"2014-05-05","Time of Day":"16:33","Day-Time":"2014-05-05 16:33"},{"Date":"2014-05-05T21:04:36Z","Liters":0.39,"Soda":"Diet Coke","Day":"2014-05-05","Time of Day":"17:04","Day-Time":"2014-05-05 17:04"},{"Date":"2014-05-05T21:11:37Z","Liters":0.21,"Soda":"Diet Coke","Day":"2014-05-05","Time of Day":"17:11","Day-Time":"2014-05-05 17:11"},{"Date":"2014-05-05T21:57:42Z","Liters":0.17,"Soda":"Diet Coke","Day":"2014-05-05","Time of Day":"17:57"},{"Date":"2014-05-06T11:11:53Z","Liters":0.42,"Soda":"Diet Coke"},{"Date":"2014-05-06T12:54:03Z","Liters":0.49,"Soda":"Diet Coke"},{"Date":"2014-05-06T12:55:03Z","Liters":0.48,"Soda":"Diet Coke"},{"Date":"2014-05-06T13:07:04Z","Liters":0.27,"Soda":"Diet Coke"},{"Date":"2014-05-06T13:34:07Z","Liters":0.41,"Soda":"Diet Coke"},{"Date":"2014-05-06T13:55:09Z","Liters":0.19,"Soda":"Diet Coke"},{"Date":"2014-05-06T14:27:13Z","Liters":0.01,"Soda":"Diet Coke"},{"Date":"2014-05-06T15:42:21Z","Liters":0.02,"Soda":"Diet Coke"},{"Date":"2014-05-06T16:01:23Z","Liters":0.45,"Soda":"Diet Coke"},{"Date":"2014-05-06T16:05:23Z","Liters":0.52,"Soda":"Diet Coke"},{"Date":"2014-05-06T16:35:27Z","Liters":0.65,"Soda":"Diet Coke"},{"Date":"2014-05-06T16:49:28Z","Liters":0.4,"Soda":"Diet Coke"},{"Date":"2014-05-06T16:50:29Z","Liters":0.14,"Soda":"Diet Coke"},{"Date":"2014-05-05T18:10:18Z","Liters":0.24,"Soda":"Powerade"},{"Date":"2014-05-05T19:03:23Z","Liters":0.01,"Soda":"Powerade"},{"Date":"2014-05-05T19:37:27Z","Liters":0.01,"Soda":"Powerade"},{"Date":"2014-05-05T20:39:34Z","Liters":0.39,"Soda":"Powerade"},{"Date":"2014-05-05T21:04:36Z","Liters":0.01,"Soda":"Powerade"},{"Date":"2014-05-06T10:32:49Z","Liters":0.18,"Soda":"Powerade"},{"Date":"2014-05-06T11:11:53Z","Liters":0.01,"Soda":"Powerade"},{"Date":"2014-05-06T12:54:03Z","Liters":0.01,"Soda":"Powerade"},{"Date":"2014-05-06T14:27:13Z","Liters":0.02,"Soda":"Powerade"},{"Date":"2014-05-06T15:42:21Z","Liters":0.02,"Soda":"Powerade"},{"Date":"2014-05-06T16:01:23Z","Liters":0.03,"Soda":"Powerade"},{"Date":"2014-05-06T16:05:23Z","Liters":0.03,"Soda":"Powerade"},{"Date":"2014-05-06T16:23:25Z","Liters":0.12,"Soda":"Powerade"},{"Date":"2014-05-06T16:50:29Z","Liters":0.01,"Soda":"Powerade"}]
I've category and time axes. The problem is that the bubbles do not show up at the correct y(time) axis. My code looks like below and it also shows too many 0 values although there is no 0 value in dataset.
var series, charts, lastDate = null, sodas = dimple.getUniqueValues(data, "Soda");
charts = [new dimple.chart(svg, null)],
charts.push(myChart);
charts.forEach(function (chart, i) {
var x, y, z;
chart.setBounds(this.attributes.left, this.attributes.top, this.attributes.width - 225, this.attributes.height - 225);
x = chart.addCategoryAxis("x", "Soda");
x.overrideMax = 3;
x.hidden = (i === 0);
y = chart.addTimeAxis("y", "Day-Time", "%Y-%m-%d %H:%M", "%d-%m %H:%M");
// y.overrideMax = "17:00";
y.hidden = (i === 0);
z = chart.addMeasureAxis("z", "Liters");
z.overrideMax = 1;
// Ensure the same colors for every owner in both charts
// differing by opacity
sodas.forEach(function (soda, k) {
chart.assignColor(
soda,
charts[0].defaultColors[k].fill,
"white",
(i === 0 ? 0.3 : 1));
}, this);
}, this);
charts[1].addLegend(850, 100, 60, 300, "Right");
charts[1].setStoryboard("Time of Day", function (d) {
// Use the last date variable to manage the previous tick's data
if (lastDate !== null) {
// Pull the previous data
var lastData = dimple.filterData(data, "Time of Day", lastDate);
// Add a series to the background chart to display old position
var lastSeries = charts[0].addSeries("Soda", dimple.plot.bubble);
// Average suits these measures better
lastSeries.aggregate = dimple.aggregateMethod.avg;
// Give each series its own data at different periods
lastSeries.data = lastData;
// Draw the background chart
charts[0].draw();
// Class all shapes as .historic
lastSeries.shapes.attr("class", "historic");
// Reduce all opacity and remove once opacity drops below 5%
d3.selectAll(".historic").each(function () {
var shape = d3.select(this),
opacity = shape.style("opacity") - 0.02;
// shape.style("opacity", opacity);
if (opacity < 0.1) {
shape.remove();
} else {
shape.style("opacity", opacity);
}
});
}
lastDate = d;
});
series = charts[1].addSeries("Soda", dimple.plot.bubble)
series.aggregate = dimple.aggregateMethod.avg;
// Draw the main chart
charts[1].draw();
Here is the screenshot.

Thanks for adding the fiddles. If you remove the x.hidden = (i === 0); and y.hidden = (i === 0); lines (so that the back chart's axes are displayed) you can see the issue. Unlike measure axes where you can fix the max and min to make sure that the charts are identically proportioned, category axes will only draw for the elements in the data, so a frame with a single category will only draw a single point in the centre of the axis. Also the ordering will change by default.
To get your case working you need to fix ordering for the x axis.
x.addOrderRule(["Coke", "Diet Coke", "Powerade"]);
Define a max and min value for the y axis:
y.overrideMin = d3.time.format("%Y-%m-%d").parse("2014-05-05");
y.overrideMax = d3.time.format("%Y-%m-%d").parse("2014-05-07");
And stick some dummy rows in for any missing categories.
lastData = lastData.concat([
{
"Date": "2014-05-06T00:00:00Z",
"Liters": 0,
"Soda": "Coke"
},
{
"Date": "2014-05-06T00:00:00Z",
"Liters": 0,
"Soda": "Diet Coke"
},
{
"Date": "2014-05-06T00:00:00Z",
"Liters": 0,
"Soda": "Powerade"
}
]);
This results in this output: http://jsfiddle.net/4qBqJ/9/

Related

how do i update a Google Charts pie chart to reflect filtered data but retain filtered out segments as a diff colour

I'm new to Google Charts and I'm struggling to solve this.
I have a datatable (called "result" in the code)
Name Liquidity percent
a 1.3 20%
b 2.0 20%
c 3.4 20%
d 4 20%
e 5 20%
My pie chart is set to show 5 segments of equal size - 20% - and each segment is blue
I have set a 'Number Range Filter' control wrapper to filter the liquidity - when i set the control to the range 1 to 4 the pie moves to 4 equal sized segments.
BUT... I don't want it to do this. Instead of 1 segment disappearing I want the 5 segments to remain visible and the colour of the filtered segment to change to be a different colour.
The aim being that I can see visually a total percentage that falls within the number filter.
EDIT:
So I've had a mess about and this is as far as I've got incorporating dlaliberte's comment below.
function drawChart3(chartData3) {
var result = google.visualization.arrayToDataTable(chartData3,false); // 'false' means that the first row contains labels, not data.
var chart3 = new google.visualization.ChartWrapper({
'chartType': 'PieChart',
'containerId': 'chart3_div',
'dataTable': result,
'options': {
'width': 500,
'height': 500,
'legend': {position: 'none'},
'pieSliceText': 'none',
'colors': ['blue']
},
'view': {'columns': [0 , 1]}
});
var liquidityDT = new google.visualization.DataTable();
// Declare columns
liquidityDT.addColumn('number', 'Liquidity');
// Add data.
liquidityDT.addRows([
[1],
[2],
[3],
[4],
[5],
]);
// Create a range slider, passing some options
var liquidityRangeSlider = new google.visualization.ControlWrapper({
'controlType': 'NumberRangeFilter',
'containerId': 'filter3_div',
'dataTable': liquidityDT,
'options': {
'filterColumnLabel': 'Liquidity',
'minValue': 0,
'maxValue': 5
}
});
liquidityRangeSlider.draw();
chart3.draw();
google.visualization.events.addListener(liquidityRangeSlider, 'statechange', setChartColor);
function setChartColor(){
var state = liquidityRangeSlider.getState();
var stateLowValue = state.lowValue;
var stateHighValue = state.highValue;
for (var i = 0; i < result.getNumberOfRows(); i++) {
var testValue = result.getValue(i,2);
if (testValue < stateLowValue || testValue > stateHighValue){
alert("attempting to set colors")
//this bit I have no clue how to change the color of the table row currently being iterated on
chart3.setOption({'colors' : ['red']});
}
}
chart3.draw();
}
}
so it produces the pie chart with 5 blue segments. I can move the number filter and it fires the listener event but I can't get it to affect anything on the piechart (Chart3) - The code currently attempts just to change the whole chart to RED but that isn't even working never mind the just colouring the filtered segments
So how do I effect the changes into Chart3 and how do I only effect the filtered segments?.
any clues welcome?
thanks
You can (and must, for your application) use a NumberRangeFilter outside of a Dashboard because it will otherwise always do the data filtering. Instead, just listen for the change events on the NumberRangeFilter, get its current state, and then iterate through the colors array for your chart to set the appropriate colors. You'll have to draw the chart again with the updated colors. Here is the loop to set the colors and redraw.
var colors = [];
for (var i = 0; i < result.getNumberOfRows(); i++) {
var color = (testValue < stateLowValue || testValue > stateHighValue) ? 'red' : 'blue';
colors.push(color);
}
chart3.setOption('colors', colors);
chart3.draw();

making a linegraph that shows population decay with dc.js and crossfilter

I am creating a dashboard in DC.js. One of the visualizations is a survival curve showing the percentage of survival on the y-axis and the time in weeks on the x-axis
Each record in the dataset contains a deathAfter column called recidiefNa. This shows the number of weeks after death occurred, and shows -99 for survival.
See sketches for example dataset and desired chart form:
I created this code to create the dimensions and groups and draw the desired chart.
var recDim = cf1.dimension(dc.pluck('recidiefNa'));//sets dimension
var recGroup = recDim.group().reduceCount();
var resDim = cf1.dimension(dc.pluck('residuNa'));
var resGroup = resDim.group().reduceCount();
var scChart = dc.compositeChart("#scStepChart");
scChart
.width(600)
.height(400)
.x(d3.scale.linear().domain([0,52]))
.y(d3.scale.linear().domain([0,100]))
.clipPadding(10)
.brushOn(false)
.xAxisLabel("tijd in weken")
.yAxisLabel("percentage vrij van residu/recidief")
.compose([
dc.lineChart(scChart)
.dimension(recDim)
.group(recGroup)
.interpolate("step-after")
.renderDataPoints(true)
.renderTitle(true)
.keyAccessor(function(d){return d.key;})
.valueAccessor(function(d){return (d.value/cf1.groupAll().reduceCount().value()*100);}),
dc.lineChart(scChart)
.dimension(resDim)
.group(resGroup)
.interpolate("step-after")
.renderDataPoints(true)
.colors(['orange'])
.renderTitle(true)
.keyAccessor(function(d){return d.key;})
.valueAccessor(function(d){return (d.value/cf1.groupAll().reduceCount().value()*100 );})
])
.xAxis().ticks(4);
scChart.render();
This gives the following result:
As you can see my first problem is that I need the line to extend until the y-axis showing x=0weeks and y=100% as the first datapoint.
So that's question number one: is there a way to get that line to look more like my sketch(starting on the y-axis at 100%?
My second and bigger problem is that it is showing the inverse of the percentage I need (eg. 38 instead of 62). This is because of the way the data is structured (which is somehting i rather not change)
First I tried changing the valueaccessor to 100-*calculated number. Which is obviously the normal way to solve this issue. However my result was this:
As you can see now the survival curve is a positive incline which is never possible in a survival curve. This is my second question. Any ideas how to fix this?
Ah, it wasn't clear from the particular example that each data point should be based on the last, but your comment makes that clear. It sounds like what you are looking for is a kind of cumulative sum - in your case, a cumulative subtraction.
There is an entry in the FAQ for this.
Adapting that code to your use case:
function accumulate_subtract_from_100_group(source_group) {
return {
all:function () {
var cumulate = 100;
return source_group.all().map(function(d) {
cumulate -= d.value;
return {key:d.key, value:cumulate};
});
}
};
}
Use it like this:
var decayRecGroup = accumulate_subtract_from_100_group(recGroup)
// ...
dc.lineChart(scChart)
// ...
.group(decayRecGroup)
and similarly for the resGroup
While we're at it, we can concatenate the data to the initial point, to answer your first question:
function accumulate_subtract_from_100_and_prepend_start_point_group(source_group) {
return {
all:function () {
var cumulate = 100;
return [{key: 0, value: cumulate}]
.concat(source_group.all().map(function(d) {
cumulate -= d.value;
return {key:d.key, value:cumulate};
}));
}
};
}
(ridiculous function name for exposition only!)
EDIT: here is #Erik's final adapted answer with the percentage conversion built in, and a couple of performance improvements:
function fakeGrouper(source_group) {
var groupAll = cf1.groupAll().reduceCount();
return {
all:function () {
var cumulate = 100;
var total = groupAll.value();
return [{key: 0, value: cumulate}]
.concat(source_group.all().map(function(d) {
if(d.key > 0) {
cumulate -= (d.value/total*100).toFixed(0);
}
return {key:d.key, value:cumulate};
}));
}
};
}

Dimple Stacked Bar Chart - adding label for aggregate total

I'm trying to add a label to the aggregate total for a stacked bar chart above each bar. I used this example (http://dimplejs.org/advanced_examples_viewer.html?id=advanced_bar_labels) to add the totals for each section of the bar, but I'm not sure how to add the total above. I've also been able to add total labels above each bar for a single series (not stacked). I just can't get it to work with a stacked bar chart.
My current workaround is plotting an additional null series line, but making the line and markers transparent so you can still see the total value in the tooltip. However, I'd really like to just have the totals displayed above each bar.
Here's the code:
var svg = dimple.newSvg("#chartContainer", 590, 400);
var myChart = new dimple.chart(svg, data);
myChart.setBounds(80, 30, 510, 305);
var x = myChart.addCategoryAxis("x", "Month");
x.addOrderRule(Date);
var y = myChart.addMeasureAxis("y", "Calls");
y.showGridlines = true;
y.tickFormat = ',6g';
y.overrideMin = 0;
y.overrideMax = 800000;
var s = myChart.addSeries("Metric", dimple.plot.bar);
s.afterDraw = function (shape, data) {
var s = d3.select(shape),
rect = {
x: parseFloat(s.attr("x")),
y: parseFloat(s.attr("y")),
width: parseFloat(s.attr("width")),
height: parseFloat(s.attr("height"))
};
if (rect.height >= 1) {
svg.append("text")
.attr("x", rect.x + rect.width / 2)
.attr("y", rect.y + rect.height / 2 + 3.5)
.style("text-anchor", "middle")
.style("font-size", "9px")
.style("font-family", "sans-serif")
.style("opacity", 0.8)
.text(d3.format(",.1f")(data.yValue / 1000) + "k");
}
};
myChart.addLegend(60, 10, 510, 20, "right");
myChart.draw();
Here is the JSFiddle: http://jsfiddle.net/timothymartin76/fusaqyhk/16/
I appreciate any assistance on this.
Thanks!
You can add them after drawing by calculating the bar totals and deriving the y position from that:
// Iterate every value on the x axis
x.shapes.selectAll("text").each(function (d) {
// There is a dummy empty string value on the end which we want to ignore
if (d && d.length) {
// Get the total y value
var total = d3.sum(data, function (t) { return (t.Month === d ? t.Calls : 0); });
// Add the text for the label
var label = svg.append("text");
// Set the x position
// x._scale(d) is the tick position of each element
// (myChart._widthPixels() / x._max) / 2 is half of the space allocated to each element
label.attr("x", x._scale(d) + (myChart._widthPixels() / x._max) / 2)
// Vertically center the text on the point
label.attr("dy", "0.35em")
// Style the text - this can be better done with label.attr("class", "my-label-class")
label.style("text-anchor", "middle")
.style("font-size", "9px")
.style("font-family", "sans-serif")
.style("opacity", 0.8)
// Set the text itself in thousands
label.text(d3.format(",.1f")(total / 1000) + "k");
// Once the style and the text is set we can set the y position
// y._scale(total) gives the y position of the total (and therefore the top of the top segment)
// label.node().getBBox().height gives the height of the text to leave a gap above the bar
label.attr("y", y._scale(total) - label.node().getBBox().height)
}
});
Here is your updated fiddle: http://jsfiddle.net/fusaqyhk/17/

Adobe Edge Animate—how do I get the current label?

In Adobe Edge Animate, how do I get the name of the label that corresponds to a given time? I've seen that I can get the current time as an integer using
sym.getPosition()
but if there's a label at that position, how do I get the label as a string?
function getLabel() {
var stage = sym.getComposition().getStage();
var labels = stage.timelines['Default Timeline'].labels;
var currentLabel;
var currentPosition = stage.getPosition();
$.each( labels, function( label, position ){
if (position <= currentPosition) currentLabel = label;
});
return currentLabel;
}
console.log( getLabel() );
this will return the label on (or next previous to) the current position.
For those of us here looking for a Adobe Animate 2019 solution (like I was), it's similar, but slightly different:
function getLabel(_this) {
var currentLabel;
var currentPosition = _this.currentFrame;
_this.labels.forEach(function( label, index ){
if (label.position <= currentPosition) currentLabel = label.label;
});
return currentLabel;
}
Your position on the timeline is easier to get, and the labels object is organized differently. (Also jQuery is unavailable.)

Adding x axis labels when using dojox.charting.DataSeries

I'm creating a Dojo line chart from a dojo.data.ItemFileReadStore using a dojox.charting.DataSeries. I'm using the third parameter (value) of the constructor of DataSeries to specify a method which will generate the points on the chart. e.g.
function formatLineGraphItem(store,item)
{
var o = {
x: graphIndex++,
y: store.getValue(item, "fileSize"),
};
return o;
}
The graphIndex is an integer which is incremented for every fileSize value. This gives me a line chart with the fileSize shown against a numeric count. This works fine.
What I'd like is to be able to specify the x axis label to use instead of the value of graphIndex i.e. the under lying data will still be 1,2,3,4 but the label will show text (in this case the time at which the file size was captured).
I can do this by passing in an array of labels into the x asis when I call chart.addAxis() but this requires me to know the the values before I iterate through the data. e.g.
var dataSeriesConfig = {query: {id: "*"}};
var xAxisLabels = [{text:"2011-11-20",value:1},{text:"2011-11-21",value:2},{text:"2011-11-22",value:3}];
var chart1 = new dojox.charting.Chart("chart1");
chart1.addPlot("default", {type: "Lines", tension: "4"});
chart1.addAxis("x", {labels: xAxisLabels});
chart1.addAxis("y", {vertical: true});
chart1.addSeries("Values", new dojox.charting.DataSeries(dataStore, dataSeriesConfig, formatLineGraphItem));
chart1.render();
The xAxisLabels array can be created by preparsing the dataSeries but it's not a very nice work around.
Does anyone have any ideas how the formatLineGraphItem method could be extended to provide the x axis labels. Or does anyone have any documentation on what values the object o can contain?
Thanks in advance!
This will take a unix timestamp, multiply the value by 1000 (so that it has microseconds for JavaScript, and then pass the value to dojo date to format it).
You shouldn't have any problems editing this to the format you need.
You provided examples that your dates are like "1", "2", "3", which is clearly wrong. Those aren't dates.. so this is the best you can do unless you edit your question.
chart1.addAxis("x",{
labelFunc: function(n){
if(isNaN(dojo.number.parse(n)) || dojo.number.parse(n) % 1 != 0){
return " ";
}
else {
// I am assuming that your timestamp needs to be multiplied by 1000.
var date = new Date(dojo.number.parse(n) * 1000);
return dojo.date.locale.format(date, {
selector: "date",
datePattern: "dd MMMM",
locale: "en"
});
}
},
maxLabelSize: 100
}