Let's start from this example:
https://vega.github.io/vega/examples/heatmap/
This is a glimpse of the underlying data:
date pressure temperature wind
2010-01-01T01:00:00 1016.6 4 3.8
2010-01-01T02:00:00 1016.6 3.9 3.8
2010-01-01T03:00:00 1016.7 3.8 3.8
2010-01-01T04:00:00 1016.7 3.8 3.7
2010-01-01T05:00:00 1016.5 3.7 3.8
2010-01-01T06:00:00 1016.4 3.7 3.8
In the above figure, the color of each cell in the heatmap represents the value of temperature from a single row in the data table.
Suppose we want to change the display, so that the color of each cell in the heatmap represents the average of multiple rows in the data table?
For example, suppose we want to apply binning to both the x-axis and to the y-axis.
For the y-axis, we would create 3 bins:
6am-11am, 12pm-6pm, 7pm-12am
For the x-axis, we would create 12 bins:
one bin for each month
Then, the heatmap would have 3 rows and 12 columns, and the color of each cell in the heatmap would correspond to the average of temperature values in the corresponding bin.
Questions:
How would you do this with vega? Can we use a transform to accomplish this task?
Should we use another javascript library to do the binning first, and then pass the result to vega?
Could you share a code snippet or suggest a library for efficient 2d binning (e.g., for a million items with continuous x,y positions)?
Suppose some of the bins correspond to no data (0 rows in the data table). Can we skip drawing them entirely? Or color them with a background color?
Thanks for your help!
You can use transform calculate to group them in your bands using ternary conditions and create a new field as timeGroup and then use it in your y-axis as done below or in
Try it in the editor: link
Here's the figure:
Here's the code:
{
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"autosize": {"contains": "padding", "type": "fit", "resize": true},
"width": 600,
"height": 150,
"padding": {"left": 15, "right": 60, "bottom": 5},
"data": {
"url": "data/seattle-weather-hourly-normals.csv",
"format": {"type": "csv", "parse": {"date": "date"}}
},
"transform": [
{"calculate": "month(datum.date)", "as": "cvDate"},
{"calculate": "utchours(datum.date)", "as": "hoursDate"},
{
"calculate": "0 < datum.hoursDate && datum.hoursDate < 7 ? '1 am - 6 am': 6 < datum.hoursDate && datum.hoursDate < 13 ? '7 am - 12 pm' : 12 < datum.hoursDate && datum.hoursDate < 19 ? '1 pm - 6 pm': '7 pm - 12 am'",
"as": "timeGroup"
},
{
"calculate": "datum.timeGroup == '1 am - 6 am' ? 0 : datum.timeGroup == '7 am - 12 pm' ? 1 : datum.timeGroup == '1 pm - 6 pm' ? 2 : 3",
"as": "orderRank"
}
],
"encoding": {
"y": {
"field": "timeGroup",
"type": "ordinal",
"sort": {"field": "orderRank", "order": "descending"}
},
"x": {
"field": "date",
"type": "ordinal",
"timeUnit": "month",
"sort": null
}
},
"layer": [
{
"mark": {"type": "rect"},
"encoding": {
"fill": {
"field": "temperature", "type": "quantitative",
"aggregate": "mean"
}
}
}
]
}
After looking through the vega-lite documentation again, I think I stumbled into an answer that looks good.
Try it in the editor: link
Here's the figure:
And the code:
{
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"data": {"url": "data/movies.json"},
"transform": [
{
"filter": {
"and": [
{"field": "IMDB Rating", "valid": true},
{"field": "Rotten Tomatoes Rating", "valid": true}
]
}
}
],
"mark": "rect",
"width": 300,
"height": 200,
"encoding": {
"x": {
"bin": {"maxbins": 60},
"field": "IMDB Rating",
"type": "quantitative"
},
"y": {
"bin": {"maxbins": 40},
"field": "Rotten Tomatoes Rating",
"type": "quantitative"
},
"color": {
"aggregate": "mean",
"field": "Worldwide Gross",
"type": "quantitative"
}
},
"config": {"view": {"stroke": "transparent"}}
}
And the link to documentation about how to use "aggregate":
https://vega.github.io/vega-lite/docs/aggregate.html#aggregate-op-def
Related
For this, I have been using the Vega Editor.
So I initially had the code for showing sin and cos absolutely fine, then when I tried to add tan, I understandably had some issues with scale, as the y values of tan were relatively huge when it approached the points where the function becomes undefined.
In order to tackle this, I added a range filter on the tan element, but it seems to be trying to join the points either side of when it is undefined. For some reason, this has also altered the sin line.
Here is the code I have so far:
{
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"description": "Plots three functions using a generated sequence.",
"width": 300,
"height": 150,
"data": {
"sequence": {
"start": 0,
"stop": 12.7,
"step": 0.1,
"as": "x"
}
},
"transform": [
{
"calculate": "sin(datum.x)",
"as": "sin(x)"
},
{
"calculate": "cos(datum.x)",
"as": "cos(x)"
},
{
"calculate": "tan(datum.x)",
"as": "tan(x)"
},
{
"filter": {
"field": "tan(x)",
"range": [-1, 1]
}
},
{
"fold": [
"sin(x)",
"cos(x)",
"tan(x)"
]
}
],
"mark": "line",
"encoding": {
"x": {
"type": "quantitative",
"field": "x"
},
"y": {
"field": "value",
"type": "quantitative"
},
"color": {
"field": "key",
"type": "nominal",
"title": null
}
}
}
How I can I get this data to not try and join up points but it instead let the tan line rise until it is out of the range? I also have very little idea what is happening with sin line, it is fine without the addition of the tan line.
Any help is greatly appreciated.
To avoid drawing a line between adjacent points, you'll need to split each segment into a separate group: the detail encoding is useful for this. Unfortunately, a grouping like this will apply to all the lines affected by the encoding, so to avoid breaks in the sin(x) and cos(x) curves, you'll need to split the tangent into a separate layer.
Here is how you might do this (open in editor):
{
"width": 300,
"height": 150,
"data": {"sequence": {"start": 0, "stop": 12.7, "step": 0.1, "as": "x"}},
"transform": [
{"calculate": "sin(datum.x)", "as": "sin(x)"},
{"calculate": "cos(datum.x)", "as": "cos(x)"},
{"calculate": "tan(datum.x)", "as": "tan(x)"},
{"calculate": "floor(datum.x / PI - 0.5)", "as": "phase"}
],
"encoding": {
"x": {"type": "quantitative", "field": "x"},
"y": {
"field": "value",
"type": "quantitative",
"scale": {"domain": [-3, 3]}
},
"color": {"field": "key", "type": "nominal", "title": null}
},
"layer": [
{"transform": [{"fold": ["sin(x)", "cos(x)"]}], "mark": "line"},
{
"transform": [{"fold": ["tan(x)"]}],
"mark": {"type": "line", "clip": true},
"encoding": {"detail": {"field": "phase", "type": "ordinal"}}
}
]
}
I've to change default bar color in a custom tfs widget, how can I do this?
I know there a "color" option but I can't find the correct syntax.
Thanks.
Here's my chart code:
chartOptions = {
"hostOptions": {
"height": "290",
"width": "300"
},
"chartType": "bar",
"series": [{
"data": [myBugs, myVuln, myCodeSm]
}],
"xAxis": {
"labelValues": ["Bugs", "Vulnerabilities", "Code smells"]
},
"specializedOptions": {
"showLabels": "true",
"size": 200
}
};
Try to add your custom colors like below:
chartOptions = {
"hostOptions": {
"height": "290",
"width": "300"
},
"colorCustomizationOptions": {
"customColors": ["#FF0000", "#00CC00", "#302772"]
},
"chartType": "bar",
"series": [{
"data": [myBugs, myVuln, myCodeSm]
}],
"xAxis": {
"labelValues": ["Bugs", "Vulnerabilities", "Code smells"]
},
"specializedOptions": {
"showLabels": "true",
"size": 200
}
};
In vss-web-extension-sdk/typings/charts.d.ts I found that customColors is a ColorEntry array. ColorEntry has two string properties: value, backgroundColor. With some trial and error I found out that value should be the label for which you want to set the color.
So I think this should work:
chartOptions = {
"hostOptions": {
"height": "290",
"width": "300"
},
"chartType": "bar",
"series": [{
"data": [myBugs, myVuln, myCodeSm]
}],
"xAxis": {
"labelValues": ["Bugs", "Vulnerabilities", "Code smells"]
},
"colorCustomizationOptions": {
"customColors": [
{backgroundColor: "#FF0000", value: "Bugs"},
{backgroundColor: "#00CC00", value: "Vulnerabilities"},
{backgroundColor: "#302772", value: "Code smells"}
]
},
"specializedOptions": {
"showLabels": "true",
"size": 200
}
};
I am using nvd3's multibar chart. The problem is the series I provide to the chart is not symmetrical. Like there will be values for a some series and others won't have.
[
{
"key": "ALL POS",
"color": "#39a5cf",
"values": [
{
"x": "4/01/2012",
"y": 54,
"series": 0
}
]
},
{
"key": "MIX POS",
"color": "#2227f4",
"values": [
{
"x": "4/01/2012",
"y": 34,
"series": 1
}
]
},
{
"key": "PURE POS",
"color": "#9fa9f7",
"values": []
}
]
You can see the pure pos series doesnt have values compared to the other two. Because of this the stacked effect is not working. Can someone help me regarding this?
Can I set Custom label values for X axis in amcharts js?
Type is xy.
On X axis I had labels 0, 10, 20, 30, 40, 50... Need to set 0, 1, 10, 100
You could just add the values into your data provider components, such as...
"dataProvider": [
{
"category": "0",
"column-1": 32
},
{
"category": "1"
},
{
"category": "10",
"column-1": 32
},
{
"category": "100"
},
{
"category": "1000",
"column-1": 14
}
];
To make the data on the chart start on the axis then ensure the "startOnAxis": true is within your categoryAxis section.
"categoryAxis":
{
"startOnAxis": true
}
I am trying to make horizontal grouped stacked bar graph in NVD3.js.
Everything works great until I got a "gap" in my JSON data like bellow:
[{
"key": "Education & news",
"values": [{
"label": "2014-02-26",
"value": 702
}, {
"label": "2014-02-27",
"value": 204
}, {
"label": "2014-02-28",
"value": 3213
}]
}, {
"key": "Entertainment",
"values": [{
"label": "2014-02-26",
"value": 21
},
//Missing entry for 2014-02-27
{
"label": "2014-02-28",
"value": 3213
}]
}]
The error which I got is Uncaught TypeError: Cannot read property '1' of undefined in d3.js. The example and the error of the problem I put on http://jsfiddle.net/vaa3V/
Can I somehow fill gaps automatically?
#shabeer90's comment is on track. You can use underscore.js to get the domain values and apply a sensible default.
//Find domain values
DEFAULT = 0
defaults = _.chain(data)
.pluck('values')
.flatten().pluck('label')
.uniq()
.value()
.map( function(item){ return {label:item, value:DEFAULT} })
// Group by 'label' so we can apply defaults
defaults = _.groupBy(defaults, 'label'))
// Apply defaults
_.each(data, function (series) {
grouped = _.groupBy(series.values, 'label')
series.values = _.flatten( _.defaults(grouped, defaults))
})
Should give you:
[
{
"key": "Education & news",
"values": [
{
"label": "2014-02-26",
"value": 702
},
{
"label": "2014-02-27",
"value": 204
},
{
"label": "2014-02-28",
"value": 3213
}
]
},
{
"key": "Entertainment",
"values": [
{
"label": "2014-02-26",
"value": 21
},
{
"label": "2014-02-28",
"value": 3213
},
{
"label": "2014-02-27",
"value": 0
}
]
}
]