I am trying to show two data sets: one with a lower opacity and the other with normal dots using D3.js. I tried this:
svg.selectAll("*").remove();
if (olddset!=dset) {
svg.selectAll("circle") .data(datasets[olddset]) .enter() .append("circle")
.attr('cx',function(a){ return xscales[whichscale][xval](a[xval]); })
.attr('cy',function(a){ return yscales[whichscale][yval](a[yval]); })
.attr('r',1)
.style("opacity", 0.2)
;
}
svg.selectAll("circle") .data(datasets[dset]) .enter() .append("circle")
.attr('cx',function(a){ return xscales[whichscale][xval](a[xval]); })
.attr('cy',function(a){ return yscales[whichscale][yval](a[yval]); })
.attr('r',3)
.style("opacity", 1)
;
However, that does not do what I was looking for. I wanted olddset to be small dots and the new dataset (dset) with r=3 and opacity=1. What am I doing wrong?
You are using the same selection for the first AND second dataset. Thus for the second dataset you are selecting <circle>s that already exist. Instead, use a different selection, for example:
svg.selectAll("circle2")
You can handle this with styles. Like:
svg.selectAll("circle.oldset").data(datasets[olddset]).enter().append("circle")
.attr('cx',function(a){ return xscales[whichscale][xval](a[xval]); })
.attr('cy',function(a){ return yscales[whichscale][yval](a[yval]); })
.attr('r',1)
.classed("oldset", true); // where the oldset class in yr styles has the opacity defined
svg.selectAll("circle.dset") .data(datasets[dset]) .enter() .append("circle")
.attr('cx',function(a){ return xscales[whichscale][xval](a[xval]); })
.attr('cy',function(a){ return yscales[whichscale][yval](a[yval]); })
.attr('r',3)
.classed("dset", true);
You will run into trouble with the "circle2" trick if you try to update because it will never select anything.
Related
I'm having a mental block about using the result of:
.rollup(function(leaves) { return leaves.length;})
as the radius of a circle in a scatter plot. My complete code (and sample data) is in a plunk here https://plnkr.co/edit/Cwuce6inLV5jouCWTFfN
The scatter works with a static value of 5 but I'd like to use value based on the .rollup from the d3.nest as explained in this other SO question I had: Capturing leaves.length value from d3.nest
I think I'm missing a key concept about in this section of code:
d3.tsv("etds_small.tsv", function(error, dataset) {
dataset.forEach(function(d) {
if(deptlist.indexOf(d.dept) == -1) deptlist.push(d.dept);
if(years.indexOf(d.year) == -1) years.push(d.year);
})
var deptYearCount = d3.nest()
//.key(function(d) { return d.college;} )
.key(function(d) { return d.dept})
.key(function(d) { return d.year })
.rollup(function(leaves) { return leaves.length;})
.map(dataset);
console.log(dataset); // retains the college, dept, and year labels
console.log(deptYearCount); // replaces labels with "key"
x.domain(years.sort(d3.ascending));
y.domain(deptlist.sort(d3.ascending));
//console.log(y.domain());
//console.log(x.domain());
svg.selectAll(".dot")
.data(dataset) //should this be deptYearCount from the d3.nest above?
.enter().append("circle")
.attr("class", "dot")
.attr("r", 5) // Would like to use length (from d3.nest) as radius
//.attr("r", function(d) {return d.values.length*1.5;}) works with .data(debtYearCount)
.style("opacity", 0.3)
.style("fill", "#e31a1c" )
.attr("cx", function(d) {
return x(d.year);
})
.attr("cy", function(d) {
return y(d.dept);
});
Give this a try for your radius:
.attr("r", function(d) {return Object.keys(deptYearCount[d.dept]).length*1.5;})
Because you are using .map(dataset) instead of .entries(dataset), d3.next() is returning one-big-object instead of an array of objects. That one-big-object does not contain a property called values.
Updated explanation:
First, look at the structure of the object deptYearCount. It has property names like Earth Sciences., Education., etc.
Our d3 data is iterating over an array of objects. Each object has property dept that looks like Earth Sciences., Education., etc.
So, deptYearCount[d.dept] is getting us to the correct property within deptYearCount.
For example, at one round of our iteration we are looking at deptYearCount["Education."]. That turns out to be another object with properties like 2007,2008, etc. Therefore, the number of properties in deptYearCount["Education."] is the value we want for the radius.
How do we find the number of properties of deptYearCount["Education."]? One way is the Object.keys(someObject) function. It returns an array of strings corresonding to the property names, and we just need its .length.
I am getting data from backend, where some of the values are 0. I don't want to create the circle for those values.
I understand, I need to use a filter, but I don't have any idea how to use that.
Here is my code:
var circles = city.selectAll("circle")
.data(function(d) {
return d.values //this is a array length 20 how to filter that? i need only with values more than 0
})
.enter()
.append("circle")
.attr("r", 3.5)
.attr("cx", function(d, i) {
return x(d.date)
})
.attr("cy", function(d) {
return y(d.temperature)
})
.style('cursor', 'pointer')
.attr("fill", "transparent")
.attr("stroke", "yellow")
.attr("stroke-width", 0);
If d.values is an array you can use Array.prototype.filter() to extract all values different from 0 from it. The following will bind only non-zero values and create circles only for these:
.data(function(d) {
return d.values.filter(function(v) { return v != 0; });
})
Firstly, I'm assuming that you meant something more like .data(dataFromBackend, function (d) { return d.values; }), otherwise there's no bind!
Anyway, to do what you're describing, and making the assumption that you're not bothered about having the 0-values in the bind at all, you can simply do:
filteredData = backendData.filter(function (d) {
return d.value != 0;
});
There's a detailed reference for .filter here, but briefly, the function passed to .filter is called for every element in backendData, with each element referred to by d in the usual way. The return value should be boolean (true or false), but will work with anything, using 'truthiness'. Any element the function returns something 'truthy' for will be included in the final array (filteredData in my example), any element the function returned something 'falsey' will not.
I have data that can have a variable numbers of series. And inside each of those series is a date and number that I want to plot as a scatter plot in D3js.
This is my (non working) code. It works when I do it straight, but not once I add the $.each loop. I'm pretty sure its some sort of problem with indexing or something like that.
var color = d3.scale.category20();
// Now actually add the data to the graph
$.each(mpgData, function(k, v) {
console.log(v);
//console.log(k);
svg.selectAll('circle')
.data(v)
.enter()
.append('circle')
.attr('cx', function(d, i) {
console.log(i);
//console.log(d);
return xScale(getDate(d[1]));
})
.attr('cy', function(dd, ii) {
//console.log(ii);
return yScale(dd[2]);
})
.attr('fill', function(d, i) {
return color(k);
})
.attr("class", "mpgColorClass"+k)
.attr("r", 5)
.on("mouseover", function() {
d3.selectAll(".mpgColorClass"+k)
.attr("r", 8);
})
.on("mouseout", function() {
d3.selectAll(".mpgColorClass"+k)
.attr("r", 5);
});
});
I only showed what I think is the relevant part.
So that code kind of works. But it only shows 6 things, which I think is because the 2nd 'series' has 6 items. So somehow its not looping over everything at the "attr('cx', function(d, i)) part. I think I'm not understanding how to get that function to loop over each part of the series.
I'm new to D3js, so still struggling through the learning curve. But it works and I get a graph out with the correct data. Its just not ALL the data, only 6 points out of the entire (variable) dataset.
Thanks!
in your $.each() block you are overwriting the same set of circles in the SVG element. So instead of using selectAll('circle') you can do this:
$.each(mpgData, function(k, v) {
svg.selectAll('circle' + k)
.data(v)
.enter()
.append('circle')
.attr('class','circle' + k)
});
truncated rest of details in your code... edit at will.
So I'm using code based off of this..
http://bl.ocks.org/mbostock/3884955
Essentially, what I'm trying to do is at every data point I want to add a circle. Any help would be much appreciated seeing that I have no idea where to start.
This is my code so far: It worked when I was using single lines.
var circlegroup = focus.append("g")
circlegroup.attr("clip-path", "url(#clip)")
circlegroup.selectAll('.dot')
.data(data)
.enter().append("circle")
.attr('class', 'dot')
.attr("cx",function(d){ return x(d.date);})
.attr("cy", function(d){ return y(d.price);})
.attr("r", function(d){ return 4;})
.on('mouseover', function(d){ d3.select(this).attr('r', 8)})
.on('mouseout', function(d){ d3.select(this).attr('r', 4)});
You need nested selections for this. Assuming that data is a two-dimensional array, you would do something like this.
var groups = svg.selectAll("g").data(data).enter().append("g");
groups.data(function(d) { return d; })
.enter()
.append("circle")
// set attributes
I built a grid of squares based on the example given in http://bl.ocks.org/bunkat/2605010. Now i am trying to color code each of the cells in the grid based on the data from csv file. Say for example, i have a csv file with data as
cell, col1
1,2
2,3
3,2
4,1
cells are colored based on data in col1. Like cell 1 colored with blue, cell 2 colored with green, cell 3 colored again with blue, cell 4 colored with red.
I have been trying something like this, but it doesn't work. Please help?
d3.text("frame.csv", function(datasetText) {
var parsedCSV = d3.csv.parseRows(datasetText);
var col = row.selectAll(".cell")
.data(function (d) { return d; })
.enter().append("svg:rect")
.attr("class", "cell")
.attr("x", function(d) { return d.x; })
.attr("y", function(d) { return d.y; })
.attr("width", function(d) { return d.width; })
.attr("height", function(d) { return d.height; })
.style("fill", function(d) { return color(parsedCSV[d].col1); })
.style("fill", '#FFF')
.style("stroke", '#555');
});
There is a 'fill' attribute that you can use to set the fill color of the item. Like other d3 functions, you can pass it either a value, or a function which returns the value. It sounds like you would want to use a function which will determine what colour you want, then return that value. Note that you will have to return it as a string (so wrapped in quotes) as either '#660066' or 'rgb(166,0,166)'. You could also use rgba if you wish.
After this it is just a matter of writing your function to return the right colour, which I can't really help you with as I don't know what you want.
Also this may be a duplicate of d3js fill color.
I found my mistake. I had to use d3.csv.parse(string) instead of d3.csv.parseRows(string[, accessor]) function since my csv file contained column names. Thanks for the help. Appreciate it.