Unable to set the right d3.extent accessor - javascript

I am having problems setting my Y scale dynamically in this example. Would love if someone could help!
I am able to get the X axis setup perfectly, but I can't find how to refer to my Y data to define the extent of my axis. Right now I get the scale to go from -2 to 500, the part of the code that's not working is the commented out line.
Thanks for your time!
Plunk here:
https://plnkr.co/edit/eQ4HgxQC49CZIPVBXjQX
data.forEach(function(d) {
d["year"] = parseDate(d["year"])
});
var subset = data.filter(function(el) {
return el.metric === "rank"
});
var concentrations = productCategories.map(function(category) {
return {
category: category,
datapoints: subset.map(function(d) {
return {
date: d["year"],
concentration: +d[category]
}
})
}
})
xScale.domain(d3.extent(subset, function(d) {
return d["year"];
}));
yScale.domain([-2, 500]);
//yScale.domain(d3.extent(subset, function(d) {return d["rank"] })); //

Actually, i was finally able to solve that in a clean way:
yScale.domain([
d3.min(concentrations, function(c) { return d3.min(c.datapoints,function(v) { return v.concentration; }); }),
d3.max(concentrations, function(c) { return d3.max(c.datapoints, function(v) { return v.concentration; }); })
]);
But now I have another problem, since I want the Y scale to display the extremes of only one selected array. That means I have to filter while I parse the data. Not really sure how to do it, will probably open another question. Thank you so much #gerardofurtado for your time and patience!

Ok, I'll provide a temporary solution, because I'm sure there must be a shorter and cleaver way to do it.
First, let's extract all values that are not "year" from your data:
var values = productCategories.map(function(category){
return subset.map(function(d){
return +d[category]
})
});
Then, let's merge this array of arrays:
var merged = [].concat.apply([], values);
And, finally:
yScale.domain(d3.extent(merged));

Related

Picking quartile value on each point

I'm plotting sentiment value of tweet over last 10 years.
The csv file has the three columns like below.
I plotted each value by date successfully.
However, when I tried to generate an area graph,
I encountered a problem which is,
each date has multiple values.
That's because each data point is from one single tweets that
one x point ended up with having multiple y values.
So I tried to pick quartile value of each date or pick largest or least y value.
For clarity, please see the example below.
January 8 has multiple y values (textblob)
I want to draw area graph by picking the largest value or 2nd quartile value of each point.
How do I only pick the point?
I would like to feed the point in the following code as a
x/y coordinate for line or area greaph.
function* vlinedrawing(data){
for(let i;i<data.length;i++){
if( i%500==0) yield svg.node();
let px = margin+xscale(data[i].date)
let py = height-margin-yscale(data[i].vader)
paths.append('path')
.attr('x',px)
.attr('y',py)
}
yield svg.node()
}
The entire code is in the following link.
https://jsfiddle.net/soonk/uh5djax4/2/
Thank you in advance.
( The reason why it is a generator is that I'm going to visualize the graph in animated way)
For getting the 2nd quartile you can use d3.quantile like this:
d3.quantile(dataArray, 0.5);
Of course, since the 2nd quartile is the median, you can also just use:
d3.median(dataArray);
But d3.quantile is a bit more versatile, you can just change the p value for any quartile you want.
Using your data, without parsing the dates (so we can use a Set for unique values`), here is a possible solution:
const aggregatedData = [...new Set(data.map(function(d) {
return d.date
}))].map(function(d) {
return {
date: parser(d),
textblob: d3.quantile(data.filter(function(e) {
return e.date === d
}).map(function(e) {
return e.textblob
}), 0.5)
}
});
This is just a quick answer for showing you the way: that's not a optimised code, because there are several loops within loops. You can try to optimise it.
Here is the demo:
var parser = d3.timeParse("%m/%d/%y");
d3.csv('https://raw.githubusercontent.com/jotnajoa/Javascript/master/tweetdata.csv', row).then(function(data) {
const aggregatedData = [...new Set(data.map(function(d) {
return d.date
}))].map(function(d) {
return {
date: parser(d),
textblob: d3.quantile(data.filter(function(e) {
return e.date === d
}).map(function(e) {
return e.textblob
}), 0.5)
}
});
console.log(aggregatedData)
});
function row(d) {
d.vader = +d.vader;
d.textblob = +d.textblob;
return d;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

How to guarantee that a brush in a scatterplot made based in dimension with 3 keys interact with other charts?

How can I guarantee the multi-chart filter behavior by using three keys in the scatterplots dimensions?
as you can see in [ https://jsfiddle.net/rogeraleite/dmf3fstw/2/ ]
..specific in the lines where I declare the dimensions for the scatterplots:
dim1 = ndx.dimension(function (d) {
return [+d.x, +d.y, d.fruit];
//return [+d.x, +d.y];
})
If I comment " return [+d.x, +d.y, d.fruit]; " and use " return [+d.x, +d.y]; ", the interactions (brushing) works perfectly. However, once I try to add the third key (d.fruit) in order to color the dots in the chart, the brushing stop working.
Any idea of how to deal with it?
I just realize that to keep the interaction working I had to use only two keys in the dimension creation. However, to create the group I could use another dimension with more than two keys and it would guarantee the right behavior.
See in https://jsfiddle.net/rogeraleite/L8mjrnr1/ :
dim1 = ndx.dimension(function (d) {
return [+d.x, +d.y];
}),
dim1_2 = ndx.dimension(function (d) {
return [+d.x, +d.y, d.fruit];
}),
dim2 = ndx.dimension(function (d) {
return [+d.y, +d.z];
}),
dim2_2 = ndx.dimension(function (d) {
return [+d.y, +d.z, d.fruit];
}),
group1 = dim1_2.group(),
group2 = dim2_2.group();
...so when the colorAccessor try to access the third key (d.key[2]), it works! =D
.colorAccessor(function(d) { return d.key[2]; })

I'm having trouble with formatting when using tooltips (or labels?) in D3.js

I’m totally noob-tastic (i.e. I have very little programming background and am trying to hack together a dashboard using borrowed code) but I am eager to learn more!
I’m currently hung up with what I hope is a small issue.
I’m using a version of Bostock’s calendar example...
calendar
...and have managed to get close to my desired outcome but I’m having trouble with the tooltip(?), specifically the second .attr under rect.filter below.
d3.csv("BugeseraData.csv", function(error, csv) {
csv.forEach(function(d) {
d.Daily_total = parseInt(d.Daily_total);
});
var Daily_total_Max = d3.max(csv, function(d) { return d.Daily_total; });
var data = d3.nest()
.key(function(d) { return d.Date1; })
.rollup(function(d) { return Math.sqrt(d[0].Daily_total / Daily_total_Max); })
.map(csv);
rect.filter(function(d) { return d in data; })
.attr("fill", function(d) { return color(data[d]); })
.attr("data-title", function(d) { return 'RwF ' +d });
$("rect").tooltip({container: 'body', html: true, placement:'top'});
});
The data has been imported from a .csv and has two “columns” (I'm using excel), titled 'Date1' and 'Daily_total' respectively.
When you mouse over a particular day I want to to return “RwF” + the value from the second column (the amount or Daily_total).
like this
Currently, the “RwF” shows up but when I use d.Daily_total (the second column) the result is returning as “RwF undefined”. If I use “RwF” + d then I get “RwF” with the date (the value from the first column).
Would someone please help me understand the correct way to achieve this?
This is data from local government revenue collection and I want to be able to show collection amounts by day at a glance. I want the actual collection amounts (not the date) to show upon mouseover.
Thank you!
The variable "data" contains your csv data in the form of JSON data. Take a look here how it works.
http://bl.ocks.org/phoebebright/raw/3176159
Try
'RwF ' + data[d]
instead of
'RwF ' + d

Specify LinkDistance and LinkStrength for Curved Links in D3

I cannot get the linkDistance and linkStrength based on link value functions to work for curved links, how and where am I supposed to specify these parameters?
I've made a JSFiddle based on the Curved Links example in which the code
.linkDistance(function(d) { return d.value; })
is written in two different places (and currently commented out because it doesn't work in either place). My best guess is that it belongs in the lower location, after force.links(links) is specified. I thought maybe that wasn't working because d is referring to the bilinks instead of the links, so I changed it to
.linkDistance(function(d) { return d[3]; })
in which d[3] is the link.value stored in the bilinks and that also doesn't work. Both versions return NaN errors.
I expected implementing this refinement to be easy and straightforward, so maybe it's just a simple and obvious thing I'm missing. But nothing I've tried and nothing I've found online has helped me make any progress for a few hours so hopefully somebody out there knows what's going on and how to fix it.
For both linkDistance and linkStrength function you will get the source and target node so depending on that, you can return the linkDistance value in this case i am returning weight of the target node:
var force = d3.layout.force()
.linkDistance(function(d) { return d.target.weight; })
.linkStrength(function(d) { console.log(d);return 2; })
In your case you doing
.linkDistance(function(d) { return d.value; })
//this is going to return undefined, as there is nothing like that
Working code here
EDIT
Since you need the link value in the link object add that value when you create the links like shown below:
var nodes = graph.nodes.slice(),
links = [],
bilinks = [];
graph.links.forEach(function (link) {
var s = nodes[link.source],
t = nodes[link.target],
i = {}, // intermediate node
linkValue = link.value // for transfering value from the links to the bilinks
;
nodes.push(i);
links.push({
source: s,
target: i,
linkValue: linkValue //add the link value
}, {
source: i,
target: t,
linkValue: linkValue//add the link value
});
bilinks.push([s, i, t, linkValue]);
});
so now in the linkdistance/linkStrength function you can get the value
var force = d3.layout.force()
.linkDistance(function (d) {
return d.linkValue;
})
.linkStrength(function (d) {
console.log(d.linkValue);
return d.linkValue;
})
Working code here
Hope this helps!

updating a line graph in d3 is not working

i am trying to update a line graph and it is not throwing any error but it is also not updating the graph.
i am deleting a point and adding a new one with an incremented rate and incremented created_at date by a second(trying to follow http://bl.ocks.org/benjchristensen/1148374)
function redrawWithoutAnimation() {
for (var i in chart_data) {
linedata = chart_data[i];
//delete first element of array
linedata.points.reverse().shift();
//create a new point
rate = linedata.points[0].rate + 1;
created_at = linedata.points[0].created_at + 6000;
new_point = {};
new_point.rate = rate;
new_point.created_at = created_at;
linedata.points.push(new_point);
console.log(linedata);
}
// static update without animation
svg.selectAll("path")
.data([linedata.points]); // set the new data
line(linedata.points); // apply the new data values
}
redrawWithoutAnimation();
setInterval(function () {
redrawWithoutAnimation();
}, 8000);
here is my code
http://jsfiddle.net/yr2Nw/8/
Working fiddle: http://jsfiddle.net/reblace/GsaGb/1
There's a few issues here...
First, you were updating all the chart_data in the for loop, but outside the loop, you were only trying to update the line still stored in the linedata variable after loop execution. You should try to avoid having variables with greater scope than they need. It can lead to bugs like this one:
svg.selectAll("path").data([linedata.points]);
line(linedata.points);
You should instead use D3's data joining to rejoin the new data to all the paths at once declaratively like so:
linesGroup.selectAll("path")
.data(chart_data)
.attr("d", function(d){ return line(d.points); });
What that code's doing is it's selecting the paths and then joining each of them to the chart_data elements and then binding the appropriate line generator to the "d" attribute for the appropriate path.
Then, you need to update your x axis and y axis otherwise the plot will just shoot off the drawn area. This code is updating the domains and then rebinding the axes to the dom elements so they redraw:
xAxis.scale().domain([
d3.min(chart_data, function (c) { return d3.min(c.points, function (v) { return v.created_at; }); }),
d3.max(chart_data, function (c) { return d3.max(c.points, function (v) { return v.created_at; }); })
]);
yAxis.scale().domain([
0,
d3.max(chart_data, function (c) { return d3.max(c.points, function (v) { return v.rate; }); })
]);
svg.select(".x.axis").call(xAxis);
svg.select(".y.axis").call(yAxis);
There were a few other bugs I fixed them in the Fiddle. For example, you need to calculate the time for the new point based on the last element in the array, not the first, otherwise the line can't interpolate properly since its no longer a continuous function... and this is a bit more concise way to do your line updates:
for (var i=0; i<chart_data.length; i++) {
linedata = chart_data[i];
//delete first element of array
var removedPoint = linedata.points.shift();
//create a new point
var lastpoint = linedata.points[linedata.points.length-1];
var new_point = {
rate: removedPoint.rate,
created_at: lastpoint.created_at + 6000
};
linedata.points.push(new_point);
}
Also note that you shouldn't use the for(var in) loop for Arrays, that's for iterating over the properties in an object.
There's still some issues, but I think this should help get you over the hurdle you were stuck on. Anyways, it looks cool in action!
Fine fenac.. You facing so many problems since your data is not in good format for your requirements..
as per http://bl.ocks.org/benjchristensen/1148374 The x-axis data must be (data[] (data array))
Your data is something like this
[objects,object,object] where each object holds one element of xaxis value.. so the pushing and shifting is not possible..
try to change the format of the data (linedata.points) to an array (data[]) and try it out sure it works..
You just need to put all the values in linedata.points into an array data[] and use this data[] to animate your line..
Since yours the multiline.. you need to create 2D array and must pass them accordingly...
Cheers..
I updated your jsfiddle
setInterval(function () {
console.log(linedata.points);
var v = linedata.points.shift(); // remove the first element of the array
linedata.points.push(v); // add a new element to the array (we're just taking the number we just shifted off the front and appending to the end)
redrawWithoutAnimation();
}, 3000);
http://jsfiddle.net/yr2Nw/9/
But still it wont works till you do that work...
Personal Suggestion: First Try with single line graph then go with looping for multiline...

Categories