A Comprehensive Intro to Data Visualization with Seaborn: Distribution Plots
By: Filip Dzuroska
Tags: Data Visualization, Seaborn, Data Science, Python, Coronavirus

A Comprehensive Intro to Data Visualization with Seaborn: Distribution Plots

Data Visualization of COVID-19 Outbreak with Python

Welcome, everyone, to Intro to Seaborn. In the following series, I will go through the core concepts of the Seaborn library, its advantages, and the way how and when to use it.

Photo by Clay Banks on Unsplash

The whole series is based on diverse Coronavirus datasets from all over the world. We’ll explore time series data, demographics, economic of Corona’s impact in different industries, and much more! All datasets are publicly available. We will explore them one by one and get familiar with the different ways how to explore relationships between individual variables.

Seaborn

The landscape of libraries in Python is rich, and they often overlay each other. So where we stand now? Seaborn is built on top of Matplotlib, and they work well together when exploring data visually. The package is oriented on statistical graphics and offers many functions starting with confidence interval or Kernel Density Estimate, know as KDE. We will explain these terms later. Besides this, Seaborn works well together with Pandas, and it’s DataFrame structure.

We could say that Seaborn is a statistical extension of Matplotlib. The library comes with many functions, aiming to help you better understand your data. The library is putting visualization and data exploration in the center of the data analysis. One of the things, which are making the package exceptional is the broad spectrum of available plots to analyze relationships between multiple variables. Seaborn works well also with the categorical variables and show aggregated statistics and count plots.

Other cool features are bivariate graphs and multiplot grids. These are great tools enabling you to understand underlying relationships, not visible in tabular data. You can immediately see the correlation between multiple variables.

Worth to mention is also styling and the overall look of the analyzed dataset. The variety of different background styles, color pallets, and other customization features will enhance data comprehension for your audience. To make your insights impactful, you need to communicate your findings accurately and properly. Seaborn has practical tools to support you in that.

Seaborn & Pandas

Seaborn is designed to work well with Pandas Dataframe. Important to mention is that a dataset for Pandas must be organized or tidy. In a tidy dataset, each row represents an observation, and each column is variable, often referred to as a feature. If we would like the library to perform properly, the first look and understanding of data are crucial. To determine if your dataset is tidy, try to use .head() and .tail() methods.

Seaborn vs. Matplotlib

If you’ve ever seen abbreviation SNS in python code, it refers to Seaborn. Importing it is as straight forward as with any other python library. Together with the seaborn, we need also to import Matplotlib lib Pyplot to leverage the synergy of both. Even though most of the job might be done by seaborn, Matplotlib adds some part of its functionalities:

Importing libraries:

import pandas as pd

import matplotlib.pyplot as plt

import seaborn as sns

import numpy as np

Loading the data

The primary data source is available at Kaggle:

We’ll begin with ‘COVID19_open_line_list.csv’ which contains worldwide information coronavirus individual cases:

#loading the data

df = pd.read_csv("novel-corona-virus-2019-dataset 2/COVID19_open_line_list.csv")

Let’s explore how the dataset looks like:

#checking the structure

df.head()

Output:

#checking the shape

df.shape

Output:

(14126, 45)

And let’s have a look at the columns as well:

#checking the columns

df.columns

Output:

Index(['ID', 'age', 'sex', 'city', 'province', 'country',

'wuhan(0)_not_wuhan(1)', 'latitude', 'longitude', 'geo_resolution',

'date_onset_symptoms', 'date_admission_hospital', 'date_confirmation',

'symptoms', 'lives_in_Wuhan', 'travel_history_dates',

'travel_history_location', 'reported_market_exposure',

'additional_information', 'chronic_disease_binary', 'chronic_disease',

'source', 'sequence_available', 'outcome', 'date_death_or_discharge',

'notes_for_discussion', 'location', 'admin3', 'admin2', 'admin1',

'country_new', 'admin_id', 'data_moderator_initials', 'Unnamed: 33',

'Unnamed: 34', 'Unnamed: 35', 'Unnamed: 36', 'Unnamed: 37',

'Unnamed: 38', 'Unnamed: 39', 'Unnamed: 40', 'Unnamed: 41',

'Unnamed: 42', 'Unnamed: 43', 'Unnamed: 44'],

dtype='object')

The Dataframe contains 45 columns, with many different formats and data types. It’s oriented on time-series analysis, describing case by case of coronavirus. Besides the ones where we are not sure what they represent, we also have clear variables, which we explore in our analysis.

Important to say, a dataset for seaborn must be tidy. Therefore we perform fundamental data transformation: getting rid of nan values for age, which we are going to plot as well as removing ranges and all non-numeric values:

#keeping only numeric, not nan values and casting to integer type

df_age = df[df['age'].notna()]

df_age = df_age[df_age['age'].str.isnumeric()]

df_age['age'] = df_age['age'].astype(str).astype(int)

Let’s start with plotting:

Distribution Plots with Seaborn

When analyzing your dataset, you will start with one variable at a time. Looking at how your data is distributed is a part of univariate analysis. For this purpose, there are distribution plots, scatter plots, or histograms.

Let us plot our first distribution plot or as in Seaborn called distplot() of age:

sns.distplot(df_age['age'])

plt.show()

Output:

To compare Seaborn with Matplotlib, let’s plot the same graph:

#plot the same histogram with matplotlib

df_age['age'].plot.hist() #show the plot

plt.show()

Output:

At first sight, we can spot a difference between Seaborn and Matplolib. The colors are muted; we have labeled x-axis and more organically defined bins in Seaborn. On top of that, see a line describing our distribution called kernel density estimation(KDE):

In statistics, kernel density estimation (KDE) is a non-parametric way to estimate the probability density function of a random variable. Kernel density estimation is a fundamental data smoothing problem where inferences about the population are made, based on a finite data sample.

source: https://en.wikipedia.org/wiki/Kernel_density_estimation

Based on the visualization, we can say that majority of confirmed cases are between 35–55 years.

You can always add some customization to your plots in Seaborn:

set style

remove KDE plot as kde=False

or change color

#set sns style as sns.set()

sns.set() #create distplot without kde:

sns.distplot(df_age[‘age’], kde=False, color = 'y') #show the plot()

plt.show()

Output:

… or make a plot vertical, setting parameter ‘vertical’ =True

#create vertical ditplot

sns.distplot(df_age['age'], kde = False, vertical=True, color="y") #show the plot()

plt.show()

Output:

If you wish to see the distribution from a different perspective, Seaborn also comes with a rub plot, which draws small vertical lines to represent each observation.

#create ditplot with rugplot and

sns.distplot(df_age['age'], rug=True, color="g") #show the plot()

plt.show()

Once you are done with univariate analysis, the very next step is to understand relationships between two and more variables.

The most common charts to understand distribution between two variables are:

line plots

scatter plots

or pair plots

For the next exercise, we will work with a dataset related to total numbers of confirmed, recovered, and deceased cases. Also coming from Kaggle- ‘covid_19_data.csv’.

After some data transformation steps, we have the following DataFrame:

df_cases_a.head()

The features represent a country, a continent, an observation date, confirmed, deceased, and recovered cases. Besides the observation day, we have transformed the dataset to have the initial day of the first confirmed case.

There are more ways how to plot the graphs as mentioned earlier — In most cases, you will work with .relplot()

This function provides access to several different axes-level functions that show the relationship between two variables with semantic mappings of subsets. The kind parameter selects the underlying axes-level function to use:

scatterplot() (with kind="scatter" ; the default)

(with ; the default) lineplot() (with kind="line" )

Line Plots

Let’s start with the line plot — a line plot displays information in the form of points connected by straight. The line chart is often used in time series visualization or to show a trend of a continuous variable. Let’s plot one here:

#create lineplot of ’day_since_first_confirmed’ and ’Confirmed’ sns.relplot(x=’day_since_first_confirmed’, y=’Confirmed’,kind = ‘line’, data=df_cases_a)

Output:

What we see here is a line connecting the number of confirmed cases per each day since the first patient was diagnosed with COVID-19 on a global scale. The shadowed area is the size of the confidence interval drawn when aggregating data for each observation. By default it’s mean, but might be replaced by standard deviation, setting estimator=’ std’:

#change estimator parameter to std

sns.relplot(x=’day_since_first_confirmed’, y=’Confirmed’,kind = ‘line’, data=df_cases_a, estimator = ‘std’) #show the plot

plt.show()

We can see that the absolute values changed because the calculation in the background has changed. We will talk about the standard deviation in the next series on statistics.

It is often the case that you would like to see more values to be present in visualization. You can always add dimension in the form of either color, markers, and other parameters:

#set hue=‘continent’ parameter and set ci=None

sns.relplot(x=’day_since_first_confirmed’, y=’Confirmed’,kind = ‘line’, data=df_cases_a, hue=’continent’, ci = None) #show the plot()

plt.show()

Output:

#set hue=‘continent’ parameter and set ci=None

sns.relplot(x=’day_since_first_confirmed’, y=’Confirmed’,kind = ‘line’, data=df_cases_a, hue=’continent’, ci = None) #show the plot()

plt.show()

What we see is the majority of confirmed cases are in North America, far ahead of any different continent.

If you would like to customize your plot, you can always add markers:

#set marker='o'.

sns.relplot(x='day_since_first_confirmed', y='Confirmed',kind = 'line', data=df_cases_a, hue='continent', marker='o', ci = None) #show the plot

plt.show()

Output:

It is often useful to have also markers in place to better navigate in the chart. In this case, the visibility and orientation in the chart are troublesome due to extreme values. To avoid this situation, we can use the logarithmic function and add a different marker for each value of the continent. Setting marker=True and style=’continent’.

#add log_value of confirmed cases for all 3 variables

df_cases_a['log_value_confirmed'] = np.log(df_cases_a['Confirmed'])

df_cases_a['log_value_deaths'] = np.log(df_cases_a['Deaths'])

df_cases_a['log_value_recovered'] = np.log(df_cases_a['Recovered']) #set parameter markers to True and style='continent'

sns.relplot(x='day_since_first_confirmed', y='log_value_confirmed',kind = 'line', data=df_cases_a, hue='continent', style = 'continent', ci = None, markers = True) #show the plot

plt.show()

Output:

Each value of the continent variable got its marker, as well as the scale of confirmed cases, is now comparable between each other. The log transformation is applied when we have strongly skewed distributions. It is instrumental in predictive modeling to find patterns or to make data better interpretable.

Scatter Plots

Let’s have a look at relationships between two variables. The convenient way how to compare two variables and their distribution is with a scatter plot. Each dot represents an observation. We will have a look at the maximum values of confirmed, decease, and recovered cases in 50 countries with the highest numbers. To show scatter plot, set kind=’ scatter’:

#plot a scatterplot with 'Confirmed' on x and 'Deaths' on y-axis

sns.relplot(x = 'Confirmed', y= 'Deaths', kind='scatter', data=df_cases_scatter) #show the plot()

plt.show()

The interpretation of the scatterplot is rather straightforward. We can make assumptions about relationships between deaths and confirmed cases. We can, for sure, say the more confirmed cases, the more deaths. We will again apply logarithmic transformation as we still have skewed data, and the majority of cases are in the bottom left corner.

Besides log transformation, let’s add dimensionality. With the ‘col’ parameter, you can show the values of a variable individually. Let’s set col=’continent’ and col_wrap=3 to have only three plots per row:

#plot a scatterplot with 'Confirmed' on x and 'Deaths' on y-axis

sns.relplot(x = 'log_value_confirmed', y= 'log_value_deaths', kind='scatter',

data=df_cases_scatter, col='continent', col_wrap=3)

#show the plot()

plt.show()

Output:

Including dimensionality in the analysis can help you to gain a comprehensive understanding of data. We can make the assumptions why the numbers in Oceania are so low, why the US and Europe are affected that heavily, etc.

The simplified, but more information-rich plot with more dimensions at once can be reached with setting hue=’continent’, size=’log_value_recovered’ or setting markers=True and style=’continent’:

#assign 'continent' to hue and log_value_recovered to size

sns.relplot(x = 'log_value_confirmed', y= 'log_value_deaths', kind='scatter',

data=df_cases_scatter, hue ='continent', size = 'log_value_recovered') #show the plot

plt.show()

Output:

Also with set markers=True, style=’continent’ and alpha=0.8 (80%) for better interpretability:

It is common to provide even more detailed information applying different colors or markers to group observations. We can also give each dot size based on specific value and create a bubble chart.

Joint Plots

Seaborn library also offers the next level of distribution charts — joint plots. Joint plots. Seaborn’s joint plot shows a relationship between 2 variables and their common as well as individual distribution. To create once, just use .joinplot():

#jointplot of log_value_confirmed and log_value_deaths

sns.jointplot(x=’log_value_confirmed’,y=’log_value_deaths’,

data=df_cases_scatter) #show the plot

plt.show()

By default, the joint plot is displayed as a scatter plot, with the distribution of log_value_confirmed and log_value_deaths. It is handy in cases when you would like to check individual distribution and all in one chart.

There are several different joint plot chart styles like:

reg plot

hex plot

KDE plot

#jointplot of log_value_confirmed and log_value_deaths, regression

sns.jointplot(x=’log_value_confirmed’,y=’log_value_deaths’,data=df_cases_scatter, kind=’reg’, color=’y’) #show the plot

plt.show()

#jointplot of log_value_confirmed and log_value_deaths, hex

sns.jointplot(x=’log_value_confirmed’,y=’log_value_deaths’,data=df_cases_scatter, kind=’hex’, color=’b’) #show the plot

plt.show()

#jointplot of log_value_confirmed and log_value_deaths, kde

sns.jointplot(x=’log_value_confirmed’,y=’log_value_deaths’,data=df_cases_scatter, kind=’kde’, color=’g’) #show the plot

plt.show()

A density plot an upgraded version of the histogram. In case you have rather many observation and scatter chart is overplotted, it is recommended to use a density chart, which counts the number of observations within a smaller area. The area is usually defined by shapes like hexagon, square, etc. Another more advanced way is to plot KDE and display its contours.

We have covered most methods of how to visualize a variable’s distribution. With some specific cases, we will go into detail in the next articles, like regression charts.

Note from the editors: Towards Data Science is a Medium publication primarily based on the study of data science and machine learning. We are not health professionals or epidemiologists, and the opinions of this article should not be interpreted as professional advice. To learn more about the coronavirus pandemic, you can click here.

Follow me on LinkedIn

and Twitter:

Sources: