I would like to draw dynamic vertical marker line in my chart. Position of marker is available in dataset named ml_data in column ml_position. This is a dataset with one row and one column only. Is there a way how I can get value of ml_position in javascript? Currently I have the following working example:
function beforeDrawMarkerLine(axis, markerLine, icsc)
{
importPackage(Packages.org.eclipse.birt.chart.model.data.impl);
importPackage(Packages.org.eclipse.birt.chart.model.component.impl);
var ml_value = 20;
markerLine.setValue(NumberDataElementImpl.create(ml_value)) ;
}
The value is currently fixed (20). I would like to assign value of ml_position to ml_value.
My working solution:
Create global variable "mpos" and put your chart into simple table with one row a one column. Assign dataset with ml_position to table. This is important for executing dataset onFetch script before rendering chart. Finally set following scripts:
Dataset onFetch script:
reportContext.setPersistentGlobalVariable("mpos",row["ml_position"].toString());
Chart onRender script:
function beforeDrawMarkerLine(axis, markerLine, icsc)
{
importPackage(Packages.org.eclipse.birt.chart.model.data.impl);
importPackage(Packages.org.eclipse.birt.chart.model.component.impl);
var ci = icsc.getExternalContext().getScriptable().getPersistentGlobalVariable("mpos");
markerLine.setValue(NumberDataElementImpl.create(ci)) ;
}
Related
I am trying to recreate this example in Vega-Lite API in an Observable notebook. I am able to recreate the ruler with the multiple line series from another example in Observable. But I am having trouble adding tooltips, I would like to add the symbol ticker and the price of the stock. Here is my Observable notebook. Where would I put the tooltip specifications? Thanks!
plot = {
// select a point for which to provide details-on-demand
const hover = vl.selectSingle()
.encodings('x') // limit selection to x-axis value
.on('mouseover') // select on mouseover events
.nearest(true) // select data point nearest the cursor
.empty('none'); // empty selection includes no data points
// define our base line chart
const line = vl.markLine()
.data(stocks)
.encode(
vl.x().fieldT('date'),
vl.y().fieldQ('price'),
vl.color().fieldN('symbol'),
);
// shared base for new layers, filtered to hover selection
const base = line.transform(vl.filter(hover));
return vl.data(stocks)
.layer(
line,
// add a rule mark to serve as a guide line
vl.markRule({color:'#c0c0c0'})
.transform(
vl.filter(hover),
vl.pivot({pivot: 'symbol', value: 'price', groupby: ['date']}))
.encode(vl.x().fieldT('date'),
vl.tooltip().fieldQ('price')),
// add circle marks for selected time points, hide unselected points
line.markCircle()
.select(hover) // use as anchor points for selection
.encode(vl.opacity().if(hover, vl.value(1)).value(0),
vl.tooltip(['symbol','price']))
)
.render(); }
Here's how you use pivot on that example
vl.pivot('symbol').value('price').groupby( ['date']))
The pivot there helps you getting the data into table format, so you can have all the symbol prices available in one row. Here is a full working example of a Vega-Lite API multi-line series chart with tooltips:
https://observablehq.com/#vega/multi-series-line-chart-with-tooltip
I've been trying to use dc.js and crossfilter to both build charts and tables from a certain dataset.
So far building charts works fine, but I want to use the datatable functionality to build a small html table to summarize the data as follows:
|Year|TotalEmployees|
|2015|555|
|2016|666|
|2017|777|
My dataset has around 20 000 rows, here's a sample of the data:
var data = [
{"Year":"2015","Category":"1","NbEmployee":"51"},
{"Year":"2015","Category":"2","NbEmployee":"31"},
{"Year":"2015","Category":"3","NbEmployee":"14"}
{"Year":"2016","Category":"1","NbEmployee":"51"},
{"Year":"2016","Category":"2","NbEmployee":"55"},
{"Year":"2016","Category":"3","NbEmployee":"65"},
{"Year":"2017","Category":"1","NbEmployee":"76"},
{"Year":"2017","Category":"2","NbEmployee":"98"},
];
So far this piece of code returns one row of result per row of data, and although it feels like it should be a simple manipulation, I can't figure out the right syntax to build a summarized table with one row per year:
var ndx = crossfilter(data);
var tableDim = ndx.dimension(function(d) {
return d.Year;
});
var datatable = dc.dataTable("#dc-data-table");
datatable
.dimension(tableDim)
.group(function(d) {
d.NbEmployee += d.NbEmployee;
return d.Year;
})
.columns([
function(d) {return d.Year;},
function(d) {return d.NbEmployee;},
]);
I've tried countless times to apply the
.group().reduceSum()
functions to the dimension into a variable and then passing it to the .group() parameter, but I always end up with a compilation error, I'm pretty clueless right now.
The SQL translation of what I'm looking for is this:
SELECT
Year,
NbEmp = SUM(NbEmploye)
FROM DB
GROUP BY
Year
ORDER BY
Year
Thanks in advance for your help!
The dataTable's group is not a group - yes, pretty confusing to use this method to mean something completely different from what it means in all the other charts. Here, it's a function, everywhere else it's a crossfilter object.
The dataTable is unique out of the dc.js charts in that it reads its data from the .dimension() object. This is because it displays the raw rows of data, rather than aggregated data, by default.
However, it can be used to display a group instead. This works because the only method it actually calls on the dimension is .top(), if you choose to display in descending order.
If you want to display in ascending order, you can use a fake group to produce an object which supports the .bottom() method.
Good Evening Everyone,
I'm trying to take the data from a database full of hour reports (name, timestamp, hours worked, etc.) and create a plot using dc.js to visualize the data. I would like the timestamp to be on the x-axis, the sum of hours for the particular timestamp on the y-axis, and a new bar graph for each unique name all on the same chart.
It appears based on my objectives that using crossfilter.js the timestamp should be my 'dimension' and then the sum of hours should be my 'group'.
Question 1, how would I then use the dimension and group to further split the data based on the person's name and then create a bar graph to add to my composite graph? I would like for the crossfilter.js functionality to remain intact so that if I add a date range tool or some other user controllable filter, everything updates accordingly.
Question 2, my timestamps are in MySQL datetime format: YYYY-mm-dd HH:MM:SS so how would I go about dropping precision? For instance, if I want to combine all entries from the same day into one entry (day precision) or combine all entries in one month into a single entry (month precision).
Thanks in advance!
---- Added on 2017/01/28 16:06
To further clarify, I'm referencing the Crossfilter & DC APIs alongside the DC NASDAQ and Composite examples. The Composite example has shown me how to place multiple line/bar charts on a single graph. On the composite chart I've created, each of the bar charts I've added a dimension based off of the timestamps in the data-set. Now I'm trying to figure out how to define the groups for each. I want each bar chart to represent the total time worked per timestamp.
For example, I have five people in my database, so I want there to be five bar charts within the single composite chart. Today all five submitted reports saying they worked 8 hours, so now all five bar charts should show a mark at 01/28/2017 on the x-axis and 8 hours on the y-axis.
var parseDate = d3.time.format('%Y-%m-%d %H:%M:%S').parse;
data.forEach(function(d) {
d.timestamp = parseDate(d.timestamp);
});
var ndx = crossfilter(data);
var writtenDimension = ndx.dimension(function(d) {
return d.timestamp;
});
var hoursSumGroup = writtenDimension.group().reduceSum(function(d) {
return d.time_total;
});
var minDate = parseDate('2017-01-01 00:00:00');
var maxDate = parseDate('2017-01-31 23:59:59');
var mybarChart = dc.compositeChart("#my_chart");
mybarChart
.width(window.innerWidth)
.height(480)
.x(d3.time.scale().domain([minDate,maxDate]))
.brushOn(false)
.clipPadding(10)
.yAxisLabel("This is the Y Axis!")
.compose([
dc.barChart(mybarChart)
.dimension(writtenDimension)
.colors('red')
.group(hoursSumGroup, "Top Line")
]);
So based on what I have right now and the example I've provided, in the compose section I should have 5 charts because there are 5 people (obviously this needs to be dynamic in the end) and each of those charts should only show the timestamp: total_time data for that person.
At this point I don't know how to further breakup the group hoursSumGroup based on each person and this is where my Question #1 comes in and I need help figuring out.
Question #2 above is that I want to make sure that the code is both dynamic (more people can be handled without code change), when minDate and maxDate are later tied to user input fields, the charts update automatically (I assume through adjusting the dimension variable in some way), and if I add a names filter that if I unselect names that the chart will update by removing the data for that person.
A Question #3 that I'm now realizing I'll want to figure out is how to get the person's name to show up in the pointer tooltip (the title) along with timestamp and total_time values.
There are a number of ways to go about this, but I think the easiest thing to do is to create a custom reduction which reduces each person into a sub-bin.
First off, addressing question #2, you'll want to set up your dimension based on the time interval you're interested in. For instance, if you're looking at days:
var writtenDimension = ndx.dimension(function(d) {
return d3.time.hour(d.timestamp);
});
chart.xUnits(d3.time.hours);
This will cause each timestamp to be rounded down to the nearest hour, and tell the chart to calculate the bar width accordingly.
Next, here's a custom reduction (from the FAQ) which will create an object for each reduced value, with values for each person's name:
var hoursSumGroup = writtenDimension.group().reduce(
function(p, v) { // add
p[v.name] = (p[v.name] || 0) + d.time_total;
return p;
},
function(p, v) { // remove
p[v.name] -= d.time_total;
return p;
},
function() { // init
return {};
});
I did not go with the series example I mentioned in the comments, because I think composite keys can be difficult to deal with. That's another option, and I'll expand my answer if that's necessary.
Next, we can feed the composite line charts with value accessors that can fetch the value by name.
Assume we have an array names.
compositeChart.shareTitle(false);
compositeChart.compose(
names.map(function(name) {
return dc.lineChart(compositeChart)
.dimension(writtenDimension)
.colors('red')
.group(hoursSumGroup)
.valueAccessor(function(kv) {
return kv.value[name];
})
.title(function(kv) {
return name + ' ' + kv.key + ': ' + kv.value;
});
}));
Again, it wouldn't make sense to use bar charts here, because they would obscure each other.
If you filter a name elsewhere, it will cause the line for the name to drop to zero. Having the line disappear entirely would probably not be so simple.
The above shareTitle(false) ensures that the child charts will draw their own titles; the title functions just add the current name to those titles (which would usually just be key:value).
I have straight table with lot of data...
I need to create bar chart from this table - dimension to be year and expressions Column(1)-Column(2).
column(1) = sum(Saldo)
column(2) = sum(Saldo)/3
I try to set dimension to be year and expresion
sum(sum(Saldo) - (sum(Saldo)/3))
but I don't get same total like I get sum of rows of Column(1)-Column(2).
This is because I need to include in dimension Line.
How can I create bar chart with dimensions I have in straight table...?
Any idea?
You need to use AGGR function:
sum(aggr(sum(Saldo) - (sum(Saldo)/3),Document,Line,Item,Unit))
or just use plain math:
Sum(Saldo * (2/3)) or Sum(Saldo)*(2/3)
In a D3 or NVD3.js line graph, how can I select a particular line once the graph is rendered? For example, suppose I want to animate the stroke width on a line, like this:
d3.selectAll('path').transition().duration(2000).style("stroke-width", "20");
The above will select all paths, obviously, but I would like to select a particular series—for example, the Oranges series in a data set defined like this:
var data = [{key: "Apples", values: array1},{key: "Oranges", values: array2}]
I thought something this might work, but it did not:
d3.select('#chart svg').datum(data[1]).transition... // or alternatively,
d3.select('#chart svg').datum(data[1].values).transition...
I've been trying to figure it out using the Cumulative Line Chart example in the code editor here, with no success: http://nvd3.org/livecode/#codemirrorNav
This is a very basic question, but I'm new to D3 and have been unable to figure it out.
There are couple of simple ways that I can think of:
You can store each path in its own variable (or inside an array):
var path1 = graph.append("g").append("path").data([data1]).attr("class", "line1");
Now you can apply your transitions to just this path variable and it should work.
Another option is to give each path a unique class and then use d3.selectAll(".uniqueclassname") and apply your transitions.
In this fiddle, look at the tick function (specially for the following piece of code).
// redraw the lines
graph.select(".line1").attr("d", line).attr("transform", null);
path2.attr("d", line).attr("transform", null);
path3.attr("d", line).attr("transform", null);
graph.select(".line4").attr("d", line).attr("transform", null);