How to build Curvy Line Charts in Tableau Software

The use of curvy line charts is often arguable. The issue is that the curves between the points can lead to misrepresentation, suggesting than more data are connected. But I can’t help it, I really like the design of those smooth lines. In some cases, It definitely looks better.

If you want to know more about the distortion and misrepresentation of curvy line charts, I invite you to begin with this article from Andy Kriebel or this other article or this one from Lisa Charlotte Rost

A few days ago, I went through the work of Joakim Dalen about a Makeover Monday dataset based on U.S. favorite sports. I liked his chart at first sight. The curvy lines definitely add something. Of course it is not the only good thing here, look at those axis ❤

Joakim develops with d3.js and the javascript library offers a few functions to add curvy aspects. In Tableau Software, you cannot “smooth” your lines natively and easily. But, as often with Tableau, we can find a way! Let’s learn more about some theory first.

The Catmull-Rom Spline

When you look at the code behind Joakim’s chart, you see that the function curveCatmullRom is called on the line object.

Our dear wikipedia explains that the Catmull-Rom spline is a kind of interpolating spline defined by four control points. In other words, some new extra points are created to draw a curve between two original points. Those points are an interpolation based on four points (previous point, original points, ,next points) even if the end result is only the curve between the two intermediate points.

I first managed to develop the algorithm from the wikipedia article. You can imagine the number of new calculated fields I had to create. I was satisfied with the result but I was sure that something simplier existed somewhere. And I finally found an other version of the equation “much simplier” than the first one.

 P = \left[ \begin{matrix} 1 & t & t^2 & t^3 \end{matrix} \right].\left[ \begin{matrix} 0 & 1 & 0 & 0 \\-\tau & 0 & \tau & 0 \\2.\tau & \tau-3 & 3-2\tau & -\tau \\-\tau & 2-\tau & \tau-2 & \tau\end{matrix} \right].\left[ \begin{matrix} P_{i-1} & P_{i} & P_{i+1} & P_{i+2}\end{matrix} \right]^\top
Catmull-Rom Spline equation (with Tension)

Pi-1 to Pi+2 are the four points, t is some value between 0 and 1, Tau is what is called tension and is between 0 and 1 too.

Now, how do I calculate the four needed points for each points Pi? How do I write the equation in Tableau? Let’s the fun begin!

Prepare the data

Data can be find here.

You first have to use a main dataset with at least x and y coordinates. Here I also have a category column for each sports.

Main dataset

Then, you have to perform a cartesian product with a model dataset hosting the extra points you will add between the original points. It will draw the curve!

Model dataset (at least 10 points)

Bring the two datasets in Tableau and use a calculated field “1” for both to perform the cartesian product.

Cartesian product in Tableau

And your data is ready. You have all the extra points needed to build the curve.

Extra points in Tableau

Calculate the coordinates

Workbook can be find here.

First, add a new calculated field to reuse the P.x coordinate. I call it Year here.

Year

[P.x]

we will use year to recalculate the real coordinates of our original points (remember, we duplicate the data because of the cartesian product).

Pi.x

min({FIXED [Year],[Category]:
min([P.x])
})

Pi.y

min({FIXED [Year],[Category]:
min([P.y])
})

Then remember, for each points, we have to calculate the coordinates of the previous point and the two next points (Pi-1 to Pi+2). We will use the lookup() function and we will have to deal with the first and last points of our datasets (no previous or next points in these cases).

Pi-1.x

IF [Pi.x]=min({min([P.x])}) THEN //if first point of the dataset
[Pi.x] //use the current coordinates
ELSE LOOKUP([Pi.x],-1) END // use the previous coordinates

Pi+1.x

IF [Pi.x]=min({min([P.x])}) THEN
[Pi.y]
ELSE LOOKUP([Pi.y],-1) END

Pi+1.x

IF [Pi.x]=max({max([P.x])}) THEN //if last point of the dataset
[Pi.x] //use the current coordinates
ELSE LOOKUP([Pi.x],1) END //use the next coordinates

Pi+1.y

IF [Pi.x]=max({max([P.x])}) THEN
[Pi.y]
ELSE LOOKUP([Pi.y],1) END

Pi+2.x

IF [Pi.x]=max({max([P.x])}) THEN //if last point of the dataset
[Pi.x] //use the current coordinates
ELSEIF [Pi+1.x]=max({max([P.x])}) THEN //if before last point of the dataset
[Pi+1.x] //use the next coordinates
ELSE LOOKUP([Pi.x],2) END //use the next next coordinates

Pi+2.y

IF [Pi.x]=max({max([P.x])}) THEN //if last point of the dataset
[Pi.y] //use the current coordinates
ELSEIF [Pi+1.x]=max({max([P.x])}) THEN //if before last point of the dataset
[Pi+1.y] //use the next coordinates
ELSE LOOKUP([Pi.y],2) END //use the next next coordinates

Almost done for the preparation, we then need to add the variable t thanks to the I.id column of the model dataset.

t

min([I.id]/{max([I.id])})

Finally, we can write the equation using the coordinates we created and a parameter called Tension.

Pf.x

([Pi.x]) +(([Pi-1.x]*-[Tension])+([Pi+1.x]*[Tension]))*[t]
+(([Pi-1.x]*2*[Tension])+([Pi.x]*([Tension]-3))+([Pi+1.x]*(3-2*[Tension]))+([Pi+2.x]*-[Tension]))*[t]^2 +(([Pi-1.x]*-[Tension])+([Pi.x]*(2-[Tension]))+([Pi+1.x]*([Tension]-2))+([Pi+2.x]*[Tension]))*[t]^3

Pf.y

([Pi.y]) +(-[Pi-1.y]*[Tension]+[Pi+1.y]*[Tension])*[t]
+(([Pi-1.y]*2*[Tension])+([Pi.y]*([Tension]-3))+([Pi+1.y]*(3-2*[Tension]))-[Pi+2.y]*[Tension])*[t]^2 +(-[Pi-1.y]*[Tension]+([Pi.y]*(2-[Tension]))+([Pi+1.y]*([Tension]-2))+[Pi+2.y]*[Tension])*[t]^3

To conclude, we will use a calculated field to exclude the extra points of the last original point.

Exclude_Points

[Pf.x]<=(max({max([P.x])})*1.00001)

Plot the curvy line chart

Using the fields we created in the sheet in pretty simple. Please refer to the image below. You can change the value of the parameter Tension to modify the “Tension” of the line (Tension=0 -> no more curves).

You have to compute Pf.x, Pf.y and Exclude points using P.x.

My advice is to add the points to refer to the original and real data values (like in Joakim’s chart). To do so, we will add a final caculated field, a new version of Pf.y, and add a dual axis.

Pf.y for points

IF [Pf.x]=[Pi.x] THEN [Pf.y] END

And here we go, we have our curvy line based on Catmull-rom equation. You can now “pimp” your chart as you want. Below I tried to rebuild the exact same chart as Joakim in Tableau.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s