Is there a way to set a facet's header labelColor based on the facet's field? - vega-lite

I'm in Vega-Lite trying to set the labelColor of a facet's header using the datum value as an array index for an array of colors. With the below facet, the labelExpr correctly sets the text to show the color value corresponding to the datum value (e.g. label is "#6969FF" for value 1), but when I set labelColor.expr to "categoryColors[datum.value]", the expression is invalid and the label is hidden. (I know that categoryColors is available because "categoryColors[0]" works as expected.)
Searching around I haven't found a solution that works for my case, and I'm beginning to wonder if I just don't have datum available to me in the labelColor.
Is there a way to make this work? Thanks!
{
"$schema": "https://vega.github.io/schema/vega-lite/v4.json",
"data": {
"values": [{ "differentiator": 1 }, { "differentiator": 2 }]
},
"params": [{
"name": "categoryColors",
"value": ["#69ffff", "#6969ff", "#ff6969"]
}],
"facet": {
"row": {
"field": "differentiator",
"type": "ordinal",
"header": {
"labelColor": { "expr": "categoryColors[datum.value]"},
"labelExpr": "categoryColors[datum.value]"
}
}
},
"spec": {
"width": 184.0625,
"height": 154.0625,
"layer": [
{
"mark": {
"type": "rect"
}
}
]
}
}

Related

How to manually set a color of a LineSeries via json config in amcharts v4?

How to manually set a color of a LineSeries via json config in amcharts v4?
I've tried lots of properties and it had no effect:
...
"series": [
{
"type": "LineSeries",
"propertyFields": {
"stroke": "#color",
"fill": "#color"
}
},
{
"type": "LineSeries",
"color": "#color"
},
{
"type": "LineSeries",
"fill": "#color"
},
{
"type": "LineSeries",
"sprite": {
"color": "#color"
}
},
{
"type": "LineSeries",
"sprite": {
"fill": "#color"
}
},
{
"type": "LineSeries",
"stroke": {
"color": "#color"
}
},
{
"type": "LineSeries",
"stroke": {
"fill": "#color"
}
}
]
Does amcharts v4 have a simple and easy way to manually set a color of a line?
You almost had it. You have to set the stroke at the top-level of the LineSeries definition (see JSON tab in the docs):
"series": [{
"type": "LineSeries",
"stroke": "#567890",
// ...
},
// ...
]
JSON values line up very closely to the declarative syntax, hierarchy-wise. You can find more full JSON examples in the github repo.

Dynamically Change Y-Axis Field in Encoding Based on Selection Vega-Lite

How can I dynamically change a data field encoded for the y-axis based upon a selection? I am trying to build a visualization to display event count data over the 24 hours of a day, and I want the user to be able to select different timezones (e.g. EST, CST, MST, or PST).
To do this, I have built out a single selection where I specify all the options I list above in the parentheses, with EST being set as my default. I want to create a condition where when I chose another option besides EST, I see the visualization dynamically update. I've explored creating other hour fields specifically for those timeframes, or adding in condition logic to try to account for these dynamic changes, but I have not arrived at a good solution yet. Can anyone help out?
Here is an example of what a few lines of my data look like
"data": {
"values": [
{
"title_column":"example",
"Type": "Technology",
"Events": "100",
"Hour": "0",
"Date": "9/1/20",
"Time Period": "Last Time"
},
{
"title_column":"example",
"Type": "Technology",
"Events": "110",
"Hour": "1",
"Date": "9/1/20",
"Time Period": "Last Time"
},
and the visualization looks like this when it is put together, with it dynamically updating based on the selection:
And when my code is static, it looks like this:
"layer":[
{"mark":{
"type":"bar",
"point":true,
"color":"#FFC94E",
"height":15
},
"selection": {
"timezone": {
"type": "single",
"init": {"changer": "EST"},
"bind": {
"changer": {"input": "select",
"options": ["EST","CST (-1 Hour)","MST (-2 Hours)","PST (-3 Hours)"]}
}
}
},
"encoding":
{
"x":{"field":"Events",
"type":"quantitative",
"aggregate":"sum",
"axis":null},
"y": {"field":"Hour",
"type":"ordinal",
"axis":{
"labelSeparation":1,
"labelPadding":4,
"title":null
}
}
}}]
}
However, focusing in particular on the y encoding of the bottom part of the code, I would ideally like to make that dynamic. I'm thinking I could create calculations for each of the timezones and then write a condition that works like the following below, but I have not been able to get this to work. Any help is greatly appreciated!
"y": {
"condition": {
"selection": {"timezone" : "EST"},
"datum": "datum.Hour"
}
"condition": {
"selection": {"timezone" : "CST (-1 Hour)"},
"datum": "datum.Hour_CST"
}
...
}
Here is the link to my code:
vega editor.
Selections can only filter on column values, not column names. Fortunately, you can convert column names to column values by using a Fold Transform.
To accomplish what you want, I'd suggest the following:
Use a series of Calculate Transforms to calculate new columns containing the values you want to show.
Use a Fold Transform to stack these values into a single column with an associated key column.
Link the selection binding to the key column created in the fold transform.
Use a Filter Transform to filter the values based on the selection
Finally, add a row encoding so that the selected column is labeled on the axis.
Put together, it looks like this (open in vega editor):
{
"width": 300,
"data": {
"values": [...]
},
"transform": [
{"filter": {"field": "Time Period", "equal": "Last Time"}},
{"calculate": "datum.Hour - 0", "as": "EST"},
{"calculate": "datum.Hour - 1", "as": "CST (-1 Hour)"},
{"calculate": "datum.Hour - 2", "as": "MST (-2 Hours)"},
{"calculate": "datum.Hour - 3", "as": "PST (-3 Hours)"},
{
"fold": ["EST", "CST (-1 Hour)", "MST (-2 Hours)", "PST (-3 Hours)"],
"as": ["Zone", "Hour"]
},
{"filter": {"selection": "timezone"}}
],
"selection": {
"timezone": {
"type": "single",
"init": {"Zone": "EST"},
"bind": {
"Zone": {
"name": "timezone",
"input": "select",
"options": [
"EST",
"CST (-1 Hour)",
"MST (-2 Hours)",
"PST (-3 Hours)"
]
}
}
}
},
"mark": {"type": "bar", "point": true, "color": "#FFC94E", "height": 15},
"encoding": {
"x": {
"field": "Events",
"type": "quantitative",
"aggregate": "sum",
"axis": null
},
"y": {
"field": "Hour",
"type": "ordinal",
"axis": {"labelSeparation": 1, "labelPadding": 4, "title": null}
},
"row": {
"field": "Zone",
"type": "nominal",
"title": null
}
}
}

Labeled bar chart with fill encoding does not respect sorting

I am trying to make a sorted bar chart with labels and fill encoding. But when I add the the fill encoding it breaks the sort. Via the github issues it seems like there are ways to get around this, but I can seem find a solution.
Given the spec without using the fill encoding the sorting works as expected.
{
"$schema": "https://vega.github.io/schema/vega-lite/v3.json",
"data": {
"values": [
{
"a": "A",
"b": 28,
"color": "black"
},
{
"a": "B",
"b": 55,
"color": "grey"
},
{
"a": "C",
"b": 43,
"color": "red"
}
]
},
"encoding": {
"y": {
"field": "a",
"type": "ordinal",
"sort": {
"encoding": "x",
"order": "descending"
}
},
"x": {
"field": "b",
"type": "quantitative"
}
},
"layer": [
{
"mark": "bar"
},
{
"mark": {
"type": "text",
"align": "left",
"baseline": "middle",
"dx": 3
},
"encoding": {
"text": {
"field": "b",
"type": "quantitative"
}
}
}
]
}
When you add the fill encoding to the top level encoding object it breaks the sort with the following warning
"fill": {
"field": "color",
"type": "ordinal",
"scale": null
}
[Warning] Domains that should be unioned has conflicting sort properties. Sort will be set to true.
Full vega-editor here
Is there a work around for this.
It appear to relate to the these issues (maybe) #2536, #5408
Yep, the underlying issue is https://github.com/vega/vega-lite/issues/5048. In this particular case, adding color to once layer adds a stack transform to one part of the dataflow but not the other so we cannot merge it. This is a great test case. Can you add this example to a new github issue so we can try to resolve it?
You can manually fix this example by disabling stacking the x encoding.
"stack": null
See this spec.

Build bar chart in Vega with different colors for negative and positive values

So I need to build something like this using Vega library:
Now, I'm a super n00b, so please have mercy.
First solution: use some sort of conditional formatting (like in Excel): if bar value < 0, make it red. If bar value > 0, make it green. I could find some conditional syntax for Vega-Lite, which gave me hope, but how do I translate the syntax to normal Vega, I have no clue.
Second, I thought about using some color scheme for ranges, like the ones that have a threshold. But I got completely confused about what scale range type to use, and noticed there is a relationship between scale range type and color schemes, so yeah. Confused.
Then, my colleague suggested this: https://vega.github.io/editor/#/examples/vega-lite/layer_bar_annotations
So in the example, we can see that the value of the bar above the threshold has conditional formatting. So I tried filtering the data to obtain 2 subsets: values_lower_than_0 and values_higher_than_0 use them as sources for marks. But it seems like I don't know how to filter. My data looks like this:
[
{ "date": "2018-12-10", "difference": 20 },
{ "date": "2018-10-21", "difference": -10 }
...
]
So then I apply a transform on it:
...
{
'name': 'values_lower_than_0',
'source': 'temps',
'transform': [{ 'type': 'filter', 'expr': 'datum.difference.Value < 0' }]
}
But when I use the values_lower_than_0 in the marks nothing seems to be happening.
So, I have 2 questions:
Is this the best approach to build such a chart? (Tbh, it seems pretty convoluted to me).
If yes, then how am I supposed to get the two data sets and use them to obtain the correct colors?
A better approach would be the one where transforms on the dataset are not applied.
Taking the example from here
The idea is to set the y2 value as mid of Height. y will then adjust based on whether the value is negative or positive to below midHeight or above midHeight, respectively. Please refer to rect type marks config below.
{
"$schema": "https://vega.github.io/schema/vega/v4.json",
"width": 600,
"height": 360,
"autosize": "fit",
"data": [
{
"name": "table",
"url": "https://uat.gramener.com/vega/chart/data/pos-neg-items.json"
}
],
"scales": [
{
"name": "xscale",
"type": "band",
"domain": {
"data": "table",
"field": "category"
},
"range": "width",
"padding": 0.2,
"round": true
},
{
"name": "yscale",
"domain": {
"data": "table",
"field": "amount"
},
"nice": true,
"range": "height"
}
],
"marks": [
{
"name": "bars",
"type": "rect",
"from": {
"data": "table"
},
"encode": {
"enter": {
"x": {
"scale": "xscale",
"field": "category"
},
"width": {
"scale": "xscale",
"band": 1
},
"y": {
"scale": "yscale",
"field": "amount"
},
"y2": {
"signal": "scale('yscale', 0)"
},
"fill": {
"signal": "datum['amount'] > 0 ? '#5CB38B' : '#E6685C'"
},
"tooltip": {
"signal": "datum"
}
}
}
},
{
"name": "item_score",
"type": "text",
"from": {
"data": "table"
},
"encode": {
"enter": {
"x": {
"scale": "xscale",
"field": "category"
},
"y": {
"scale": "yscale",
"field": "amount"
},
"dy": {
"signal": "datum['amount'] > 0 ? -4 : 14"
},
"dx": {
"signal": "bandwidth('xscale')/2"
},
"align": {
"value": "center"
},
"fill": {
"value": "black"
},
"text": {
"field": "amount"
},
"fontSize": {
"value": 12
}
}
}
},
{
"name": "item_name",
"type": "text",
"from": {
"data": "table"
},
"encode": {
"enter": {
"x": {
"scale": "xscale",
"field": "category"
},
"dx": {
"value": 20
},
"dy": {
"signal": "datum['amount'] > 0 ? height/2 + 14 : height/2 - 6"
},
"align": {
"value": "center"
},
"fill": {
"value": "#000000"
},
"text": {
"field": "category"
},
"fontSize": {
"value": 12
}
}
}
}
]
}
I'm so tired, I make stupid mistakes today! If somebody wants to use the third approach described above, then I was correct, I was just passing the wrong source name to the mark.
Another remark would be: you only need to calculate one subset, for the negative values (for example values_lower_than_0).
Once you do that, you'll have a mark called bars for all bars (like a default, with a green fill). The data source for this mark will be the default data. On top of that mark, you will apply a second mark, called negative_bars (for example), whose source will be values_lower_than_0 and you'll give it a red fill.
My question regarding the best approach still stands.

ElasticSearch Function score referring to nested attribute while other, same named attribute happens to exist in top level too

I am trying to calculate the _score of the document as a sum of IDs of my nested docs.
Here is the mapping:
PUT /test/property/_mapping
{
"property": {
"properties": {
"destination_id": {
"type": "long"
},
"rates": {
"type": "nested",
"properties": {
"id": {
"type": "long"
},
"end_date": {
"type": "date",
"format": "dateOptionalTime"
},
"start_date": {
"type": "date",
"format": "dateOptionalTime"
}
}
}
}
}
}
and here is my query:
GET /test/property/_search
{
"query": {
"filtered": {
"query": {
"nested": {
"path": "rates",
"score_mode": "sum",
"query": {
"function_score": {
"functions": [
{
"script_score": {
"script": "doc['id'].value"
}
}
]
}
}
}
}
}
}
}
It works fine and score is calculated properly when I don't have field id in my main (top level) document.
Something unexpected happens though as soon as I add document constructed that way:
POST /test/property/3
{
"destination_id": 1,
"id": 12,
"rates": [
{
"id": 9,
"end_date": "2014-10-15"
},
{
"id": 10,
"end_date": "2014-10-20"
}
]
}
after that all my _scores are reduced to 0.
Seems like the top attribute kind of 'overshadows' the nested ones…!?
anyway that I think shouldn't happen as I explicitly specify nested path, right?
Can anybody validate if that's the expected behaviour or bug?
and most importantly:
Is there a way to correctly refer to the nested attribute in such situations? or really anything else I could do to prevent situation where adding one such document would not break the searches that were working before??