Data Visualization with Python: From Exploratory to Explanatory
Data Visualization with Python: From Exploratory to Explanatory
Data visualization sits at the intersection of analysis and communication. In exploratory work, charts help you see patterns, outliers, and structures your code alone might miss. In explanatory work, a well-crafted figure tells a story that a table of numbers never could.
Python’s ecosystem — Matplotlib, Seaborn, and Plotly — gives you the full spectrum, from quick scatter plots to publication-ready figures. This post walks through the practical journey from exploration to explanation.
The Exploratory Phase: Speed Over Polish
When you’re exploring a new dataset, your first goal is throughput. You want to generate many views quickly and discard what doesn’t illuminate.
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
df = pd.read_csv("housing.csv")
# Quick distribution check
df["price"].hist(bins=50)
plt.show()
# Pairwise scatter for numeric columns
sns.pairplot(df[["price", "sqft", "bedrooms", "baths"]], diag_kind="kde")
plt.show()At this stage, everything is disposable. Default settings, no axis labels, overlapping points — that’s fine. The audience is you. The question is “what’s here?” not “what does this mean?”
A few quick patterns to look for:
- Skewed distributions — log-transform before modeling
- Missing categories — your data might not be as complete as you assumed
- Clusters and outliers — these often tell the most interesting story
Moving to Explanatory: Design Principles
Once you’ve found something worth sharing, the rules shift. The audience is no longer you — it’s a reader who hasn’t stared at this data for an hour. Every chart needs a clear purpose.
1. Choose the Right Chart Type
| Goal | Chart Type | Library |
|---|---|---|
| Distribution of one variable | Histogram / KDE | Matplotlib / Seaborn |
| Relationship between two variables | Scatter plot | Matplotlib / Seaborn |
| Comparing categories | Bar plot | Seaborn / Matplotlib |
| Composition over time | Stacked area / line | Matplotlib |
| Correlation matrix | Heatmap | Seaborn |
| Geographic data | Choropleth | Plotly / GeoPandas |
Picking the wrong chart type is the most common mistake. A pie chart with 12 slices doesn’t help anyone. A line chart on categorical data creates false continuity. Think about what question the chart answers, then pick the form that answers it most directly.
2. Reduce Ink, Increase Signal
Edward Tufte’s principle of data-ink ratio is still the best guide: remove anything that doesn’t carry information.
# Before: default Seaborn with grid, ticks, spines
sns.barplot(data=df, x="region", y="price")
# After: cleaner version
fig, ax = plt.subplots()
sns.barplot(data=df, x="region", y="price", ax=ax)
ax.set_title("Median Price by Region", fontsize=14, pad=12)
ax.set_xlabel("")
ax.set_ylabel("Median Price ($)")
sns.despine(trim=True) # Remove top and right spines
ax.tick_params(axis="x", rotation=0)The difference is subtle but real. Each element you remove forces the reader’s eye back to the data.
3. Use Color Intentionally
Color is a powerful tool — and an easy one to misuse. A few guidelines:
- Sequential data (values from low to high): use a single-hue gradient like
BluesorGreens - Categorical data: use a qualitative palette. Seaborn’s
color_palette("husl", n)orSet2work well - Diverging data (positive/negative, above/below): use
RdBuorcoolwarm - Avoid rainbow colormaps — they distort perception through uneven luminance
# Sequential
sns.heatmap(corr, cmap="Blues", annot=True, fmt=".2f")
# Categorical
sns.barplot(data=df, x="region", y="price", palette="Set2")4. Annotate to Tell the Story
A chart without annotation is just decoration. Add the key insight in plain words:
fig, ax = plt.subplots(figsize=(8, 5))
sns.lineplot(data=monthly, x="month", y="revenue", marker="o", ax=ax)
# Highlight the key point
ax.annotate(
"June spike: new product launch",
xy=("2026-06", monthly.loc[monthly["month"] == "2026-06", "revenue"].values[0]),
xytext=("2026-04", monthly["revenue"].max() * 0.9),
arrowprops=dict(arrowstyle="->", color="gray"),
fontsize=10,
)
ax.set_title("Monthly Revenue — June Spike After Product Launch")
sns.despine()The annotation tells the reader where to look and why it matters. Without it, they might see the spike but miss its significance.
Interactive Visualization with Plotly
For exploratory dashboards or presentations where the audience can click, Plotly adds interactivity without much overhead:
import plotly.express as px
fig = px.scatter(
df, x="sqft", y="price", color="region",
hover_data=["bedrooms", "baths"],
title="Price vs. Square Footage by Region",
)
fig.show()Hover tooltips, zoom, and pan make Plotly ideal for data exploration you want to share. The trade-off: it’s harder to fine-tune every pixel compared to Matplotlib.
Putting It Together: A Reproducible Workflow
A good visualization workflow is also reproducible. Store your chart generation in scripts or Jupyter notebooks with clear section headers. Use consistent styling across a project by defining a theme once:
# theme.py
import matplotlib.pyplot as plt
import seaborn as sns
sns.set_theme(
style="whitegrid",
palette="Set2",
font="Inter",
font_scale=1.1,
)
plt.rcParams["figure.dpi"] = 150
plt.rcParams["savefig.bbox_inches"] = "tight"Then import it at the top of every analysis notebook. This way, every chart in your project shares the same visual language.
Conclusion
Data visualization is not a decorative afterthought — it’s a core analytical skill. During exploration, go fast and iterate. When presenting, be deliberate about every design choice: chart type, color, ink, and annotation. Python gives you the tools for both modes. The skill is knowing which one you’re in and when to switch.
~ Kang Ifaz