d3 line transition - javascript

A line chart displays a value over a given period of time (e.g. the last 2 days), one value per day. Users can change this time horizon (to e.g. the last four days).
Problem: the line transitions are really ugly. I figure the problem is the change in data:
state 1: => state 2:
days value days value
-------------- ---------------
today-2 5 today-4 3
today-1 8 today-3 9
today-2 5
today-1 8
In above case, for example, the former first data point with a value of 5 now transitions to the value of 3 and is shifted left to today-4 on the x-axis. What I would like to have is that 5 and 8 shifted to the right and remained at their values of 5 and 8, while the two new data points enter the stage from 0. Hope you can imagine what I mean.. if not have a look at this image that shows the current state of tragedy ( transition from 1 week => 4 weeks ):
Now, I know that when attaching the new data, a key can be assigned ( e.g. in this example, the key would be the date of the value) and I've got that working for circles (that are hidden in this chart and only visible when you mouse-over a value). These circles transition perfectly. Unfortunatly, I could not get this working for the lines here because of the way I structured my charts, I guess:
each chart is built by an "init_chartX" function that initializes an
"empty" chart (e.g. append a path for line)
each chart is updated by an "update_chartX" function.
So in the init function, I set up the line:
// Add paths for line1 and line2
svg.append("g")
.append("path")
.attr("class", "line1");
And when I try to add the data with key in the update function..
svg.selectAll(".line1").data(data, function (d) { return formatDate(d.date); })
..the result is an exception because "d" is not defined. I assume this is because of the setup of a ".line1" element in the init function, it works just fine when used on circles that are not setup in the init function:
var dots1 = svg.selectAll(".circle1").data(data, function (d) { return formatDate(d.date); });
dots1.enter().insert("circle")
.attr("class", "circle1");
The circles are not setup in the initfunction, they are just added on the fly. For the line on the other hand, I could not figure out how to get this done.
The answer to my question could be a link or some usefull tip..I've seen the Path Transition pages of Mike Bostock already, also the general update pattern tutorials.. maybe I've been blind there. Thanks for any help!

Wow, finally found this. Alright, no key-binding lines. That will make smooth transitions for changing data with different length a bit harder but I'm glad I finally know why I kept getting an error ..
EDIT
I found a solution: reverse the data before attaching it to the lines. So my example from above becomes:
state 1: => state 2:
days value days value
-------------- ---------------
today-1 8 today-1 8
today-2 5 today-2 5
today-3 9
today-4 3
and the transition works as supposed.. So easy, stupid me I didnt figure that out earlier. (the x-axis is not effected by that, it is still growing in time to the right)

Related

chart.js radar scaleShowLine for each scaleLine

I'm working on a radar chart and I would like to only show the last scaleline.
I found this post which could have helped me: Chart.js (Radar Chart) different scaleLineColor for each scaleLine but unfortunately, the answer is not working anymore (the jsfiddle link doesn't display anything).
I read parts of the chart.js documentation about gridLines option, then did some tests/changes on this code: [regular radar chart][2] without any result, would anyone know how to adjust it?
Thanks!
[2]: https://codepen.io/grayghostvisuals/pen/xmBpLenter code here
Here is a solution using the latest version of chart.js (v2.5.0). It uses the scale afterTickToLabelConversion callback property to overwrite the values that were set for the scale ticks (e.g. scale lines).
Since you only want to display the last line, you have to overwrite them by keeping the first tick value (which is never displayed) and only the last tick value (the last line). If you only wanted to display some other line then you would keep the first tick and only the other line that you want displayed.
Here is my implementation.
afterTickToLabelConversion: function(scaleInstance) {
// overwrite the ticks and keep the first (never shown) and last
var oldTicks = scaleInstance.ticks;
scaleInstance.ticks = [oldTicks[0], oldTicks[oldTicks.length - 1]];
// overwrite the numerical representation of the ticks and
// keep the first (never shown) and last
var oldTicksAsNumbers = scaleInstance.ticksAsNumbers;
scaleInstance.ticksAsNumbers = [oldTicksAsNumbers[0], oldTicksAsNumbers[oldTicksAsNumbers.length - 1]];
}
Here is a codepen example that shows an original radar chart and one using the approach described above so that you can see the difference.

dc.js / d3.js - How to hide a bar chart x-axis category value if its bar value equals zero?

I know this question has been asked before, but I have not found an answer that works for me.
I looked into https://github.com/dc-js/dc.js/wiki/FAQ#filter-the-data-before-its-charted and tried the remove_empty_bins function but nothing changed. I also tried the filter_bins function but I am getting the error message "f is not a function".
I am working on a dashboard. I want the bar charts to update such that the x-axis categories are removed from the graph when they are zero and will reappear when they are not. The graphs are updated when selecting/unselecting elements in other graphs.
Basically, I don't want my 20+ categories x-axis to show all of them when only 3 (for example) have values when another graph has an element selected. I would like only those 3 (for example) to be shown. I don't want to update the data in a way that I cannot undo the changes since I want to be able to unselect the element from before and have the graph return to its previous state.
In other words, when a bar chart is updated, it should only show the bars with values greater than 0.
Is there an example somewhere that shows how to do this? I have not been able to find one.
This is the code that is getting the error message:
var ndx = crossfilter(data);
var bar = dc.barChart("#bar");
var bar_dim = ndx.dimension(function(d) {return d.name;});
var bar_group = bar_dim.group().reduceSum(function(d) {return +d.count;});
var bar_Fgroup = filter_bins(bar_group);
bar.width(650).height(310)
.dimension(bar_dim)
.group(bar_Fgroup)
.renderHorizontalGridLines(true)
.gap(2)
.x(d3.scale.ordinal())
.xUnits(dc.units.ordinal)
.elasticY(true)
.yAxisLabel("count")
.margins({top:10,left:60,right:10,bottom:110});
bar.on("renderlet",function(_chart){
_chart.selectAll("g.x text").style("text-anchor","end")
.attr('dx', '-15').attr('dy', '-2').attr('transform', "rotate(-75)");
_chart.selectAll("rect.bar").on("click", _chart.onClick);
});
I guess you were probably missing elasticX:
.elasticX(true)
which is not entirely obvious, except if you think of it forcing an update of the X domain each time the filters update.
I've added an example here:
http://dc-js.github.io/dc.js/examples/filtering-removing.html

second d3.js triggered from first doesn't fully iterate over X axis

Folks -
I'm now trying to trigger a second chart based on which series in the first chart is clicked on.
Based on which is chosen, one of two data sets are sent to the function:
.on("mouseup", function(d) {return d.myCat == 0 ? updateData(yesXYZData) : updateData(nonXYZData)})
This part works, but I'm getting one big stack in the target div, not the iteration I am expecting.
function updateData(whichDataSet) {...
I've tried putting the updateData() function into the window.onload function, duping or reusing various elements (since the domain and range for the X axis are the same, I expect to reuse).
[Note- I have taken Lars Kothoff's advice regarding numbers in the data object. Also, I will create a better data structure later, using crossfilter.js and native d3.js data manipulation- for now I need a working prototype demonstrating functionality.]
here is the gist:
https://gist.github.com/RCL1/6906892
Thanks in advance!
-RL
line 242 of the gist. I needed to use a non-filtered version of the data to calculate x axis (totalAll.map).

Nvd3 multibarcharts - display gaps in the data on the xAxis

I just started using nvd3 a short while ago and am now facing a big problem for me with multibar charts:
My xAxis data has gaps in between, e.g. [1,2,3,4,9,24,120].
I want these gaps to be displayed in the graph, but nvd3 just displays all bars next to each other, so, that the distance between the bars with the x value 2 and 3 is the same as between those with 9 and 24.
Is there any way to change this, so that you can see all the gaps in the data?
The code I used is just the same as nvd3s example code.
Thank you very much.
Sure there is. You will fill in 0 for all the missing values. For each y that will correspond to a missing value you will set x = 0. That's all you need to do (it's not as simple as it sounds since there can be cases with series which have no data and so on, but this is the main trick).

Interactive area chart using Protovis

This is quite a daunting project to a Protovis newcomer, but maybe you could help me split it into digestible chunks?
What I would like to build is an "interactive Area chart", as sketched here:
First of all, it's the data ...
I have data for provinces in Excel:
Province Year 10 100 1000 10000
A 1970 2 4 6 3
A 1971 3 6 8 5
B 1970 6 9 12 6
B 1971 4 8 11 8
.... ... . . . .
For each province and year, I would like to be able to draw an area chart:
vis.add(pv.Area)
.data(data.ProvinceA[1970])
.bottom(1)
.interpolate("basis")
.left(function(d) x(d.x))
.height(function(d) y(d.y))
.fillStyle("rgb(21,173,210)")
.anchor("top").add(pv.Line)
.lineWidth(3);
Then I would like to add 2 types of interactivity:
Selection of Province
Time slider
Together, the selection checkboxes and the time slider determine which areas are visible at any given time.
If, for example, Province A is selected and the year is 1984, only that area is displayed. If the time slider is now dragged, the corresponding years are now displayed for Province A. If another Province is checked, the areas are overlayed and both areas are redrawn when the time slider moves.
Protovis questions:
How do I format the data (province, year, x, y) for this application?
How do I achieve the binding of checkboxes to area?
How do I implement the time slider? Within Protovis or like an external component with listeners that trigger re-rendering of the graph?
Formatting data: The first step is to get it into JSON, using some external tool (I really like Google Refine for this, though it's a pretty big tool if this is all you need it for - try Mr. Data Converter for a quick and dirty option). These tools will probably give you data as a JSON object, like this:
`[{"Province":"A", "Year":"1970", "10":2, "100":4, "1000":6, "10000":3}, ...]`
Once you have the data available as JSON, you'll want to get it into shape for your vis. You're going to want to pass each pv.Area an array of values - from your description it looks like you want the [10, 100, 1000, 10000] values. Protovis has a lot of tools for manipulating data - see the pv.Nest operator. There are lots of ways you might approach this - I might do this:
data = pv.nest(data)
.key(function(x) {return x.Province})
.key(function(x) {return x.Year})
.rollup(function(v) {
return [v[0]['10'], v[0]['100'], v[0]['1000'], v[0]['10000']];
});
which gives you an object like:
{
A: {
1970: [2,4,6,3]
// ...
},
// ...
}
This sets you up for the interface elements. Keep the array of checked Provinces and the current year in global variables:
var currentProvinces = ['A', 'B', ...];
var currentYear = 1970;
and set up your area to reference those variables:
// a containing panel to help with layout and data
var panel = vis.add(pv.Panel)
.data(function() currentProvinces); // making this a function allows it to
// be re-evaluated later
// the area itself
var area = panel.add(pv.Area)
.data(function(province) data[province][currentYear]);
// plus more area settings as needed
Now use some other library - I'm partial to jQuery, with jQuery UI for the slider - to create your interface elements. The onchange function for each element just needs to set the corresponding global variable and call vis.render() (assuming your root panel is called vis). This should be pretty simple - see here for a Protovis example using jQuery UI to make a time slider very similar to what you have in mind.
I think that you are trying to make that pair of charts:
http://mbostock.github.io/protovis/ex/zoom.html

Categories

Resources