I have a dataset that looks something like this:
MONTH CAT VAL
may A 1.0
may B 3.2
may C 4.6
jun A 2.7
jun B 4.2
jun C 5.8
jul A 4.1
jul B 9.2
jul C 13.0
I've been able to create a chart in DC.js that shows the sum of VAL according to the CAT variable, with this code:
let chart = dc.barChart('#chart');
let ndx = crossfilter(data);
let catDim = ndx.dimension(function(d){return d.cat;});
let catGroup = catDim.group().reduceSum(function(d){return +d.val;});
chart
.dimension(catDim)
.group(catGroup)
.x(d3.scale.ordinal().domain(catDim))
.xUnits(dc.units.ordinal)
.elasticY(true)
My problem is that instead of the sum, I would like to show in the grafic the average of VAL per MONTH for each CAT (MONTH can be filtered in another graph).
Is there a way to do it?
Thanks in advance for your answers!
So instead of using crossfilter to "just" keep track of the sum, use a custom reduce that tracks both the sum and the number of items, and add a valueAccessor to return x.value.sum/x.value.qty
I would suggest you to use reductio to handle the custom reduce, check the examples, you have one for the average.
Related
How can I format the labels that appear on AG-Grid chart's axis? I have a lot of time-series data so I expect users frequently to produce charts where the horizontal axis is a date. Unfortunately, this produces unreadable chart labels because the dates are not formatted (see attached image) - The labels look like "Thu Jan 09 2020 00:00:00 GMT+0000 (Greenwich Mean Time)" when all I would like is simply "2020-01-09". My The dates in the grid look fine thanks to a valueFormatter for dates.
It is also very common for users to produce pivot tables using the date. This produces similarly terrible results for the labels, but I've found I can use "processSecondaryColGroupDef" to format dates that appear in the column headers. Is there a similar way to do this for charts?
Thankyou,
Troy.
From the docs -
For time axes, a format string can be provided, which will be used to
format the data for display as axis labels
You can either explicitly set the axis type to 'time', but you can also remove this and the chart will still use the time axis as it automatically detects the axis type from the data in the Date column.
You can implement processChartOptions callback and add your customizations -
processChartOptions(params) {
var options = params.options;
var dateFormatter = function(params) {
return params.value.value && para[enter link description here][1]ms.value.value.toLocaleDateString
? params.value.value.toLocaleDateString()
: params.value;
};
if (["line"].indexOf(params.type) < 0) {
if (options.xAxis && options.yAxis) {
options.xAxis.label.formatter = dateFormatter;
options.yAxis.label.formatter = dateFormatter;
}
} else {
options.xAxis.type = "time";
options.xAxis.label.format = "%d %B";
}
Example and details here
Im looking for someone to point me in the right direction to solve a small project im working on using javascript. The idea is i would like the user to be able to input some raw data (which has been copied and pasted) from a website into a form box or input of some sort on day one and then again on day two etc etc.
What i would like the JS to do is compare the two sets of data and return any changes. For example
Day One Raw Data: (copy and pasted from a website)
Welcome
Home
Contact
Info
A
Andy 29 5'9 $3000 low
B
Betty 19 4'8 $2800 low
Bella 23 5'2 £4300 medium
C
Charles 43 5'3 $5000 high
Your local date/time is Thu Jan 11 2018 20:58:14 GMT+0000 (GMT Standard Time).
Current server date/time is 11-01-2018 | 21:58
Logout
Day Two Raw Data: (copy and pasted from a website)
Welcome
Home
Contact
Info
A
Andy 29 5'9 $3200 low
B
Betty 19 4'8 $2900 low
Bella 23 5'2 £3900 high
C
Charles 43 5'3 $7000 high
Carrie 18 5'8 $1000 medium
Your local date/time is Thu Jan 11 2018 20:58:14 GMT+0000 (GMT Standard Time).
Current server date/time is 11-01-2018 | 21:58
Logout
The only bit of data im looking to compare is the name + information lines
Andy 29 5'9 $3200 low
for example. The rest of the raw data is just noise which should always be the same, the links on the top of the page for example and the footer at the bottom also including the A, B,C etc which are alphabet links.
What i would like the outcome to be is something like the following:
Results: (printed to page)
Andy 29 5'9 $3200 low --- (+ $200)
Betty 19 4'8 $2900 low --- (+ $100)
Bella 23 5'2 £3900 high --- (- $400 medium)
Charles 43 5'3 $7000 high --- (+ $2000)
Carrie 18 5'8 $1000 medium --- (**New Entry**)
How the results are displayed and the actually figures are irrelevant. Im looking for suggestions for methods to actually achieving this kind of data comparisons where i ignore certain parts of the raw input and compare those that are of importance. Report back with the new and removed entries, changes to duplicate entries. The only data that will ever change is the amount of people in the raw data the headers, footers and alphabet tags will always be there.
Hopefully ive explained well enough to get pointed in the right direction. Thanks for any help in advance.
Ok this is messy (its late) but this will do what you want I think...
There is huge room for cleaning this up so take this as a steer in the right direction. The key is you need regex to analyse the strings. Then there's a fair amount of manipulation to compare the results.
<script>
var dayOne = `Welcome
Home
Contact
Info
A
Andy 29 5'9 $3000 low
B
Betty 19 4'8 $2800 low
Bella 23 5'2 £4300 medium
C
Charles 43 5'3 $5000 high
Your local date/time is Thu Jan 11 2018 20:58:14 GMT+0000 (GMT Standard Time).
Current server date/time is 11-01-2018 | 21:58
Logout `;
var dayTwo = `
Welcome
Home
Contact
Info
A
Andy 29 5'9 $3200 low
B
Betty 19 4'8 $2900 low
Bella 23 5'2 £3900 high
C
Charles 43 5'3 $7000 high
Carrie 18 5'8 $1000 medium
Your local date/time is Thu Jan 11 2018 20:58:14 GMT+0000 (GMT Standard Time).
Current server date/time is 11-01-2018 | 21:58
Logout `;
/**
* Converts an array to an object with keys for later comparison
*/
function convertNamesToKeys(arr){
var obj = {}
for(var i=0, j=arr.length; i<j; i+=1){
var name = arr[i].substring(0,arr[i].indexOf(' '));
obj[name] = arr[i];
}
return obj;
}
/**
* Count object length
*/
function getObjectLength(obj) {
var length = 0;
for( var key in obj ) {
if( obj.hasOwnProperty(key) ) {
length+=1;
}
}
return length;
};
/**
* Compares two objects for differences in values
* retains objects with different keys
*/
function compareObjectValue(primaryObject, secondaryObject){
for(var name in primaryObject){
if( primaryObject.hasOwnProperty(name)
&& name in secondaryObject){
if(primaryObject[name] === secondaryObject[name]){
delete primaryObject[name];
}
}
}
//This is your final array which should contain just unique values between the two days
console.log(primaryObject);
}
//split the large string into lines for manageability and simplicity of regex
var dayOneArray = dayOne.match(/[^\r\n]+/g);
var dayTwoArray = dayTwo.match(/[^\r\n]+/g);
//discard any lines which are noise
var regex = /^[a-z\s0-9']+(\$|£)[0-9\sa-z]+$/i
var dayOneFiltered = dayOneArray.filter(line => regex.test(line));
var dayTwoFiltered = dayTwoArray.filter(line => regex.test(line));
//convert the arrays into objects using name as key for easy comparison
var dayOneConverted = convertNamesToKeys(dayOneFiltered);
var dayTwoConverted = convertNamesToKeys(dayTwoFiltered);
//Determine which of the two objects is the larger and loop that one
//We will unset keys which have values that are the same and leave keys
//in the larger array which must be unique - not sure if you want that?
if( getObjectLength(dayOneConverted) > getObjectLength(dayTwoConverted)){
compareObjectValue(dayOneConverted, dayTwoConverted)
}
else {
compareObjectValue(dayTwoConverted, dayOneConverted);
}
</script>
I have an object called totalRevenue which is responsible for storing stacked revenue of company on a monthly basis.
var totalRevenue = {
jan : 147,
feb : 290,
mar : 400
};
Not all month names are present in totalRevenue object & they are created dynamically when populating/estimating their respective values.
In the beginning of every month, we estimate revenue of that month using an algorithm. sth like this:
with(totalRevenue){
mar = mar - feb; // here we calculate pure mar revenue
feb = feb - jan; // here we calculate pure feb revenue
apr = mar - feb; // this is part one of the algo.
}
(I'm using with construct to avoid repetition.)
Now I'm using totalRevenue.apr for the rest of algo computations. But after some challenge, I got that totalRevenue.apr = undefined !!
Does anyone know why?! As I expected it to have a value equal to mar - feb.
If apr isn't a property of totalRevenue then it's not brought into scope by with and your code will create a new global named apr. There would be no way for the interpreter to know if a given name refers to a global or was intended to refer to a heretofore undefined property of the nearest with block so the assumption is it's a global. You can either ensure that totalRevenue has a property for every month or avoid using the with statement entirely. Using with is discouraged MDN has this to say:
Use of the with statement is not recommended, as it may be the source of confusing bugs and compatibility issues. See the "Ambiguity Contra" paragraph in the "Description" section below for details.
How do I parse an array of dates like this: "2013-07-11T00:00:00", into something useable for a line graph? The graph will stretch back a full year.
I'm trying to create a dynamic graph like the one in the "Plotting a chart" section on this page.
It would be great if I could have the tick marks like that as well (Apr '13).
I'm currently using the following function in JavaScript:
var parseDate = d3.time.format("%Y-%m-%dT%H:%M:%SZ").parse;
But it doesn't work.
You have a trailing "Z" in your format specifier which makes your parser return null for the dates you give to it. Here's an example that works:
var parse = d3.time.format("%Y-%m-%dT%H:%M:%S").parse;
parse('2013-07-11T00:00:00'); // Thu Jul 11 2013 00:00:00 GMT+0200 (CEST)
You can use d3 time scales to display time-based axes with appropriate tick marks.
Edit:
I guess I misunderstood your question... here's another take which (hopefully) explains the usage of time scales and date formatting:
var scale = d3.time.scale(),
axis = d3.svg.axis(),
// Assuming you have your data as strings:
data = ['2013-07-11T00:00:00', '2013-08-11T00:00:00', ...],
range = [0, 500], // size of chart
domain = d3.extent(data, function (d) { // generate the data extent (min,max)
return new Date(d); // use Date instances for values
});
// Configure the scale with domain and range:
scale.domain(domain).range(range);
// Use the above scale for the axis:
axis.scale(scale)
// Print the dates in "Mmm dd" format:
.tickFormat(d3.time.format('%b %d'));
// Render the axis in your svg node:
svg.call(axis);
I am trying to have my barchart in d3.js update it's values when the user changes the time scale from days, to weeks or months. (i.e. when the time scale is changed to weeks, I want all the data values for each day in a given week summed together). For example, here is the default graph with the x-axis time scale in days:
x-Axis in days
When a user changes the time scale to weeks and the x-axis updates, the data values remain grouped by day, as shown here:
x-Axis scale in weeks, but data values remain grouped by day:
What I want is for there to be only one bar for each week number of the year on the x-axis, showing the sum of all the data values the user provided for all 7 days of that week. How do I achieve this?
Does this have to be done on the server-side, or can it be on the client-side with javascript, or is there some easy d3.js way I'm overlooking?
This is what my data looks like:
[{"date":"2013-04-20","load_volume":400},{"date":"2013-04-23","load_volume":400},{"date":"2013-04-24","load_volume":400},{"date":"2013-04-28","load_volume":1732},{"date":"2013-04-30","load_volume":400}]
I figured to achieve this I could convert the date values to weekNumberOfYear format (for e.g., 17 for this week), push them into an array and remove all duplicates, then sum the data values for each of the days in that array. I did this and the data looked like this:
[{"date":"15","load_volume":400},{"date":"16","load_volume":2532},{"date":"17","load_volume":400}]
However, I don't believe this is the correct approach because I always get an "Error: Invalid value for attribute x="NaN"" in the JS console. This I think is because I use the x scale to position the rects on my graph:
.attr("x", function(d) { return padding + x(new Date(d.date)); })
... which would result in x(Wed Dec 31 1969 19:00:00 GMT-0500 (EST)), which throws a NaN error.
I am now trying to format the date into %Y-%m-%d format and have it be the beginning Monday of each week, but I'm wondering if there is an easier solution since I've been at this all day.
Well, I think I've figured it out. I just had to convert the dates to millisecond time and remove the double quotes I had around the date value in the JSON string (the double quotes were giving me a NaN error). I did this with the following function:
function getWeekDate(d) {
d = new Date(d); // get current date
var day = d.getDay();
var diff = d.getDate() - day + (day == 0 ? -6 : 1); // Subtract day number of month from day number of week, and adjust when day is sunday
var date = new Date(d.setDate(diff));
return date.setHours(0);
}
which is adapted from this SO question.
Not sure if D3 has a better way of doing it. If I find out i'll post it here.