Forum Discussion
Deneb Vega Lite Sub Columns?
- 1 year ago
have you considered using the standard visual?
- 1 year ago
Deneb/Vega(-Lite) newb, but tried to tackle this as a good learning experience. Playing around with this, from what I can tell, if you want nested column facets, the only way to do it is with the facet operator (i.e. facet/spec).
General pattern is:
"facet": { "column": { ... } }, "spec": { "facet": { "column": { ... } }, "spec": { "mark": { ... }, "encoding": { ... } } }I tried to figure out how to get something close to your desired output. The closest I got was the following:
A note before I share the vega lite json (tldr: we need to construct and pass a measure along with your other columns in order to get the right scale throughout) :
- The only way I was able to get rid of the repeating Y axis to give a continuous temporal axis look, was to turn the y axis off completely.
- But since we still want one axis on the left, I ended up creating a dummy visual to stick in the front of an hconcat with the main visual. E.g. ... hconcat: [ {dummy visual to get axis}, {main nested facet visual} ]
- I then had the issue of how to ensure that the y-axis scale of the dummy and the actual matched up. Additionally, the y-axis scale of the actual visual was off anyway (I'm guessing that this had something to do how the underlying data at that level was not being filtered by the nested Location facet context).
- My solution, while a bit brute force-ish, was to just calculate the max [Value_01 + Value_02] by YearMonth | Location | Type, and pass that into Deneb to kind of hard-code in the correct scale
- (I feel like there is perhaps a more elegant approach to this by doing transforms within lower layers to massage the underlying data to just provide the right outputs and scales. I got this working for the dummy axis, but doing it with the facets was creating new complexities so I just gave up as the measure approach was already working at that point)
Measure to also pass into Deneb:
MaxY = VAR _iter = CALCULATETABLE( GENERATE( ALL( Sheet1[Location], Sheet1[Type] ), SUMMARIZE( GENERATE( VALUES(Sheet1[Date]), ROW( "YearMonth", FORMAT( Sheet1[Date], "YYYY-MMM" ) ) ), [YearMonth], "Val01+Val02", CALCULATE(SUM(Sheet1[Value_01])) + CALCULATE(SUM(Sheet1[Value_02])) ) ), REMOVEFILTERS(Sheet1) ) RETURN CONVERT( MAXX( _iter, [Val01+Val02] ) * 1.1, //tailor factor to control vertical buffer on max value INTEGER )So, Values well of the visual should look like:
And 'dataset' when viewing the Deneb's source data table will look like:
We will then, regardless of the current input data object (datum), grab MaxY value as needed with:
{"expr":"data('dataset')[0].MaxY"}With that setup, here is the Vega-Lite JSON spec to get the visual I shared at the top:
{ "data": {"name": "dataset"}, "transform": [{"fold": ["Value_01", "Value_02"], "as": ["MT", "MTvalue"]}], "spacing": 0, "bounds": "flush", "hconcat": [ { "bounds": "flush", "view": {"stroke": "transparent"}, "width": 1, "mark": {"type": "bar", "clip": true}, "encoding": { "y": { "field": "Value_01", "type": "quantitative", "aggregate": "sum", "scale": {"domainMax": {"expr": "data('dataset')[0].MaxY"}}, "axis": {"title": "Total Value"} }, "opacity": {"value": 0} } }, { "spacing": 0, "resolve": {"scale": {"x": "independent"}}, "facet": { "column": { "field": "Date", "timeUnit": "yearmonth", "type": "temporal", "title": "Year Month", "header": { "labelExpr": "[timeFormat(datum.value, '%Y'), timeFormat(datum.value, '%b')]" } } }, "spec": { "spacing": 0, "bounds": "flush", "resolve": {"scale": {"x": "independent"}}, "facet": { "column": { "field": "Location", "type": "ordinal", "title": "Location" } }, "spec": { "width": {"step": 25}, "mark": {"type": "bar", "width": {"band": 0.9}}, "encoding": { "x": { "field": "Type", "type": "ordinal", "axis": {"title": null, "labelAngle": 0} }, "y": { "aggregate": "sum", "field": "MTvalue", "type": "quantitative", "axis": { "domain": false, "labels": false, "ticks": false, "title": false, "grid": true }, "scale": {"domainMax": {"expr": "data('dataset')[0].MaxY"}} }, "color": { "field": "MT", "type": "nominal", "legend": {"title": "MT"} } } } } } ] }So, pretty close I think to what you wanted. Some limitations / stuff I couldn't figure out / thoughts:- There are a lot of limits to setting up responsive sizing when you get into "multi-view displays" (includes facets among some other things): https://vega.github.io/vega-lite/docs/size.html#width-and-height-of-multi-view-displays
I tried to, among other things, dynamically set the height/width of the innermost facet, but found that expressions just didn't work with height/width at that nesting level. E.g. expressions/signals that pulled in pbiContainer.height worked fine at top level of the JSON spec, but neither they nor any defined params could be used where I actually needed them (innermost facet) - I could not for the life of me figure out how to draw one line or border of any kind around the facet header labels
- I could transform the data to sum Value_01 and Value_02 by YearMonth, Location, and Type and get a somewhat similar bar chart (in terms of bars, not grouped labels that you get ootb with facets) into a single-view display (meaning we can make the visual a lot more responsive per first bullet), but figuring out 2-3 levels of label hierarchy (and grid groups) seemed more complicated that the facet approach
Edit: repasted in same spec JSON but formatted by the Vega-Lite editor rather than PBI Deneb Vega-Lite editor. The former actually introduces a little less white space but in a way that, I think, makes it slightly more readable.
have you considered using the standard visual?
Morning Ibendlin,
Thanks for the suggestion.
I've tried the native graphs but later found that the graph that i'm trying to achieve would require much more details and customization of the graph, hence, i've decided to give Deneb a go.
Thanks
Jay