Show negative values in Deneb column chart in red - powerbi

I've been playing around with the Deneb visual in PowerBI Desktop and (amongst many other things) have been trying to create a simple column chart that shows negative values in red and positive values in green, however can't for the life of me seem to get it working - I believe the condition/test in my script is correct, but it refuses to 'fire' when it's 'true'
I've read through the condition page of the Vega-Lite documentation https://vega.github.io/vega-lite/docs/condition.html and have a condition section within the encoding/color
I've added Month End and MonthYear columns from my Calendar table and an EBITDA measure from a fact table to the Deneb visual
Month End
MonthYear
EBITDA
31/7/2021
"Jul-21"
8277.56
31/8/2021
"Aug-21"
-15123.66
30/9/2021
"Sep-21"
9502.11
31/10/2021
"Oct-21"
13090.99
{
"data": {"name": "dataset"},
"mark": "bar",
"encoding": {
"x": {
"field": "MonthYear",
"sort": {"field": "Month End"}
},
"y": {
"field": "EBITDA",
"aggregate": "sum"
},
"color": {
"condition": {
"test": "datum['EBITDA']<0",
"value": "red"
},
"value": "green"
}
}
}
If I adjust the condition to be "test": "1==1" then the 'true' path works, so I assume I've got something wrong with my test line, though this seems to be correct per a lot of blogs, stackoverflow questions etc.
I've also tried using a "tranform:" channel to create a new Neg field in the Deneb dataset and referring to that field in my test, but it still won't adjust the colour.

It doesn’t like your aggregation. It looks like the data you are sending in is already aggregated by Power BI. If so, this will work:
"y": {
"field": "b",
"type": "quantitative"
},
View sample in the Vega Editor
If your data isn’t aggregated, add an aggregate transform like this:
"transform": [
{"aggregate": [{
"op": "sum",
"field": "b",
"as": "bsum"
}],
"groupby": ["a"]}
],
"mark": "bar",
"encoding": {
"x": {
"field": "a",
"sort": {"field": "a"}
},
"y": {
"field": "bsum",
"type": "quantitative"
},
"color": {
"condition": {
"test": "datum['bsum']<0",
"value": "red"
},
"value": "green"
}
}
}
Open the Chart in the Vega Editor

Related

Encode a Field using Conditions in Vega-Lite

OBJECTIVE
I'm trying to add data labels to my chart, however I have multiple bars layered on top of each other, and I need the data labels to hover over different bars depending on if values in a certain field are positive or negative.
ATTEMPT
This could be achieved by changing the "field" property of the "y" encoding using a condition. I've spent some time exploring Vega-Lite documentation and experimenting with some stuff, but I can't get it to work no matter what I try. Vega seems to ignore by condition. I'm also curious if I'm able to apply conditions to "mark" rather than "encoding". When values are negative, I'd like to change "dy" to 10 from -10.
Any suggestions?
'''
"mark": {
"type":"text",
"dy":-10
},
"encoding": {
"text": {
"field": "field_one"
},
"y": {
"condition":{
"test":"datum['test_data'] < 0",
"field": "field_one"
},
"field": "field_two"
}....
}
'''
A couple solutions exist.
Two Text Encodings:
One solution is to create two texts with varying y offsets and fields, and hide them depending on whether the values are positive or negative.
{
"mark": {
"type":"text",
"dy":-10
},
"encoding": {
"text": {
"condition":{
"test": "datum['test_data'] >= 0",
"type":"quantitative",
"field": "test_data"
}
"value": ""
},
"y": {
"field": "field_one",
"type": "quantitative"
}
}
},
{
"mark": {
"type":"text",
"dy":10
},
"encoding": {
"text": {
"condition":{
"test": "datum['test_data'] < 0",
"type":"quantitative",
"field": "test_data"
}
"value": ""
},
"y": {
"field": "field_two",
"type": "quantitative"
}
}
}
Transformation and Calculation
The other solution only solves the "dy" problem and was answered using another technique involving transform and calculate on GitHub.

Deneb plot (Vega-Lite) for Power BI: How to use an free scale y-axis with facet

I am using Deneb custom visual to repeat visual for different tasks. Is it possible to only show the relevent Y-axis values. Following data is used:
The following Vega-lite JSON is used:
{
"data": {"name": "dataset"},
"mark": {
"type": "bar",
"opacity": 1,
"tooltip": true,
"cornerRadius": 15
},
"encoding": {
"x": {
"field": "Earliest StartDate",
"type": "temporal"
},
"y": {
"field": "MachGrpCode",
"type": "nominal",
"axis": {
"title": null,
"grid": true,
"tickBand": "extent"
}
},
"row": {
"field": "ProdHeaderOrdNr",
"header": {"labelAngle": 0}
}
},
"resolve": {
"axis": {
"x": "independent",
"y": "independent"
}
}
}
Which results in:
Is it possible to only use the relevent task values (for 022 --> erase 6700 row)?
From what I've seen, this is a Vega bug. The recommended work-around is to use vconcat with a filter transform. If you only have a few ProdHeaderOrdNr, this is doable.
Open the Chart in the Vega Editor

How do I query an AWS OpenSearch index using a Vega visualization?

I have data in an index in JSON format, and I want to use a Vega visualization to display this (full Vega, not Vega-Lite). I've found however that every example out there is for Vega-Lite and all they're trying to do it stick their data into a time series graph. I'd like to do something different, and thus I find myself at a dead-end.
A sample doc in my index:
{
"_index": "myindex",
"_type": "doc",
"_id": "abc123",
"_version": 1,
"_score": null,
"timestamp": "2022-05-23T07:43:21.123Z",
"_source": {
"fruit": [{
"amount": 15,
"type": {
"grower_id": 47,
"grower_country": "US",
"name": "apple"
}
},
{
"amount": 43,
"type": {
"grower_id": 47,
"grower_country": "CAN",
"name": "apple"
}
},
{
"amount": 7,
"type": {
"grower_id": 23,
"grower_country": "US",
"name": "orange"
}
},
{
"amount": 14,
"type": {
"grower_id": 23,
"grower_country": "CAN",
"name": "orange"
}
}
]
}
}
What I want to do is create 2 text marks on the visualization that will display the sum of the values as follows.
Symbol1 = sum of all apples (i.e. all apples grown in the US and CAN combined)
Symbol2 = sum of all oranges (i.e. all oranges grown in the US and CAN combined)
I tried the following data element with no success:
"data": [{
"name": "mydata",
"url": {
"index": "myindex",
"body": {
"query": "fruit.type.name:'apple'",
},
}
}
]
However obviously this query isn't even correct. What I want to be able to do is return a table of values and then be able to use those values in my marks as values to drive the mark behaviour or color. I'm comfortable with doing the latter in Vega, but getting the data queried is where I'm stuck.
I've read and watched so many tutorials which cover Vega-Lite, but I'm yet to find a single working example for Vega on AWS OpenSearch.
Can anyone please help?

How to interpret user search query (in Elasticsearch)

I would like to serve my visitors the best results possible when they use our search feature.
To achieve this I would like to interpret the search query.
For example a user searches for 'red beds for kids 120cm'
I would like to interpret it as following:
Category-Filter is "beds" AND "children"
Color-filter is red
Size-filter is 120cm
Are there ready to go tools for Elasticsearch?
Will I need NLP in front of Elasticsearch?
Elasticsearch is pretty powerful on its own and is very much capable of returning the most relevant results to full-text search queries, provided that data is indexed and queried adequately.
Under the hood it always performs text analysis for full-text searches (for fields of type text). A text analyzer consists of a character filter, tokenizer and a token filter.
For instance, synonym token filter can replace kids with children in the user query.
Above that search queries on modern websites are often facilitated via category selectors in the UI, which can easily be implemented with querying keyword fields of Elasticsearch.
It might be enough to model your data correctly and tune its indexing to implement the search you need - and if that is not enough, you can always add some extra layer of NLP-like logic on the client side, like #2ps suggested.
Now let me show a toy example of what you can achieve with a synonym token filter and copy_to feature.
Let's define the mapping
Let's pretend that our products are characterized by the following properties: Category, Color, and Size.LengthCM.
The mapping will look something like:
PUT /my_index
{
"mappings": {
"properties": {
"Category": {
"type": "keyword",
"copy_to": "DescriptionAuto"
},
"Color": {
"type": "keyword",
"copy_to": "DescriptionAuto"
},
"Size": {
"properties": {
"LengthCM": {
"type": "integer",
"copy_to": "DescriptionAuto"
}
}
},
"DescriptionAuto": {
"type": "text",
"analyzer": "MySynonymAnalyzer"
}
}
},
"settings": {
"index": {
"analysis": {
"analyzer": {
"MySynonymAnalyzer": {
"tokenizer": "standard",
"filter": [
"MySynonymFilter"
]
}
},
"filter": {
"MySynonymFilter": {
"type": "synonym",
"lenient": true,
"synonyms": [
"kid, kids => children"
]
}
}
}
}
}
}
Notice that we selected type keyword for the fields Category and Color.
Now, what about these copy_to and synonym?
What will copy_to do?
Every time we send an object for indexing into our index, value of the keyword field Category will be copied to a full-text field DescritpionAuto. This is what copy_to does.
What will synonym do?
To enable synonym we need to define a custom analyzer, see MySynonymAnalyzer which we defined under "settings" above.
Roughly, it will replace every token that matches something on the left of => with the token on the right.
How will the documents look like?
Let's insert a few example documents:
POST /my_index/_doc
{
"Category": [
"beds",
"adult"
],
"Color": "red",
"Size": {
"LengthCM": 150
}
}
POST /my_index/_doc
{
"Category": [
"beds",
"children"
],
"Color": "red",
"Size": {
"LengthCM": 120
}
}
POST /my_index/_doc
{
"Category": [
"couches",
"adult",
"family"
],
"Color": "blue",
"Size": {
"LengthCM": 200
}
}
POST /my_index/_doc
{
"Category": [
"couches",
"adult",
"family"
],
"Color": "red",
"Size": {
"LengthCM": 200
}
}
As you can see, DescriptionAuto is not present in the original documents - though due to copy_to we will be able to query it.
Let's see how.
Performing the search!
Now we can try out our index with a simple query_string query:
POST /my_index/_doc/_search
{
"query": {
"query_string": {
"query": "red beds for kids 120cm",
"default_field": "DescriptionAuto"
}
}
}
The results will look something like the following:
"hits": {
...
"max_score": 2.3611186,
"hits": [
{
...
"_score": 2.3611186,
"_source": {
"Category": [
"beds",
"children"
],
"Color": "red",
"Size": {
"LengthCM": 120
}
}
},
{
...
"_score": 1.0998137,
"_source": {
"Category": [
"beds",
"adult"
],
"Color": "red",
"Size": {
"LengthCM": 150
}
}
},
{
...
"_score": 0.34116736,
"_source": {
"Category": [
"couches",
"adult",
"family"
],
"Color": "red",
"Size": {
"LengthCM": 200
}
}
}
]
}
The document with categories beds and children and color red is on top. And its relevance score is twice bigger than of its follow-up!
How can I check how Elasticsearch interpreted the user's query?
It is easy to do via analyze API:
POST /my_index/_analyze
{
"text": "red bed for kids 120cm",
"analyzer": "MySynonymAnalyzer"
}
{
"tokens": [
{
"token": "red",
"start_offset": 0,
"end_offset": 3,
"type": "<ALPHANUM>",
"position": 0
},
{
"token": "bed",
"start_offset": 4,
"end_offset": 7,
"type": "<ALPHANUM>",
"position": 1
},
{
"token": "for",
"start_offset": 8,
"end_offset": 11,
"type": "<ALPHANUM>",
"position": 2
},
{
"token": "children",
"start_offset": 12,
"end_offset": 16,
"type": "SYNONYM",
"position": 3
},
{
"token": "120cm",
"start_offset": 17,
"end_offset": 22,
"type": "<ALPHANUM>",
"position": 4
}
]
}
As you can see, there is no token kids, but there is token children.
On a side note, in this example Elasticsearch wasn't able, though, to parse the size of the bed: token 120cm didn't match to anything, since all sizes are integers, like 120, 150, etc. Another layer of tweaking will be needed to extract 120 from 120cm token.
I hope this gives an idea of what can be achieved with Elasticsearch's built-in text analysis capabilities!

CouchDB-Why my rerduce is always coming as false ? I am not able to reduce anything properly

I am new to CouchDB. I have a 9 gb dataset loaded into my couchdb. I am able to map everything correctly. But I cannot reduce any of the results using the code written in the reduce column. When i tried log, log shows that rereduce values as false. Do i need to do anything special while doing the Map() or how to set the rereduce value is TRUE??
A sample of my data is as follows:
{
"_id": "33d4d945613344f13a3ee92933b160bf",
"_rev": "1-0425ca93e3aa939dff46dd51c3ab86f2",
"release": {
"genres": {
"genre": "Electronic"
},
"status": "Accepted",
"videos": {
"video": [
{
"title": "[1995] bola - krak jakomo",
"duration": 349,
"description": "[1995] bola - krak jakomo",
"src": "http://www.youtube.com/watch?v=KrELXoYThpI",
"embed": true
},
{
"title": "Bola - Forcasa 3",
"duration": 325,
"description": "Bola - Forcasa 3",
"src": "http://www.youtube.com/watch?v=Lz9itUo5xtc",
"embed": true
},
{
"title": "Bola (Darrell Fitton) - Metalurg (MV)",
"duration": 439,
"description": "Bola (Darrell Fitton) - Metalurg (MV)",
"src": "http://www.youtube.com/watch?v=_MYpOOMRAeQ",
"embed": true
}
]
},
"labels": {
"label": {
"catno": "SKA005",
"name": "Skam"
}
},
"companies": "",
"styles": {
"style": [
"Downtempo",
"Experimental",
"Ambient"
]
},
"formats": {
"format": {
"text": "",
"name": "Vinyl",
"qty": 1,
"descriptions": {
"description": [
"12\"",
"Limited Edition",
"33 ⅓ RPM"
]
}
}
},
"country": "UK",
"id": 1928,
"released": "1995-00-00",
"artists": {
"artist": {
"id": 390,
"anv": "",
"name": "Bola",
"role": "",
"tracks": "",
"join": ""
}
},
"title": 1,
"master_id": 13562,
"tracklist": {
"track": [
{
"position": "A1",
"duration": "4:33",
"title": "Forcasa 3"
},
{
"position": "A2",
"duration": "5:48",
"title": "Krak Jakomo"
},
{
"position": "B1",
"duration": "7:50",
"title": "Metalurg 2"
},
{
"position": "B2",
"duration": "6:40",
"title": "Balloom"
}
]
},
"data_quality": "Correct",
"extraartists": {
"artist": {
"id": 388200,
"anv": "",
"name": "Paul Solomons",
"role": "Mastered By",
"tracks": "",
"join": ""
}
},
"notes": "Limited to 480 copies.\nA1 is a shorter version than that found on the 'Soup' LP.\nA2 ends in a lock groove."
}
}
My intention is to count the mapped values. My mapping function is as follows:
function(doc){
if(doc.release)
emit(doc.release.title,1)
}
Map results shows around 5800 results
I want to use the following functions in the reduce tab to count:
Reduce:
_count or _sum
It does not give single rounded value. Even i cannot get the simple _count operations right !!! :(
for screenshot,
Please help me !!!
What you got was the sum of values per title. What you wanted, was the sum of values in general.
Change the grouping drop-down list to none.
Check CouchdDB's wiki for more details on grouping.