I want to visualize data count in d3. I have a dataset similar to this:
[
{
"name": "Team blue",
"color": "#0433ff"
"count": 9
},
{
"name": "Team red",
"color": "#ff2600"
"count": 12
}
]
and I want to visualize it like this: http://i.imgur.com/xjFeNYd.png
I understand the basics of data and enter() but I do not know which is the best way to create the red or blue boxes based on the count value.
Any help will be appreciated.
You can d3.range(number) to generate a range of numbers from 1 to number. You can then combine this with nested selections. The code looks like this:
block.selectAll("span")
.data(function(d) { return d3.range(d.count); })
.enter()
.append('span')
Complete demo (with fixed CSS) here. The way of getting the color for the span elements is a bit hacky at the moment as it indexes into the top-level data set. A cleaner way would be to make this data part of the elements generated with d3.range() and is left as an exercise for the reader.
Related
Well, I want to color my scatter using a vector with values. Actually, I want to use other dimension than the one used for creating the scatter.
Using these lines it gives a color to my scatter using the values given by the dimension that scatter is built on.
.colorAccessor(function(d) {return d.key[1]})
.colors(d3.scaleSequential(d3.interpolateOranges))
.colorDomain(y_range)
y_range = [y_min, y_max]
I tried to include the column for color in the dimension of the scatter, but it slows down the process of filtering. Something like this:
scatterDim = crossFilter.dimension(function(d) { return [d[it.variable[0]], d[it.variable[1]], d[it.color]]})
.colorAccessor(function(d) {return d.key[2]})
.colors(d3.scaleSequential(d3.interpolatePlasma))
.colorDomain([colorUnits[0], colorUnits[colorUnits.length - 1]]),
I want to have a different dimension for color:
colorDimension = crossFilter.dimension(function (d) { return d[it.color] }),
colorGroup = colorDimension.group().reduceCount(),
colorAll = colorGroup.all(),
colorUnits = [],
count = 0;
for(var color in colorAll)
{
colorUnits[count] = colorAll[color].key;
count++;
}
.colorAccessor(//some different code for my vector colorUnits or even for dimension?!//)
.colors(d3.scaleSequential(d3.interpolatePlasma))
.colorDomain([colorUnits[0], colorUnits[colorUnits.length - 1]]),
I would also like to know how to use scaleOrdinal for color. In case that the vector colorUnits contains strings.
The name "dimension" is a little confusing in crossfilter and dc.js. It isn't used to describe the "Y" (aggregated) values, or the color.
It really means, "I want to bin my data by this key, and filter on it."
The reason you will find color as a third element in dimension keys in many examples is that it's expedient. It's easier to change the keys than the aggregated values. But it doesn't really make sense.
The fact that your chart got slower when you added color to your dimension key tells me that you don't have a unique color for each X/Y pair. Instead of drawing a dot for each X/Y pair, you end up with a dot for each X/Y/color triplet.
You also don't need to create a separate color dimension unless you want to bin, aggregate, or filter on color.
Assuming you only want one dot per X/Y pair, you need to decide which color to use. Then you can change the reduction, instead of the key, to add this data:
scatterDim = crossFilter.dimension(function(d) {
return [d[it.variable[0]], d[it.variable[1]]];
}),
scatterGroup = scatterDim.group().reduce(
function(p, v) { // add
p.count++; // reduceCount equivalent
p.color = combine_colors(p.color, v[it.color]);
return p;
},
function(p, v) { // remove
p.count--;
// maybe adjust p.color
return p;
},
function() { // init
return {count: 0, color: null};
}
);
If you don't care which of the colors is used, you don't need combine_colors; just use v[it.color]. Otherwise, that's something you need to decide based on your application.
Now the scatter group has objects as its values, and you can change the scatter plot to take advantage of them:
scatterPlot
.existenceAccessor(d => d.value.count) // don't draw dot when it is zero
.colorAccessor(d => d.value.color)
If in fact you do want to draw all the dots with different colors, for example using opacity to allow overplotting, you probably need a canvas implementation of a scatter plot, because SVG is only good up to thousands of points. There is one in the works for dc.js but it needs to be ported to the latest APIs.
I would also like to know how to use scaleOrdinal for color. In case that the vector colorUnits contains strings.
Not sure what you mean here. scaleOrdinal takes strings as its domain, so
.colors(d3.scaleOrdinal(colorUnits, output_colors))
should work?
Example
Since I'm failing to communicate something or another, here is an example. The color strings come from an array since I don't have an example of your data or code:
const names = ["Zero", "One", "Two", "Three", "Four", "Five"];
speedSumGroup = runDimension.group()
.reduce(
function(p, v) { // add
p.count++; // reduceCount equivalent
p.color = names[+v.Expt];
return p;
},
// ... as before
);
chart
.colorAccessor(d => d.value.color)
.colors(d3.scaleOrdinal(names, d3.schemeCategory10))
Once again, if the method isn't working for you, the best way to figure it out is to log speedSumGroup.all(). I get:
[
{
"key": [
1,
850
],
"value": {
"count": 1,
"color": "One"
}
},
{
"key": [
1,
880
],
"value": {
"count": 1,
"color": "Three"
}
},
{
"key": [
1,
890
],
"value": {
"count": 2,
"color": "Five"
}
},
// ...
]
Example fiddle.
How can I set the vertical lines related to the Category Axis which are NOT the grid ?
Watch the pictures to see what I m looking for.
It s not the grid, I don't think it's neither a trend line nor a guide. It s related to the data.
Any help will be deeply appreciated.
(I tryed to use the Editor to find it out... no magical result lol)
Amchart picture
amchart my result
This is how I fixed my problem :
somewhere after "type": "serial",
"gridAboveGraphs": true, // to get the vertical lines above the graph
And I added in "categoryAxis": { ... }
"gridAlpha": 1, // to make the vertical lines appear
"gridColor": "#ffffff", // to set the color of the lines
"gridPosition": "middle", // instead of 'start' to see the lines like I wanted
I wanna thank people who tried to help me.
Best regards.
My data consists of simple information for contracts. Simplified each one looks like this:
{
"id": 1234567890,
"district": "districtname",
"city": "cityname",
"test": "testnumber",
"date": "2015-03-15",
"score": 0.0 - 1.0
}
I already have a simple dc.barChart for the number of tests per day and a dc.rowChart for districts and another one for cities to filter the currently displayed data with crossfilter.
What I need now is a line chart that plots every score of every contract in ascending or descending order. By that I mean that I do not want to group the data by score but simply plot every score on the y-axis and have nothing on the x-axis. Assuming data is plotted in ascending order the resulting picture will be an ever increasing graph. See below:
How do I do this with data that lives inside a crossfilter instance, dc.js and d3.js?
First I'm a d3.js noob :)
How you can see from the title I've got a problem with duplicated data and aggregate the values is no option, because the name represent different bus stops. In this example maybe the stops are on the fron side and the back side of a building.
And of course I like to show the names on the x-axis.
If i created an example and the result is a bloody mess, see jsFiddel.
x = index
name = bus stop name
n = value
I've got a json e.g.:
[{
"x": 0,
"name": "Corniche St / Abu Dhabi Police GHQ",
"n": 113
},
{
"x": 1,
"name": "Corniche St / Nation Towers",
"n": 116
},
{
"x": 2,
"name": "Zayed 1st St / Al Khalidiya Public Garden",
"n": 146
},
...
{
"x": 49,
"name": "Hamdan St / Tariq Bin Zeyad Mosque",
"n": 55
}]
The problem: It is possible that the name could appear more then once e.g.
{
"x": 1,
"name": "Corniche St / Nation Towers",
"n": 116
}
and
{
"x": 4,
"name": "Corniche St / Nation Towers",
"n": 105
}
I like to know is there a way to tell d3.js not to "delete" duplicated names and instead just show all names in sequence with their values.
Any ideas or suggestions are very welcome :) If you need more information let me know.
Thanks in advanced
Mario
Lars is right: the d3.ordinal scale is doing exactly what it should: treating duplicate values as repeat instances. See here for more details: https://github.com/mbostock/d3/wiki/Ordinal-Scales
You can use a regular linear scale instead, like this: http://jsfiddle.net/vy8vjy4r/2/
The changes are to make the scale linear and set the domain to be the length of your dataset.
var x = d3.scale.linear().domain([0,j_data.length]).range([0, width]),
When you pass a value to the scale, you simply pass the position in the list. I'm using the index - the i in function(d,i) - but you could have used the x in your dataset. (I didn't use it as it looks like you don't need it.)
.x(function (d,i) { return x(i); })
Hopefully this works for you.
Additional information on axis
Strictly speaking, I guess this should have been an additional question, but to get the text on the axis, you can simply add these two lines of code in where you modify the text in xAxisGroup, after .selectAll("text"):
.data(j_data.filter(function(d,i) { return !(i%5); }))
.text(function(d){ return d.name; })
The axis is displaying numbers every fifth item, so we choose every fifth item from the dataset. This gives us data that matches the existing labels, and we change the text to the .name value, see http://jsfiddle.net/vy8vjy4r/4/
This approach isn't particularly strong: it depends on D3 displaying every fifth stop, and for short or very long routes (or whatever these are) it might display all stops, or every tenth, etc. I would rather not use the D3 axis and build your own. For something like this, it shouldn't be too hard, although fitting all the names in might be hard in this space.
Try this: http://jsfiddle.net/vy8vjy4r/5/
Try this filter,
var names = [];
var result = [];
var indx=-1;
for(var i=0; i< j_data.length; i++){
indx = names.indexOf(j_data[i].name);
if(indx==-1){
names.push(j_data[i].name);
result.push(j_data[i]);
}
}
j_data= result;
Do this after your j_data array, it'll remove the duplicated objects from your j_data array. And see this http://jsfiddle.net/vy8vjy4r/1/
If it is not, what you are looking for, ask what change you need.
I'm working on a visualization tool for time series with multiple dimensions.
To simplify my case, each data-point has a dimension on type, clusterId and a set of months:
{
type: "green",
clusterId:42,
months:[1392185580000, 1394604780000, 1397279580000]
}, {
type: "red",
clusterId:43,
months:[1392185580000]
}
Now I would like to show the dates in a dc.barChart, which shows the months of all datasets as keys(bars), and the number of observations of each month as value of the bar.
In the given case, it would result in 3 bars, the first one with a height of 2, and the other with a height of 1.
How can I create this dimension and implement the grouping/reducing function?
You don't have to worry about filtering by this dimension, I already have a custom filter for this dimension. The only thing is displaying the bars on the barChart.
If i get this correctly, you need some code, that outputs: 1392185580000: 2, 1394604780000: 1, 1397279580000:1?
arr.forEach(function(d) {
d.months.forEach(function(month) {
if (!store.hasOwnProperty(month)) {
store[month]=0;
}
store[month]++;
});
});
demo fiddle