Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

y-axis is short #671

Closed
pmsoltani opened this issue Aug 21, 2019 · 33 comments
Closed

y-axis is short #671

pmsoltani opened this issue Aug 21, 2019 · 33 comments
Labels

Comments

@pmsoltani
Copy link

First of all, thanks for the library. Keep up the good work!

Describe/explain the bug
The y-axis is shorter than the tallest bar

To Reproduce

import React, { Component } from "react";
import { ResponsiveBar } from "@nivo/bar";
import "./NivoBarChart.css";

const data = [
  { year: "2011", papers: 190 },
  { year: "2012", papers: 61 },
  { year: "2013", papers: 31 },
  { year: "2014", papers: 106 },
  { year: "2015", papers: 109 },
  { year: "2016", papers: 16 },
  { year: "2017", papers: 16 },
  { year: "2018", papers: 16 },
  { year: "2019", papers: 16 }
];

class NivoBarChart extends Component {
  render() {
    return (
      <ResponsiveBar
        data={data}
        keys={["papers", ]}
        indexBy="year"
        margin={{ top: 50, right: 130, bottom: 50, left: 60 }}
        padding={0.1}
        colors={{scheme: "nivo"}}
        colorBy="id"
        borderRadius={10}
        axisBottom={{
          tickSize: 5,
          tickPadding: 5,
          tickRotation: 0,
          legend: "year",
          legendPosition: "middle",
          legendOffset: 32
        }}
        axisLeft={{
          tickSize: 5,
          tickPadding: 5,
          tickRotation: 0,
        }}
        theme={{
          tooltip: {
            container: {
              background: "black",
              color: "white",
              fontSize: "inherit",
              borderRadius: "2px",
              boxShadow: "0 1px 2px rgba(0, 0, 0, 0.25)",
              padding: "5px 9px"
            },
            basic: {
              whiteSpace: "pre",
              display: "flex",
              alignItems: "center"
            },
            table: {},
            tableCell: {
              padding: "3px 5px"
            }
          },
          axis: {
            domain: {
              line: {
                stroke: "#000",
                strokeWidth: 1
              }
            }
          }
        }}
        enableGridX={false}
        enableGridY={false}
        label={false}
        animate={true}
        motionStiffness={90}
        motionDamping={15}
        isInteractive
        // handling mouse-over effect: borderRadius
        onMouseEnter={(d, e) => {
          e.target.classList.remove("smooth");
          e.target.classList.add("sharp");
        }}
        onMouseLeave={(d, e) => {
          e.target.classList.remove("sharp");
          e.target.classList.add("smooth");
        }}
      />
    );
  }
}

export default NivoBarChart;

Expected behavior
The biggest number is 190, but y-axis only goes up to 180. It should include 200 as well.

Screenshots
image

Desktop (please complete the following information):

  • OS: Win 10
  • Browser Opera
  • Version 62
  • Nivo Bar 0.59.2
@marnett-git
Copy link
Contributor

Just set the maxValue property to your highest value

@pmsoltani
Copy link
Author

@marnett-git Thanks for the reply.
I did not see a maxValue property in node_modules\@nivo\bar\index.d.ts
I did, however, used the code below to replace the axisLeft part in the code I provided:

axisLeft = {{
  maxValue: 301,
  tickSize: 5,
  tickPadding: 5,
  tickRotation: 0
}}

This, unfortunately, did not work. And even if it did, it wouldn't solve the bigger problem: I am going to use this chart with some dynamic data. I'd like nivo to figure out the axis for itself. I noticed that even on the website (https://nivo.rocks/bar) this problem exists.

@RilRil
Copy link

RilRil commented Nov 26, 2019

hi @pmsoltani , did you find a solution ? I get the same problem sometimes with my charts even if I set some margin
And in my case it's even simpler because my max value is always 100

@wyze
Copy link
Contributor

wyze commented Nov 26, 2019

You can set the max property of the yScale property. Something like yScale={{ max: 100, type: 'linear' }}.

@RilRil
Copy link

RilRil commented Nov 26, 2019

@wyze yeah I already have that in place ..
looks like a "transform: translate()" issue .. for whatever reason, the origin of translate of the first < g > is not on x:0 and y:0 of the < rect > or even the < svg >

@RilRil
Copy link

RilRil commented Nov 26, 2019

ok by actually setting minValue and maxValue it fixes my problem !

@pmsoltani
Copy link
Author

@RilRil so sorry for the late reply. No, I didn't find a way to solve the issue. I decided to keep an eye on the Nivo but switch to another library for my current project.

@CaptainT33mo
Copy link

Hi guys,
Did anyone find a dynamic solution for this? I am using responsive line graph when there are data sets coming from APIs and I don't know what will be the max value for those data sets plotted on the Y-axis so I would like to do something like, get the max value of the highest point generated at the Y-axis and then add my custom threshold value something like 50.

Even the graphs on Google Analytics and other chat libraries do the same they give some room after the max value generated by your data.

Google Analytics Graph:
image

@prashant-jump360
Copy link

ok by actually setting minValue and maxValue it fixes my problem !

hi.. can you please share your code. I tried with adding max property in yScale but no luck. :(

@mia-pelino
Copy link

mia-pelino commented May 6, 2020

@prashant-jump360
I set maxValue property directly on the ResponsiveBar component, at the same level as the data and keys properties.

to calculate the maxValue, i used Math.max.apply(Math, potatoData) in order to get the maximum value of the array and dynamically set the chart height.

<ResponsiveBar data={potatoData} keys={['totalPotatoes']} maxValue={potatoMax} />

@amihalopoulos
Copy link

amihalopoulos commented Sep 1, 2020

I simply want the y-axis ticks to increase until the largest tick > the largest value. Is there a way to accomplish this dynamically?

Screen Shot 2020-09-01 at 11 41 21 AM

@CaptainT33mo
Copy link

CaptainT33mo commented Sep 2, 2020

@amihalopoulos I added the following code to give a small buffer or breathing room to the Y-axis on a <ResponsiveLine /> graph component:

yScale={{
 	type: "linear",
 	min: 0,
	max: this.getMaxValue(array),
        stacked: true,
 	reverse: false,
}}

Where the this.getMaxValue(array) is a function returning the maximum y-axis value based on the passes array of objects to that given function.

@Jacse
Copy link

Jacse commented Sep 28, 2020

I simply want the y-axis ticks to increase until the largest tick > the largest value. Is there a way to accomplish this dynamically?

I would love to have a prop á la encompassData so ticks always encompass all data. What I've done to achieve that currently is work direcly with d3 to add an extra tick above and below the regular ones:

import { tickStep } from 'd3-array';

const ticks = 6;

const step = tickStep(minVal, maxVal, ticks - 2);
min = minVal - step;
max = maxVal + step;

return (
    <Line
        yScale={{ type: 'linear', stacked: false, min, max }}
        axisLeft={{
            tickValues: ticks,
        }}
    />
);

I briefly looked into adding it to scales, but those functions don't currently receive the number of ticks, which is required for this approach to work and I am much too unfamiliar with the codebase to try and add it atm.

@Ollymid
Copy link

Ollymid commented Oct 13, 2020

@CaptainT33mo I have a similar problem with the graph data extending beyond the range of the Y axis
Screenshot 2020-10-13 at 10 56 14

What did you do for your getMaxValue function?

I am using a workaround by passing the highest message count (rounded up to nearest 10) separately and using that as the yScale max value, which works but is not ideal

@CaptainT33mo
Copy link

Hi @Ollymid ,

This is the exact code I uses as a hack to get the max value of the Y-axis.

getMaxValue = preppedData => {
		const maxArray = preppedData.map(data =>
			data.data.reduce((max, p) => (p.y > max ? p.y : max), data.data[0].y)
		);

		let padding;
		if (maxArray[0] <= 10) {
			padding = 10;
		} else if (maxArray[0] > 10 && maxArray[0] <= 1000) {
			padding = 100;
		} else {
			padding = 1000;
		}

		return (
			Math.ceil(
				maxArray.reduce((max, p) => (p > max ? p : max), maxArray[0]) / padding
			) * padding
		);
};

Maybe you can optimize the code as per your need

@Ollymid
Copy link

Ollymid commented Oct 13, 2020

Thanks @CaptainT33mo - appreciate it 🙌

@stale
Copy link

stale bot commented Jan 11, 2021

This issue has been automatically marked as stale. If this issue is still affecting you, please leave any comment (for example, "bump"), and we'll keep it open. We are sorry that we haven't been able to prioritize it yet. If you have any new additional information, please include it with your comment!

@stale stale bot added the stale label Jan 11, 2021
@Ollymid
Copy link

Ollymid commented Jan 11, 2021

This issue is still an issue - I used CaptainT33mo's solution to as a way to circumvent the problem in my project, but it'd be nice to not have to manually round up the highest value for the graph to fit the data properly

@stale stale bot removed the stale label Jan 11, 2021
@bensussman
Copy link

I also have this exact same issue, i want auto to result in a bit of headroom in the graph, such that the line doesn't look cut off when it touches the "max":

image

You can see the line thickness is smaller when it touches the "max" because it's being cut off. Why wouldn't auto give a tiny bit of headroom so we don't have this cut-off look to it?

I will go with the solutions proposed above, but it really should be incorporated into nivo directly.

@WBialekMerixStudio
Copy link

WBialekMerixStudio commented Jan 29, 2021

I adapted CaptainT33mo function to my charts, maybe it will be useful to someone:

export const getMaxChartValue = preppedData => {
  const maxArray =
    preppedData.map(data => data.data.reduce((max, p) => (p.y > max ? p.y : max), data.data[0].y));

  const max = Math.max(...maxArray);

  return (
    max * 1.2
  );
};

export const getMinChartValue = preppedData => {
  const maxArray =
    preppedData.map(data => data.data.reduce((min, p) => (p.y < min ? p.y : min), data.data[0].y));

  const min = Math.min(...maxArray);

  return (
    Math.floor(min * 0.6)
  );
};

@dooleyb1
Copy link

dooleyb1 commented Feb 2, 2021

I also have this exact same issue, i want auto to result in a bit of headroom in the graph, such that the line doesn't look cut off when it touches the "max":

image

You can see the line thickness is smaller when it touches the "max" because it's being cut off. Why wouldn't auto give a tiny bit of headroom so we don't have this cut-off look to it?

I will go with the solutions proposed above, but it really should be incorporated into nivo directly.

+1 on this implementation, I think this would be the cleanest solution

@ytliuSVN
Copy link

ytliuSVN commented Feb 4, 2021

Similar issues occur in the Stream chart.
How to manually adjust the scale of y-axis from 0 to 10? Any corresponding property?
Also, need to differently scale both negative and positive tick values on the y-axis when using offsetType="silhouette".

These could be considered for future features. Thank you! 🙏

chart-os

@stale
Copy link

stale bot commented May 6, 2021

This issue has been automatically marked as stale. If this issue is still affecting you, please leave any comment (for example, "bump"), and we'll keep it open. We are sorry that we haven't been able to prioritize it yet. If you have any new additional information, please include it with your comment!

@stale stale bot added the stale label May 6, 2021
@Ollymid
Copy link

Ollymid commented May 6, 2021

Bump - because as far as I'm aware it is still affecting people

@stale stale bot removed the stale label May 6, 2021
@fftristan
Copy link

fftristan commented May 10, 2021

Hi, how can you set min and max y scale values so that the data is "zoomed in" instead of having y axis scaled from 0 to your max y value. I tried to set a min value in the yscale but in this simple example scaling the y axis does not seem to work...

Screen Shot 2021-05-10 at 4 51 42 PM

<Bar
width={600}
height={600}
margin={{ top: 60, right: 80, bottom: 60, left: 80 }}
data={[
{ country: "AD", "hot dogs": 105 },
{ country: "AE", "hot dogs": 110 }
]}
indexBy="country"
keys={["hot dogs"]}
labelTextColor="inherit:darker(1.4)"
axisLeft={{
tickValues: [100, 105, 110]
}}
yScale={{
type: 'linear',
min: 100,
max: 110,
}}
/>

@stale
Copy link

stale bot commented Aug 8, 2021

This issue has been automatically marked as stale. If this issue is still affecting you, please leave any comment (for example, "bump"), and we'll keep it open. We are sorry that we haven't been able to prioritize it yet. If you have any new additional information, please include it with your comment!

@stale stale bot added the stale label Aug 8, 2021
@nleapai
Copy link

nleapai commented Aug 10, 2021

Bump

I've been using a workaround where I track the max values in my data for different modes (grouped/stacked for Bar, stacked/not stacked for Line), then if the Y-Axis max is set to auto, I pad the corresponding max value by a small percentage and explicitly set the max on the chart.

It's been working pretty well, but I recently upgraded past v0.71.0 wanting to try out the new toggleSerie feature of legends (which is awesome), but because of the workaround, the chart doesn't auto-adjust the Y-Axis when toggling series which is kind of the main reason I was interested in the toggle.

So, for now, I'll probably just update the workaround to track each individual series max value and step up or down to the next highest/lowest when toggling series, but it would be great if auto added some room at the top of the chart by default, and even better if that was possible to define how much.

As always, thank you so much for this library. It has been wonderful to work with.

@stale stale bot removed the stale label Aug 10, 2021
@wiznotwiz
Copy link

So, for now, I'll probably just update the workaround to track each individual series max value and step up or down to the next highest/lowest when toggling series, but it would be great if auto added some room at the top of the chart by default, and even better if that was possible to define how much.

Hey @nleapai, how are you tracking each series' max value upon toggle? I can't figure out how to "grab" the visible lines to generate the max/min numbers.

I agree that the "auto" property should have some padding, both for max and min on the y-axis.

@nleapai
Copy link

nleapai commented Oct 27, 2021

Hey @wiznotwiz, in my case, I wasn't grabbing those values upon toggle or grabbing them from the chart itself. Short version: I was tracking it beforehand/managing it in wrapper components.

Long version: I was dealing with time series data from an API that needed to be formatted first, so while that preprocess was iterating over the data, I just kept track of the highest values for each series then passed that info along to a component wrapping the Line chart. That component would then be responsible for determining which max to use based on which series were toggled (through its own state, not grabbing it from the internal hiddenItems in Line's state that toggleSerie maps to), add some padding, and set that on the Line component.

BUT, I actually abandoned all this "series max tracking for padding" stuff because it got real hairy once I turned on Line Stacking. Without stacking, just use the largest value from the pool of active series. With stacking, now everything is cumulative and there's no accurate/consistent way to determine the max without just tracking the value of every individual slice. Then you factor in toggling and it was just way too much effort for some padding. So I've just accepted no padding for now.

@zetaab
Copy link

zetaab commented Jan 9, 2022

I am currently precalculating maxvalue and using yScale={{ type: 'linear', max: maxValue * 1.2 }}. However, I have same problem that others has also: when using toggleSerie there is no way to update max. Could we for instance add new variable here https://github.com/plouc/nivo/blob/master/packages/scales/src/types.ts#L49 maxMultiplier which defaults to 1 and then people could easily do this automatically? Then this row https://github.com/plouc/nivo/blob/master/packages/scales/src/linearScale.ts#L26 is just multiplied with that value?

@stale
Copy link

stale bot commented Apr 11, 2022

This issue has been automatically marked as stale. If this issue is still affecting you, please leave any comment (for example, "bump"), and we'll keep it open. We are sorry that we haven't been able to prioritize it yet. If you have any new additional information, please include it with your comment!

@stale stale bot added the stale label Apr 11, 2022
@stale
Copy link

stale bot commented Apr 18, 2022

Closing this issue after a prolonged period of inactivity. If this issue is still present in the latest release, please create a new issue with up-to-date information. Thank you!

@stale stale bot closed this as completed Apr 18, 2022
@DanWttvn
Copy link

The cleanest solution I found for the ResponsiveLine component:

  const yScale = useMemo(
    () =>
      !data.length
        ? { min: 0, max: 800 }
        : {
            min: 0,
            max: Math.max(...data.map((x) => x.y)) * 1.2,
          },
    [data],
  );
yScale={{
  type: 'linear',
  ...yScale,
}}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests