4 Quick Tips for Better Data Visualization
By: Alec Morgan
Tags: Visualization, Data, Data Science, Matplotlib, Seaborn

Data visualization is one of the most important tricks in any data scientist’s toolkit. Our brains are visual: when we make our data visual too, we make it easier to digest — both for ourselves and for the decision makers and stakeholders we communicate our insights to. As such it’s a critical tool both for EDA (Exploratory Data Analysis) and of course for presentation.

Here are 4 quick-and-easy tips to become better at data visualization.

1. Use KDE Plots for Density Checks

This is a tremendously under-appreciated feature of Seaborn. KDE stands for Kernel Density Estimation, and KDE plots quite singlehandedly solve the problem of not being able to see into densely packed scatter plots. Consider this example with a dataset of King County, WA housing prices…

plt.figure(figsize=(9, 9))

plt.title("Price vs Square Feet", fontsize=32)

plt.xlabel("Square Feet", fontsize=20)

plt.ylabel("Price (in $1000s)", fontsize=20)

plt.scatter(df["sqft_living"], df["price"]/1000);

As you can see, there are almost as many homebuyers in the $1 to $2 million dollar range as below — but intuitively, obviously, we know that this isn’t true. The usual band-aid solution to this situation is to apply alpha transparency — and it works sometimes — but it won’t help us much here.

plt.figure(figsize=(9, 9))

plt.title("Price vs Square Feet", fontsize=32)

plt.xlabel("Square Feet", fontsize=20)

plt.ylabel("Price (in $1000s)", fontsize=20)

plt.scatter(df["sqft_living"], df["price"]/1000, alpha=0.1);

Our data is now much harder to see, but at least we can tell the density of it a bit better. It no longer seems that there are so many buyers in the $1 to $2 million range, but buyers at the price of around $1 million seem about as common as anything below. But once again, the KDE plot quite singlehandedly solves this.

plt.figure(figsize=(9, 9))

plt.title("Price vs Square Feet", fontsize=32)

plt.xlabel("Square Feet", fontsize=20)

plt.ylabel("Price (in $1000s)", fontsize=20)

sns.kdeplot(df["sqft_living"], df["price"]/1000, shade=True);

By drawing a series of color gradients instead of all the individual data points, the KDE plot shows us a density estimation of our data. Now we can finally see what we knew all along: that the overwhelming majority of buyers purchase homes below 2,000 sq. ft and $500,000. In a situation where we didn’t come in with prior knowledge of our data, this insight would have been both invaluable and very easy to miss. Now if we just zoom in by adding plt.ylim(1000) to our code…

plt.figure(figsize=(9, 9))

plt.title("Price vs Square Feet", fontsize=32)

plt.ylim(1000)

plt.xlabel("Square Feet", fontsize=20)

plt.ylabel("Price (in $1000s)", fontsize=20) # Setting the ylim oddly flips the axis upside down

# in this case, so we have to flip it back.

ax = sns.kdeplot(df["sqft_living"], df["price"]/1000, shade=True)

ax.invert_yaxis();

After all these changes, we’re now able to get a much clearer picture of what’s going on inside our data.

2. Label Your Axes, and Make Them Readable

A very wise mentor of mine once advised that if you’re going to go on a date with another data scientist and you’re not the kind of data scientist that labels your x and y axes, the date won’t go very well. By extension, it’s no doubt true that labeling your axes using minuscule font sizes will lead to mediocre dates. Cheeky humor aside, making it plain for others to see what you’re actually visualizing is exceedingly important. These next two figures are a good example of what not to do:

Always label your axes and set your fontsize .

3. Declutter Your Output

This is just a small quality of life thing, but if you’re as keen on details as I am then you might appreciate it a lot. When you’re using Matplotlib and Seaborn in Jupyter Notebook or Lab it goes without saying that you want your graphs to appear in your actual notebook; however, they simply don’t until the second time you run that first graphing cell. This is because Matplotlib (and Seaborn by extension, since it’s a Matplotlib wrapper) isn’t exclusively for Jupyter, so it’s not sure if you want to just look at the graph in a different window, save it, or what. So you have to be explicit. Throw in this line of code (preferably somewhere around your imports) and never worry about it again:

%matplotlib inline

Additionally, whenever you run a plotting function, you get a very redundant little readout that looks something like this:

<matplotlib.collections.PathCollection at 0x1a23ccacf8>

Well here’s a fun and little known fact, Python has semi-colons! They’re completely optional, but you can use them in much the same way as in other languages like Java if you want to (for some reason). And if you add one to the end of the final line of code in any of your plotting cells, the readouts simply disappear. Voilà!

4. Use Violin Plots and Box Plots for Distribution Checks

Histograms are great for getting an idea of the distribution of a single variable, but what if you want to compare multiple variables? Or get a more precise idea of the median and the percentile distributions of your data? In these cases, box plots and violin plots are your friends!

plt.figure(figsize=(15, 8))

plt.title('Distributions of Scores by Genre', fontsize=32)

sns.set(font_scale=2)

sns.boxplot(x='genre', y='score', data=genre_scores)

plt.tight_layout();

This graph depicts the distribution of Pitchfork review scores for songs of various genres. genre_scores is a dataframe with just two columns: the numerical score of each song, and its genre as represented by a categorical string variable (ex: "jazz" ). With the box plots, you can easily see the median (the line at the center of the colored box), the 25th and 75th percentiles (the bounds of the colored box), and the tails of each distribution. Even the outliers are depicted!

But while box plots show these bounds with great precision, we lose granularity in our view of the exact distribution of the data at each point. Not only that, but whenever there’s a long tail and/or a lot of outliers, our visualization becomes messy. Violin plots solve both of these problems and look amazing while doing it.

plt.figure(figsize=(15, 8))

plt.title('Distributions of Scores by Genre', fontsize=32)

sns.set(font_scale=2)

sns.violinplot(x='genre', y='score', data=genre_scores)

plt.tight_layout();

The wider the horizontal portion is, the more commonly a particular value on the y-axis occurs. What’s more, the thick black line with the dot in the center does for us exactly what the box plot was doing earlier: showing us the median as well as the 25th and 75th percentile marks. Violin plots are one of my favorite visualizations: they’re beautiful and they tell you a lot in just one picture. They can be a bit difficult to fully ingest, however, so there’s actually still a lot of value in simpler alternatives like box plots sometimes. Sometimes less is more.

Conclusion

I hope that these tips help you when you’re exploring your data and explaining your discoveries. Thanks for reading!