I have made a bar chart in Vega which have two columns per category. These columns can have positive or negative values. I have set my tickCount with this value: tickCount: 4
but depending on the data, the tickCount is sometimes less than 4. With the sample data below, the tickCount is just two. I’m quite confused on how the tickCount is being computed and displayed by Vega.
Is this an expected behavior and is it possible for the chart to always have a tickCount of 4 regardless of data passed?
Here is the current output which I expected to have 4 tick counts and a min value of -400 and max value of 400.
{
"$schema": "https://vega.github.io/schema/vega/v5.json",
"width": 817,
"height": 278,
"autosize": {"type": "fit", "contains": "padding"},
"padding": {"bottom": 2, "right": 9, "left": 5},
"config": {
"axis": {
"labelFont": "HelveticaNeueLTW01-55Roman",
"titleFont": "HelveticaNeueLTW01-55Roman"
}
},
"data": [
{
"name": "capital_flows",
"values": [
{"label": "Aug 2023", "capital_inflow": 0, "capital_outflow": 0},
{"label": "Sep 2023", "capital_inflow": 0, "capital_outflow": 0},
{"label": "Oct 2023", "capital_inflow": 0, "capital_outflow": 0},
{"label": "Nov 2023", "capital_inflow": 0, "capital_outflow": 0},
{"label": "Dec 2023", "capital_inflow": 0, "capital_outflow": -317.18},
{"label": "Jan 2024", "capital_inflow": 0, "capital_outflow": -117.19},
{"label": "Feb 2024", "capital_inflow": 0, "capital_outflow": 0},
{"label": "Mar 2024", "capital_inflow": 0, "capital_outflow": 0},
{"label": "Apr 2024", "capital_inflow": 0, "capital_outflow": 0},
{"label": "May 2024", "capital_inflow": 0, "capital_outflow": 0},
{"label": "Jun 2024", "capital_inflow": 0, "capital_outflow": 0},
{"label": "Jul 2024", "capital_inflow": 0, "capital_outflow": 0},
{"label": "Aug 2024", "capital_inflow": 0, "capital_outflow": 0}
],
"transform": [
{"type": "formula", "expr": "split(datum.label, ' ')", "as": "label"}
]
},
{
"name": "parsed",
"source": ["capital_flows"],
"transform": [
{
"type": "aggregate",
"fields": [
"capital_inflow",
"capital_inflow",
"capital_outflow",
"capital_outflow"
],
"ops": ["min", "max", "min", "max"],
"as": ["min_value1", "max_value1", "min_value2", "max_value2"]
},
{
"type": "formula",
"expr": "datum.max_value1 == 0 && datum.max_value2 == 0 ? 1 : datum.max_value1 > datum.max_value2 ? abs(datum.max_value1) : abs(datum.max_value2)",
"as": "mergedMax"
},
{
"type": "formula",
"expr": "datum.min_value1 == 0 && datum.min_value2 == 0 ? -1 : datum.min_value1 < datum.min_value2 ? abs(datum.min_value1) : abs(datum.min_value2)",
"as": "mergedMin"
},
{
"type": "formula",
"expr": "datum.mergedMax > datum.mergedMin ? datum.mergedMax : datum.mergedMin",
"as": "max"
},
{"type": "formula", "expr": "-(datum.max)", "as": "min"}
]
}
],
"scales": [
{
"name": "x",
"type": "band",
"range": "width",
"domain": {"data": "capital_flows", "field": "label"}
},
{
"name": "y",
"type": "linear",
"range": "height",
"nice": true,
"zero": false,
"domain": {"data": "parsed", "field": "limit"},
"domainMin": {"signal": "pluck(data('parsed'), 'min')"},
"domainMax": {"signal": "pluck(data('parsed'), 'max')"}
}
],
"axes": [
{
"orient": "bottom",
"scale": "x",
"zindex": 1,
"labelFontSize": 12,
"labelColor": "#85868C",
"labelAlign": "center",
"labelLineHeight": 16.8,
"labelFontWeight": "normal",
"labelPadding": 8,
"labelOpacity": 1,
"domain": false,
"domainColor": "#e5e5e5",
"domainWidth": 1,
"ticks": false
},
{
"orient": "left",
"scale": "y",
"zindex": 0,
"labelFontSize": 12,
"labelColor": "#85868C",
"labelLineHeight": 16.8,
"labelFontWeight": "normal",
"labelPadding": 8,
"labelOpacity": 1,
"domain": false,
"domainColor": "#e5e5e5",
"grid": true,
"gridOpacity": 0.5,
"ticks": false,
"tickCount": 4,
"gridDash": {"signal": "datum.value == 0 ? [0,0] : [4,4]"},
"encode": {
"labels": {
"update": {
"text": {
"signal": "if(abs(datum.value) >= 1e9, format(datum.value/1e9, '$,.1~f') + 'B', if(abs(datum.value) >= 1e6, format(datum.value/1e6, '$,.1~f') + 'M', if(abs(datum.value) >= 1e3, format(datum.value/1e3, '$,.1~f') + 'K', format(datum.value, '$,.2~f'))))"
}
}
}
}
}
],
"marks": [
{
"name": "inflow",
"type": "rect",
"from": {"data": "capital_flows"},
"encode": {
"enter": {
"xc": {"scale": "x", "field": "label", "band": 0.5},
"width": {"value": 14},
"y": {"scale": "y", "field": "capital_inflow"},
"y2": {
"scale": "y",
"value": 0,
"offset": {"signal": "datum.capital_inflow < 0 ? 1.5: 0"}
},
"fill": {"value": "#68c487"},
"cornerRadiusTopLeft": {
"signal": "datum.capital_inflow > 0 ? '1' : '0'"
},
"cornerRadiusTopRight": {
"signal": "datum.capital_inflow > 0 ? '1' : '0'"
},
"cornerRadiusBottomLeft": {
"signal": "datum.capital_inflow > 0 ? '0' : '1'"
},
"cornerRadiusBottomRight": {
"signal": "datum.capital_inflow > 0 ? '0' : '1'"
},
"stroke": {"signal": "datum.capital_inflow !== 0 ? '#68c487': null"},
"strokeWidth": {"value": 1.5},
"fillOpacity": {"signal": "datum.capital_inflow !== 0 ? 1 : 0"}
}
}
},
{
"name": "outflow",
"type": "rect",
"from": {"data": "capital_flows"},
"encode": {
"enter": {
"xc": {"scale": "x", "field": "label", "band": 0.5, "offset": 17},
"width": {"value": 14},
"y": {"scale": "y", "field": "capital_outflow"},
"y2": {
"scale": "y",
"value": 0,
"offset": {"signal": "datum.capital_outflow < 0 ? 1.5: 0"}
},
"fill": {"value": "#FFB066"},
"cornerRadiusTopLeft": {
"signal": "datum.capital_outflow > 0 ? '1' : '0'"
},
"cornerRadiusTopRight": {
"signal": "datum.capital_outflow > 0 ? '1' : '0'"
},
"cornerRadiusBottomLeft": {
"signal": "datum.capital_outflow > 0 ? '0' : '1'"
},
"cornerRadiusBottomRight": {
"signal": "datum.capital_outflow > 0 ? '0' : '1'"
},
"stroke": {"signal": "datum.capital_outflow !== 0 ? '#FFB066': null"},
"strokeWidth": {"value": 1.5},
"fillOpacity": {"signal": "datum.capital_outflow !== 0 ? 1 : 0"}
}
}
}
]
}
2
Answers
tickCount uses some internal logic and heuristics so unfortunately it can’t always guarantee you will see the number of ticks you specify. You can control it with the values attribute though if you want full control. In your case, you’re overriding through the use of domainMin (-317) and domainMax (317). For instance:
produced by the following. You need to ensure you calculation for your domain min and max are appropriate.
What about something like this: