nivo icon indicating copy to clipboard operation
nivo copied to clipboard

Is there a way to remove gaps when using a grouped bar chart with missing data?

Open drj17 opened this issue 5 years ago • 22 comments

Is your feature request related to a problem? Please describe. I'm using a grouped bar component, but there's not always a guarantee that the group members will be the same for each group. The way things are working right now, even members without a key in the datum will take up space in the bar chart and cause gaps (see screenshot). Is there a way to solve this using existing options?

Describe the solution you'd like It would be helpful if there was an option to have the graph draw the bars so that they ignore entries without data. Ideally the bars would be centered on the tick as if they were the only elements, not spaced based on data that doesn't exist.

Additional context Screen Shot 2019-07-25 at 10 42 43 AM

drj17 avatar Jul 25 '19 15:07 drj17

Change your keys in your code. Check your data before you put it into the chart. Remove the entries with a 0 or null or something.

MariusHendriks avatar Nov 20 '19 09:11 MariusHendriks

I came across this issue after encountering the same problem and searching for a workaround. I agree that the proposed solution would probably be the best way of addressing this. To note, this behavior will occur if the keys for grouped bar charts are not exactly the same across each item and is not caused by values of null or zero.

djpowers avatar May 24 '20 17:05 djpowers

@drj17 @djpowers is there a solution for this? Running into this now...

hanford avatar Sep 15 '20 00:09 hanford

@hanford, still on the lookout for a solution, myself.

djpowers avatar Sep 15 '20 01:09 djpowers

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[bot] avatar Dec 14 '20 02:12 stale[bot]

Bump

djpowers avatar Dec 14 '20 02:12 djpowers

Bump

Nathaniel-Fernandes avatar Dec 15 '20 01:12 Nathaniel-Fernandes

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[bot] avatar Mar 15 '21 04:03 stale[bot]

Bump

djpowers avatar Mar 15 '21 14:03 djpowers

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[bot] avatar Jun 13 '21 16:06 stale[bot]

Bump

djpowers avatar Jun 13 '21 21:06 djpowers

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[bot] avatar Sep 11 '21 22:09 stale[bot]

Bumping again

djpowers avatar Sep 11 '21 23:09 djpowers

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[bot] avatar Dec 10 '21 23:12 stale[bot]

bump

djpowers avatar Dec 11 '21 00:12 djpowers

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[bot] avatar Mar 12 '22 10:03 stale[bot]

^

djpowers avatar Mar 12 '22 13:03 djpowers

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[bot] avatar Jun 10 '22 19:06 stale[bot]

Bump, please

djpowers avatar Jun 10 '22 22:06 djpowers

@djpowers, is there a specific use-case/scenario that you are looking at? Could you share an example?

A personal view is that gaps can be quite informative. When the data is dense, then a few gaps don't take up a lot of space and help preserve the structure/order of bars across the groups. Omiting the gaps could make a chart confusing (again, personal view).

In cases like in the original example, grouped bars can indeed become hard to read. Such data can sometimes be visualized with other techniques, e.g. perhaps a series of smaller charts? or a heatmap?

https://codesandbox.io/s/nifty-joliot-jy6fv3?file=/src/App.js

tkonopka avatar Jun 11 '22 07:06 tkonopka

Hi @tkonopka, in my case, I personally didn't find the gap to provide any useful information. My scenario had the gaps in the last position of a given bar chart group, which to me looks more like a spacing issue. It also results in the grouped bars not being centered on the x-axis hash mark, which also looks a bit odd to me: image

That being said, I understand your points and recognize this would introduce some complexity to things. Feel free to close this as wontfix if it seems like something that won't realistically be implemented.

djpowers avatar Jun 13 '22 14:06 djpowers

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[bot] avatar Sep 20 '22 16:09 stale[bot]

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[bot] avatar Sep 27 '22 19:09 stale[bot]

Has anyone found a solution to this. Or any alternative to this?

awais-shafiq avatar Mar 16 '23 05:03 awais-shafiq

Has anyone found a solution to this. Or any alternative to this?

I was never able to find any sort of solution or workaround, unfortunately.

djpowers avatar Mar 16 '23 19:03 djpowers

I encountered a version of this problem, that is a bit more simple than the general use case.

What I need to do:

  • have 3 bars that get shown for different keys
  • I know that the 2 bars are only displayed with some constraints: I either display the first bar or the other 2

Given the simplified version of my problem I wrote the following BarComponent

import { BarItem, BarItemProps } from "@nivo/bar";
import React, { useEffect, useState } from "react";


interface CustomBarDatum extends BarDatum {
  key_1: number;
  key_2: number;
  key_3: number;
}


type TranslationValues = {
  x: number;
  y: number;
};

function parseTranslate(translateString: string): TranslationValues {
  const result = { x: 0, y: 0 };

  // Extracting translate values using a regular expression
  const matches = translateString.match(
    /translate(?:X|Y)?\(([^,]+)(?:,\s*([^)]+))?\)/
  );

  if (matches) {
    // If there's a match, parse X and Y values
    result.x = parseFloat(matches[1]);
    if (matches[2]) {
      result.y = parseFloat(matches[2]);
    } else {
      // If only one value is present, determine if it's X or Y based on the function used
      if (translateString.startsWith("translateY")) {
        result.y = result.x;
        result.x = 0;
      }
    }
  }

  return result;
}

const CustomBarNoGaps = (props: BarItemProps<CustomBarDatum>) => {
  const { style, fullData, ...otherProps } = props;
  const [isAdapted, setIsAdapted] = useState(false);

  useEffect(() => {
    if (isAdapted) return;

    const isBarShown = [
      "key_1",
      "key_2",
      "key_3",
    ].map((k) => !!props.bar.data.data[k]);

    const numberBars = isBarShown.filter((t) => t).length;

    console.log(style);
    const barTranslate = parseTranslate(style.transform.goal);
    console.log(style.transform.goal);
    if (numberBars === 1) {
      style.labelX.set(Math.round(style.labelX.goal + style.width.goal));
      style.width.set(Math.round(style.width.goal * 3));
    } else if (numberBars === 2) {
      const newTranslationString = `translate(${Math.round(
        barTranslate.x -
          (props.bar.data.id === "key_2"
            ? style.width.goal
            : 0.5 * style.width.goal)
      )}, ${barTranslate.y})`;
      style.labelX.set(Math.round(style.labelX.goal + style.width.goal / 4));
      style.transform.set(newTranslationString);
      style.width.set(Math.round(style.width.goal * 1.5));
    }
    setIsAdapted(true);
  }, [style, props, isAdapted]);
  console.log(props.bar.data);
  console.log(fullData);
  return <BarItem style={style} {...otherProps} />;
};

export { CustomBarNoGaps };

This can be passed to the barComponent property. See how it is used

This solution is very specific for my use case but it can be generalized if one would need to dynamically compute my hardcoded values (1.5, 3, etc.) to take into account the number of bars skipped vs the number of bars rendered so far.

damianr13 avatar Feb 20 '24 15:02 damianr13