My data happens to be in the form:
data = { "year" : 2001,
"category 1": 1234,
"category 2": 2345,
"category 3": 3456,
...
}
And so I'm going to be making a graphic with these lines of code:
svg.selectAll("left.coord")
.data(data)
.enter()
.append("circle")
.attr("r", 3)
.attr("cy", function(d, i){
return d[i]; //or whatever
})
.attr("cx", 0)
Basically, I need the function inside cy to return 1234, then 2345, then 3456, etc. I know how to do this if my data was an array of objects, but what do I do in this case? The code doesn't throw any errors, but it doesn't do anything either.
Related
This is JSON file named Unit2Vec_tSNE.json and we can get the data points from pos element.
{
"No 0": {
"dur": 135,
"name": "00000001_0",
"lab": "sil",
"pos": [
17.64800262451172,
-1.794445514678955
]
},
"No 1": {
"dur": 28,
"name": "00000001_1",
"lab": "uo",
"pos": [
-17.94196891784668,
-0.8764857649803162
]
},
"No 2": {
"dur": 21,
"name": "00000001_2",
"lab": "x",
"pos": [
2.7473323345184326,
13.970715522766113
]
}
}
The JavaScript code is the following and I try use .data(dataset) function to bind the JSON data to points.
But very strangely, it displays nothing and the console.log('Here!!!!!!!!!') of .attr("cx", function(d) doesn't run.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>D3: A simple scatterplot, setting radii from data</title>
<script type="text/javascript" src="../d3.js"></script>
<style type="text/css">
/* No style rules here yet */
</style>
</head>
<body>
<script type="text/javascript">
//Width and height
var w = 500;
var h = 100;
var dataset; // a global
d3.json("Unit2Vec_tSNE.json", function(error, json) {
if (error) return console.warn(error);
dataset = json;
visualize();
});
function visualize() {
//Create SVG element
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
console.log(dataset); //work at here
svg.selectAll("circle")
.data(dataset)
.enter()
.append("circle")
.attr("cx", function(d) {
console.log('Here!!!!!!!!!'); //not work at here
return d.pos[0];
})
.attr("cy", function(d) {
return d.pos[1];
})
.attr("r", 1);
}
</script>
</body>
</html>
The points can't be seen and console.log('Here!!!!!!!!!'); doesn't run.
Why? How to fix it? Thanks.
I am a new man to D3.js. Because I want to use it do an interactive project for my AI experiment, so display this points (in real application, there are 450000 points) is needed.
It is because dataset is an object and not array.
Quoting the d3 API :
selection.data([data[, key]]) Joins the specified array of data with the selected elements
So, if you change the structure of your JSON accordingly, you will see that your console.log is correctly executed.
You will have to tweak your code though to make it compatible in order to display the circles.
Demo with a correct format for the dataset variable:
var dataset = [
{"No 0": {
"dur": 135,
"name": "00000001_0",
"lab": "sil",
"pos": [
17.64800262451172,
-1.794445514678955
]
}},
{"No 1": {
"dur": 28,
"name": "00000001_1",
"lab": "uo",
"pos": [
-17.94196891784668,
-0.8764857649803162
]
}},
{"No 2": {
"dur": 21,
"name": "00000001_2",
"lab": "x",
"pos": [
2.7473323345184326,
13.970715522766113
]
}}
];
//Width and height
var w = 500;
var h = 100;
visualize();
function visualize()
{
//Create SVG element
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
console.log(dataset); //work at here
svg.selectAll("circle")
.data(dataset)
.enter()
.append("circle")
.attr("cx", function(d) {
console.log('Here!!!!!!!!!'); //work at here now too
// return d.pos[0]; // edit this according to the new structure
})
.attr("cy", function(d) {
// return d.pos[1]; // edit this according to the new structure
})
.attr("r", 1);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
I have a data array that looks like this:
var data = [
{
"key": "user",
"values": [
"roles",
"manager",
"otherRoles",
"reports",
"devices",
"coffees"
]
},
{
"key": "role",
"values": [
"assignments",
"members",
"otherMembers"
]
},
{
"key": "assignment",
"values": [
"roles"
]
},
{
"key": "Device",
"values": [
"owners",
"roles"
]
},
{
"key": "Coffee",
"values": [
"drinkers"
]
}
];
I am attempting to render tables from SVG rectangles where the table header is the "key" and the table rows are the "values". The only way I was able to make this work was to give a unique incremental class to each table (table0, table1 etc.) I know this is terrible and prevents me from easily accessing all tables in the future. Here is the pertinent code:
parentBox = svgContainer.selectAll('.table')
.data(data, function(d) {return d.key;})
.enter()
.append('g')
.attr('class', function(d, i) {
return "table" + i;
})
.attr("transform", function(d, i) {
return "translate(" + boxWidth*i + "," + rowHeight*i + ")";
});
I'd like to figure out the right way to access nested data in D3. Here is the code in its entirety: D3 Example
Turns out the solution just requires understanding selections better. I first created the parentBox container:
parentBox = svgContainer.selectAll('.table')
.data(data, function(d) {return d.key;})
.enter()
.append('g')
.attr('class', 'table') (etc.)
Then, when populating the table with rows I first selected the created tables, used the each method to loop through each table and create each row. The one trick was to use d3.select(this) to make sure the rows were created properly.
svgContainer.selectAll('.table')
.each(function (d, i) {
tableRows = d3.select(this).selectAll('.row')
.data(d.values)
.enter()
.append(g) (etc.)
Say I have an Array of these objects:
[
{name: "P1", Subj1: 9.7, Subj2: 10, Subj3: 9.5, Subj4: 8.2, Subj5:9.3, Subj6: 8.9},
{name: "P2", Subj1: 9.7, Subj2: 10, Subj3: 9.5, Subj4: 8.2, Subj5:9.3, Subj6: 8.9},
{name: "P3", Subj1: 9.7, Subj2: 10, Subj3: 9.5, Subj4: 8.2, Subj5:9.3, Subj6: 8.9} ]
I would like to plot a bar chart for one person. I currently have this code:
d3.select("#chart").selectAll("div.h-bar")
.data(getObj(name))
.enter()
.append("div")
.attr("class", "h-bar")
.append("span");
//delete elements not associated with data element
d3.select("#chart").selectAll("div.h-bar")
.data(getObj(name))
.exit().remove();
d3.select("#chart").selectAll("div.h-bar")
.data(getObj(name))
.attr("class", "h-bar")
.style("width", function (d) {
return ( d.Subj1* 10) + "px";
}
)
.select("span")
.text(function (d) {
return d.name;
});
with the getObj(name) function:
function getObj(Name){
for(var i = 0; i <studentList.length; i++){
if(studentList[i].Naam == Name){
console.log(studentList[i]);
return [studentList[i]];
}
}
}
I cant figure out how to get a bar chart for person 1 where every bar has a width of achieved note, and the span text the name of the subject.
Any hints?
I'm very new to doing anything with d3 and jSon. Here is a pice of data I'm trying to get out from json and I would just like to know if I'm even on the right path.
Basically each status group would have more servers inside than just one like at the moment and the idea would be to get rectangle graph for one server and list these nicely next to each other.
I've been reading a lot of tutorials and trying to browse for similiar kind of issues other people might've had, but so far had really no luck...
jSon data I'm trying to pull out
[
{
"status": "ok",
"servers":
[
{
"id": "VR01",
"servername": "Server_1",
"cpu": 45, "mem": 25,
"diskIO": 0, "bandwith": 200
}
]
},
{
"status": "attention",
"servers":
[
{
"id": "VR10",
"servername": "Server_10",
"cpu": 55, "mem": 35,
"diskIO": 1, "bandwith": 2000
}
]
},
{
"status": "warning",
"servers":
[
{
"id": "VR02",
"servername": "Server_02",
"cpu": 98, "mem": 85,
"diskIO": 1,
"bandwith": 2000
}
]
},
{
"status": "dead",
"servers":
[
{
"id": "VR20",
"servername": "Server_20",
"cpu": 0, "mem": 0,
"diskIO": 0,
"bandwith": 0
}
]
}
]
the D3 bit
<script>
var width = ("width", 1000);
var height = ("height", 800);
d3.json("mydata.json", function(data) {
var canvas = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
var status = function sortData(data){
for (i = 0; i < d.length; i++) {
if(d.status ==="ok")
canvas.selectAll("rect")
.data(d.server)
.enter()
.append("rect")
.attr("x", 25)
.attr("y", function(d, i){return 25 * i;})
.attr("fill", "purple")
}
}
})
</script>
Really appreciate any suggestions you might have!
I think that it would be better to use nested selections to create your dashboard.
// Create one group for each server group
var serverGroup = svg.selectAll('g')
.data(data)
.enter()
.append('g')
.attr('transform', function(d, i) { return 'translate(0, ' + 50 * i + ')');
// Create the inner elements for each group
var servers = serverGroup.selectAll('rect')
.data(function(d) { return d.servers; })
.enter()
.append('rect')
// ... more settings here ...
This will create three groups, one for each group of servers and translate each one vertically. Each group contains the group data, so we can use the group data to create elements inside each group. Also, you can add a title, background color and other settings for each group using this structure. This article contains the concepts that you need to work on your problem: How Selections Work. Regards,
tl;dr
Can I create elements using data that looks like:
[{"id": 1, ...}, {"id: 2, ...}, ..]
and update attributes of the elements using data that looks like:
[2, 4, 7]
(where the elements of the array are a subset of the ids of the initial data set).
Long version
I have data that looks like this:
[
{
"id": 1,
"latitude": 38.314552,
"longitude": -88.9025347755102,
"name": "JEFFERSON COUNTY JAIL ",
"official_name": "Jefferson County Justice Center",
"url": "https://www.ice.gov/detention-facilities/facilities/jeffeil.htm"
},
{
"id": 2,
"latitude": 41.875702,
"longitude": -87.63072,
"name": "CHICAGO HOLD ROOM ",
"official_name": "Chicago Hold Room",
"url": ""
},
{
"id": 3,
"latitude": 43.407029,
"longitude": -88.704349,
"name": "DODGE COUNTY JAIL, JUNEAU ",
"official_name": "Dodge Detention Facility",
"url": "https://www.ice.gov/detention-facilities/facilities/dodgewi.htm"
},
...
]
I put it on an SVG map like this:
var projection = d3.geo.albersUsa()
.scale(scale)
.translate([width / 2, height / 2]);
var facilityCircles = svg.append("svg:g")
.attr("id", "facilities");
d3.json("data/facilities.json", function(facilities) {
var positions = [];
var positionsByFacilityId = {};
var pointSize = 5;
facilities.forEach(function(facility) {
var loc = [facility.longitude, facility.latitude];
var pos = projection(loc);
positions.push(pos);
positionsByFacilityId[facility.id] = pos;
});
facilityCircles.selectAll("circle")
.data(facilities, function(d) { return d.id; })
.enter().append("svg:circle")
.attr("data-facility-id", function(d, i) { return d.id; })
.attr("cx", function(d, i) { return positionsByFacilityId[d.id][0]; })
.attr("cy", function(d, i) { return positionsByFacilityId[d.id][1]; })
.attr("r", function(d, i) { return pointSize; });
}
However, later on, I want to update attributes of the circles based on another data object, which would just be an array of ids representing a subset of the id properties from the initial data, e.g. [2, 4, 5]
Can I do something like this to update the attributes of only selected elements mapped to data objects with the given ids?
facilitiesSubset = [2, 4, 5];
facilityCircles.selectAll("circle")
.data(facilitiesSubset)
.attr("fill", "green");
or, should I just extract the ids from the initial data and use those in the call to data() that is used to create the elements?
To make this a little easier, I'd suggest changing the naming convention a little bit:
var facilityCircleContainer = svg.append("svg:g")
.attr("id", "facilities");
Since the svg:g object isn't actually the circles, it is just holding them. So then:
var facilityCircles = facilityCircleContainer.selectAll("circle")
.data(facilities, function(d) { return d.id; })
.enter().append("svg:circle")
.attr("data-facility-id", function(d, i) { return d.id; })
.ect(...)
facilityCircles now refers to the circles with their attached data now. Updating the fill based on the array is pretty simple:
facilityCircles.attr("fill", function(d){
facilitiesSubset.indexOf(d.id) != -1 ? "green" : "red"});