Skip to main content
cancel
Showing results for 
Search instead for 
Did you mean: 

Compete to become Power BI Data Viz World Champion! First round ends August 18th. Get started.

Reply
distra
Frequent Visitor

Dynamic y-Axis calculation with Deneb

Hi everyone, hi @giammariam,

I have recently started exploring Deneb Visuals. In my particular case I want to display Average Cost per Month while offering to switch between currencies on my Dashboard. So I decided to try and adapt a Deneb template for a KPI Card with a line chart, that shows the KPI for the month the user hovers over: Here is what I (almost) want it to look like:

 

distra_0-1753863077802.png

 

This is what I achieved with the Template and some help by Claude and Copilot.
However, although I usually advocate for the zero-based approach to show the actual dimensions, in this case the changes month over month are so small, that the customers of this dashboard want this chart to be zoomed in on the area around the max and min values to actually see changes.

 

I calculated max and min  boundaries (global max + 20% of the global min value & global min value - 20% of global min value) but I can't get the axis to adapt to the borders I defined. To test the functionality I wanted to provide static values first to set my boundaries and after that apply my calculated values. But even with the static values I don't get to the desired result.

 

When I add the following section to my y-Encoding, my generated KPI card does not display any axis anymore.

My data range is from 228 to 232, so that should well be within the defined range.

"scale": {
    "zero": false,
    "domain": [150,250]
},

distra_1-1753864319780.png

If I use an even wider range like [80, 300] the Axis re-appears but will not use the entire range of the Line Chart anymore. Plus it also does not correctly scale the axis.

 

distra_2-1753864619877.png

 

Do I have a wrong understanding of the domain attribute? 

I am using Vega-Lite with a Specification- and a Config-File, I can provide both codes in full if needed.

 

Thanks for any help!

 

 

 

1 ACCEPTED SOLUTION
dm-p
Super User
Super User

Hi @distra,

 

The area mark is designed to have a zero baseline, so even if you cut the domain, it is trying to plot the remainder of the shape outside the domain (as the second y-value is zero). It would work with a line mark as there is only a single y-coordinate to worry about.

 

So, what is happening here is that your y-scale and axis are being correctly set to the desired range, but the size of your chart is condensing the vertical scale into the equivalent amount of space. You can see it "kind of" working if you make the visual container larger, e.g.:

 

dmp_0-1754343706774.png

(ignore the 'undefined'; that's a copy/paste issue with your sample data at my end)

 

One way to resolve this is to instruct Vega-Lite to clip the area mark. You should be able to make the following changes to your spec:

 

1. Update the scale as follows (removing "zero": false), e.g.:

{
  ...
  "encoding": {   
    "y": {
      "field": "Avg Rate dynamic Currency",
      "type": "quantitative",
      "scale": {
        "domain": [80, 235]  // or whatever you need min/max to be
      },
      ...
    },
    ...
  }
  ...
}

 

2. Update the area mark definition to include a clip as follows:

 

{
  ...
  "layer": [
    {
      "description": "sparkline definition",
      "mark": {
        "type":"area",
        "clip": true
      },
      ...
    },
    ...
  }
  ...
}

 

This will now cut the baseline of the plotted area, e.g.:

 

dmp_1-1754344042001.png

Hopefully this should be all you need to resolve. Good luck!

 

(and thanks for using Deneb!)

 

Daniel





Did I answer your question? Mark my post as a solution!

Proud to be a Super User!


On how to ask a technical question, if you really want an answer (courtesy of SQLBI)




View solution in original post

17 REPLIES 17
dm-p
Super User
Super User

Hi @distra,

 

The area mark is designed to have a zero baseline, so even if you cut the domain, it is trying to plot the remainder of the shape outside the domain (as the second y-value is zero). It would work with a line mark as there is only a single y-coordinate to worry about.

 

So, what is happening here is that your y-scale and axis are being correctly set to the desired range, but the size of your chart is condensing the vertical scale into the equivalent amount of space. You can see it "kind of" working if you make the visual container larger, e.g.:

 

dmp_0-1754343706774.png

(ignore the 'undefined'; that's a copy/paste issue with your sample data at my end)

 

One way to resolve this is to instruct Vega-Lite to clip the area mark. You should be able to make the following changes to your spec:

 

1. Update the scale as follows (removing "zero": false), e.g.:

{
  ...
  "encoding": {   
    "y": {
      "field": "Avg Rate dynamic Currency",
      "type": "quantitative",
      "scale": {
        "domain": [80, 235]  // or whatever you need min/max to be
      },
      ...
    },
    ...
  }
  ...
}

 

2. Update the area mark definition to include a clip as follows:

 

{
  ...
  "layer": [
    {
      "description": "sparkline definition",
      "mark": {
        "type":"area",
        "clip": true
      },
      ...
    },
    ...
  }
  ...
}

 

This will now cut the baseline of the plotted area, e.g.:

 

dmp_1-1754344042001.png

Hopefully this should be all you need to resolve. Good luck!

 

(and thanks for using Deneb!)

 

Daniel





Did I answer your question? Mark my post as a solution!

Proud to be a Super User!


On how to ask a technical question, if you really want an answer (courtesy of SQLBI)




distra
Frequent Visitor

Hi Daniel,

 

thank you for reaching out, this works like a charm! I used it with the same boundaries I had provided for the example chart with native Power BI visuals and it looks great!

distra_0-1754380925426.png


I was expecting it to be a simple task to replace the static values by my pre-calculated values, but it seems like Vega-Lite won't let me use the calculations in the domain directly.

I have made the following update to my transform section. Is there a way to directly reference my calculated upper and lower boundaries in the domain?

"transform": [
    {
      "joinaggregate": [
        {
          "op": "max",
          "field": "Month",
          "as": "max_period"
        },
        {
          "op": "max",
          "field": "Avg Rate dynamic Currency",
          "as": "max_avg_rate"
        },
        {
          "op": "min",
          "field": "Avg Rate dynamic Currency",
          "as": "min_avg_rate"
        }
      ]
    },
    {
      "calculate": "datum.max_avg_rate - datum.min_avg_rate",
      "as": "delta_max_min"
    },
    {
      "calculate": "datum.max_avg_rate + datum.delta_max_min",
      "as": "upper_limit_yAxis"
    },
    {
      "calculate": "datum.min_avg_rate - datum.delta_max_min",
      "as": "lower_limit_yAxis"
    }
  ],

 

Thanks a lot!

Dirk

I'm a bit short on time as it's later here, but you can do this with the extent transform, which will let you hoist your min/max to a top-level signal (called params in Vega-Lite). These are easier to access in expressions, which you can use in the scale domain.

 

Here's a quick, minimal example that you can use as a reference. This clips the area to the extents of the values (in conjunction with the clip approach discussed previously):

 

dmp_0-1754382250185.png

 

{
  "$schema": "https://vega.github.io/schema/vega-lite/v6.json",
  "data": {
    "url": "data/stocks.csv"
  },
  "transform": [
    {
      "filter": "datum.symbol==='GOOG'"
    },
    {
      "extent": "price",
      "param": "y_extents"
    }
  ],
  "mark": {
    "type": "area",
    "clip": true
  },
  "encoding": {
    "x": {
      "field": "date",
      "type": "temporal"
    },
    "y": {
      "field": "price",
      "type": "quantitative",
      "scale": {
        "domain": [{"expr": "y_extents[0]"}, {"expr": "y_extents[1]"}]
      }
    }
  }
}

 

You can explore this in the Vega Editor here.

 

The pertinent parts are as follows:

 

{
  ...
  "transform": [
    ...
    {
      "extent": "price",
      "param": "y_extents"
    }
  ],
  ...
}

 

This is a calculation that will take the min/max of a specified field and store them as a variable. You can see this in the Signal pane (in either Deneb or the Vega Editor), e.g.:

 

dmp_1-1754382427345.png

 

This is an array that can then be referenced in the domain evaluation, e.g.:

{
  "encoding": {
    ...
    "y": {
      ...
      "scale": {
        "domain": [
          {"expr": "y_extents[0]"},
          {"expr": "y_extents[1]"}
        ]
      }
    }
  }
}

 

This is an example of extraction of the direction values, but you could also use downstream params to manipulate these values (such as adding a padding factor). this is similar to a transform in a data stream.

 

Anyway, I hope this is clear and provides you with some ideas on how to apply it to your design. If you need me to implement your specification exactly, it would be helpful if you could supply an example .pbix file, and I can review it when I'm next back online.

 

Good luck!

 

Daniel





Did I answer your question? Mark my post as a solution!

Proud to be a Super User!


On how to ask a technical question, if you really want an answer (courtesy of SQLBI)




distra
Frequent Visitor

Daniel, you're my hero!

 

By replacing my existing min and max calculations with the proposed y_extents of my field 

Avg Rate dynamic Currency and recreating my calculations for upper and lower boundaries directly in the expression for the domain, it works exactly as intended. 
 
I already tested it, I can toggle through all currencies used in our organization and the chart will perfectly adapt the axis to the right values in that currency, zoomed into the desired area!

Thank you so much, have a good rest!

Glad it's working for you! It occurred to me (after some sleep) that you might be able to set the y2 encoding channel for the area mark to your derived minimum value from the join aggregate, which would minimize the number of moving parts. You would still need to set the y-scale's zero to false, but it works just as well without needing to apply the extents and expressions to the domain. Here's a quick revision using the above example:

 

{
  "$schema": "https://vega.github.io/schema/vega-lite/v6.json",
  "data": {
    "url": "data/stocks.csv"
  },
  "transform": [
      {
        "filter": "datum.symbol==='GOOG'"
      },
      {
        "joinaggregate": [
          {
            "op": "min",
            "field": "price",
            "as": "min_price"
          }
        ]
      }
  ],
  "mark": {
    "type": "area"
  },
  "encoding": {
    "x": {
      "field": "date",
      "type": "temporal"
    },
    "y": {
      "field": "price",
      "type": "quantitative",
      "scale": {
        "zero": false
      }
    },
    "y2": {
      "field": "min_price"
    }
  }
}

 And here it is in the Vega Editor.

 

Cheers,

 

Daniel





Did I answer your question? Mark my post as a solution!

Proud to be a Super User!


On how to ask a technical question, if you really want an answer (courtesy of SQLBI)




distra
Frequent Visitor

Thank you for further enhancing my understanding of the logic.

Since this was my first encounter with Deneb and Vega-Lite I yet have to make some big effort to fully understand the structure of it. But your comments and the link to the Vega-Editor to play around help a lot.

 

Thanks!

v-menakakota
Community Support
Community Support

Hi @distra ,
Thanks for reaching out to the Microsoft fabric community forum. 

Make sure the values you're plotting actually fall within the domain [150, 250] in the layer where you're applying the scale.

In layered visuals like KPI cards, ensure the scale override is set on the correct layer, ideally the one rendering the line/chart.

Make sure that axis is not set to null or being turned off in your encoding.

 

If I misunderstand your needs or you still have problems on it, please feel free to let us know.  

Best Regards, 
Community Support Team 

Thank you @v-menakakota,

 

all values are well within that range, these are my current values:

distra_0-1753881027211.png

 

Maybe I did not understand the concept of layers well enough to define the axis correctly. THis is my code that defines the encoding:

 

"encoding": {   
    "y": {
      "field": "Avg Rate dynamic Currency",
      "type": "quantitative",     
      "scale": {
        "zero": false,
        "domain": [80,300]
      },
      "axis": {
        "title": null,
        "labels": true,
        "ticks": true,
        "grid": true,
        "domain": true,
        "labelColor": "#605E5C",
        "labelFont": "Segoe UI Semibold",
        "labelFontSize": 10,
        "tickColor": "#E1DFDD",
        "gridColor": "#F3F2F1",
        "domainColor": "#E1DFDD"
      }
    },
    "x": {
      "field": "Month",
      "type": "temporal"
    }
  },

But actually, this is not defined inside the layer definitions, maybe that's the mistake?

Hi @distra ,

Please go through the below blog and solved issues which may help you in resolving the issues:

DENEB and Power BI Enthusiasts - Microsoft Fabric Community

Solved: Deneb Vega Lite Sub Columns? - Microsoft Fabric Community

Solved: haRe: dynamic y-axis with dynamic y-axis label - Microsoft Fabric Community

 

If I misunderstand your needs or you still have problems on it, please feel free to let us know.   

Best Regards, 
Community Support Team  

Hello @v-menakakota,

 

thank you for your reply. I checked the group and the two solved issues but unfortunately none of the displayed visuals contained what I need to achieve.

 

Maybe to make it clearer, all I need to achieve with this visual is to define a range for the y-axis which I want to see. Let me give you an example:

 

In my case I want the y axis to begin at the lower and at min_avg_rate - ( 40% of min_avg_rate). And the top end of the axis should be max_avg_rate + ( 40% of min_avg_rate).

 

And I tried to first scale the y-axis with static values, but even that gets me weird results. The boundaries are not respected and the y-Axis is not displayed properly anymore.

 

I am wondering if I defined the scale of the axis with the domain in the wrong place or in the wrong way.

Hi @distra ,

Please provide sample data that covers your issue or question completely, in a usable format (not as a screenshot).
Do not include sensitive information. Do not include anything that is unrelated to the issue or question.
Please show the expected outcome based on the sample data you provided.
How to provide sample data in the Power BI Forum - Microsoft Fabric Community

Best Regards, 
Community Support Team  

  

Hi @v-menakakota,

 

please find the sample data in a usable format. Sorry for not providing it in this format earlier, I thought it was only needed to verify my defined y-Axis range is not too small for my actual range of values.

__row__    Month         MonthName    Avg Rate dynamic Currency    Avg Rate formatted     
0

2025-Jan

Jan

232,9179259$ 232.92
1

2025-Feb

Feb

232,1264879$ 232.13
2

2025-Mar

Mar

232,1775412$ 232.18
3

2025-Apr

Apr

232,0739998$ 232.07
4

2025-May

May

230,4090975$ 230.41
5

2025-Jun

Jun

229,1148892$ 229.11
6

2025-Jul

Jul

230,0921796$ 230.09
7

2025-Aug

Aug

229,7630773$ 229.76
8

2025-Sep

Sep

229,6737823$ 229.67
9

2025-Oct

Oct

229,8118923$ 229.81
10

2025-Nov

Nov

229,8118923$ 229.81
11

2025-Dec

Dec

229,8935053$ 229.89

 

Here is a quick summary on what I have vs. what I want to achieve:
Bild_2025-08-01_100016489.png
I am very satisfied with the visual created, everything works well, besides the Y-Axis Range. By default it spans from 0 to a little above my maximum Value. As you can see in the table, my values for Avg Rate dynamic Currency are in a very small range, so the actual changes are hardly visible. So instead of the default range that should be about [0, 250], I want something like this:


Bild_2025-08-01_100648659.png

This was created using a standard Line chart in Power BI. But I would prefer to use my custom Deneb visual over this one, because when I hover I immediately see the current month's value in the proper format, without needing a tooltip.

 

I hope this is clearer, all that's missing is the ability to set the axis on my Deneb chart to a smaller range. For the reference chart with the standard line chart I used the exact same data with a y-Axis range of [225, 235].

Hi @distra   ,

Apologies for the delayed response. Could you please review the following configuration and confirm if it meets the requirement.

 

"scale": {

        "zero": false,

        "domain": [225, 235]

      }

If I misunderstand your needs or you still have problems on it, please feel free to let us know.   
Best Regards, 
Community Support Team  

Hello @v-menakakota,

 

no problem at all, take all the time you need please.

It was my understanding that the exact code you put there would scale my y-Axis to the samE range that I had shared in my example. However, when I add it, the visual output is the following:

 

distra_0-1754293198755.png

 

The y-Axis and area shading completely disappear, although the range is absolutely correct.

For your reference, I added it here:

"encoding": {   
    "y": {
      "field": "Avg Rate dynamic Currency",
      "type": "quantitative",
      "scale": {
        "zero": false,
        "domain": [225, 235]
      },
      "axis": {
        "title": null,
        "labels": true,
        "ticks": true,
        "grid": true,
        "domain": true,
        "labelColor": "#605E5C",
        "labelFont": "Segoe UI Semibold",
        "labelFontSize": 10,
        "tickColor": "#E1DFDD",
        "gridColor": "#F3F2F1",
        "domainColor": "#E1DFDD"
      }
    },
    "x": {
      "field": "Month",
      "type": "temporal"
    }
  },

 

This is the encoding block after the fransforms and before the layers (please reference my full configuration in my other comment if more context is needed). If I defined it in the wrong way or position, please let me know. I am quite confused why this does not work, because I had exactly the same approach.

 

Thank you for your continous support!

 

Hi @distra ,

Thank you for sharing the sample data.

We are currently trying to reproduce the issue on our side for further analysis. To help with that, could you please also share the corresponding pbix file? This will ensure we are aligned with your  visual configurations.

Thank you.

Hi @v-menakakota

unfortunately the pbix contains sensitive information which I cannot share. 

Since Deneb is a standalone visual, which can be configured completely decoupled from the rest of my file, what can I provide besides the sample dataset that could help you?
I will try to provide whatever is required to solve this.

 

Thank you!

distra
Frequent Visitor

Vega Lite Specification

{
  "description": "Area chart with text updated when hovering over a point. Author: Joanna Sekiewicz",
  "title": "Average Rate by Month",
  "data": {"name": "dataset"}, 
  "transform": [
    {
      "joinaggregate": [
        {
          "op": "max",
          "field": "Month",
          "as": "max_period"
        }
      ]
    },
    {
      "calculate": "datum.max_avg_rate + datum.min_avg_rate * 0.4",
      "as": "upper_limit_yAxis"
    },
    {
      "calculate": "datum.min_avg_rate + datum.min_avg_rate * 0.4",
      "as": "lower_limit_yAxis"
    }
  ],
  "encoding": {   
    "y": {
      "field": "Avg Rate dynamic Currency",
      "type": "quantitative",     
      "scale": {
        "zero": false,
        "domain": [80,300]
      },
      "axis": {
        "title": null,
        "labels": true,
        "ticks": true,
        "grid": true,
        "domain": true,
        "labelColor": "#605E5C",
        "labelFont": "Segoe UI Semibold",
        "labelFontSize": 10,
        "tickColor": "#E1DFDD",
        "gridColor": "#F3F2F1",
        "domainColor": "#E1DFDD"
      }
    },
    "x": {
      "field": "Month",
      "type": "temporal"
    }
  },
  "layer": [
    {
      "description": "sparkline definition",
      "mark": "area",
      "encoding": {
        "y": {"field": "Avg Rate dynamic Currency"}
      }
    },
    {
      "description": "points activated by hovering over",
      "params": [
        {
          "name": "hoverPoint",
          "select": {
            "type": "point",
            "on": "pointermove",
            "encodings": ["x"],
            "clear": "pointerout",
            "nearest": true
          }
        }
      ],
      "encoding": {
        "y": {"field": "Avg Rate dynamic Currency"},
        "opacity": {"value": 0}
      },
      "mark": {"type": "point"}
    },
    {
      "description": "details for hovered point (or the last one)",
      "transform": [
        {
          "calculate": "isDefined(hoverPoint.Month) ? hoverPoint.Month[0] == datum.Month : datum.Month==datum.max_period",
          "as": "displayDetails"
        },
        {
          "filter": "datum.displayDetails"
        }
      ],
      "layer": [
        {
          "description": "point referring to displayed details",
          "mark": "point",
          "encoding": {
            "opacity": {"value": 1},
            "y": {"field": "Avg Rate dynamic Currency"}
          }
        },
        {
          "mark": "rule",
          "encoding": {
            "opacity": {
              "condition": {
                "param": "hoverPoint",
                "empty": false,
                "value": 1
              },
              "value": 0
            }
          }
        },
        {
          "mark": {
            "type": "text",
            "size": 28,
            "fontWeight": "normal",
            "color": "#2D7A89"
          },
          "encoding": {
            "text": {
              "field": "Avg Rate formatted"
            },
            "x": {"value": -10},
            "y": {"value": -50}
          }
        },
        {
          "mark": {
            "type": "text",
            "size": 13
          },
          "encoding": {
            "text": {
              "field": "Month",
              "format": "%b-%Y",
              "formatType": "time"
            },
            "x": {"value": -10},
            "y": {"value": -25}
          }
        }
      ]
    }
  ]
}

 

Config

{
  "view": {
    "stroke": "transparent",
    "fill": "transparent"
  },
  "font": "Segoe UI semibold",
  "area": {
    "line": true,
    "interpolate": "monotone",
    "color": {
      "gradient": "linear",
      "x1": 1,
      "y1": 1,
      "x2": 1,
      "y2": 0,
      "stops": [
        {"offset": 0, "color": "#ffffff00"},
        {
          "offset": 1,
          "color": "#2D7A89A0"
        }
      ]
    }
  },
  "rule": {
    "strokeWidth": 0.3,
    "color": "grey"
  },
  "line": {
    "interpolate": "monotone",
    "color": "#2D7A89",
    "strokeWidth": 3,
    "strokeCap": "round",
    "strokeJoin": "round"
  },
  "point": {
    "filled": true,
    "size": 85,
    "color": "#2D7A89"
  },
  "text": {
    "align": "left",
    "fill": "#605E5C"
  },
  "axis": {
    "title": null,
    "labels": false,
    "ticks": false,
    "grid": false,
    "domain": false
  },
  "title": {
    "anchor": "start",
    "dx": 0,
    "dy": -8,
    "fontSize": 18.5,
    "fontWeight": "bold",
    "color": "#252423"
  }
}

Helpful resources

Announcements
August Power BI Update Carousel

Power BI Monthly Update - August 2025

Check out the August 2025 Power BI update to learn about new features.

August 2025 community update carousel

Fabric Community Update - August 2025

Find out what's new and trending in the Fabric community.