d3 static Cubism chart - can't get data input right - d3.js

I'm trying to make a static cubism chart like this http://bl.ocks.org/bae25/10797393
The csv file ("cubism_test.csv") looks something like this:
date,one,two,three,four,five
2018-06-01,132.54,18.44,68.36,0,56.63
2018-06-02,146.64,19.18,71.74,0,59.66
2018-06-03,160.77,117.98,75.15,0,62.71
2018-06-04,193.29,171.53,78.59,0,65.76
2018-06-05,275.92,78.64,82.05,0,68.82
<script>
// create context and horizon
var context = cubism.context()
.size(30)
.stop();
var horizon = context.horizon()
.extent([0,2]);
d3.csv("cubism_test.csv", function(data)
{
var format = d3.time.format("%Y-%m-%d");
data.forEach(function(d, i)
{
d.date = format.parse(d.date);
d.one= +d.one;
d.two= +d.two;
d.three= +d.three;
d.four= +d.four;
d.five= +d.five;
})
console.log(data);
// define metric accessor
context.metric(function(start,stop,step,callback)
{
var values = data;
console.log(values);
callback(null, values);
}, name);
d3.select("#graph").selectAll(".horizon")
.data(data)
.enter()
.append("div")
.attr("class", "horizon")
.call(horizon);
// set rule
d3.select("#body").append("div")
.attr("class", "rule")
.call(context.rule());
// set focus
context.on("focus", function(i) {
d3.selectAll(".value")
.style( "right", i == null ? null : context.size() - i + "px");
});
// set axis
var axis = context.axis()
d3.select("#graph").append("div").attr("class", "axis").append("g").call(axis);
});
</script>
Obviously this isn't working, but I don't know to fix it. I can't find a proper recourse on how to work with d3 data. The ones I've found are very basic and tell you how to use data to make simple circles, but not time series.
I don't know how to tell d3 to use the column headers as names or get it to use the values in the columns as the values for each cubism/horizon chart.
Your advice would be highly appreciated.

Related

How can I use 2 range sliders at the same time?

I want to filter data in the table based on the age and height at the same time using 2 range sliders.
I have implemented 2 range sliders (Age and Height) using d3.slider.js and a dc.dataTable. I want to use these 2 range sliders at the same time, but it seems that they are not working properly.
Also, under the table, there is the text "49 selected out of 49 records". The numbers are not changing while using the sliders.
Code:
var dataTable = dc.dataTable("table#list");
var dispatch = d3.dispatch('load','filter');
d3.json('data.json',function(json){
dispatch.load(json)
});
dispatch.on('load',function(json) {
var formatNumber = d3.format( ",d");
var facts = crossfilter(json);
var dimensionAge = facts.dimension(function(d) {
return +d.age;
});
var accessorAge = function(d) {
return d.age;
};
var dimensionHeight = facts.dimension(function(d) {
return +d.height;
});
var accessorHeight = function(d) {
return d.height;
};
var range = d3.extent(json, accessorAge);
var range2 = d3.extent(json, accessorHeight);
var all = facts.groupAll();
d3.select("div#slider3")
.call(d3.slider().axis(true).min(range[0]).max(range[1]).value(range)
.on("slide", function(evt,value) {
dispatch.filter(value);
d3.select("#slider3textmin").text(Math.floor(value[0]));
d3.select("#slider3textmax").text(Math.floor(value[1]))
}))
d3.select("div#slider4")
.call(d3.slider().axis(true).min(range2[0]).max(range2[1]).value(range2)
.on("slide", function(evt,value) {
dispatch.filter(value);
d3.select("#slider4textmin").text(Math.floor(value[0]));
d3.select("#slider4textmax").text(Math.floor(value[1]))
}))
FieldNames = [
"",
"Age",
"Weight",
"Height",
"Eye Color",
"Hair Color",
"Race",
"Sex",
"Annual Income"
];
d3.select("tr#FieldNames").selectAll("th")
.data(FieldNames)
.enter()
.append("th")
.append("text")
.text(function(d){
return d;
});
dataTable
.dimension(dimensionAge)
.group(function(d) {
return d.sex;
})
.columns([
function(d) {return "";},
function(d) {return d.age;},
function(d) {return d.weight;},
function(d) {return d.height;},
function(d) {return d.eyeColor;},
function(d) {return d.hairColor;},
function(d) {return d.race;},
function(d) {return d.sex;},
function(d) {return formatNumber(d.annualIncome);}
]);
dispatch.on('filter',function(value){
dataTable.replaceFilter(dc.filters.RangedFilter(value[0], value[1]));
dataTable.redraw();
})
dc.dataCount(".dc-data-count")
.dimension(facts)
.group(all);
dc.renderAll();
});
Link to the website
Plunker
Original response on the dc.js users group.
Nice use of d3.slider.js - I haven't seen that used with dc.js before.
At a quick glance, I see two problems here. First, you're using one
dispatch for both sliders, so both sliders are filtering the age,
since that's the dimension of the table. You'd probably want to create
another dimension for filtering by height, and you don't really need
to attach that to a chart.
Second, instead of just redrawing the chart with dataTable.redraw(),
you probably want to call dataTable.redrawGroup() so that all charts
in its chart group get redrawn, including the dataCount.
Specifically:
you'll need two filter events in your dispatch
var dispatch = d3.dispatch('load','filterAge','filterHeight');
the age slider will call filterAge
dispatch.filterAge(value);
and the height slider will call filterHeight
dispatch.filterHeight(value);
the current filter event handler will now handle filterAge and it will call redrawGroup
dispatch.on('filterAge',function(value){
dataTable.replaceFilter(dc.filters.RangedFilter(value[0], value[1]));
dataTable.redrawGroup();
})
we add another filterHeight handler which directly filters dimensionHeight and also redraws the chart group
dispatch.on('filterHeight',function(value){
dimensionHeight.filter([value[0], value[1]]);
dataTable.redrawGroup();
})
Reset All will also have to clear dimensionHeight. (Since this dimension isn't used by any chart, dc.filterAll() won't find it.)
Reset All
Fork of your plunker.
this for reset all, the 49 selected out of 49 records already change correcly
replace this
Reset All
to this
Reset All
add this after dispatch on load
dispatch.on('load',function(json) {
//your code
})
function sololo(){
//table
dispatch.filterAge([0,100]);
dispatch.filterHeight([0,100]);
//text slider
d3.select("#slider4textmin").text(0)
d3.select("#slider4textmax").text(0)
d3.select("#slider3textmin").text(0);
d3.select("#slider3textmax").text(0)
//slider
d3.select('#slider3').select('#handle-one').style('left','0%')
d3.select('#slider3').select('#handle-two') .style('right','0%')
d3.select('#slider3').select('div').style('left','0%').style('right','0%')
d3.select('#slider4').select('#handle-one').style('left','0%')
d3.select('#slider4').select('#handle-two') .style('right','0%')
d3.select('#slider4').select('div').style('left','0%').style('right','0%')
}

how to draw a d3 bubble chart for a local geojson file

I want to plot a d3 bubble chart. By taking the example from d3
This link
i tried to get the bubble chart for my local file i.e myfile.geojson. The code which i tried is in the plunker link. I want to plot a bubble chart based on the value "Profit". Tried everything in the google and youtube but i didnt get the solution to my problem.
Plunker link
I am new to d3. If i do any mistakes in the code please suggest me to make them correct. Thanks In advance.
Your data is way different from flare.json so copying the recurse code will not make your data. Your dataset is very simple it does not need a recursion to flatten the dataset.
function classes(root) {
var classes = [];
function recurse(profit, node) {
if (node.profit) node.profit.forEach(function(child) { recurse(node.profit, child); });
else classes.push({packageName: type, className: node.profit, value: node.profit});
}
recurse(null, root);
return {features: classes};
}
This should have been:
function classes(root) {
var classes = root.features.map(function(f, i) {
//here i is the index
return {
value: f.properties.profit,
className: "Hello" + i,////just giving some dummy values
packageName: i//just giving some dummy values
}
});
return classes;
}
Now pass this data to the bubble layout like this:
var node = svg.selectAll(".node")
.data(bubble.nodes({
children: classes(root)
}).filter(function(d) {
return !d.children;
}))
.enter().append("g")
.attr("class", "node")
.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
EDIT
The bubble chart is based on the profit value:
The radius of the circle depends on the value you give here inside the classes function.
return {
value: f.properties.profit,
className: "Hello" + i,////just giving some dummy values
packageName: i//just giving some dummy values
}
Thus more the value or f.properties.profit the bigger will be the radius.
The radius will be relative to the diameter you setting here:
var bubble = d3.layout.pack()
.sort(null)
.size([diameter, diameter])
Read Domain range in d3 https://www.dashingd3js.com/d3js-scales
In place of className and packageName what should i give to get the bubble chart based on the profit value.
This i don't know what to answer I think your concept is not clear so is the naive question.
If you see the code packageName defines the color
.style("fill", function(d) {
return color(d.packageName);
});
and className defines the text to be displayed in the bubble
.text(function(d) {
return d.className;
});
Kindly see the code in fiddle its very simple to understand.
Working code here.

D3 Transitionning data with sequences sunburst

Introducing
I'm using Sequences Sunburst of d3.js for visualization of data.
I want to add a transition between two datasets (triggered by a button). I would like each arc to animate to display the new data.
Something like this: (1)Sunburst_Exemple, but without changing the accessor.
Research
In the (1)Sunburst_Example, the value accessor is modified. But I want to change the data, not the function that defines how to reach the data.
So, I searched a way for redefining data into path.
I was inspired by (2)Sunburst_Exemple, using each() method to store old values and attrTween() for transitions. But nothing is changing and I have the following error message:
Maximum call stack size exceeded . Maybe caused by the fact I have a method and I'm not in a global scope.
(2) link : _http://ninjapixel.io/StackOverflow/doughnutTransition.html
Then I have tried (3)Zoomable_Sunburst example, but nothing it happens in my case... .
(3) link : _http://bl.ocks.org/mbostock/4348373
My Example
Here is my example : JSFIDDLE
Problem is :
colors are lost
transition is not happening
I think I don't understand how transitioning is really working, I could have missed something that could help me in this case.
Any help ?
-
Listener of button call click method that redefined nodes and path.
/*CHANGING DATA*/
function click(d){
d3.select("#container").selectAll("path").remove();
var nodes = partition.nodes(d)
.filter(function(d) {
return (d.dx > 0.005); // 0.005 radians = 0.29 degrees
}) ;
var path = vis.data([d]).selectAll("path")
.data(nodes)
.enter().append("svg:path")
.attr("display", function(d) { return d.depth ? null : "none"; })
.attr("d", arc)
.attr("fill-rule", "evenodd")
.style("fill", function(d) { return colors[d.name]; })
.style("opacity", 1)
.on("mouseover", mouseover)
.each(stash)
.transition()
.duration(750)
.attrTween("d", arcTween);
;
// Get total size of the tree = value of root node from partition.
totalSize = path.node().__data__.value;
}
_
// Stash the old values for transition.
function stash(d) {
d.x0 = d.x;
d.dx0 = d.dx;
}
// Interpolate the arcs in data space.
function arcTween(a) {
var i = d3.interpolate({x: a.x0, dx: a.dx0}, a);
return function(t) {
var b = i(t);
a.x0 = b.x;
a.dx0 = b.dx;
return arc(b);
};
}
Data Characteristics :
the root node is the same for the two datasets.
the structure is globally the same, only the values are changing.
Some fields disappear but we can add them with value equal to 0.

how to transition a multiseries line chart to a new dataset

I could really use some guidance setting up a transition on my multiseries line chart. As an example of what I need, I've started with this great multiseries line chart: http://bl.ocks.org/mbostock/3884955. To that code, I've added an update() function that's called once using setInterval(). I've also created a new data set called data2.csv which is similar to data.tsv but has different values.
The update function should change the data that the line chart is displaying. Forget about making a nice smooth transition, I can't even get the data to update in the chart view. When I try using the update function, it looks like the new data is loaded properly into the javascript variables, but the lines on the chart don't change at all.
I've seen variations on this question asked a few times but haven't found an answer yet. Can anyone help me figure out how to transition this multi-series line chart to a new dataset (also multiseries)?
function update() {
d3.csv("data2.csv", function(error, data) {
color.domain(d3.keys(data[0]).filter(function(key) { return key !== "date"; }));
// format the date
data.forEach(function(d) {
d.date = parseDate(d.date);
});
// rearrange the data, same as in the original example code
var cities2 = color.domain().map(function(name) {
return {
name: name,
values: data.map(function(d) {
return {date: d.date, temperature: +d[name]};
})
};
});
// update the .city g's to the new dataset
var city2 = svg.selectAll(".city")
.data(cities2);
// redraw the lines with the new data???
city2.selectAll("path")
.attr("d", function(d) { return line(d.values); });
clearInterval(transitionInterval);
});
}
UPDATE: NikhilS's answer contains the key to the solution in the comment trail.
You should make sure you are following the enter + update process as outlined by Mike Bostock in his stuff on the General Update Pattern. It looks like you haven't invoked any kind of d3 transition. You also haven't specified an enter or exit for the update function, which will cause problems if you have new data coming in and/or old data going out. Try changing this:
var city2 = svg.selectAll(".city")
.data(cities2);
city2.selectAll("path")
.attr("d", function(d) { return line(d.values); });
to the following:
var city2 = svg.selectAll('.city')
.data(cities2);
var cityGroups = city2.enter().append('g')
.attr('class', 'city');
cityGroups.append('path')
.attr('class', 'line');
d3.transition().selectAll('.line')
.attr('d', function(d) { return line(d.values); });
city2.exit().remove();
I made a basic data re-join and update demo a while back, which you can view here.
use d3 Transition, you can make some sort of animation.
If you want to select a sub-interval of the data to plot the graph, no need manipulation on the data, just use a d3 brush and clip the graph
For a multi-series line graph with most of the line graph elements, you could refer to this example: http://mpf.vis.ywng.cloudbees.net/

NVD3 line chart with realtime data

I have a really simple line chart written using NVD3.js. I've written a simple redraw based on timer, pulled from examples I've seen, but I get the error
Uncaught TypeError: Cannot read property 'y' of undefined
The JS is
var data = [{
"key": "Long",
"values": getData()
}];
var chart;
nv.addGraph(function () {
chart = nv.models.cumulativeLineChart()
.x(function (d) { return d[0] })
.y(function (d) { return d[1] / 100 })
.color(d3.scale.category10().range());
chart.xAxis
.tickFormat(function (d) {
return d3.time.format('%x')(new Date(d))
});
chart.yAxis
.tickFormat(d3.format(',.1%'));
d3.select('#chart svg')
.datum(data)
.transition().duration(500)
.call(chart);
nv.utils.windowResize(chart.update);
return chart;
});
function redraw() {
d3.select('#chart svg')
.datum(data)
.transition().duration(500)
.call(chart);
}
function getData() {
var arr = [];
var theDate = new Date(2012, 01, 01, 0, 0, 0, 0);
for (var x = 0; x < 30; x++) {
arr.push([theDate.getTime(), Math.random() * 10]);
theDate.setDate(theDate.getDate() + 1);
}
return arr;
}
setInterval(function () {
var long = data[0].values;
var next = new Date(long[long.length - 1][0]);
next.setMonth(next.getMonth() + 1)
long.shift();
long.push([next.getTime(), Math.random() * 100]);
redraw();
}, 1500);
Second Answer (after comment)
I looked at source for cumulativeLineChart. You can see the display.y property get created during chart creation. It relies on a private method: "indexify". If some derivative of that method was made public, then perhaps you could do something like chart.reindexify() before redrawing.
As a temporary workaround, you could recreate the chart from scratch on every update. If you remove the transition, that seems to work okay. Example jsfiddle: http://jsfiddle.net/kaliatech/PGyKF/.
First Answer
I believe there is bug in cumulativeLineChart. It appears that the cumulativeLineChart adds a "display.y" property dynamically to data values in the series. However, it does not regenerate this property when new values are added to the series for a redraw. I don't know of anyway to make it do this, although I'm new to NVD3.
Do you really need a CumulativeLineChart, or would a normal line chart be sufficient? If so, I had to make the following changes to your code:
Change from cumulativeLineChart to lineChart
Change from using 2 dimension arrays of data, to using objects of data (with x,y properties)
(I'm not familiar enough with NVD3 to say what data formats is expects. The 2D array obviously works for initial loads, but I think it fails to work for subsequent redraws. This is likely related to the same issue you are having with cumulativeLineChart. I thought changing to objects would fix cumulativeLineChart as well, but it didn't seem to.)
I also changed the following, although not as important:
Modified your getData function to create a new instance of Date to avoid unexpected consequences of sharing a reference as the date gets incremented.
Modified the update interval function to generate new data in increments of days (not months) with y values in the same range as the getData function.
Here's a working jsfiddle with those changes:
http://jsfiddle.net/kaliatech/4TMMD/
I found what I think is a better solution. The problem occurs because the cumulative chart sets the y function during processing. Whenever your want to refresh the chart, first set it back to a default which returns the correct original y. In your redraw function do this before updating:
chart.y(function (d) { return d.y; });
Even better would be if the cumulative chart could do this for itself (store the original access function before setting the new one, and put it back before re-indexing). If I get a chance, I'll try to push a fix.
I ran into the same issue. I changed the y() function on the lines from
.y(function(d) { return d.display.y })
to
.y(function(d) { return d.display ? d.display.y : d.y })
This gets rid of the error. Obviously it won't be displaying the (non-existent) indexed value in the error case, but in my experience, the chart gets updated again with display defined, and it looks correct.

Resources