Home > Software engineering >  D3.js: Is there a way to cap a "displayed" value at an axis max/min while retaining the &q
D3.js: Is there a way to cap a "displayed" value at an axis max/min while retaining the &q

Time:11-13

UPDATE: SOLVED

Most of you probably don't remember me from my previous question, Circles not updating correctly for multiline charts with two-level nested data. I managed to figure out/work around my issue there (although I still am not convinced that what I came up with is 100% correct); however, I now have another question.

My y-axis data (d.mval) has extreme outlier values. As a result, my charts get compressed, with quite a bit of blank space. See, for example, the "Cash Flow" chart in this JSFiddle: https://jsfiddle.net/etonblue/a98m52k4/3/. I've tried various combinations of median, mean, average, std dev, etc., to try to set the domains, but haven't yet found a solution that works consistently for all of my data. So I've resorted to hard-coding my domains:

    if (val == 1) {ydom = [-2,20]}
        else if (val == 2) {ydom = [-40,240]}
        else if (val == 3) {ydom = [-.5,1.5]}
        else if (val == 4) {ydom =  [-.6,.9]}
        else if (val == 5) {ydom = [-.6,.6]}
        else if (val == 6) {ydom = [-.16,.16]}
        else if (val == 7) {ydom = [-.5,3]}
        else if (val == 8) {ydom = [-1000000,2000000]}
        else if (val == 9) {ydom = [-1000000,2000000]}
        else if (val == 10) {ydom = [-4,10]}

which is not only ugly, it also means that any extreme values are now either far above or far below my displayed y-axis.

My question, is there a way to use D3 to cap a displayed value at the max/min of a domain while retaining the actual value (so that a tooltip shows the actual value rather than the displayed value)?

I'm currently using d3.v4.

UPDATE The solution:

var line = d3.line()
    .x(d => x(d.year))
    .y(d => d.mval < y.domain()[0] ? y(y.domain()[0]) : d.mval > y.domain()[1] ? y(y.domain()[1]) : y(d.mval))

and

.attr("cx", d => x(d.year))
.attr("cy", d => d.mval < y.domain()[0] ? y(y.domain()[0]) : d.mval > y.domain()[1] ? y(y.domain()[1]) : y(d.mval))

Updated fiddle here: https://jsfiddle.net/etonblue/6v924ory/2/.

The above code caps the y-axis value (in this case: d.mval) at the minimum (y.domain()[0]) or maximum (y.domain()[1]) values of the domain itself while not changing the "actual" value of d.mval (so it can be used in a tooltip).

Now I just need to figure out why my update function isn't working properly (e.g., If you click the "Annual Enrollment Change %" function, you can see two green lines (without dots) at the bottom of the chart that are clearly left over from the "Current Ratio" chart). However, this probably warrants a separate question.

Ugh. It's always something.

Thank you for the answer, I'm not sure I ever would have figured this out myself.

CodePudding user response:

The code you provided isn't too relevant to the solution. Working from your fiddle, the solution is in your callbacks for the y positions of your line and circles. For your line:

var line = d3.line()
    .x(d => x(d.year))
    .y(d =>  d.mval > 240 ? y(240) : y(d.mval)) //If mval is over 240 plot 240

Likewise for your circles cy:

.attr('cy', d =>  d.mval > 240 ? y(240) : y(d.mval))

More Generally, you could use the callback:

d => d.mval > y.domain()[1] ? y(y.domain()[1]) : y(d.mval)

I don't quit understand your setup,

  • Related