I need to create a rowchart in dc.js with inputs from multiple columns in a csv. So i need to map a column to each row and each columns total number to the row value.
There may be an obvious solution to this but i cant seem to find any examples.
many thanks
S
update:
Here's a quick sketch. Apologies for the standard
Row chart;
column1 ----------------- 64 (total of column 1)
column2 ------- 35 (total of column 2)
column3 ------------ 45 (total of column 3)
Interesting problem! It sounds somewhat similar to a pivot, requested for crossfilter here. A solution comes to mind using "fake groups" and "fake dimensions", however there are a couple of caveats:
it will reflect filters on other dimensions
but, you will not be able to click on the rows in the chart in order to filter anything else (because what records would it select?)
The fake group constructor looks like this:
function regroup(dim, cols) {
var _groupAll = dim.groupAll().reduce(
function(p, v) { // add
cols.forEach(function(c) {
p[c] += v[c];
});
return p;
},
function(p, v) { // remove
cols.forEach(function(c) {
p[c] -= v[c];
});
return p;
},
function() { // init
var p = {};
cols.forEach(function(c) {
p[c] = 0;
});
return p;
});
return {
all: function() {
// or _.pairs, anything to turn the object into an array
return d3.map(_groupAll.value()).entries();
}
};
}
What it is doing is reducing all the requested rows to an object, and then turning the object into the array format dc.js expects group.all to return.
You can pass any arbitrary dimension to this constructor - it doesn't matter what it's indexed on because you can't filter on these rows... but you probably want it to have its own dimension so it's affected by all other dimension filters. Also give this constructor an array of columns you want turned into groups, and use the result as your "group".
E.g.
var dim = ndx.dimension(function(r) { return r.a; });
var sidewaysGroup = regroup(dim, ['a', 'b', 'c', 'd']);
Full example here: https://jsfiddle.net/gordonwoodhull/j4nLt5xf/5/
(Notice how clicking on the rows in the chart results in badness, because, what is it supposed to filter?)
Are you looking for stacked row charts? For example, this chart has each row represent a category and each color represents a sub-category:
Unfortunately, this feature is not yet supported at DC.js. The feature request is at https://github.com/dc-js/dc.js/issues/397. If you are willing to wade into some non-library code, you could check out the examples referenced in that issue log.
Alternatively, you could use a stackable bar chart. This link seems to have a good description of how this works: http://www.solinea.com/blog/coloring-dcjs-stacked-bar-charts
Related
May I know If I could parse differently, so that performance and load time can be improved. I deal with file size that is in MBs.
My file with multiple objects is parsed as below:
I display multiple graphs in a single page.
'chart1' to 'chart5' arrays are used for each graph's Y-axis.
'xAxisTime' array is used for x-axis (common x-axis for all graphs).
eg: I get 50000 values in each array (chart1, chart2...xAxisTime).
data = fileWithMultipleObjects;
objects= data.split('\n');
for (var i= 0; i < objects.length - 2; i++) {
var obj = JSON.parse(objects[i])
if (obj.type=== "A") {
chart1.push(obj.c1)
chart2.push(obj.c2)
}
else {
chart1.push(null)
chart2.push(null)
}
if (obj.type=== "B") {
chart3.push(obj.c3)
chart4.push(obj.c4)
chart5.push(obj.c5)
}
else {
chart3.push(null)
chart4.push(null)
chart5.push(null)
}
//common for all charts - xAxis data
if (obj.date === undefined) {
obj.date = null
}
if (obj.date!== null) {
var date= obj.date
xAxisTime.push(date)
}
}
Reason for pushing null values when no data:
I need to show all 5 graphs in line with the time the job was run.
Also, this helps me use "this.point.y" (var index = this.point.y), which helps me display tooltips on plotline. Fiddle showing tooltip(when clicked on green line) with "this.point.y" :https://jsfiddle.net/xqb0cn5r/4/
This is how my graphs look. Fiddle: https://jsfiddle.net/xgpL1w3t/. Only charts that meets if condition are rendered. chart1 and chart5 are rendered in this case. Thank you.
Edited:
Below is the data structure of my file:
{"C1":55.77,"C2":11367.25,"type":"A","date":"10/24/2022 12:05:37.236"}
{"C3":55.77,"C4":11367.25,"type":"B","date":"10/24/2022 12:05:37.236","C5":445.21}
{"C1":55.77,"C2":11367.25,"type":"A","date":"10/24/2022 12:05:37.236"}
{"C1":55.77,"C2":11367.25,"type":"A","date":"10/24/2022 12:05:37.240"}
{"C3":55.77,"C4":11367.25,"type":"B","date":"10/24/2022 12:05:37.250","C5:445.25}
{"C3":55.77,"C4":11367.25,"type":"B","date":"10/24/2022 12:05:37.275","C5":445.26}
I display like 8 to 10 charts in real scenario with around 50000 points each. Common x-axis for all. Out of 50000, only 2000 to 4000 are real data, remaining are all null values. I push null, so that I can keep graphs in line with the whole time(x-axis) the job was run.
Right now, it takes time to load charts. As so much data to parse and load. I believe performance can be be improved if my data is parsed differently for the same scenario. I would appreciate any suggestions.
Performing single json parse as below:
var convertStringToJsonFormat= "[" + data.split('\n').join(", ")
JsonFormat = convertStringToJsonFormat.slice(0, -2);
JsonFormat = JsonFormat + "]"
data = JSON.parse(JsonFormat)
Is it possible using the framework ag-grid in JS to apply a conditional background color formatting of a cell based on its value such as Excel conditional formatting (eg the second table formatting in this link is a great example of what I am trying to achieve).
Basically, cells containing the highest values are green and tend to be red as they lower, being yellow when they reach the median (the inverse is applied in above link)
As you see, it is not a simple CellClassRules as the cell color depends cell values across the table and not only a specific row or column.
I didn’t find such option on ag-grid documentation.
Write a function for the cellStyle and have this function look at each and every value in the table, determine it's ranking, then have it return the relevant colour for the cell, i.e. the lower it is, return a more "reddish" colour, the higher it is, return a "greener" colour. Something like this:
function myCellStyleFunction(params) {
const totalCellCount = params.api.getDisplayedRowCount() * columnsCount;
let allValuesInTable = [];
rowData.forEach((x) => {
const valuesForRow = Object.keys(x).map(function (k) {
return x[k];
});
allValuesInTable = allValuesInTable.concat(valuesForRow);
});
const valuesForTableOrdered = allValuesInTable.sort(function (a, b) {
return a - b;
});
const valueIndex = valuesForTableOrdered.indexOf(params.value);
console.log(valueIndex)
debugger;
const bgColour = generateColor('#FF0000','#00FF00',totalCellCount,valueIndex)
return { backgroundColor: '#' + bgColour };
}
And apply this cellStyle in defaultColDef so it is applied to every cell.
Demo.
Why don't you use the Gradient Column feature and it will do it all for you with a couple of clicks?
https://demo.adaptabletools.com/style/aggridgradientcolumndemo
I'm working on DC.js charts and I'm trying to understand of how to create an overall "Total" variable of a column in the dimension.group() function. Lets say a column is called packages and its just integers. The dimensions is Area (North, South, East, West). When I develop the DC.js rowChart, I have to create the groups. Is it possible to create an overall total of package column inside the group() function?
Here is a simple create groups code below.
function createGroup(dimension, config) {
return dimension.group().reduce(
function(p,v) {
p.Total += v.packages;
/* p.TotalOverall = ??*/
return p;
},
function(p,v) {
p.Total -= v.packages;
return p;
},
function() {
return {
Total : 0,
}
}
); // end of return
}
within the reduce function, is it possible to create a variable or object that will state only total overall package column? In the p object, I just want to add a total Overall package. I know there's a way using the groupAll() but I want my charts to have the total Overall Packages number (Object) available so I can display that in the tooltip, etc. This object will not change during the filter, I just need that total.
We have scatter plots working great in our dashboard, but we have been thrown a curve ball. We have a new dataset that provides multiple y values for a single key. We have other datasets were this occurs but we had flatten the data first, but we do not want to flatten this dataset.
The scatter plot should us the uid for the x-axis and each value in the inj field for the y-axis values. The inj field will always be an array of numbers, but each row could have 1 .. n values in the array.
var data = [
{"uid":1, "actions": {"inj":[2,4,10], "img":[10,15,25], "res":[15,19,37]},
{"uid":2, "actions": {"inj":[5,8,15], "img":[5,8,12], "res":[33, 45,57]}
{"uid":3, "actions": {"inj":[9], "img":[2], "res":[29]}
];
We can define the dimension and group to plot the first value from the inj field.
var ndx = crossfilter(data);
var spDim = ndx.dimension(function(d){ return [d.uid, d.actions.inj[0]];});
var spGrp = spDim.group();
But are there any suggestions on how to define the scatter plot to handle multiple y values for each x value?
Here is a jsfiddle example showing how I can display the first element or the last element. But how can I show all elements of the array?
--- Additional Information ---
Above is just a simple example to demonstrate a requirement. We have developed a dynamic data explorer that is fully data driven. Currently the datasets being used are protected. We will be adding a public dataset soon to show off the various features. Below are a couple of images.
I have hidden some legends. For the Scatter Plot we added a vertical only brush that is enabled when pressing the "Selection" button. The notes section is populated on scatter plot chart initialization with the overall dataset statistics. Then when any filter is performed the notes section is updated with statistics of just the filtered data.
The field selection tree displays the metadata for the selected dataset. The user can decide which fields to show as charts and in datatables (not shown). Currently for the dataset shown we only have 89 available fields, but for another dataset there are 530 fields the user can mix and match.
I have not shown the various tabs below the charts DIV that hold several datatables with the actual data.
The metadata has several fields that are defined to help use dynamically build the explorer dashboard.
I warned you the code would not be pretty! You will probably be happier if you can flatten your data, but it's possible to make this work.
We can first aggregate all the injs within each uid, by filtering by the rows in the data and aggregating by uid. In the reduction we count the instances of each inj value:
uidDimension = ndx.dimension(function (d) {
return +d.uid;
}),
uidGroup = uidDimension.group().reduce(
function(p, v) { // add
v.actions.inj.forEach(function(i) {
p.inj[i] = (p.inj[i] || 0) + 1;
});
return p;
},
function(p, v) { // remove
v.actions.inj.forEach(function(i) {
p.inj[i] = p.inj[i] - 1;
if(!p.inj[i])
delete p.inj[i];
});
return p;
},
function() { // init
return {inj: {}};
}
);
uidDimension = ndx.dimension(function (d) {
return +d.uid;
}),
uidGroup = uidDimension.group().reduce(
function(p, v) { // add
v.actions.inj.forEach(function(i) {
p.inj[i] = (p.inj[i] || 0) + 1;
});
return p;
},
function(p, v) { // remove
v.actions.inj.forEach(function(i) {
p.inj[i] = p.inj[i] - 1;
if(!p.inj[i])
delete p.inj[i];
});
return p;
},
function() { // init
return {inj: {}};
}
);
Here we assume that there might be rows of data with the same uid and different inj arrays. This is more general than needed for your sample data: you could probably do something simpler if there is indeed only one row of data for each uid.
To flatten out the resulting group, with we can use a "fake group" to create one group-like {key, value} data item for each [uid, inj] pair:
function flatten_group(group, field) {
return {
all: function() {
var ret = [];
group.all().forEach(function(kv) {
Object.keys(kv.value[field]).forEach(function(i) {
ret.push({
key: [kv.key, +i],
value: kv.value[field][i]
});
})
});
return ret;
}
}
}
var uidinjGroup = flatten_group(uidGroup, 'inj');
Fork of your fiddle
In the fiddle, I've added a bar chart to demonstrate filtering by UID. Filtering on the bar chart works, but filtering on the scatter plot does not. If you need to filter on the scatter plot, that could probably be fixed, but it could only filter on the uid dimension because your data is too course to allow filtering by inj.
I'm using a line chart (I think) for my data, and I'm trying to have red, yellow or green dots based upon the value of the data. The problem is, I can't even change the symbols used on the graph!
I'm using data pulled from a database, so I can't simply define the data within a series[] and then define the symbol from there, it's added using the chart.addSeries() function.
I'm sorry if this is a total noob question, I'm a total noob when it comes to JavaScript and Highcharts.
EDIT: For security reasons, I can't post the code.
Answer may not be 100% accurate, but I would do something like this:
// Loop over series and populate chart data
$.each(results.series, function (i, result) {
var series = chart.get(result.id);
//I think I have to do some sort of marker: set here
$.each(result.data, function (i, point) {
var x = point.x, // OR point[0]
y = point.y; // OR point[1]
result.data[i] = {
color: y > 100 ? 'red' : 'blue',
x: x,
y: y
}
});
if (series) {
series.update(result, false);
} else {
chart.addSeries(result, false);
}
});
chart.redraw();
As you can see, here I am adding color property to the point. Right now there is simple logic (value < 100), but you can apply there anything you want to, for example function which will return correct color etc.
Note that I am extracting x and y values. How to get them depends on how your data is formatted. It can be {x: some_valueX, y: some_valueY} or [some_valueX, some_valueY] or even some_valueY only.
Important: if you have a lot of points (1000+), don't forget to increase turboThreshold or disable it.