Creating a dropdown/select list from csv with d3 nest - javascript

I am looking to create a dropdown/select list from a .csv file. Is it possible to create a dropdown/select list from d3 nest? I'm working on a force directed graph that is being populated from a .csv file. I would like for the user to be able to choose from the dropdown list which node it wants to highlight. Previously I used a text search based and it worked, but I am actually looking for a dropdown list instead of text based search. Thank you in advance!
You can refer to this link
var nodesmap = d3.nest()
.key(function (d) { return d.name; })
.rollup(function (d) { return { "name": d[0].name, "group": d[0].group, "size": d[0].size }; })
.map(graph.nodes);
var output = document.createElement('block_container');
var select = d3.select("#searchName").append("select");
list.selectAll("option")
.data(nodesmap)
.enter()
.append("option")
.attr("value", function(d) {return d.key;})
.text(function(d) {
return d.key; });
return output;
I used the les_mis.csv
P.s: I am not even sure if I had setup the jsfiddle appropriately. Excuse my noobness.

First, I think you typed list when the variable name is select. Second, your nodesmap object does not have properties of .key.
This will work though:
var select = d3.select("#searchName")
.append("select")
.on('change', searchNode); //<-- fire your search function on change
select.selectAll("option")
.data(graph.nodes) //<-- use graph.nodes
.enter()
.append("option")
.attr("value", function(d) {return d.name;}) //<-- it has a name property
.text(function(d) {
return d.name;
});
function searchNode() {
//find the node
var selectedVal = this.options[this.selectedIndex].value; //<-- get value from dropdown
...
Full working code here.

Related

How to update data in a diverging stacked bar-chart d3js

I've made a plunker that updates data from one csv file to another, the yaxis updates accordingly but the rectangles don't.
The .attr("height", function(d) { return Math.abs(y(d[0])) - y(d[1]); }); portion of the code still has the old data from the previous file (I'm guessing).
I'm guessing this is because I haven't declared .data(series) in the updateData() function, I remember doing something like this in another chart
g.selectAll(".bar").data(series).transition()
etc...
but this doesn't work in this chart.
I can't figure it out, any help is appreciated!
The problem was that you didn't join the new data to existing bars.
To make this work well, you will want to specify a key for category of data when you join the series to the g elements to ensure consistency (although I notice that category-1 is positive in the first dataset, and negative in the second, but this is test data i guess)
Here's the updated plunkr (https://plnkr.co/edit/EoEvVWiTji7y5V3SQTKJ?p=info), with the relevant code highlighted below:
g.append("g")
.selectAll("g")
.data(series, function(d){ return d.key }) //add function to assign a key
.enter().append("g")
.attr("class", "bars") //so its easy to select later on
//etc
...
function updateData() {
d3.csv("data2.csv", type, function(error, data) {
///etc
let bars = d3.selectAll(".bars") //select the g elements
bars.data(series, function(d){ return d.key }) //join the new data
.selectAll(".bar")
.data(function(d) { return d; })
.transition()
.duration(750)
.attr("y", function(d) { return y(d[1]); })
.attr("height", function(d) { return Math.abs(y(d[0])) - y(d[1]); });

How to use d3 filter and update function to toggle between data selections

I'm trying to get to grips with d3 by creating a bar chart of house prices based on whether the property address ends with 'Street', 'Road', 'Way' etc.
However, I'd also like the view of the data to change based on a column of data for local neighbourhoods.
It's the second query on this topic. Here's the previous query - How to extract nominal labels for d3 chart
You can see the structure of the data extracted through Pandas' to_json function here: http://plnkr.co/edit/He3yPUfuE8k7hvIkupjS?p=preview
I've used a nest function to key the data on the the local areas, but can't work out how to plug in a d3.filter method to restrict the data to a selected area.
I've got a function which creates a select button based on the keys:
var options = dropDown.selectAll("option")
.data(nested_data)
.enter()
.append("option");
options.text(function (d) { return d.key; })
.attr("value", function (d) { return d.key; });
But what I can't work out is how to plug the value from this selection into the plotting part of the d3 script.
d3.select("svg")
.selectAll("circle")
.data(nested_data)
.enter()
.append("circle")
d3.selectAll("circle")
.attr("cx", function(d) {
console.log(d["street_name"]);
return street_scale(d["street_name"]);
})
.attr("cy", function(d) {
return price_scale(d["value"]);
})
.attr("r", 5)
.attr("fill", "steelblue");
And while I know I need an update function to continue to change the chart as users select between, I've not found an example that I can adapt.
Thank you in advance for your patience - I'm very new to d3 and a Javascript noob.
Think about the data structure you want in the end. Since d3 likes arrays of objects and you want to filter by district, I'm picturing this:
var data = {
district1: [
{
street_split: 'a',
value: 1
},{
street_split: 'b',
value: 2
}
],
district2: [
{
street_split: 'a',
value: 1
},{
street_split: 'b',
value: 2
}
],
etc...
Your filter then simply becomes:
data[someDistrict]
So, how do we get your data in this format. I'd do everything in one loop, the data, the extents, the labels, etc...:
var orgData = {}, // our final data like above
street_labels = d3.set(), // set of streets
districts = d3.set(), // set of districts
minVal = 1e99, // min of values
maxVal = -1e99; //max of values
data.forEach(function(d){
d.value = +d.value; // convert to numeric
street_labels.add(d.street_split); // set of street_labels
districts.add(d.district); // set of districts
if (d.value < minVal) minVal = d.value; // new min?
if (d.value > maxVal) maxVal = d.value; //new max?
if (!orgData[d.district]){ // we want a associate array with keys of districts
orgData[d.district] = []; // and values that are arrays of object
}
orgData[d.district].push({ // those objects are street_split and value
street_split: d.street_split,
value: d.value
});
});
Now how do we update on a different select? That simply becomes:
dropDown.on("change", function() {
d3.selectAll("circle")
.data(orgData[this.value]) // new data
.attr("cx", function(d) { // update attributes
return street_scale(d.street_split);
})
.attr("cy", function(d) {
return price_scale(d.value);
});
});
Here's my working code.

Want to show unique values only using d3.js not working

I have large dataset with the same detector having multiple occurrence. There are also many detectors. I want to fill a combobox with the unique detectors only.
I am using the following code:
d3.select("#detectors")
.selectAll("option")
.data(d3.map(data, function(d) {
return d.code;
})
.keys())
.enter()
.append("option")
.text(function(d) {
return d;
}).attr("value", function(d) {
return d;
});
But its not showing unique detector codes. Rather the combobox is being filled with number from 1 to ongoing.
How can I do my desired goal. My sample simple dataset is
var data=[{"code":5222,"date":3-4-2015},{"code":5222,"date":3-6-2015},{"code":5222,"date":3-7-2015},];
The data has in real a large number of detectors with unique code. I want to show these unique codes in the combobox. For the above case there will be only one 5222 in the options
I think you need to use nest().
var nest = d3.nest()
.key(function(d) { return d.code; })
.entries(data);
That will create a new array with each code only once and an object of all the objects that have that value.
Edit
var data=[{"code":5222,"date":3-4-2015},{"code":5222,"date":3-6-2015},{"code":5222,"date":3-7-2015}];
var nest = d3.nest()
.key(function(d) { return d.code;})
.entries(data);
d3.select("#detectors").selectAll("option")
.data(nest)
.enter()
.append("option")
.text(function(d){ return d.key;})
.attr("value",function(d){return d.key;});
This code works for me with no errors
You could solve this problem using d3.set() method: https://github.com/d3/d3-collection/blob/master/README.md#sets.
The code will be:
var data=[{"code":5222,"date":3-4-2015},{"code":5222,"date":3-6-2015},{"code":5222,"date":3-7-2015},];
var Unique = d3.set(data, function(d) {return d.code});
Unique returns only '5222', as you expected.
Please, note that d3 converts data to strings in this case, so it might be the case you would need to convert them back to numbers.

Obtaining data bound to dom elements

I am currently learning data visualizations with d3.js. I am using the tutorial on the d3.js site. I am at the part where the data bound to DOM elements have to retrieved. I did it exactly as they have shown, but I am not able to get the data from it.
Here is the code from the beginning:
var theData=[1,2,3]
var p= d3.select("body")
.selectAll("p")
.data(theData)
.enter()
.append("p")
.text("hello")
This displays:
hello
hello
hello
Now, on the site, it tells me to enter the following code to obtain the data bound i.e. 1,2 & 3.
var theData=[1,2,3]
var p= d3.select("body")
.selectAll("p")
.data(theData)
.enter()
.append("p")
.text(function (d) { return d; } )
Even after doing this, the page does not change,like it should to:
1
2
3
It remains the same(it keeps showing the three hello's).
How do I get the data back?
when just using this :
var theData2 = [ 1, 2, 3 ]
var p2 = d3.select("body").selectAll("p")
.data(theData2)
.enter()
.append("p")
.text( function (d) {
console.log(d);
return d; } );
It works, you're using variables with the same names which you really shouldn't do
http://jsfiddle.net/cwqwbw3u/1/
When I try your code, it works (however, I am a all for pretty javascript, so I added ';' when approriate).
I would suggest checking out the console on the browsers developers tools (perhaps there are showing errors already) and change your code to something like this:
var theData=[1,2,3];
var p= d3.select("body")
.selectAll("p")
.data(theData)
.enter()
.append("p")
.text(function (d) {
console.log("checking d");
console.log(d);
return d;
} );
it would be really weird if you did not see at least "checking d" in your console...
When our dataset contains more items than available DOM elements, the surplus data items are stored in a sub set of this selection called the enter selection. When you try to use enter for the second time, there are already 3 elements and so no items are stored in enter selection. You should have used the same enter selection for both tasks.
var theData=[1,2,3]
var p = d3.select("body")
.selectAll("p")
.data(theData);
p.enter()
.append("p")
.text("hello");
p.enter()
.append("p")
.text(function (d) { return d; } );
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

Conditionally build group in d3

I am trying to build a graph using the force layout in D3. I would like to build different looking nodes depending on the data. Currently all nodes have a category and a name. So I draw an svg:g consisting of two rect and two text elements.
My code currently looks something like this:
// nodes are in the form: { group: 'string', name: 'string2' }
this.node = this.node.data(this.node, function(d) { return d.id; });
var g = this.node.enter().
append('svg:g').
attr('transform', function(d) { return 'translate('+ d.x +','+ d.y +')'; });
g.append('svg:rect').attr('h', 20).attr('w', 100);
g.append('svg:rect').attr('y', 20).attr('h', 20).attr('w', 100);
g.append('svg:text').text(function(d) { d.group; });
g.append('svg:text').attr('y', 20).text(function(d) { d.name; });
If the node doesn't have a name, however, I'd like to supress the creation of the second rect and text. Logically, if it wasn't for the implicit iterator in d3 I'd be doing something like:
var g = this.node.enter().
append('svg:g').
attr('transform', function(d) { return 'translate('+ d.x +','+ d.y +')'; });
g.append('svg:rect').attr('h', 20).attr('w', 100);
g.append('svg:text').text(function(d) { d.group; });
// unfortunately 'd' isn't defined out here.
// EDIT: based on comment from the answer below; the conditional should
// be for the text and the related rectangle.
if(d.name) {
g.append('svg:rect').attr('y', 20).attr('h', 20).attr('w', 100);
g.append('svg:text').attr('y', 20).text(function(d) { d.name; });
}
You could use an each call on your g selection to decide whether or not to add the label.
g.each(function(d) {
if (d.name){
var thisGroup = d3.select(this);
thisGroup.append("text")
.text(d.group);
thisGroup.append("text")
.attr("y", 20)
.text(d.name);
});
However, be aware that this structure could get confusing if you're going to be updating the data.
If you want to be able to update neatly, I would recommend doing a nested selection:
var labels = g.selectAll("text")
.data(function(d){ d.name? [d.group, d.name]:[]; });
labels.enter().append("text");
labels.exit().remove();
labels.text(function(d){return d;})
.attr("y", function(d,i){return i*20;});
The data-join function tests the parent's data object, and based on it either passes an array containing the two values you want to use for the label text, or an empty array. If it passes the empty array, then no labels are created; otherwise, each label has it's text set by the value in the array and it's vertical position set by the index.

Categories

Resources