"Total rows" in custom Power BI visualizations - powerbi

I have a question about creating the custom visualization in Power BI.
I want to implement a "total row" functionality which is available in the built-in matrix visualization. The main concept is to automatically sum-up every value and group it by the rows. This is how it's looks like on the matrix visualization:
But, to be honest, I don't know how to achieve this. I try different things but I can't receive this grouped values in the dataViews.
I tried to analyze the built-in matrix.ts code but it's quite different that the custom visualizations code. I found the customizeQuery method which set the subtotalType property to the rows and columns - I tried to add this in my code but I don't see any difference in the dataViews (I don't found the grouped value).
Currently my capabilities.dataViewMappings is set like this:
dataViewMappings: [
{
conditions: [
{ 'Rows': { max: 3 } }
],
matrix: {
rows: {
for: { in: 'Rows' },
},
values: {
for: { in: 'Values' }
},
},
}
]
Does anyone know how we could achieve this "total row" functionality?
UPDATE 1
I already found the solution: when we implement the customizeQuery method (in the same way as the customizeQuery method in the matrix.ts code), and then add the reference to it in the powerbi.visuals.plugins.[visualisationName+visualisationAddDateEpoch].customizeQuery then it works as expected (I receive in the dataViews[0].matrix.row.root children elements that has the total values from row).
The only problem now is that I don't know exactly how to add correctly this reference to the customizeQuery method. For example the [visualisationName+visualisationAddDateEpoch] is Custom1451458639997, and I don't know what those number will be (I know only the name). I created the code in my visualisation constructor as below (and it's working):
constructor() {
var targetCustomizeQuery = this.constructor.customizeQuery;
var name = this.constructor.name;
for(pluginName in powerbi.visuals.plugins) {
var patt = new RegExp(name + "[0-9]{13}");
if(patt.test(pluginName)) {
powerbi.visuals.plugins[pluginName].customizeQuery = targetCustomizeQuery;
break;
}
}
}
But in my opinion this code is very dirty and inelegant. I want to improve it - what is the correct way to tell the Power BI that we implement the custom customizeQuery method and it should use it?
UPDATE 2
Code from update 1 works only with the Power BI in the web browser (web based). On the Power BI Desktop the customizeQuery method isn't invoked. What is the correct way to tell the Power BI to use our custom customizeQuery method? In the code from PowerBI-visuals repository using PowerBIVisualPlayground we could declare it in the plugin.ts file (in the same way like the matrix visual is done):
export let matrix: IVisualPlugin = {
name: 'matrix',
watermarkKey: 'matrix',
capabilities: capabilities.matrix,
create: () => new Matrix(),
customizeQuery: Matrix.customizeQuery,
getSortableRoles: (visualSortableOptions?: VisualSortableOptions) => Matrix.getSortableRoles(),
};
But, in my opinion, from the Power BI Dev Tools we don't have access to add additional things to this part of code. Any ideas?

It seems you're missing the columns mapping in your capabilities. Take a look at the matrix capabilities (also copied for reference below) and as a first step adopt that structure initially. The matrix calculates the intersection of rows and columns so without the columns in capabilities its doubtful you'll get what you want.
Secondly, in the matrix dataview passed to Update you'll get a 'DataViewMatrixNode' with isSubtotal: true Take a look at the unit tests for matrix to see the structure.
dataViewMappings: [{
conditions: [
{ 'Rows': { max: 0 }, 'Columns': { max: 0 }, 'Values': { min: 1 } },
{ 'Rows': { min: 1 }, 'Columns': { min: 0 }, 'Values': { min: 0 } },
{ 'Rows': { min: 0 }, 'Columns': { min: 1 }, 'Values': { min: 0 } }
],
matrix: {
rows: {
for: { in: 'Rows' },
/* Explicitly override the server data reduction to make it appropriate for matrix. */
dataReductionAlgorithm: { window: { count: 500 } }
},
columns: {
for: { in: 'Columns' },
/* Explicitly override the server data reduction to make it appropriate for matrix. */
dataReductionAlgorithm: { top: { count: 100 } }
},
values: {
for: { in: 'Values' }
}
}
}],

Related

Numeric input in Power BI with min and max

I'm doing some custom R HTML visuals in Power BI. I can get a number input in Power BI by adding
"TestNumeric": {
"displayName": "Number",
"description": "test number",
"type": {
"numeric": true
}
}
in capabilities.json (and adapting src/settings.ts accordingly).
I would like to constrain this number input with a minimum and a maximum value. How can I do that?
I've found a solution. One has to modify the file src/visual.ts.
At the beginning, in the block of imports, add this import:
import VisualObjectInstanceEnumeration = powerbi.VisualObjectInstanceEnumeration;
Then, at the end, replace the function enumerateObjectInstances with:
public enumerateObjectInstances(options: EnumerateVisualObjectInstancesOptions): VisualObjectInstanceEnumeration {
var enumeratedObjects: VisualObjectInstanceEnumerationObject =
<VisualObjectInstanceEnumerationObject>VisualSettings.enumerateObjectInstances(
this.settings || VisualSettings.getDefault(), options
);
if (options.objectName === "YOUR_OBJECT_NAME") {
enumeratedObjects.instances[0].validValues = {
YOUR_PROPERTY_NAME: { numberRange: { min: 8, max: 20 } }
};
}
return enumeratedObjects;
}

Plotting multiple lines on a Cube.js line graph

Imagine a simple line graph plotting a person count (y-axis) against a custom time value (x-axis), as such:
Suppose you have another dimension, say specific groupings of people, how do you draw a separate line on this graph for each group?
You have to use the PivotConfig here an example I used in Angular
(EDIT) Here is the Query
Query = {
measures: ['Admissions.count'],
timeDimensions: [
{
dimension: 'Admissions.createdDate',
granularity: 'week',
dateRange: 'This quarter',
},
],
dimensions: ['Admissions.status'],
order: {
'Admissions.createdDate': 'asc',
},
}
(END EDIT)
PivotConfig = {
x: ['Admissions.createdDate.day'],
y: ['Admissions.status', 'measures'],
fillMissingDates: true,
joinDateRange: false,
}
Code to extract data from resultset :
let chartData = resultSet.series(this.PivotConfig).map(item => {
return {
label: item.title.split(',')[0], //title contains "ADMIS, COUNT"
data: item.series.map(({ value }) => value),
}
})
Result Object (not the one in the chart):
[{
"label": "ADMIS",
"data": [2,1,0,0,0,0,0]
},{
"label": "SORTIE",
"data": [2,1,0,0,0,0,0]
}]
Here is what the output looks like!
The chart renderer in the Developer Playground is meant to be quite simplistic; I'd recommend creating a dashboard app or using one of our frontend integrations in an existing project to gain complete control over chart rendering.

Replacing blank with zero for a mesure gives som unexpected extra rows

I have a little puzzle that annoys me in PowerBI/DAX. I'm not looking for workarounds - I am looking for an explanation of what is going on.
I've created some sample data to reconstruct the problem.
Here are my two sample tables written in DAX:
Events =
DATATABLE (
"Course", STRING,
"WeekNo", INTEGER,
"Name", STRING,
"Status", STRING,
{
{ "Python", 1, "Joe", "OnSite" },
{ "Python", 1, "Donald", "Video" },
{ "DAX", 2, "Joe", "OnSite" },
{ "DAX", 2, "Hillary", "Video" },
{ "DAX", 3, "Joe", "OnSite" },
{ "DAX", 3, "Hillary", "OnSite" },
{ "DAX", 3, "Donald", "OnSite" }
}
)
WeekNumbers =
DATATABLE ( "WeekNumber", INTEGER, { { 1 }, { 2 }, { 3 }, { 4 } } )
I have a table with events and another table with all weeknumbers and there is a (m:1) relation between them on the weekNo/weeknumber (I've given them different names to easily distinguish them in this example). I have a slicer in PowerBI on the weeknumber. And I have a table which shows aggregation and counts the participants based on the status with the following measures:
#OnSite = COUNTROWS(FILTER(events,Events[Status]="OnSite"))
#Video = COUNTROWS(FILTER(events,Events[Status]="Video"))
I visualize the two measures in a table together with the Course and the weekNo. With the slicer on weekNumber 3 there are nobody with status video so #video is blank. See screenshot.
Then I decided to create a new measure which should show a 0 instead of blank for the #video:
#VideoWithZero = VAR counter=COUNTROWS(FILTER(events,Events[Status]="Video"))
RETURN IF(ISBLANK(counter),0,counter)
I add the #VideoWithZero to the table and get a lot of extra rows in the table for the other weekNo's:
So my question is - Why do I get the extra rows for week 1 and 2 in the table? I would expect my filter on WeekNumber to filter them out.
The filter is being applied to the context of the query executed, and then the measures are calculated. Now the issue is that one of your measures is always returning a value (0), so regardless of your context it will always show a result, thus making it seem that it is ignoring the filter.
One way I tend to get implement this is by providing some additional context to when I might want to show 0 instead of blank. In your case it would be when one of the counts is not blank:
#OnSite =
VAR video = COUNTROWS(FILTER(events,Events[Status]="Video"))
VAR onsite = COUNTROWS(FILTER(events,Events[Status]="OnSite"))
RETURN IF(ISBLANK(video), onsite, onsite + 0) //+0 will force it not to be blank
#Video =
VAR video = COUNTROWS(FILTER(events,Events[Status]="Video"))
VAR onsite = COUNTROWS(FILTER(events,Events[Status]="OnSite"))
RETURN IF(ISBLANK(onsite), video, video + 0)
So on the OnSite measure it will check if there are Videos and if so, it adds +0 to the result of the OnSite count to force it not to be blank (and vice versa)
One other way could be to count total rows and subtract the ones different to the status you need:
#OnSite =
VAR total= COUNTROWS(Events[Status])
VAR notOnsite = COUNTROWS(FILTER(events,Events[Status]<>"OnSite"))
RETURN total - notOnsite
#Video =
VAR total= COUNTROWS(Events[Status])
VAR notVideo= COUNTROWS(FILTER(events,Events[Status]<>"Video"))
RETURN total - notVideo

How do I hide values past the x-axis in chartjs 2.0?

How do I hide values past the x-axis in chartjs 2.0? You will notice the chart juts past the -60 mark. The x-axis uses a time scale and I have the max and min values set.
Here's my chart configuration:
{
"type":"line",
"data":{
"datasets":[
{
"label":"Scatter Dataset",
"data":[
{
"x":"2016-09-16T16:36:53Z",
"y":88.46153846153845
},
...
{
"x":"2016-09-16T16:37:54Z",
"y":88.3076923076923
}
],
"pointRadius":0,
"backgroundColor":"rgba(0,0,255,0.5)",
"borderColor":"rgba(0,0,255,0.7)"
}
]
},
"options":{
"title":{
"display":true,
"text":"Water Level Over Last 60 Seconds"
},
"animation":false,
"scales":{
"xAxes":[
{
"type":"time",
"position":"bottom",
"display":true,
"time":{
"max":"2016-09-16T16:37:54Z",
"min":"2016-09-16T16:36:54.000Z",
"unit":"second",
"unitStepSize":5
},
"ticks":{
callback: function(value, index, values) {
return "-" + (60 - 5 * index);
}
}
}
],
"yAxes":[
{
"display":true,
"ticks":{
}
}
]
},
"legend":{
"display":false
}
}
}
You can achieve this using Chart.js plugins. They let you handle events occuring while creating, updating or drawing the chart.
Here, you'll need to affect before the chart is initialised :
// We first create the plugin
var cleanOutPlugin = {
// We affect the `beforeInit` event
beforeInit: function(chart) {
// Replace `ticks.min` by `time.min` if it is a time-type chart
var min = chart.config.options.scales.xAxes[0].ticks.min;
// Same here with `ticks.max`
var max = chart.config.options.scales.xAxes[0].ticks.max;
var ticks = chart.config.data.labels;
var idxMin = ticks.indexOf(min);
var idxMax = ticks.indexOf(max);
// If one of the indexes doesn't exist, it is going to bug
// So we better stop the program until it goes further
if (idxMin == -1 || idxMax == -1)
return;
var data = chart.config.data.datasets[0].data;
// We remove the data and the labels that shouldn't be on the graph
data.splice(idxMax + 1, ticks.length - idxMax);
data.splice(0, idxMin);
ticks.splice(idxMax + 1, ticks.length - idxMax);
ticks.splice(0, idxMin);
}
};
// We now register the plugin to the chart's plugin service to activate it
Chart.pluginService.register(cleanOutPlugin);
The plugin is basically a loop through the data to remove the values that shouldn't be displayed.
You can see this plugin working in a live example on jsFiddle.
For instance, the following chat with a min set to 2 and a max to 6 ...
... would give the following result :

Rails, Highchart maps - adding custom data

I need some basic assistance with a Highmap (via Highcharts) I am trying to put in my Rails 4 app. I suspect I have some fundamental misunderstanding of it but can't find any clear guidance.
See a simple fiddle taken from the documentation, here
http://jsfiddle.net/SimonWalsh/zpdc1btu/
What I ultimately need to do is provide membership numbers for each country so that it will be displayed much the same as the population density is in this map.
I know I need to provide my data and the means to join it to the map data in
series : [{
data : data,
mapData: Highcharts.maps['custom/world'],
joinBy: ['iso-a2', 'code'],
name: 'Population density',
states: {
hover: {
color: '#BADA55'
}
}
}]
In this example, I am guessing that the data is being pulled from an external source and that the map data is the 'iso-a2' part of the array.
If this is the case, then why can't I supply this with my data....as an example see the added array with my data.....(just one example given for Denmark)
var mydata = [
{
"iso-a2": "dk",
"value": 30
},
]
and then do
series : [{
data : mydata,
mapData: Highcharts.maps['custom/world'],
joinBy: ['iso-a2', 'value'],
name: 'Population density',
states: {
hover: {
color: '#BADA55'
}
}
}]
This does not work.....any guidance at all (other than simply pointing me to docs would be greatly appreciated)
The joinBy specifies on which value you map a country with your data. With
joinBy: ['iso-a2', 'code']
you say that the 'iso-a2' value of the mapData should be equal to the 'code' value of your data. Therefore, your data must have this format:
var mydata = [
{
"code": "dk",
"value": 30
},
/* ... */
]