Statistical plotting with relplot() | Towards Data Science

The goal of this article is that you come away with a strong knowledge of any type of statistical plotting of quantitative variables using Seaborn’s relplot() function.
When I started learning Data Visualization, I was first introduced to Matplotlib. It is a library that is so vast and deep, you can visualize almost anything data-related. It is this vastness that enables people to create a single plot in many ways.
While its flexibility is ideal for experienced scientists, as a beginner, it was a hell of a nightmare for me to distinguish the code between the methods. I even considered going for the no-code interface of Tableau, which I am deeply ashamed to admit, as a programmer. I wanted something that was easy to use and at the same time, enable me to create those cool plots others were making (in code).
I learned about Seaborn while I was doing a Nanodegree at Udacity and finally, found my pick. That’s why my golden rule for Data Visualization is “Do it in Seaborn if you can do it in Seaborn”. It offers many advantages over its counterpart, Matplotlib.
Firstly, it is very easy to use. You can create complex plots with just a few lines of code and still make it look pretty with built-in styles. Secondly, it works amazingly well with Pandas DataFrames, which is just what you need as a Data Scientist.
Last but not least, it is built on top of Matplotlib itself. This means that you will get to enjoy most of the flexibility offered by Mpl, and still keep the code syntax to the minimum.
And yes, I really mean what I say in the headline. Seaborn divides all of its API into three categories: Plotting statistical relationships, visualizing the distribution of data, and categorical data plotting. Seaborn provides three high-level functions which encompass most of its features and one of them is relplot().
reltplot() can visualize any statistical relationships between quantitative variables. In this article, we will cover almost all the features of this function, including how to create subplots and many more.
  I. Introduction II. SetupIII. Scatter plots with relplot()     1. Scatter plot point size     2. Scatter plot point hue     3. Scatter plot point style     4. Scatter plot point transparency     5. Scatter plot in subplots IV. Seaborn lineplots     1. Lineplot multiple lines     2. Lineplot line styling     3. Lineplot point markers     4. Lineplot confidence intervals  V. Conclusion
Get the notebook and the sample data for the article on this GitHub repo.
We import Seaborn as sns. You might have been wondering why it is not aliased as sb like any normal person would. Well, get this: It is aliased after a fictional character Samuel Norman Seaborn from the TV show The West Wing. It is a joked initialism.
For the sample data, I will be using one of the built-in datasets of Seaborn and one I downloaded from Kaggle. You can get it using this link.
The first dataset is about cars containing data about their engine, model, etc. The second dataset gives information on New York stock prices for over 500 companies.
cars.head()cars.info()cars.describe()
<class 'pandas.core.frame.DataFrame'>RangeIndex: 398 entries, 0 to 397Data columns (total 9 columns): #   Column        Non-Null Count  Dtype  ---  ------        --------------  -----   0   mpg           398 non-null    float64 1   cylinders     398 non-null    int64   2   displacement  398 non-null    float64 3   horsepower    392 non-null    float64 4   weight        398 non-null    int64   5   acceleration  398 non-null    float64 6   model_year    398 non-null    int64   7   origin        398 non-null    object  8   name          398 non-null    object dtypes: float64(4), int64(3), object(2)memory usage: 28.1+ KB
stocks.head()stocks.info()
<class 'pandas.core.frame.DataFrame'>DatetimeIndex: 851264 entries, 2016-01-05 to 2016-12-30Data columns (total 6 columns): #   Column  Non-Null Count   Dtype  ---  ------  --------------   -----   0   symbol  851264 non-null  object  1   open    851264 non-null  float64 2   close   851264 non-null  float64 3   low     851264 non-null  float64 4   high    851264 non-null  float64 5   volume  851264 non-null  float64dtypes: float64(5), object(1)memory usage: 45.5+ MB
There are some null values in both datasets. Since we are not doing any serious analysis, we can safely drop them.
cars.dropna(inplace=True)stocks.dropna(inplace=True)
Pro Tip: Make your dataset as tidy as possible for Seaborn to perform well. Ensure that each row is an observation and each column is a single variable.
Let’s start with scatter plots. A Scatter plot is one of the best and most widely used plots to find out patterns and relationships between variables.
These variables are often quantitative such as measurements, the temperature in a day, or any numerical value. Scatter plots visualize every tuple of x and y values as a single dot and the plot will form a cloud of dots. These types of plots are ideal for the human eye to detect patterns and relationships.
You can create a scatter plot using SB’s (I will be abbreviating from now on) built-in scatterplot() function. But this function lacks the flexibility offered by the version in relplot(). Let's see an example using relplot().
Using cars datasets, we want to find out if heavier cars tend to have more horsepower. Since both features are numerical, we can use a scatter plot:
sns.relplot(x='weight', y='horsepower',             data=cars, kind='scatter');
relplot() function has arguments x, y and data parameters to specify values to be plotted on XAxis, YAxis and the data it should use, respectively. We use the kind parameter to specify that it should use a scatter plot. Actually, by default, it is set to scatter.
From the plot, it can be interpreted that heavier cars do have more horsepower. It is also clear that there are many more cars weighing between 1500 and 3000 with horsepower 50–110.
Building on our previous plot, now we also want to add a new variable. Let’s see whether heavier cars have bigger displacement (how much fuel they can store). Ideally, we would want this variable plotted as point size:
sns.relplot(x='weight',            y='horsepower',            data=cars,            kind='scatter',            size='displacement');
To make the point size change with respect to a third variable is done using the size parameter. Just pass the column name as a string and you will be set, just like in our example. This plot shows a clear relationship between weight and engine size. However, you can see some points in the center which does not fit the trend.
It is important you pass a numerical variable that will have fewer ‘value periods’. In our example, displacement had 5 (0–80, 80–160, etc). If these periods become too many, it will be hard to interpret your plot as the human eye is not good at sizing things up. You can see this if we create the plot above with the third variable being the weight:
sns.relplot(x='displacement',            y='horsepower',            data=cars,            kind='scatter',            size='weight');
As you see, the trend is not obvious and it is difficult to differentiate between the sizes.
It is also possible to use a color marker for a third variable in scatter plots. It is also very straightforward just like point size. Let’s say we also want to encode the acceleration (the time it takes for a car to reach 60 miles/hour in seconds) as point color:
sns.relplot(x='weight',            y='horsepower',            data=cars,            kind='scatter',            size='displacement',            hue='acceleration');
From the plot, we can see that some of the fastest cars (darker dots) in the dataset have lower horsepower but also weigh less. Note that we used hue parameter to encode color. The colors change depending on the type of variable you pass to this parameter. If we had passed origin column, which is a categorical variable, it would have three color markers instead of a sequential (going from light to dark) palette:
sns.relplot(x='weight',            y='horsepower',            data=cars,            kind='scatter',            size='displacement',            hue='origin');
Pro Tip: Be aware of the type of variables you are inputting to hue parameter. The type can completely change the result.
If you don’t like the default color palette (which is very nice by default), you can easily customize:
sns.relplot(x='weight',            y='horsepower',            data=cars,            kind='scatter',            size='displacement',            hue='acceleration',            palette='crest');
Set the palette parameter to your own color mapping. A list of available palettes can be found here.
Let’s go back to the first plot. We created a scatter plot of weight vs. horsepower. Now, let’s add the origin column as the third variable:
sns.relplot(x='weight', y='horsepower',             data=cars, hue='origin');
While the color adds an extra layer of info in this plot, it might be difficult to differentiate between the colors in the swarm of dots in bigger datasets. Let’s add a point style to the plot for more clarity:
sns.relplot(x='weight',            y='horsepower',            data=cars,            hue='origin',            style='origin');
Now, that’s a little better. Changing the point style together with the color is very effective. If we used just the point style to specify the origin, look at what would have happened:
sns.relplot(x='weight', y='horsepower',             data=cars, style='origin');
Pro Tip: Use hue and style parameters together to give your plots more clarity.
We removed the hue argument and it made our plot much harder to interpret. If you don't like the default colors, you can change it:
First, you should create a dictionary that maps individual colors to each category. Note that the keys of that dictionary should be the same as the names in the legend.
hue_colors = {'usa': 'red',               'japan': 'orange',               'europe': 'green'}sns.relplot(x='weight',            y='horsepower',            data=cars,            hue='origin',            style='origin',            palette=hue_colors);
From the plot, we can see that majority of cars in our dataset are from the USA.
Let’s again go back to our first example. Let’s plot it again but with a little transparency added to the points:
sns.relplot(x='weight', y='horsepower',             data=cars, alpha=0.6);
We used alpha parameter to set the transparency of points. It accepts values between 0 and 1. 0 is completely transparent and 1 is fully opaque. It can be a very useful feature when you have a large dataset and you want to find out the clusters or groups in the plot. As you lower the transparency, parts of your plot with many dots will become darker.
It is also possible to use a grid of subplots in Seaborn. The reason why I have been using relplot() instead of scatterplot(), which can do all the things above, is that it cannot create a subplot grid.
As relplot is a figure-level function, it produces a FacetGrid(a grid of many plots) object, whereas, scatterplot() plots onto a single matplotlib.pyplot.Axes(a single plot) objects which cannot be turned into subplots:
fg = sns.relplot()print(type(fg))plot = sns.scatterplot()print(type(plot))<class 'seaborn.axisgrid.FacetGrid'><class 'matplotlib.axes._subplots.AxesSubplot'>
Let’s see an example where we want to use subplots. In one of the plots above, we encoded 4 variables into a single plot (Weight vs. horsepower encoded with displacement and acceleration). Now, we want to also add the origin of cars. To make the information easier to interpret, we should plot it in subplots:
sns.relplot(x='weight',            y='horsepower',            data=cars,            kind='scatter',            size='displacement',            hue='acceleration',            palette='crest',            col='origin');
This time in the end, we added a new parameter, col to point out that we want to create subplots in columns. These types of subplots are very useful since now it is easy to see the trend across the fifth variable. By the way, the variable you pass to col should be categorical for this to work. Also, SB displays columns in one row. We don't want that if there are multiple categories to plot. Let's see a use-case using our data although it has fewer categories:
sns.relplot(x='weight',            y='horsepower',            data=cars,            kind='scatter',            size='displacement',            hue='acceleration',            palette='crest',            col='origin',            col_wrap=2);
col_wrap argument tells SB how many columns we want in a row.
It is also possible to specify the order of categories in the columns:
sns.relplot(x='weight',            y='horsepower',            data=cars,            kind='scatter',            size='displacement',            hue='acceleration',            palette='crest',            col='origin',            col_order=['japan', 'europe', 'usa']);
It is also possible to display the same information in rows:
sns.relplot(x='weight',            y='horsepower',            data=cars,            kind='scatter',            size='displacement',            hue='acceleration',            palette='crest',            row='origin');
Using rows cannot be very useful if you have many categories, best to stick to columns. You can again use row_order to specify the order of rows.
Another common type of a relational plot is a line plot. While in scatter plots, every dot is an independent observation, in line plot we have a variable plotted along with some continuous variable, typically a period of time. Our second sample dataset contains New York stock exchange data for 501 companies tracked from 2010 to 2016.
Let’s just for illustration purposes observe close prices for all companies for the given time period. As there is a date involved, line plot is the best visual type for this task:
Pro Tip: If you have a date column in your dataset, set it as a datetype and set it as an index using set_index() function. Or use pd.read_csv(parse_dates=['date_column'], index_col='date_column'). It will allow you to plot line plots and make subsetting for dates easier.
For line plots, we again use relplot() with kind set to line. This plots a single variable tracked across the second continuous variable, typically time.
sns.relplot(x=stocks.index, y='close',             data=stocks, kind='line');
It took quite a while to plot but we can see the obvious trend. Stocks kept increasing in the given time period for all companies. The blue darker line represents the mean of the close price for all companies tracked over 6 years.
SB automatically adds a confidence interval, that’s that shaded area around the line if there are multiple observations for a single point, more on this later. Now, let’s subset our data to only 3 companies:
am_ap_go = stocks[stocks['symbol'].isin(['AMZN', 'AAPL', 'GOOGL'])]am_ap_go.shape(5286, 6)
Now, let’s recreate the above line plot by grouping the data into the three companies. This will create not one but three lines in the same plot:
sns.relplot(x=am_ap_go.index,            y='close',            data=am_ap_go,            kind='line',            hue='symbol');
Just like we did in the scatter plot example, we use the hue parameter to create subgroups in our data. Again, you have to pass a categorical variable into hue. In this plot, we can see that after 2016, the stocks of Amazon and Google were very close together, while Apple was at the bottom throughout the period. Now let's see how we style each line apart from its color:
We use style parameter to specify that we want different line styles for each line:
sns.relplot(x=am_ap_go.index,            y='close',            data=am_ap_go,            kind='line',            hue='symbol',            style='symbol');
OK, this might not be the best example of different line styles, because observations are for each day and are very tight. Let’s subset only for the period 2015–2016 and plot the same line plot:
am_ap_go_subset = am_ap_go.loc['2015-01-01':'2015-12-31']sns.relplot(x=am_ap_go_subset.index,            y='close',            data=am_ap_go_subset,            kind='line',            hue='symbol',            style='symbol');
Now the line styles are clearer. You might be saying that styling the lines does not add any more information than the previous one. But when you have a plot that has its lines situated very close together, this will be a huge help for interpretation.
When you have a smaller dataset and you want to create a line plot, it might be useful to mark each data point with a marker. The distribution of the stock prices for our smaller data still is very tight. So let’s subset for an even smaller period to see how we might use point markers:
smaller_subset = am_ap_go_subset.loc["2015-01-01":"2015-03-01"]sns.relplot(x=smaller_subset.index,            y='close',            data=smaller_subset,            kind='line',            hue='symbol',            style='symbol',            markers=True)plt.xticks(rotation=60);
Now we have different markers for each line. We set the markers parameter set to True to activate this feature.
Next, we will explore the confidence intervals calculated for line plots in SB. Confidence intervals are added automatically if there are multiple observations for a single point.
In our data for the three companies, for each day in the period, we have three observations: one for Amazon, one for Google, one for Apple. When we create a line plot without subgrouping (without the hue parameter), SB takes the average of these three by default:
sns.relplot(x=am_ap_go.index, y='close',             data=am_ap_go, kind='line');
The darker line indicates the mean of the three companies’ stock prices. The shaded area is the 95% confidence interval. It is very huge because our sample data is only for three companies. The shaded area means it is 95% sure that the mean for the population is within this interval. It indicates the uncertainty in our data.
For example, for 2017, the mean for the population can be between 100 and 800. The range of the confidence interval is huge because, again, we subset for only three companies. Sorry that I had to add statistics terminologies, but it was important for understanding confidence intervals. You can turn off the confidence intervals setting the ci parameter to None:
sns.relplot(x=am_ap_go.index,              y='close',             data=am_ap_go,              kind='line', ci=None);
Or if you want, you can show the standard deviation instead of the confidence interval. Set the ci parameter to sd:
sns.relplot(x=am_ap_go.index,             y='close',             data=am_ap_go,             kind='line',             ci='sd');
Maybe these parameters were not very useful for our sample data but they will be very important when you work on real data with large volumes.
A final note: you can use the same col and row parameters if you want to create subplots for your lineplots.
Finally, we finished relational plots in Seaborn. Line plots and scatterplots are very important visual aids for discovering insights and trends among the sea of data. Therefore, it is important that you master them. And try to create them using Seaborn whenever possible. You saw how easy it was using the library.
Check out my other articles on Data Visualization: