Find everything you need to get certified on Fabric—skills challenges, live sessions, exam prep, role guidance, and more. Get started
My report has a deneb vega line chart and a table. I want to filter the table by brushing the chart and then clicking on the brushed area. The idea is to only apply cross filtering when required.
The filter works partially:
(1) brushing on the area works as expected:
(2) Then clicking on the brushed area filters the table:
However the brushed area disappears. I'd like to keep the brushed area after the click.
I've tried different debounce, throttle and consume values for the event - not sure what is causing the brushed area to disappear.
{
"data": [
{
"name": "dataset"
}
],
"width": 200,
"height": 200,
"signals": [
{
"name": "colour",
"value": "blue"
},
// brush
{
"name": "brush",
"value": 0,
"on": [
{
"events": "@main:pointerdown",
"update": "[x(), x()]"
},
{
"events": "[@main:pointerdown, window:pointerup] > window:pointermove!",
"update": "[brush[0], clamp(round(x()-brush[0])+brush[0], 0, width)]"
},
{
"events": {
"signal": "delta"
},
"update": "clampRange([anchor[0] + delta, anchor[1] + delta], 0, width)"
}
]
},
// anchor
{
"name": "anchor",
"value": null,
"on": [
{
"events": "@brush:pointerdown",
"update": "slice(brush)"
}
]
},
// xdown
{
"name": "xdown",
"value": 0,
"on": [
{
"events": "@brush:pointerdown",
"update": "x()"
}
]
},
// delta
{
"name": "delta",
"value": 0,
"on": [
{
"events": "[@brush:pointerdown, window:pointerup] > window:pointermove!",
"update": "x() - xdown"
}
]
},
// main_brush
{
"name": "main_brush",
// "push": "outer",
"on": [
{
"events": {
"signal": "brush"
},
"update": "span(brush) ? invert('x_scale', brush) : null"
}
]
},
// cross filter start ------------------------------------------
{
"name": "pbiCrossFilterSelection",
"value": [],
"on": [
// when mark is brushed apply cross filter
{
"events": {
"markname": "brush",
"type": "click",
"consume": true,
"debounce": 100,
"throttle": 100
},
"update": "pbiCrossFilterApply(event, \"datum['x'] >= main_brush[0] & datum['x'] <= main_brush[1]\" )"
}
]
}
// cross filter end ------------------------------------------
],
"scales": [
{
"name": "x_scale",
"range": "width",
"padding": 10,
"domain": {
"data": "dataset",
"field": "x"
}
},
{
"name": "y_scale",
"range": "height",
"padding": 10,
"domain": {
"data": "dataset",
"field": "y"
}
}
],
"axes": [
{
"orient": "bottom",
"scale": "x_scale"
},
{
"orient": "left",
"scale": "y_scale"
}
],
"marks": [
{
"name": "main",
"type": "group",
"encode": {
"enter": {
"x": {
"value": 0
},
"y": {
"value": 0
},
"height": {
"value": 200
},
"width": {
"value": 200
},
"fill": {
"value": "transparent"
}
}
},
"marks": [
// main line chart
{
"name": "main_line",
"type": "line",
"from": {
"data": "dataset"
},
"encode": {
"enter": {
"x": {
"scale": "x_scale",
"field": "x"
},
"y": {
"scale": "y_scale",
"field": "y"
}
},
"update": {
"stroke": {
"signal": "colour"
}
}
}
},
// brush area mark
{
"type": "rect",
"name": "brush",
"encode": {
"enter": {
"y": {
"value": 0
},
"height": {
"value": 200
},
"fill": {
"value": "#333"
},
"fillOpacity": {
"value": 0.2
}
},
"update": {
"x": {
"signal": "brush[0]"
},
"x2": {
"signal": "brush[1]"
}
}
}
}
]
}
]
}
Solved! Go to Solution.
Hi @vfx661,
When you apply cross-filtering, Deneb will currently re-initialize the Vega view. This is partially due to how Power BI updates the canvas to keep the selection state in sync across other visuals. I will be working on this for future versions of Deneb so that the Vega view retains a single lifecycle in line with that of the visual container and patches in updates to data from Power BI rather than re-building. Still, it is a significant change (and will likely materialize as Deneb 2.0, which I don't have an ETA for yet).
For now, though, this means signal values are not preserved between visual update events, so the conventional ways you handle this for a Vega view outside of Power BI will not work as expected. Therefore, you must try to construct the selected area from your dataset's __selected__ values after it updates. You may be able to do this using pluck and extent.
I don't have the available capacity to fix your specification up completely, but the following may get you closer to where you need to be:
1. Add a dataset that is filtered to selected values, e.g.:
{
"data": [
...
{
"name": "cross_filter_on",
"source": "dataset",
"transform": [
{
"type": "filter",
"expr": "datum.__selected__ == 'on'"
}
]
}
]
...
}
2. Add a signal to get the extents from this, e.g.:
{
...
"signals": [
...
{
"name": "cross_filter_extents",
"update": "extent(pluck(data('cross_filter_on'), 'x'))"
},
...
],
...
}
3. Modify your brush signal to use the scaled values from this, e.g.:
{
...
"signals": [
...
{
"brush": "cross_filter_extents",
"init": "[scale('x_scale', cross_filter_extents[0]), scale('x_scale', cross_filter_extents[1])]",
... // rest of your signal event logic
},
...
],
...
}
Hopefully this may help you on your way. Good luck!
Daniel
Proud to be a Super User!
My course: Introduction to Developing Power BI Visuals
On how to ask a technical question, if you really want an answer (courtesy of SQLBI)
Hopefully this link works to the pbix - I couldn't find a way to share from work so am using another account:
crossfiltering.pbix
Hi @vfx661,
When you apply cross-filtering, Deneb will currently re-initialize the Vega view. This is partially due to how Power BI updates the canvas to keep the selection state in sync across other visuals. I will be working on this for future versions of Deneb so that the Vega view retains a single lifecycle in line with that of the visual container and patches in updates to data from Power BI rather than re-building. Still, it is a significant change (and will likely materialize as Deneb 2.0, which I don't have an ETA for yet).
For now, though, this means signal values are not preserved between visual update events, so the conventional ways you handle this for a Vega view outside of Power BI will not work as expected. Therefore, you must try to construct the selected area from your dataset's __selected__ values after it updates. You may be able to do this using pluck and extent.
I don't have the available capacity to fix your specification up completely, but the following may get you closer to where you need to be:
1. Add a dataset that is filtered to selected values, e.g.:
{
"data": [
...
{
"name": "cross_filter_on",
"source": "dataset",
"transform": [
{
"type": "filter",
"expr": "datum.__selected__ == 'on'"
}
]
}
]
...
}
2. Add a signal to get the extents from this, e.g.:
{
...
"signals": [
...
{
"name": "cross_filter_extents",
"update": "extent(pluck(data('cross_filter_on'), 'x'))"
},
...
],
...
}
3. Modify your brush signal to use the scaled values from this, e.g.:
{
...
"signals": [
...
{
"brush": "cross_filter_extents",
"init": "[scale('x_scale', cross_filter_extents[0]), scale('x_scale', cross_filter_extents[1])]",
... // rest of your signal event logic
},
...
],
...
}
Hopefully this may help you on your way. Good luck!
Daniel
Proud to be a Super User!
My course: Introduction to Developing Power BI Visuals
On how to ask a technical question, if you really want an answer (courtesy of SQLBI)
Thank you - I really appreciate learning about the persistance of _selected_ and your suggested steps. I've gone ahead and implemented and there are two lingering issues - the second one (2) I don't understand.
1) After brushing, if the brushed area is clicked, then it recreates the brushed area according to your idea above:
a) do the brushing
b) click on the brushed area:
I believe the area changes because it is recreated from the x values which are less precise than the original brushed area. I'm guessing this can probably be fixed by 'quantising' the x-scale?
2) However if I click again on this new area, the brush disappears as before - I was expecting nothing to change.
*Updated*
It is working now - seems the brush disappearing has to do with the quantisation of the x-scale which is updated below.
{
"data": [
{
"name": "dataset",
"transform": [
{
"type": "extent",
"field": "x",
"signal": "extent_x"
}
]
},
// get dataset on refresh
{
"name": "selected",
"source": "dataset",
"transform": [
{
"type": "filter",
"expr": "datum.__selected__ == 'on'"
},
{
"type": "extent",
"field": "x",
"signal": "extent_sel_x"
}
]
}
],
"width": 500,
"height": 500,
"signals": [
{
"name": "colour",
"value": "blue"
},
{
"name": "c",
"update": "(481)/(1+extent_x[1]-extent_x[0])"
},
{
"name": "brush_init",
"update": "[(extent_sel_x[0]-1)*c, extent_sel_x[1]*c]"
},
// brush
{
"name": "brush",
"init": "brush_init",
"on": [
{
"events": "@main:pointerdown",
"update": "[x(), x()]"
},
{
"events": "[@main:pointerdown, window:pointerup] > window:pointermove!",
"update": "[round(brush[0]/c)*c, clamp(round(x()/c)*c, 0, width)]"
},
{
"events": {
"signal": "delta"
},
"update": "clampRange([anchor[0] + delta, anchor[1] + delta], 0, width)"
}
]
},
// anchor
{
"name": "anchor",
"value": null,
"on": [
{
"events": "@brush:pointerdown",
"update": "slice(brush)"
}
]
},
// xdown
{
"name": "xdown",
"value": 0,
"on": [
{
"events": "@brush:pointerdown",
"update": "x()"
}
]
},
// delta
{
"name": "delta",
"value": 0,
"on": [
{
"events": "[@brush:pointerdown, window:pointerup] > window:pointermove!",
"update": "round((x() - xdown)/c)*c"
}
]
},
// main_brush
{
"name": "main_brush",
// "push": "outer",
"on": [
{
"events": {
"signal": "brush"
},
"update": "span(brush) ? invert('x_scale', brush) : null"
}
]
},
// cross filter start ------------------------------------------
{
"name": "pbiCrossFilterSelection",
"value": [],
"on": [
// when mark is brushed apply cross filter
{
"events": {
"markname": "brush",
"type": "click"
},
"update": "pbiCrossFilterApply(event, \"datum['x'] >= main_brush[0] & datum['x'] <= main_brush[1]\" )"
}
]
}
// cross filter end ------------------------------------------
],
"scales": [
{
"name": "x_scale",
"range": "width",
"zero": false,
"padding": {"signal": "c/2"},
"domain": {
"data": "dataset",
"field": "x"
}
},
{
"name": "y_scale",
"range": "height",
"padding": 10,
"domain": {
"data": "dataset",
"field": "y"
}
}
],
"axes": [
{
"orient": "bottom",
"scale": "x_scale"
},
{
"orient": "left",
"scale": "y_scale"
}
],
"marks": [
{
"name": "main",
"type": "group",
"encode": {
"enter": {
"x": {
"value": 0
},
"y": {
"value": 0
},
"height": {
"signal": "height"
},
"width": {
"signal": "width"
},
"fill": {
"value": "transparent"
}
}
},
"marks": [
// main line chart
{
"name": "main_line",
"type": "line",
"from": {
"data": "dataset"
},
"encode": {
"enter": {
"x": {
"scale": "x_scale",
"field": "x"
},
"y": {
"scale": "y_scale",
"field": "y"
}
},
"update": {
"stroke": {
"signal": "colour"
}
}
}
},
// brush area mark
{
"type": "rect",
"name": "brush",
"encode": {
"enter": {
"y": {
"value": 0
},
"height": {
"signal": "height"
},
"fill": {
"value": "#333"
},
"fillOpacity": {
"value": 0.2
}
},
"update": {
"x": {
"signal": "brush[0]"
},
"x2": {
"signal": "brush[1]"
}
}
}
}
]
}
]
}
Your questions are getting a little more involved than I have the capacity for at present, so tagging @giammariam to see if he can offer any insight on this one
Proud to be a Super User!
My course: Introduction to Developing Power BI Visuals
On how to ask a technical question, if you really want an answer (courtesy of SQLBI)
I've got a brushed time series that cross filters a table when clicking on the brushed area mark. This has a problem that when the brushed area is dragged left or right, it activates the click event and filters the table. I want to be able to drag the area and not activate the filtering.
For example, I've brushed x=3 and then clicked the area to filter the table on the right.
Now I drag the brushed area to x=6:
This immediately filters the table for x=6, however the desired behaviour is to stay filtered at x=3 and only update the filter when clicking the brushed area. It seems the act of dragging includes a mouse down and mouse up which trigger the click.
One possible solution is to have a mark set up as a button (e.g. a text mark) and have it trigger the cross filter. The issue with this approach is that it seems the clicked mark needs to include the filtered data. The brushed area has this data. Is there a way to associate the brushed area dataset with a symbol mark that is being used as a button?
For example, I've added a text mark in the top left corner with text value 'activate filter' to be used as the filter trigger:
I want to be able to click on the text mark to activate the filter. How can I associate the data from the brushed area with the event?
Here's the code for the last screenshot.
*Updated
If I move the group mark down below the button, it partly works when clicking on the button to send the cross filter. To my dismay, when dragging the brushed area left or right after filtering with the button, the brushed area disappears. The idea is to maintain the brushed area so it can be dragged around and used as a cross filter when clicking on the button.
I feel each time it is two steps forward, one step back 😅
Click on the button now works:
But then dragging the brushed area with the mouse and releasing clears the brushed area unexpectedly.
{
"data": [
{
"name": "dataset",
"transform": [
{
"type": "extent",
"field": "x",
"signal": "extent_x"
}
]
},
// get dataset on refresh
{
"name": "selected",
"source": "dataset",
"transform": [
{
"type": "filter",
"expr": "datum.__selected__ == 'on'"
},
{
"type": "extent",
"field": "x",
"signal": "extent_sel_x"
}
]
}
],
"width": 500,
"height": 500,
"signals": [
{
"name": "colour",
"value": "blue"
},
{
"name": "c",
"update": "(481)/(1+extent_x[1]-extent_x[0])"
},
{
"name": "brush_init",
"update": "[(extent_sel_x[0]-1)*c, extent_sel_x[1]*c]"
},
// brush
{
"name": "brush",
"init": "brush_init",
"on": [
{
"events": "@main:pointerdown",
"update": "[x(), x()]"
},
{
"events": "[@main:pointerdown, window:pointerup] > window:pointermove!",
"update": "[round(brush[0]/c)*c, clamp(round(x()/c)*c, 0, width)]"
},
{
"events": {
"signal": "delta"
},
"update": "clampRange([anchor[0] + delta, anchor[1] + delta], 0, width)"
}
]
},
// anchor
{
"name": "anchor",
"value": null,
"on": [
{
"events": "@brush:pointerdown",
"update": "slice(brush)"
}
]
},
// xdown
{
"name": "xdown",
"value": 0,
"on": [
{
"events": "@brush:pointerdown",
"update": "x()"
}
]
},
// delta
{
"name": "delta",
"value": 0,
"on": [
{
"events": "[@brush:pointerdown, window:pointerup] > window:pointermove!",
"update": "round((x() - xdown)/c)*c"
}
]
},
// main_brush
{
"name": "main_brush",
// "push": "outer",
"on": [
{
"events": {
"signal": "brush"
},
"update": "span(brush) ? invert('x_scale', brush) : null"
}
]
},
// cross filter start ------------------------------------------
{
"name": "pbiCrossFilterSelection",
"value": [],
"on": [
// when mark is brushed apply cross filter
{
"events": {
"markname": "activate_filter",
"type": "click"
},
"update": "pbiCrossFilterApply(event, \"datum['x'] >= main_brush[0] & datum['x'] <= main_brush[1]\" )"
}
]
}
// cross filter end ------------------------------------------
],
"scales": [
{
"name": "x_scale",
"range": "width",
"zero": false,
"padding": {
"signal": "c/2"
},
"domain": {
"data": "dataset",
"field": "x"
}
},
{
"name": "y_scale",
"range": "height",
"padding": 10,
"domain": {
"data": "dataset",
"field": "y"
}
}
],
"marks": [
{
"name": "activate_filter",
"type": "text",
"encode": {
"enter": {
"xc": {
"value": 10
},
"yc": {
"value": 10
},
"text": {
"value": "activate filter"
}
}
}
},
{
"name": "main",
"type": "group",
"encode": {
"enter": {
"x": {
"value": 0
},
"y": {
"value": 20
},
"height": {
"signal": "height"
},
"width": {
"signal": "width"
},
"fill": {
"value": "transparent"
}
}
},
"axes": [
{
"orient": "bottom",
"scale": "x_scale"
},
{
"orient": "left",
"scale": "y_scale"
}
],
"marks": [
// main line chart
{
"name": "main_line",
"type": "line",
"from": {
"data": "dataset"
},
"encode": {
"enter": {
"x": {
"scale": "x_scale",
"field": "x"
},
"y": {
"scale": "y_scale",
"field": "y"
}
},
"update": {
"stroke": {
"signal": "colour"
}
}
}
},
// brush area mark
{
"type": "rect",
"name": "brush",
"encode": {
"enter": {
"y": {
"value": 0
},
"height": {
"signal": "height"
},
"fill": {
"value": "#333"
},
"fillOpacity": {
"value": 0.2
}
},
"update": {
"x": {
"signal": "brush[0]"
},
"x2": {
"signal": "brush[1]"
}
}
}
}
]
}
]
}
I've found that if the brush area is moved after cross filtering and the mouseup action is performed outside the chart area, then the brushed area remains. Not sure how the mouseup action is causing the brushed area to vanish.
PS: Is there some way to upload a PBIX without having to create a share? I can't create a shared file from work.
Is there some way to upload a PBIX without having to create a share?
you need to be a super user to be able to upload some (small) files.
Check out the September 2024 Power BI update to learn about new features.
Learn from experts, get hands-on experience, and win awesome prizes.
User | Count |
---|---|
27 | |
11 | |
8 | |
6 | |
6 |