Mathew K Analytics

Lesson 2 · Full-Length Courses

Data Visualization for Beginners, with Seaborn

Seaborn is a Python charting library built on top of matplotlib, specialized for statistical charts like bar plots, box plots, violin plots, and correlation…

What you'll learn

Datasets used in this lesson

Save these next to the notebook. In Google Colab, upload them with the 📁 icon on the left first.

📓 Full notebook

Download .ipynb

Data Visualization for Beginners, with Seaborn#

  • Seaborn is a Python charting library built on top of matplotlib, specialized for statistical charts like bar plots, box plots, violin plots, and correlation heatmaps.
  • This lesson runs entirely inside a Jupyter Notebook in VS Code, one cell at a time.
  • No prior seaborn experience is required, only basic Python, pandas, and ideally the matplotlib basics from the previous lesson.
  • We will reuse the same real-world restaurant tips dataset from the matplotlib lesson, so you can compare how each library approaches the same questions.

Before You Start#

  • Open a new Jupyter Notebook in VS Code and select your Python interpreter as the kernel.
  • If seaborn, pandas, or matplotlib are not installed yet, open a terminal in VS Code and run: pip install seaborn pandas matplotlib
  • Place tips.csv in the same folder as your notebook so pandas can find it with just its file name.
import seaborn as sns
import matplotlib.pyplot as plt
import pandas as pd
print('Seaborn version:', sns.__version__)
Seaborn version: 0.13.2

Why Seaborn, If We Already Have Matplotlib?#

  • Matplotlib gives you full control, but statistical charts like box plots or correlation heatmaps take many lines of code to build from scratch.
  • Seaborn adds ready-made chart types that understand pandas DataFrames directly, plus attractive default styling, in far fewer lines.
  • Under the hood, seaborn is still drawing on matplotlib figures, so everything you learned about plt.xlabel, plt.title, and plt.show still applies.
tips = pd.read_csv('tips.csv')
tips.head()
total_bill tip sex smoker day time size
0 21.34 4.01 Male No Sun Dinner 2
1 13.14 2.63 Male No Sat Dinner 2
2 19.50 2.84 Female No Sun Dinner 2
3 23.59 2.41 Female Yes Sun Dinner 2
4 33.88 4.54 Female No Thur Lunch 6

Bar Plots: Averages by Category#

  • seaborn's barplot draws one bar per category, showing a summary statistic, the mean by default, along with a small error bar.
sns.barplot(data=tips, x='sex', y='total_bill', hue='sex', palette='Set1', legend=False)
plt.xlabel('Sex')
plt.ylabel('Mean Total Bill ($)')
plt.title('Average Total Bill by Sex')
plt.show()
No description has been provided for this image
tips.groupby('sex', observed=True)['total_bill'].mean()
sex
Female    22.108261
Male      23.652500
Name: total_bill, dtype: float64
sns.barplot(data=tips, x='sex', y='total_bill', hue='sex', estimator='median', palette='hls', legend=False)
plt.xlabel('Sex')
plt.ylabel('Median Total Bill ($)')
plt.title('Median Total Bill by Sex')
plt.show()
No description has been provided for this image

Countplot: How Many Rows per Category#

  • countplot is barplot's simpler cousin: instead of averaging a number, it just counts how many rows fall into each category.
sns.countplot(data=tips, x='day')
plt.xlabel('Day')
plt.ylabel('Number of Bills')
plt.title('Number of Bills by Day')
plt.show()
No description has been provided for this image

A Quick Look at Color Palettes#

  • Seaborn ships with several built-in color palettes you can preview before using them in a real chart.
sns.color_palette('colorblind')
sns.countplot(data=tips, x='day', hue='day', palette='colorblind', legend=False)
plt.xlabel('Day')
plt.ylabel('Number of Bills')
plt.title('Number of Bills by Day (Colorblind Palette)')
plt.show()
No description has been provided for this image

Distribution Plots: histplot and jointplot#

  • histplot is seaborn's own histogram, and can overlay a smooth density curve on top with a single argument.
  • jointplot goes a step further, combining a scatter plot with a histogram for each axis, all in one figure.
sns.histplot(data=tips, x='total_bill', kde=True)
plt.xlabel('Total Bill ($)')
plt.ylabel('Count')
plt.title('Distribution of Total Bill, With Density Curve')
plt.show()
No description has been provided for this image
sns.jointplot(data=tips, x='total_bill', y='tip')
plt.show()
No description has been provided for this image

Box Plots: Distribution Summaries by Category#

  • A box plot summarizes a number's spread using five landmarks: minimum, 25th percentile, median, 75th percentile, and maximum, plus outlier points.
sns.boxplot(data=tips, x='day', y='total_bill', hue='day', palette='hls', legend=False)
plt.xlabel('Day')
plt.ylabel('Total Bill ($)')
plt.title('Total Bill Distribution by Day')
plt.show()
No description has been provided for this image
sns.boxplot(data=tips, x='day', y='total_bill', hue='smoker', palette='hls')
plt.xlabel('Day')
plt.ylabel('Total Bill ($)')
plt.title('Total Bill Distribution by Day and Smoker Status')
plt.show()
No description has been provided for this image

Violin Plots: Box Plots With Shape#

  • A violin plot shows the same summary information as a box plot, but also draws the full smoothed shape of the distribution, so you can see if it is lumpy or has more than one peak.
sns.violinplot(data=tips, x='day', y='total_bill', hue='day', palette='Set2', legend=False)
plt.xlabel('Day')
plt.ylabel('Total Bill ($)')
plt.title('Total Bill Distribution by Day (Violin Plot)')
plt.show()
No description has been provided for this image
sns.violinplot(data=tips, x='day', y='total_bill', hue='sex', split=True, palette='muted')
plt.xlabel('Day')
plt.ylabel('Total Bill ($)')
plt.title('Total Bill Distribution by Day and Sex (Split Violin)')
plt.show()
No description has been provided for this image

Strip Plots: Every Individual Point#

  • A strip plot plots one dot per row along a category, so instead of a summary shape, you see every single observation.
sns.stripplot(data=tips, x='day', y='total_bill', hue='day', palette='husl', legend=False)
plt.xlabel('Day')
plt.ylabel('Total Bill ($)')
plt.title('Individual Bills by Day')
plt.show()
No description has been provided for this image
sns.stripplot(data=tips, x='day', y='total_bill', hue='sex', dodge=True, palette='Set2')
plt.xlabel('Day')
plt.ylabel('Total Bill ($)')
plt.title('Individual Bills by Day and Sex')
plt.show()
No description has been provided for this image

Swarm Plots: Points That Never Overlap#

  • swarmplot works like stripplot, but instead of random jitter, it nudges points apart using an algorithm that guarantees no two points ever overlap.
sns.swarmplot(data=tips, x='day', y='total_bill', hue='day', palette='Set2', legend=False)
plt.xlabel('Day')
plt.ylabel('Total Bill ($)')
plt.title('Individual Bills by Day (Swarm Plot)')
plt.show()
No description has been provided for this image

Combining Plots: Violin Plus Strip#

sns.violinplot(data=tips, x='day', y='tip', hue='day', palette='rainbow', legend=False, inner=None)
sns.stripplot(data=tips, x='day', y='tip', color='black', size=3, alpha=0.5)
plt.xlabel('Day')
plt.ylabel('Tip ($)')
plt.title('Tip Distribution and Individual Bills by Day')
plt.show()
No description has been provided for this image

catplot: One Function, Many Chart Types#

  • catplot is a flexible wrapper: change its kind argument, and it draws a completely different chart type using the exact same data and column arguments.
sns.catplot(data=tips, x='sex', y='total_bill', kind='bar', height=4)
plt.show()
No description has been provided for this image
sns.catplot(data=tips, x='day', y='total_bill', hue='sex', kind='violin', height=5, aspect=1.2)
plt.show()
No description has been provided for this image

Small Multiples: Faceting With col#

sns.catplot(data=tips, x='sex', y='total_bill', col='time', kind='bar', height=4)
plt.show()
No description has been provided for this image

relplot and lineplot: Relationships Between Numbers#

  • relplot is scatter and line plots' equivalent of catplot: one flexible, figure-level function covering both.
  • lineplot is especially powerful because it automatically summarizes repeated x-values, drawing both an average line and a shaded confidence band.
sns.relplot(data=tips, x='total_bill', y='tip', hue='time', height=4)
plt.show()
No description has been provided for this image
sns.lineplot(data=tips, x='size', y='tip')
plt.xlabel('Party Size')
plt.ylabel('Tip ($)')
plt.title('Average Tip by Party Size, With Confidence Band')
plt.show()
No description has been provided for this image

Pairplot: Every Numeric Column Against Every Other#

  • pairplot automatically builds a grid comparing every numeric column in your data against every other numeric column, all in one call.
sns.pairplot(tips[['total_bill', 'tip', 'size']])
plt.show()
No description has been provided for this image
sns.pairplot(tips[['total_bill', 'tip', 'size', 'sex']], hue='sex', palette='Set2')
plt.show()
No description has been provided for this image

Regression Plots: Adding a Trend Line#

  • regplot draws a scatter plot plus a best-fit straight line through the points, summarizing the overall trend in a single line of code.
sns.regplot(data=tips, x='total_bill', y='tip')
plt.xlabel('Total Bill ($)')
plt.ylabel('Tip ($)')
plt.title('Total Bill vs. Tip, With Trend Line')
plt.show()
No description has been provided for this image
sns.lmplot(data=tips, x='total_bill', y='tip', hue='sex', height=4, aspect=1.2)
plt.show()
No description has been provided for this image

Correlation Heatmaps#

  • A correlation coefficient measures how strongly two numbers move together, from -1, a perfect opposite relationship, through 0, no relationship, to 1, a perfect matching relationship.
  • A heatmap turns a whole table of correlations into a single, easy-to-scan, color-coded grid.
tips.corr(numeric_only=True)
total_bill tip size
total_bill 1.000000 0.673834 0.367090
tip 0.673834 1.000000 0.252121
size 0.367090 0.252121 1.000000
sns.heatmap(tips.corr(numeric_only=True), annot=True, cmap='BuGn')
plt.title('Correlation Between Numeric Columns')
plt.show()
No description has been provided for this image

Pivot Tables and Heatmaps Together#

tips.pivot_table(values='tip', index='day', columns='time', observed=True).round(2)
time Dinner Lunch
day
Fri 2.80 3.55
Sat 3.35 NaN
Sun 3.68 NaN
Thur 3.45 3.88
pivot_tips = tips.pivot_table(values='tip', index='day', columns='time', observed=True)
sns.heatmap(pivot_tips, annot=True, cmap='coolwarm', linewidths=1, linecolor='white')
plt.title('Average Tip by Day and Time')
plt.show()
No description has been provided for this image

A Finishing Touch: despine#

  • Many charts look cleaner with the top and right border lines removed, since they rarely carry any information.
sns.boxplot(data=tips, x='day', y='total_bill', hue='day', palette='Set2', legend=False)
plt.xlabel('Day')
plt.ylabel('Total Bill ($)')
plt.title('Total Bill by Day (Despined)')
sns.despine()
plt.show()
No description has been provided for this image

A Real Gotcha: Seaborn's API Changes Over Time#

  • Older seaborn tutorials online often use a function called factorplot. It was renamed years ago, and calling it today raises an error.
  • The next cell deliberately triggers this exact error on purpose, so you instantly recognize it if you ever run into an old tutorial or old code.
sns.factorplot(data=tips, x='sex', y='total_bill', kind='bar')
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Cell In[33], line 1
----> 1 sns.factorplot(data=tips, x='sex', y='total_bill', kind='bar')

AttributeError: module 'seaborn' has no attribute 'factorplot'

The Fix: Use catplot Instead#

  • Whenever you see factorplot in an old tutorial, mentally replace it with catplot, and the rest of the code usually still works unchanged.

Catching Errors Gracefully#

  • Sometimes you want to detect a problem and handle it in your code, rather than letting the whole notebook stop.
  • Python's try and except lets you catch a specific error type and respond to it instead of crashing.
try:
    sns.barplot(data=tips, x='not_a_real_column', y='total_bill')
except ValueError as e:
    print('ValueError:', e)
ValueError: Could not interpret value `not_a_real_column` for `x`. An entry with this name does not appear in `data`.

Best Practice: A Reusable Category Explorer#

  • If you find yourself building the same box-plot-by-category chart for many different columns, wrap the pattern in a function.
def explore_by_category(df, category_col, value_col):
    sns.boxplot(data=df, x=category_col, y=value_col, hue=category_col, legend=False)
    plt.xlabel(category_col.title())
    plt.ylabel(value_col.replace('_', ' ').title())
    plt.title(f'{value_col.title()} by {category_col.title()}')
    plt.show()

explore_by_category(tips, 'time', 'tip')
No description has been provided for this image

End-to-End Mini Project: When Do People Tip the Most?#

  • Let's combine grouping, a pivot table, and a heatmap to answer a real question: does the average tip percentage change by day and by meal time?
tips['tip_pct'] = tips['tip'] / tips['total_bill'] * 100
tips[['total_bill', 'tip', 'tip_pct']].head()
total_bill tip tip_pct
0 21.34 4.01 18.791003
1 13.14 2.63 20.015221
2 19.50 2.84 14.564103
3 23.59 2.41 10.216193
4 33.88 4.54 13.400236
tip_pct_pivot = tips.pivot_table(values='tip_pct', index='day', columns='time', observed=True).round(1)
tip_pct_pivot
time Dinner Lunch
day
Fri 14.2 16.5
Sat 14.6 NaN
Sun 15.8 NaN
Thur 15.5 16.6
sns.heatmap(tip_pct_pivot, annot=True, fmt='.1f', cmap='YlGnBu', linewidths=1, linecolor='white')
plt.title('Average Tip Percentage by Day and Time')
plt.xlabel('Time')
plt.ylabel('Day')
plt.show()
No description has been provided for this image

Wrap-Up: What You Learned#

  • Bar plots and count plots for comparing averages and counts across categories.
  • Color palettes, including accessible, colorblind-friendly options.
  • Distribution plots, histplot and jointplot, for seeing shape and relationship together.
  • Box plots, violin plots, strip plots, and swarm plots for comparing distributions and individual points, including splitting them by a second category.
  • catplot for one flexible function covering many chart kinds at once, including faceting a grid of small multiples with col.
  • relplot and lineplot for relationships between numbers, including automatic confidence bands.
  • pairplot for a fast overview of every numeric column against every other.
  • Regression plots, regplot and lmplot, for adding trend lines and faceting by category.
  • Correlation heatmaps and pivot-table heatmaps for spotting patterns in a full table of numbers at a glance.
  • Small finishing touches, like despine, that make any chart look more polished.
  • Practice prompt: pick a dataset of your own and try rebuilding this lesson's end-to-end mini project a pivot table turned into a heatmap to answer your own question.
  • If this lesson helped, consider subscribing for more hands-on data tutorials and drop a comment with which chart type you want to see covered next!

Found this useful?

All lessons, notebooks and datasets here are free. If they helped you, a coffee keeps new lessons coming.