How to vertically align Vega-Lite stacked bar chart data labels - vertical-alignment

How can I align data labels in a stacked bar chart to be centered vertically within each bar segment? In the following example, you can see that the text is positioned at the top of each bar segment. Where a bar segment is thin, the data label overlaps the one below. I realize that having multiple adjacent thin segments would result in overlap even if labels were centered vertically, but that case is unlikely with my data set.
{
"$schema": "https://vega.github.io/schema/vega-lite/v2.json",
"data": {
"values": [
{"Value": 0.321, "Date": "09/30/2021", "Measure": "Measure 4"},
{"Value": 0.031, "Date": "09/30/2021", "Measure": "Measure 3"},
{"Value": 0.123, "Date": "09/30/2021", "Measure": "Measure 2"},
{"Value": 0.475, "Date": "09/30/2021", "Measure": "Measure 1"}
]
},
"width": 500,
"height": 250,
"resolve": {"scale": {"color": "independent"}},
"layer": [
{
"mark": "bar",
"encoding": {
"y": {
"field": "Value",
"type": "quantitative",
"axis": {"format": ".1%"}
},
"x": {"field": "Date", "type": "nominal", "axis": {"labelAngle": -45}},
"color": {"field": "Measure", "type": "nominal"}
}
},
{
"mark": {"type": "text"},
"encoding": {
"y": {"field": "Value", "type": "quantitative", "stack": "zero"},
"x": {"field": "Date", "type": "nominal"},
"color": {
"field": "Measure",
"type": "nominal",
"scale": {"range": ["white"]},
"legend": null
},
"text": {
"aggregate": "sum",
"field": "Value",
"type": "quantitative",
"format": ".1%"
}
}
}
]
}

You can do this using a stack and calculate transform. The text layer would look like this:
{
...
"transform": [
{"stack": "Value", "groupby": ["Date"], "as": ["lower", "upper"]},
{"calculate": "(datum.lower + datum.upper) / 2", "as": "midpoint"}
],
"encoding": {
"y": {"field": "midpoint", "type": "quantitative"},
...
}
}
You can see the full spec in the vega editor:

Related

Terraform AWS Dashboard - Widgets from nested list

Terraform beginner here. I am trying to create some widgets from a nested list. Group will be a "label" widget indicating the group followed by the metric widgets for the canaries related to the group. So the dashboard should look as follows:
Group 1
widget1, widget2 etc.
Group 2
widget3, widget4 etc.
Variable value:
dashboard = [
{
name = "Group-1",
canaries = ["canary1", "canary2", "canary3"]
},
{
name = "Group-2",
canaries = ["canary4", "canary5"]
}
]
Attempt at building json:
locals {
body = [for group in var.dashboard :
#Create text widget for Group name
{
"height": 1,
"width": 24,
"y": 4,
"x": 0,
"type": "text",
"properties": {
"markdown": "\n# > [${group.name}]\n"
}
}
#Attempt to create underlying widgets for group
[for canary in group.canaries :
{
{
"height": 3,
"width": 6,
"y": 5,
"x": 0,
"type": "metric",
"properties": {
"metrics": [
[ "CloudWatchSynthetics", "Failed", "CanaryName", "${canary}", { "label": "Canary failures count", "region": "us-west-2" } ]
],
"title": "Failed canary runs",
"period": 60,
"region": "us-west-2",
"stat": "Sum",
"view": "singleValue",
"setPeriodToTimeRange": true
}
}
}
] #TF Doesn't like the inclusion of nested loop here or my syntax is incorrect.
]
}
Resource creation:
resource "aws_cloudwatch_dashboard" "canary_dashboard" {
dashboard_name = "Canary-Dashboard"
dashboard_body = jsonencode({
"widgets": concat(local.body)
})
}
In my creation of body, Terraform complains about Missing close bracket on index, but I have triple checked that I am not missing a bracket or curly brace. How do I dynamically create the dashboard widgets from nested lists?
Edit
Including desired json output below as suggested by Jordan. In the end, there will be n number of groups, each having n number of canaries belonging to said group.
{
"widgets": [
{
"height": 1,
"width": 24,
"y": 4,
"x": 0,
"type": "text",
"properties": {
"markdown": "\n# Group1\n"
}
},
{
"height": 3,
"width": 6,
"y": 5,
"x": 6,
"type": "metric",
"properties": {
"metrics": [
[ "CloudWatchSynthetics", "Failed", "CanaryName", "Group1-Canary", { "label": "Canary failures count", "region": "us-west-2" } ]
],
"title": "Failed canary runs",
"period": 60,
"region": "us-west-2",
"stat": "Sum",
"view": "singleValue",
"setPeriodToTimeRange": true
}
},
{
"height": 1,
"width": 24,
"y": 4,
"x": 0,
"type": "text",
"properties": {
"markdown": "\n# Group2\n"
}
},
{
"height": 3,
"width": 6,
"y": 5,
"x": 6,
"type": "metric",
"properties": {
"metrics": [
[ "CloudWatchSynthetics", "Failed", "CanaryName", "Group2-Canary", { "label": "Canary failures count", "region": "us-west-2" } ]
],
"title": "Failed canary runs",
"period": 60,
"region": "us-west-2",
"stat": "Sum",
"view": "singleValue",
"setPeriodToTimeRange": true
}
},
]
}
You're trying to do something with list comprehension that Terraform doesn't allow (see where I've marked "HERE"):
locals {
body = [for group in var.dashboard :
#Create text widget for Group name
{
"height": 1,
"width": 24,
"y": 4,
"x": 0,
"type": "text",
"properties": {
"markdown": "\n# > [${group.name}]\n"
}
} <===== HERE
#Attempt to create underlying widgets for group
[for canary in group.canaries :
{
{
"height": 3,
"width": 6,
"y": 5,
"x": 0,
"type": "metric",
"properties": {
"metrics": [
[ "CloudWatchSynthetics", "Failed", "CanaryName", "${canary}", { "label": "Canary failures count", "region": "us-west-2" } ]
],
"title": "Failed canary runs",
"period": 60,
"region": "us-west-2",
"stat": "Sum",
"view": "singleValue",
"setPeriodToTimeRange": true
}
}
}
] #TF Doesn't like the inclusion of nested loop here or my syntax is incorrect.
]
}
If TF allowed you to do what you're trying to do, you'd end up with something like:
body = [
{
"height": 1,
"width": 24,
"y": 4,
"x": 0,
"type": "text",
"properties": {
"markdown": "\n# > [${group.name}]\n"
}
},
[
{
{
"height": 3,
"width": 6,
"y": 5,
"x": 0,
"type": "metric",
"properties": {
"metrics": [
[ "CloudWatchSynthetics", "Failed", "CanaryName", "${canary}", { "label": "Canary failures count", "region": "us-west-2" } ]
],
"title": "Failed canary runs",
"period": 60,
"region": "us-west-2",
"stat": "Sum",
"view": "singleValue",
"setPeriodToTimeRange": true
}
}
},
{
{
"height": 3,
"width": 6,
"y": 5,
"x": 0,
"type": "metric",
"properties": {
"metrics": [
[ "CloudWatchSynthetics", "Failed", "CanaryName", "${canary}", { "label": "Canary failures count", "region": "us-west-2" } ]
],
"title": "Failed canary runs",
"period": 60,
"region": "us-west-2",
"stat": "Sum",
"view": "singleValue",
"setPeriodToTimeRange": true
}
}
}
]
]
And I doubt that's what you're trying to do. If you can provide a sample of what you'd like the JSON to look like, we can show you how to achieve it.

Elasticsearch on fashion commerce

I have the following dataset indexed in elasticsearch
[
{
"id": "1",
"name": "Red T-shirt",
"size": "S",
"styleId": "R-1"
},
{
"id": "2",
"name": "Red T-shirt",
"size": "M",
"styleId": "R-1"
},
{
"id": "3",
"name": "Red T-shirt",
"size": "L",
"styleId": "R-1"
},
{
"id": "4",
"name": "White T-shirt",
"size": "S",
"styleId": "W-1"
},
{
"id": "5",
"name": "White T-shirt",
"size": "XL",
"styleId": "W-1"
}
]
In search result I want products grouped by styleId. Is it possible like bellow sample result? In most of the commerce websites like Amazon, Myntra, Flipkart I have seen they are doing the same what I am trying. I have tried many elasticsearch concepts like "collapse" but without success. Using kibana, and elasticsearch version 7.4. Any suggestions will be highly appreciated.
[
{
"id": "1",
"name": "Red T-shirt",
"size": "S",
"styleId": "R-1",
"sizes": [
{
"id": "2",
"name": "Red T-shirt",
"size": "M",
"styleId": "R-1"
},
{
"id": "3",
"name": "Red T-shirt",
"size": "L",
"styleId": "R-1"
}
]
},
{
"id": "4",
"name": "White T-shirt",
"size": "S",
"styleId": "W-1",
"sizes": [
{
"id": "5",
"name": "White T-shirt",
"size": "XL",
"styleId": "W-1"
}
]
},
]
You can use collapse with cardinality aggregation.
Cardinality will give group level count
{
"_source": "false",
"collapse": {
"field": "styleId.keyword",
"inner_hits": {
"name": "group-details",
"size": 5
}
},
"from": 0,
"size": 5,
"aggs": {
"type_count": {
"cardinality": {
"field": "styleId.keyword"
}
}
}
}

Vega Lite: Color scale for bar chart

I have this bar chart in vega lite in my Observable notebook
But I want to add a color scale to the bars so that the smallest numbers are red and the largest numbers are green and the numbers in between are yellow.
In order to do this -- I was thinking of setting up an ordinal scale with the domain as [0,5] since the numbers range from 0 to 5. The range of that scale would be ["red", "yellow", "green"]. But I'm just not sure how to apply that ordinal color scale to a vega lite chart. My code is below
barchart = vegalite ({
"data": {"values": barChartData},
"height": {"step": 17},
"title": "Gold",
"encoding": {
"y": {
"field": "program",
"type": "ordinal",
"sort": "-x"
},
"x": {
"aggregate": "sum",
"field": "index",
"title": "Gold",
"axis": null
}
},
"layer": [{
"mark": "bar"
}, {
"mark": {
"type": "text",
"align": "left",
"baseline": "middle",
"dx": 3
},
"encoding": {
"text": {"field": "index", "type": "quantitative"}
}
}]
})
Provide color or fill encoding in your bar chart and add your color as scales as done below or in editor:
var yourVlSpec = {
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"description": "A simple bar chart with embedded data.",
"data": {
"values": [{
"index": 0.03,
"program": "Deliciousness"
},
{
"index": 0.43,
"program": "Reno 911!"
},
{
"index": 0.07,
"program": "Curious Life and Death of"
},
{
"index": 0.01,
"program": "True Life"
},
{
"index": 4.21,
"program": "Two and a Half Men"
},
{
"index": 0.06,
"program": "How Far is Tattoo Far"
},
{
"index": 0.39,
"program": "Cheaters"
},
{
"index": 4.72,
"program": "Bar Rescue"
},
{
"index": 0.81,
"program": "Key & Peele"
},
{
"index": 0.25,
"program": "Drunk History"
},
{
"index": 1.32,
"program": "Tosh.O"
},
{
"index": 0.11,
"program": "Revenge Prank"
},
{
"index": 4.88,
"program": "Workaholics"
},
{
"index": 0.04,
"program": "My Wife and Kids"
},
{
"index": 0.05,
"program": "World of Weapons"
},
{
"index": 0.03,
"program": "Roseanne"
},
{
"index": 1.98,
"program": "Everybody Loves Raymond"
},
{
"index": 1.2,
"program": "Aerial America"
}
]
},
"height": {
"step": 17
},
"title": "Gold",
"encoding": {
"y": {
"field": "program",
"type": "ordinal",
"sort": "-x"
},
"x": {
"aggregate": "sum",
"field": "index",
"title": "Gold",
"axis": null
}
},
"layer": [{
"mark": "bar",
"encoding": {
"color": {
"field": "index",
"scale": {
"range": ["red", "yellow", "green"],
"type": "linear"
},
"legend": null
}
}
},
{
"mark": {
"type": "text",
"align": "left",
"baseline": "middle",
"dx": 3
},
"encoding": {
"text": {
"field": "index",
"type": "quantitative"
}
}
}
]
};
vegaEmbed("#vis", yourVlSpec);
<script src="https://cdn.jsdelivr.net/combine/npm/vega#5.20.2,npm/vega-lite#5.0.0,npm/vega-embed#6.17.0"></script>
<body>
<div id="vis"></div>
</body>

MongoDB create product summary collection

Say I have a product collection like this:
{
"_id": "5a74784a8145fa1368905373",
"name": "This is my first product",
"description": "This is the description of my first product",
"category": "34/73/80",
"condition": "New",
"images": [
{
"length": 1000,
"width": 1000,
"src": "products/images/firstproduct_image1.jpg"
},
...
],
"attributes": [
{
"name": "Material",
"value": "Synthetic"
},
...
],
"variation": {
"attributes": [
{
"name": "Color",
"values": ["Black", "White"]
},
{
"name": "Size",
"values": ["S", "M", "L"]
}
]
}
}
and a variation collection like this:
{
"_id": "5a748766f5eef50e10bc98a8",
"name": "color:black,size:s",
"productID": "5a74784a8145fa1368905373",
"condition": "New",
"price": 1000,
"sale": null,
"image": [
{
"length": 1000,
"width": 1000,
"src": "products/images/firstvariation_image1.jpg"
}
],
"attributes": [
{
"name": "Color",
"value": "Black"
},
{
"name": "Size",
"value": "S"
}
]
}
I want to keep the documents separate and for the purpose of easy browsing, searching and faceted search implementation, I want to fetch all the data in a single query but I don't want to do join in my application code.
I know it's achievable using a third collection called summary that might look like this:
{
"_id": "5a74875fa1368905373",
"name": "This is my first product",
"category": "34/73/80",
"condition": "New",
"price": 1000,
"sale": null,
"description": "This is the description of my first product",
"images": [
{
"length": 1000,
"width": 1000,
"src": "products/images/firstproduct_image1.jpg"
},
...
],
"attributes": [
{
"name": "Material",
"value": "Synthetic"
},
...
],
"variations": [
{
"condition": "New",
"price": 1000,
"sale": null,
"image": [
{
"length": 1000,
"width": 1000,
"src": "products/images/firstvariation_image.jpg"
}
],
"attributes": [
"color=black",
"size=s"
]
},
...
]
}
problem is, I don't know how to keep the summary collection in sync with the product and variation collection. I know it can be done using mongo-connector but i'm not sure how to implement it.
please help me, I'm still a beginner programmer.
you don't actually need to maintain a summary collection, its redundant to store product and variation summary in another collection
instead of you can use an aggregate pipeline $lookup to outer join product and variation using productID
aggregate pipeline
db.products.aggregate(
[
{
$lookup : {
from : "variation",
localField : "_id",
foreignField : "productID",
as : "variations"
}
}
]
).pretty()

Modifying elasticsearch score based on nested field value

I want to modify scoring in ElasticSearch (v2+) based on the weight of a field in a nested object within an array.
For instance, using this data:
PUT index/test/0
{
"name": "red bell pepper",
"words": [
{"text": "pepper", "weight": 20},
{"text": "bell","weight": 10},
{"text": "red","weight": 5}
]
}
PUT index/test/1
{
"name": "hot red pepper",
"words": [
{"text": "pepper", "weight": 15},
{"text": "hot","weight": 11},
{"text": "red","weight": 5}
]
}
I want a query like {"words.text": "red pepper"} which would rank "red bell pepper" above "hot red pepper".
The way I am thinking about this problem is "first match the 'text' field, then modify scoring based on the 'weight' field". Unfortunately I don't know how to achieve this, if it's even possible, or if I have the right approach for something like this.
If proposing alternative approach, please try and keep a generalized idea where there are tons of different similar cases (eg: simply modifying the "red bell pepper" document score to be higher isn't really a suitable alternative).
The approach you have in mind is feasible. It can be achieved via function score in a nested query .
An example implementation is shown below :
PUT test
PUT test/test/_mapping
{
"properties": {
"name": {
"type": "string"
},
"words": {
"type": "nested",
"properties": {
"text": {
"type": "string"
},
"weight": {
"type": "long"
}
}
}
}
}
PUT test/test/0
{
"name": "red bell pepper",
"words": [
{"text": "pepper", "weight": 20},
{"text": "bell","weight": 10},
{"text": "red","weight": 5}
]
}
PUT test/test/1
{
"name": "hot red pepper",
"words": [
{"text": "pepper", "weight": 15},
{"text": "hot","weight": 11},
{"text": "red","weight": 5}
]
}
post test/_search
{
"query": {
"bool": {
"disable_coord": true,
"must": [
{
"match": {
"name": "red pepper"
}
}
],
"should": [
{
"nested": {
"path": "words",
"query": {
"function_score": {
"functions": [
{
"field_value_factor": {
"field" : "words.weight",
"missing": 0
}
}
],
"query": {
"match": {
"words.text": "red pepper"
}
},
"score_mode": "sum",
"boost_mode": "replace"
}
},
"score_mode": "total"
}
}
]
}
}
}
Result :
"hits": [
{
"_index": "test",
"_type": "test",
"_id": "0",
"_score": 26.030865,
"_source": {
"name": "red bell pepper",
"words": [
{
"text": "pepper",
"weight": 20
},
{
"text": "bell",
"weight": 10
},
{
"text": "red",
"weight": 5
}
]
}
},
{
"_index": "test",
"_type": "test",
"_id": "1",
"_score": 21.030865,
"_source": {
"name": "hot red pepper",
"words": [
{
"text": "pepper",
"weight": 15
},
{
"text": "hot",
"weight": 11
},
{
"text": "red",
"weight": 5
}
]
}
}
]
}
The query in a nutshell would score a document that satisfies the must clause as follows : sum up the weights of the matched nested documents with the score of the must clause.

Resources