Find the maximum value in an object where the keys are arrays? - d3.js

Using D3, how can I find the maximum value in an object where all the values are arrays of floats?
var data = {
"point1": {
"level1": [...],
"level2": [...]
},
...
};
I read Mike's answer on finding the max of a nested array, but I'm not sure what to do here since this is an object of arrays.
Currently I'm doing this, which is awful:
var c1Max = d3.max(data.point1.level1, function(d) { return d.value; });
var c2Max = d3.max(data.point1.level2, function(d) { return d.value; });
var c3Max = d3.max(data.point2.level1, function(d) { return d.value; });
var c4Max = d3.max(data.point2.level2, function(d) { return d.value; });
var c5Max = d3.max(data.point3.level1, function(d) { return d.value; });
var c6Max = d3.max(data.point3.level2, function(d) { return d.value; });
var max = d3.max([c1Max, c2Max, c3Max, c4Max, c5Max, c6Max]);
Maybe I should iterate over all the keys? Or maybe I could flatten the data object somehow? Or maybe there's a nicer way.

You can use d3.entries to convert the object into something you can iterate over:
var max = d3.max(d3.entries(data), function(d) {
return d3.max(d3.entries(d.value), function(e) {
return d3.max(e.value);
});
});
In this particular case (keys not required) you can of course also use d3.values, which makes the above code a bit shorter.
To make it work with the structure in your jsfiddle, you need this code:
var max1 = d3.max(d3.entries(data), function(d) {
return d3.max(d3.entries(d.value), function(e) {
return d3.max(e.value, function(f) { return f.value; });
});
});

Related

Dynamic filtering with D3

I'm quite new to D3 and coding in general. I'm trying to set up a bar chart which includes/excludes data depending on a checkbox. I have a set of product groups and countries which I want to toggle in/out of the total represented by the bar. The output should be one bar per product.
My full data set has many more products, product groups and countries so it is not viable to create a key-value pair for each potential combination of checkboxes. Instead I would like to create a function that re-evaluates the checkboxes and re-filters the data and updates the rollup when a checkbox is changed.
I'm not sure where this function should sit in my code or what it should look like... This is what I'm working with at the moment:
var data = data.filter(function(d) {
if (document.getElementById("nz_button").checked) {
return d.country == 'NZ'
}
if (document.getElementById("au_button").checked) {
return d.country == 'AU'
}
if (document.getElementById("us_button").checked) {
return d.country == 'US'
}
})
// to see how many distinct groups there are and sum volume
var products = d3.nest()
.key(function(d) {
return d.product
})
.rollup(function(leaves) {
var sum = 0;
leaves.forEach(function(d) {
sum += d.volume;
})
return sum
})
.entries(data);
Full code: http://plnkr.co/edit/qezdwMLt48RPc8KH17hS?p=preview
Maybe I should be working with selections and re-running the nest/rollup when required?
Any help appreciated. Thanks :)
You can move the full code which makes the graph in a new function like this:
function makeDataGraph(data) {//function to make the graph.
//
// FILTER
//
var data = data.filter(function(d) {
if (document.getElementById("au_button").checked) {
return d.country == 'AU'
}
if (document.getElementById("us_button").checked) {
return d.country == 'US'
}
if (document.getElementById("nz_button").checked) {
return d.country == 'NZ'
}
})
// to see how many distinct groups there are and sum volume
var products = d3.nest()
.key(function(d) {
return d.product
})
.rollup(function(leaves) {
var sum = 0;
leaves.forEach(function(d) {
sum += d.volume;
})
return sum
})
.entries(data);
// sorting on descending total
console.log(products);
products.sort(function(a, b) {
return b.values - a.values
})
var max = d3.max(products, function(d) {
return d.values;
});
var xscale = d3.scale.linear()
.domain([0, max])
.range([0, 600])
var svg = d3.select("svg");
//
// Still needs to be cleaned up \/ \/
//
var rects = svg.selectAll("rect.product")
.data(products)
rects.exit().remove();
rects.enter().append("rect").classed("product", true)
rects.attr({
x: 200,
y: function(d, i) {
return 100 + i * 50
},
width: function(d, i) {
return xscale(d.values)
},
height: 50
}).on("click", function(d, i) {
console.log(i, d);
})
var labels = svg.selectAll("text.label")
.data(products)
labels.exit().remove();
labels.enter().append("text").classed("label", true)
labels.attr({
x: 195,
y: function(d, i) {
return 128 + i * 50
},
"text-anchor": "end",
"alignment-baseline": "middle"
}).text(function(d) {
return d.key || "N/A"
})
var volume = svg.selectAll("text.volume")
.data(products);
volume.exit().remove();
volume.enter().append("text").classed("volume", true)
volume.attr({
x: function(d, i) {
return 205 + xscale(d.values)
},
y: function(d, i) {
return 128 + i * 50
},
"text-anchor": "start",
"alignment-baseline": "middle"
}).text(function(d) {
return d.values || "N/A"
})
}
Remember to do rects.exit().remove(); so that when the data is changed on click of the checkbox, rectangles related to old data is removed.
Now you can call this function from the click event and also afterloading the tsv like this:
d3.tsv("data.tsv", function(err, udata) {
var udata = udata.map(process);
console.log("udata", udata);
var data = udata // making new var to preserve unfiltered data
makeDataGraph(data);//call the function to make graph
function handleClick() { // event handler...
makeDataGraph(data)
}
//add listener to all check boxes.
d3.selectAll(".filter_button").on("click", handleClick);
});
working code here

date sorting inside table using dc.js and d3

I'm trying to sort data based on date inside a table. However the dates are not sorted properly. Any guidance would be appreciated.
Pasted some parts of my code:
var dateFormat = d3.time.format("%Y-%m-%d");
var formatDate = d3.time.format('%d/%m/%Y');
jiraalertproject.forEach(function(d) {
d["date"] = dateFormat.parse(d["date"]);
var dataTable = dc.dataTable("#dc-table-graph");
dataTable
.width(1000)
.height(600)
.dimension(dateDim)
.group(function(d) { return "You will get the Ticket details with scroll option:"
})
.size(10)
.columns([
function(d) { return formatDate(d.date); },
function(d) { return d.ticket; },
function(d) { return d.component; },
function(d) { return d.summary; }
])
.sortBy(function(d){ return +formatDate(d.date); })
.order(d3.descending);

dc.js dataTable - how to use a pre-defined group

I would like to load a dc dataTable with dimensional data that has been grouped and filtered. Is this possible? I am assuming it is but the examples I have seen so far have simple groups coded at the time of table construction and I can't get it to work any other way!
var facts = crossfilter(data);
var testTable = dc.dataTable('#testtable');
var providerDim = facts.dimension(function (d) {
if (d.report_by_3 !== null) {
return d.report_by_3;
}
});
var MH003Group = providerDim.group().reduceSum(function (d) {
if (d.indicator_code === 'MH003') {
return (d.observed / d.expected);
} else {
return null;
}
});
var MH003Group2 = {
all: function () {
return MH003Group.all().filter(function (d) {
return d.value !== null;
});
}
};
dataTable.width(960)
.height(8000)
.dimension(providerDim)
// .group(MH003Group2)
.group(function (d) {
return 'data';
})
.size(10)
.columns([function (d) {return d.indicator_code;},
function (d) {return d.report_by_1;},
function (d) {return d.report_by_3;},
function (d) {return (d.observed / d.expected);}
]);
.sortBy(function (d) {return d.report_by_1;})
.order(d3.ascending);
In the code above I would like to display filtered rows in the dimension using the pre-defined grouping rather than all the rows of data in the dimension which is what is currently happening.

d3.js - max and min value from json data which has array of values

How do i find the max and min values from JSON data. I want the max of checkins and Checkintimes of data1,data2 ...data(n). My code is :
var max = d3.max(data, function(d) { return d.Checkintimes;} );
var min = d3.min(data, function(d) { return d.Checkintimes;} );
I tried this option too
d3.json("Values.json", function(data) {
var maxmbt = d3.max(d3.values(data) );
var maxcheckins = d3.max(d3.values(data));
console.log(maxmbt);
console.log(maxcheckins);
xScale.domain([0,maxcheckins]);
xScale.domain([0,maxmbt]);
});
I have a json in this format:
[
{
"name":"data1",
"checkins":[[1,0],[2,0],[3,0],[4,3],[5,0],[6,0],[7,0],[8,3],[9,0],[10,0],[11,0],[12,0]],
"teamsize":[[1,0],[2,0],[3,0],[4,3],[5,0],[6,0],[7,0],[8,1],[9,0],[10,0],[11,0],[12,0]],
"Checkintimes":[[1,0],[2,0],[3,0],[4,184],[5,0],[6,0],[7,0],[8,0],[9,0],[10,0],[11,0],[12,0]]
},
{"name":"data2",
"checkins":[[1,0],[2,0],[3,0],[4,0],[5,0],[6,0],[7,0],[8,0],[9,0],[10,0],[11,27],[12,12]],
"teamsize":[[1,0],[2,0],[3,0],[4,0],[5,0],[6,0],[7,0],[8,0],[9,0],[10,0],[11,11],[12,11]],
"Checkintimes":[[1,0],[2,0],[3,0],[4,0],[5,0],[6,0],[7,0],[8,0],[9,0],[10,0],[11,10],[12,12]]
}
]
currently when i use the returning value is an array. not an unitary value. For e.g checkins max value here is 27 and Checkintimes - 184 in the aforesaid example.
The d3.max function needs to have an accessor that gets individual values. You would need nested calls as you have nested data:
var max = d3.max(data, function(d) {
return d3.max(d.Checkintimes, function(e) { return d3.max(e); });
});
Try this code:
Fiddle
JS:
dataset.forEach(function(d, i) {
max= d3.max(d.checkins);
min=d3.min(d.checkins);
});

Forcing y-axis to 0 when using chained transitions

I'm new to D3 and working on a modified version of Chained Transitions: http://bl.ocks.org/mbostock/3903818.
However one problem I'm running into is forcing the y-axis to start at 0. Normally this would be simple fix with changing the y domain and range properties when declaring your other variables but in the context of the transition function, I'm running into issues. Here is the code: http://jsfiddle.net/maureenlinke/DCc6g/
I believe the issue is here:
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([
d3.min(homes, function(c) { return d3.min(c.values, function(v) { return v.price; }); }),
d3.max(homes, function(c) { return d3.max(c.values, function(v) { return v.price; }); })
]);
Thanks,
Maureen
You just need to fix the domain:
var maxPrice = d3.max(homes, function(c) {
return d3.max(c.values, function(v) { return v.price; });
});
y.domain([0, maxPrice]);
The d3.extent function returns the min/max range of the data in the form of an array [min, max].
Normally, you would code the following:
y.domain( d3.extent(homes, function(d) { return d.price; }) );
So the following should work:
var yRange = d3.extent(homes, function(d) { return d.price; });
y.domain( [0, yRange[1] ); // Force the min to 0

Resources