As per March 12, our Business Continuity Plan is triggered due to the COVID-19 outbreak. VI Company and its operations continue to function as normal, while colleagues are working remotely. Read the full message.

A bar chart, how hard can it be?

Auteur
Ruud Renssen
Categorieën
Publiceer datum
share image for the article 'a bar chart, how hard can it be?'

At VI, we deal with data visualizations a lot. A while back, when a client asked us to visualize some percentages, we said: sure! As always, we brainstormed about what kind of chart would make the most sense. We discussed a few options and eventually settled on a bar chart. This article describes how a chart perceived as very simple turned out to be far more complex.

This article contains code examples and is relevant for anybody working in software, not only developers. Please don’t let the code examples scare you. The examples illustrate how a simple piece of code got more complex. You don’t need to understand all the code to see its complexity grow.

The data
Before we can render a chart, we need some data first. We do not want to wait before all API endpoints are ready, so let's set up some dummy data first. To quickly check whether the code produced the desired result, let’s choose some numbers that are easy to do in your head.

Item A: 12 
Item B: 6 
Item C: 4 
Item D: 2 

Let's start with the simplest implementation
Although the chart consists of several classes, the only relevant class for this article is the one that takes care of a single bar. Because we are focusing on the x-coordinates we are only interested in the width of the bar. The coordinates of the chart can best be described in two classes: BarChart and SingleBar. The SingleBar will be responsible for its own width. The BarChart takes care of the SingleBars positioning along the y-axis. Since we are only interested in x-coordinates, we ignore BarChart and focus on SingleBar.

Using percentages for length properties is a common practice. Let’s start by calculating the numerical proportions as a percentage.

public double Value { get; set; } 
public double TotalValue { get; set; } 
public double percentage => value * 100 / totalValue; 

Code example 1: Simplest implementation.

The code above will result in the following chart:

image showing 4 bars'

Figure 1: The simplest implementation needs three lines of code.

Room for improvement
The chart works, but it’s far from ideal. All bars are too short. That is because the total width is 100% and the largest bar is “only” 50%. If we base all bars on the largest bar the difference in length between the bars will be amplified (making the chart easier to read). Luckily, this is easily done. The only thing to do is to divide by the highest value instead of the total value before calculating the percentage. We also need to add a variable for the width, because it will not match the percentage anymore.

public double Value { get; set; } 
public double TotalValue { get; set; } 
public double MaxValue { get; set; } 
public double percentage => value * 100 / totalValue; 
private double width => Value * 100 / MaxValue; 

Code example 2: Implementation based on the maximum value.

A few things have been added. Most notably, the SingleBar needs to be aware of the maximum value so it can calculate its width. The new code will result in this chart:

image showing 4 bars'

Figure 2: Five lines of code were needed to improve the chart

With two extra lines of code, the chart is improved. There is as much detail as possible now the largest bar is 100% wide while the other bars retain their proportional width. We now use five lines of code for dealing with the width in the SingleBar class.

It was at this point when we considered the chart as done. The only thing left was to tie it to the database.

Make it dynamic
Now that we have a functioning bar chart, we can implement the chart into our application and feed it with data from the database. And this is where it gets interesting. While most charts rendered as we expected, some charts looked like this:

image showing 3 bars but with one missing'

Figure 3: That does not look right.

How is this possible? If data is invalid, I expect an error. Or maybe for the value to be treated as zero. The system threw no error and if the value was treated as zero, I expected the missing bar on the bottom. To find out why this happens, we need to look at the data from the database.

Item A: 12 
Item B: -6 
Item C: 4 
Item D: 2 

And indeed, the data is completely valid but contained a surprise nonetheless: item 2 has a negative value. Therefore the bar has a width of -33%, which of course makes no sense at all. Its proportional value is now one third, but the width should have been positive. To fix this issue, we need to do a lot more than before. The width got more complex and we also need to take the x-position into account. On top of that, both need a bit of logic too.

public string Name { get; set; } 
public double Value { get; set; } 
public double TotalValue { get; set; } 
public double MinValue { get; set; } 
public double MaxValue { get; set; } 
private double minPercentage => MinValue * 100 / TotalValue; 
private double percentage => Value * 100 / TotalValue; 
private double width => Math.Abs(percentage) * factor; 
private double factor => TotalValue / Range(); 
private double xPosition => Value > 0 ? Math.Abs(minPercentage * factor) : 0; 

private double Range() 
{ 
    double range; 

    if (MaxValue < 0) 
    { 
        range = Math.Abs(MinValue); 
    } 
    else if (MinValue > 0) 
    { 
        range = MaxValue; 
    } 
    else 
    { 
        range = MaxValue + Math.Abs(MinValue); 
    } 

    return range; 
} 

Code example 3: Take negative values into account.

Now the chart looks like it supposed to:

image showing 4 bars in total, with 3 having a positive value, and 1 having a negative value'

Figure 4: This looks more like it!

The piece of code that takes care of the bar started with three lines and ended with thirty lines of code.

Lessons learned…

What I took away from this is that sometimes, no matter how thoroughly you analyze something, your decision might still be wrong. I was glad we did not choose a pie chart as visualization; negative values would have been impossible. But it was because of other reasons we went to a bar chart. We were also a bit lucky.

Did we understand the data poorly? I like to think differently, but maybe. However, it was hard to anticipate this situation: nearly all values were positive. I take minor comfort in the fact that the product owner initially proposed a pie chart. When even an expert overlooked it; I think it is fair to say that it is easy to overlook in general.

Would I do something different next time? Well, for one, I would start with a chart library. If we had done that, it would not have mattered that we overlooked the negative values. The library would have taken care of it. It would also have spared the product owner and me some headaches. The last thing you want is surprises during the last sprint.

But besides using a library, I am having a hard time thinking about what more we could have done. Of course, we could have had a closer look at the data. But it is also hard to be aware of all the assumptions you make, most of them unconscious. And how do you know when you truly understand something? How do you know you have the full picture? Most of the time, I assume my understanding is sufficient once the data makes sense to me.

That leads me to the unsatisfying conclusion that sometimes you just overlook things and development takes a little longer.

Terug naar boven

Wij waarderen en waarborgen je privacy. We willen tevens graag een zo goed mogelijke ervaring bieden op onze website. Daarom plaatsen we graag een aantal cookies op je computer om ons te helpen bij het personaliseren van de inhoud van onze website. Lees meer over het gebruik van cookies in het privacy statement.

Find out more about cookies or .