Sunday, September 20, 2026
HomeData ScienceData Visualization for Data Scientists – Matplotlib, Seaborn, Plotly and Best Practices

Data Visualization for Data Scientists – Matplotlib, Seaborn, Plotly and Best Practices

Table of Content

Data visualisation is the bridge between analysis and insight. A perfect model with poor visualisation fails to communicate its value; a flawed analysis made compelling with excellent visuals can mislead stakeholders. The best data scientists combine technical chart-building proficiency with design principles and storytelling ability — they know not just how to plot data but how to make the audience understand and act on it. This guide covers the full data visualisation stack used in production data science: Matplotlib fundamentals, Seaborn statistical graphics, Plotly interactive charts, and the principles that make visualisations effective.

Visualisation is central to exploratory data analysis (covered in our Feature Engineering guide), communicating model results (see Model Evaluation and Hyperparameter Tuning), and presenting findings in data science case study interviews. For building interactive dashboards from ML models, see our Streamlit ML App Deployment guide. The visualisation of time series data specifically is covered in our Time Series Forecasting Interview Q&A.

Choosing the Right Chart — The Most Important Visualisation Decision

The most common visualisation mistake is choosing a chart based on aesthetics rather than the data relationship being communicated. Every chart type answers a specific analytical question. Before writing a single line of plotting code, ask: what relationship am I trying to show?

Relationship to ShowBest ChartAvoid
Distribution of one variableHistogram, KDE, violin plot, box plotBar chart (hides shape)
Comparison across categoriesBar chart (sorted), dot plotPie chart (>5 slices), 3D bar
Trend over timeLine chartBar chart (obscures trend), scatter
Correlation between two numeric varsScatter plot, hexbin (large n)Line chart (implies order)
Composition (part of whole)Stacked bar, treemap, waffle chartPie chart (poor for comparison), donut
Correlation matrix / many pairsHeatmap, pair plotToo many scatter plots
Geographic dataChoropleth map, bubble mapTables (hides spatial patterns)
RankingHorizontal sorted bar, lollipop chartVertical bar (hard to read labels)
Uncertainty / confidence intervalsError bars, ribbon/band on line chartSingle line (implies precision)

Why pie charts are almost always wrong: Humans compare lengths far more accurately than angles or areas. A bar chart representing the same data as a pie chart almost always communicates more clearly. Pie charts are acceptable only when: there are 2-3 categories, the proportions are dramatically different (95%/5%), and the “part of whole” concept is the primary message. For any other situation, use a bar chart.

Matplotlib — The Foundation Layer

Matplotlib is the foundational plotting library that Seaborn, pandas .plot(), and many other libraries are built on. Understanding its object model — Figure (the canvas), Axes (individual plot areas), and Artist (everything drawn on a figure) — is essential for customisation and for fixing the inevitable layout issues.

import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import numpy as np

# --- Object-oriented API (preferred over pyplot state machine) ---
fig, axes = plt.subplots(2, 2, figsize=(12, 8))
fig.suptitle('Sales Dashboard Q3 2026', fontsize=14, fontweight='bold')

# Revenue trend
ax = axes[0, 0]
dates   = pd.date_range('2026-07-01', periods=90, freq='D')
revenue = np.cumsum(np.random.randn(90)) + 100
ax.plot(dates, revenue, color='#2563EB', linewidth=2)
ax.fill_between(dates, revenue, alpha=0.1, color='#2563EB')
ax.set_title('Daily Revenue', fontweight='bold')
ax.set_xlabel('Date'); ax.set_ylabel('Revenue (INR Lakhs)')
ax.xaxis.set_major_formatter(plt.matplotlib.dates.DateFormatter('%b %d'))
ax.tick_params(axis='x', rotation=45)

# Category comparison (sorted horizontal bar)
ax = axes[0, 1]
categories = ['Electronics', 'Clothing', 'Home', 'Books', 'Sports']
values     = [450, 320, 280, 190, 150]
colors     = ['#2563EB' if v == max(values) else '#93C5FD' for v in values]
ax.barh(categories, values, color=colors)
ax.set_title('Revenue by Category', fontweight='bold')
for i, v in enumerate(values):
    ax.text(v + 5, i, f'{v:,}', va='center', fontsize=9)
ax.set_xlabel('Revenue (INR Lakhs)')

plt.tight_layout()
plt.savefig('dashboard.png', dpi=150, bbox_inches='tight')
plt.show()

Matplotlib style best practices: Always use the object-oriented API (fig, ax = plt.subplots()) rather than the pyplot state machine (plt.plot()) for anything beyond a quick exploratory chart — it is more explicit, easier to debug, and composable. Set figure size before plotting: figsize=(width_inches, height_inches). Use bbox_inches='tight' when saving to avoid clipped labels. Remove chart junk: eliminate unnecessary gridlines (ax.grid(axis=’y’, alpha=0.3) — only horizontal), remove top and right spines (ax.spines[[‘top’, ‘right’]].set_visible(False)), and use direct labels instead of legends where possible.

Seaborn — Statistical Visualisation

Seaborn is built on Matplotlib and provides high-level functions for statistical graphics. It handles the tedious details — colour palettes, grouping by hue, confidence intervals, faceting — that would require significant Matplotlib boilerplate. Seaborn is the standard for EDA in data science notebooks.

import seaborn as sns
import pandas as pd

sns.set_theme(style='whitegrid', palette='Blues_d', font_scale=1.1)

# --- Distribution plots ---
fig, axes = plt.subplots(1, 3, figsize=(15, 5))

# Histogram with KDE overlay
sns.histplot(df['revenue'], kde=True, bins=30, ax=axes[0], color='steelblue')
axes[0].set_title('Revenue Distribution')

# Box plot — shows median, IQR, outliers
sns.boxplot(data=df, x='category', y='revenue', ax=axes[1], palette='Blues')
axes[1].tick_params(axis='x', rotation=45)
axes[1].set_title('Revenue by Category')

# Violin plot — shows full distribution shape
sns.violinplot(data=df, x='tier', y='revenue', ax=axes[2],
               palette='Blues', inner='quartile')
axes[2].set_title('Revenue Distribution by Tier')

plt.tight_layout(); plt.show()

# --- Pair plot for EDA ---
# Plots every pair of numeric columns + diagonal distributions
pair_grid = sns.pairplot(df[['revenue', 'quantity', 'discount', 'profit']],
                         hue='category', diag_kind='kde', plot_kws={'alpha': 0.4})
pair_grid.fig.suptitle('Feature Relationships', y=1.02)

# --- Heatmap for correlation matrix ---
fig, ax = plt.subplots(figsize=(10, 8))
corr = df.select_dtypes('number').corr()
mask = np.triu(np.ones_like(corr, dtype=bool))  # upper triangle mask
sns.heatmap(corr, mask=mask, annot=True, fmt='.2f', cmap='RdYlBu_r',
            center=0, vmin=-1, vmax=1, ax=ax,
            cbar_kws={'shrink': 0.8})
ax.set_title('Feature Correlation Matrix', fontweight='bold')
plt.tight_layout(); plt.show()

# --- FacetGrid: same plot across groups ---
g = sns.FacetGrid(df, col='region', row='year', height=3, aspect=1.3)
g.map_dataframe(sns.lineplot, x='month', y='revenue', hue='category')
g.add_legend(); g.set_titles(col_template='{col_name}', row_template='{row_name}')

Plotly — Interactive Visualisations

a person pointing at a large display of pictures
Photo by Karen Grigorean on Unsplash

Plotly produces interactive charts that run in browsers — users can hover for tooltips, zoom, pan, filter by legend click, and download as PNG. This interactivity is essential for dashboards and stakeholder presentations where exploration is needed. Plotly Express provides a high-level API with defaults tuned for data science; Plotly Graph Objects provides full control for custom charts.

import plotly.express as px
import plotly.graph_objects as go
from plotly.subplots import make_subplots

# --- Interactive scatter with hover ---
fig = px.scatter(df, x='marketing_spend', y='revenue',
                 color='category', size='profit',
                 hover_data=['region', 'quarter'],
                 trendline='ols',    # add OLS regression line
                 title='Revenue vs Marketing Spend',
                 labels={'marketing_spend': 'Marketing Spend (INR L)',
                         'revenue': 'Revenue (INR L)'})
fig.update_layout(font_family='Inter', title_x=0.5)
fig.show()

# --- Interactive line chart with range selector ---
fig = px.line(df_monthly, x='date', y='revenue',
              color='category', title='Monthly Revenue Trend')
fig.update_xaxes(
    rangeslider_visible=True,
    rangeselector=dict(buttons=[
        dict(count=3,  label='3M', step='month', stepmode='backward'),
        dict(count=6,  label='6M', step='month', stepmode='backward'),
        dict(count=1,  label='1Y', step='year',  stepmode='backward'),
        dict(step='all', label='All')
    ])
)
fig.show()

# --- Choropleth map ---
fig = px.choropleth(df_states, locations='state_code',
                    locationmode='USA-states', color='revenue',
                    scope='usa', color_continuous_scale='Blues',
                    title='Revenue by State')
fig.show()

Visualisation Design Principles

Data-ink ratio (Tufte): Every pixel of ink in a chart should be justified by the data it represents. Eliminate: 3D effects (distort perception), excessive gridlines, heavy borders, decorative patterns, gradient fills, redundant legends (label directly instead). Maximise the ratio of data-ink to total ink — the simplest chart that communicates the message is the best chart.

Pre-attentive attributes — what the eye sees first: Colour (hue and saturation), position, size, and orientation are processed before conscious attention. Use them to direct the audience’s eye: make the most important bar a different colour; position the most important trend line at the top; use size to encode a third variable in scatter plots. Use at most 2-3 pre-attentive attributes per chart — using all of them simultaneously creates visual noise.

Colour choice principles: Use sequential palettes (Blues, Greens) for ordered numeric data. Use diverging palettes (RdYlBu, PiYG) when data has a meaningful midpoint (positive/negative, above/below average). Use qualitative palettes (Set1, Paired) for categorical data with no ordering. Always test for colour-blind accessibility — approximately 8% of men have red-green colour blindness. Use the viridis/plasma palettes (perceptually uniform, colour-blind friendly) for continuous data by default.

Storytelling with data: A visualisation in a presentation or report should have: a headline that states the finding (not just the variable name) — “Revenue grew 34% YoY driven by Electronics” not “Revenue by Category”; annotations that call out the key insight directly on the chart; a clear hierarchy (what should the eye see first?); and context (what is the baseline, target, or comparison?). For communicating model results in interviews, our Data Science Case Study Interview guide covers how to present A/B test results and metric analyses clearly.

The choice between Matplotlib, Seaborn, and Plotly should be context-driven: Matplotlib for precise publication-quality static figures; Seaborn for fast statistical EDA in notebooks; Plotly for interactive dashboards and stakeholder presentations. Combined with the Matplotlib and Seaborn fundamentals covered in our earlier guide and the Streamlit deployment guide for building interactive ML apps, these three libraries cover the full data visualisation needs of a production data science team.

Leave feedback about this

  • Rating

Durgesh Kekare
Durgesh Kekarehttps://www.dataexpertise.in
Durgesh Kekare is a data science educator and founder of DataExpertise.in. With expertise in Python, machine learning, and analytics, he helps 10,000+ learners break into data careers.

Latest Posts

List of Categories