Python for Data Analytics: Phase 2 Training¶
Notebook 2.3: Data Visualization using Python -- Seaborn¶
Author: Prakash Ukhalkar
Role: Assistant Professor (MCA) | Researcher in Data Science and Machine Learning
Training Date: Monday, 06th July 2026
Notebook Scope: Seaborn for statistical visualization -- highlighting trends, distributions, and visual anomalies in production data.
Phase 2 -- Data Visualization using Python (Seaborn)¶
Learning Objectives
- Understand how Seaborn relates to Matplotlib (built on top of it)
- Use
histplot,kdeplot,boxplot,violinplot,scatterplot, andheatmap - Map a third variable to colour using Seaborn's
hueparameter - Highlight trends and detect visual anomalies in manufacturing data
- Combine Seaborn and Matplotlib annotations on the same Axes
Training Date: Monday, 06th July 2026
Estimated Duration: 60 minutes
Prerequisites: Notebook 2.2 completed (Matplotlib Visualization)
Environment Setup¶
Concept and Code Explanation (Before Use)
| Aspect | Matplotlib | Seaborn |
|---|---|---|
| Default style | Plain white | Polished themes (whitegrid, darkgrid) |
| Working with DataFrames | Pass arrays manually | Pass DataFrame + column names as strings |
| Statistical charts | Manual calculation needed | histplot with KDE, boxplot built in |
| Hue / group mapping | Manual colour loops | hue= -- one argument, automatic legend |
sns.set_theme(style="whitegrid", palette="tab10") sets background style and colour palette for the entire session.
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline
sns.set_theme(style="whitegrid", palette="tab10")
plt.rcParams["figure.figsize"] = (10, 5)
plt.rcParams["figure.dpi"] = 100
np.random.seed(42)
print("Seaborn version:", sns.__version__)
print("Visualization environment ready.")
Seaborn version: 0.13.2 Visualization environment ready.
Result Interpretation: Environment Setup¶
- Seaborn version confirms installation is active.
sns.set_theme()affects all subsequent charts in this session -- call it once at the top.- If
seabornis not found, runpip install seabornand restart the kernel.
Shared Dataset¶
Concept and Code Explanation (Before Use)
The dataset for this notebook extends the production simulation to 90 days across 4 machines.
An engineered anomaly (a period with elevated defects on machine M-103) is deliberately embedded.
Detecting this anomaly visually is the recurring challenge across all charts in this notebook.
n = 90
machines = ["M-101", "M-102", "M-103", "M-104"]
dates = pd.date_range(start="2026-07-01", periods=n, freq="B")
np.random.seed(42)
df = pd.DataFrame({
"date" : dates,
"machine_id": np.random.choice(machines, size=n),
"units" : np.random.normal(500, 22, size=n).round(0).astype(int),
"defects" : np.random.poisson(lam=3, size=n),
"temp_c" : np.random.normal(72, 2.5, size=n).round(1),
})
# Engineered anomaly: M-103 rows in positions 5-20 get elevated defects
m103_idx = df[df["machine_id"] == "M-103"].index[5:20]
df.loc[m103_idx, "defects"] += np.random.randint(5, 12, size=len(m103_idx))
df["yield_pct"] = (
(df["units"] - df["defects"]) / df["units"] * 100
).round(2)
print(f"Dataset shape: {df.shape}")
display(df.head(8))
Dataset shape: (90, 6)
| date | machine_id | units | defects | temp_c | yield_pct | |
|---|---|---|---|---|---|---|
| 0 | 2026-07-01 | M-103 | 472 | 3 | 67.7 | 99.36 |
| 1 | 2026-07-02 | M-104 | 524 | 3 | 75.4 | 99.43 |
| 2 | 2026-07-03 | M-101 | 561 | 2 | 71.7 | 99.64 |
| 3 | 2026-07-06 | M-103 | 526 | 3 | 75.1 | 99.43 |
| 4 | 2026-07-07 | M-103 | 505 | 3 | 68.0 | 99.41 |
| 5 | 2026-07-08 | M-104 | 519 | 1 | 70.5 | 99.81 |
| 6 | 2026-07-09 | M-101 | 478 | 6 | 72.0 | 98.74 |
| 7 | 2026-07-10 | M-101 | 465 | 1 | 72.1 | 99.78 |
Result Interpretation: Shared Dataset¶
- 90 business days across 4 machines gives each machine approximately 22-23 observations.
- The anomaly adds 5-12 extra defects to 15 M-103 observations. A good visualization should reveal this.
yield_pctis computed consistently with Notebook 2.1.
Chart 1 -- histplot with KDE: Defect Count Distribution¶
Concept and Code Explanation (Before Use)
sns.histplot() improvements over Matplotlib's hist():
kde=Trueoverlays a Kernel Density Estimate -- a smooth probability shape curve.hue=automatically plots separate histograms per group with distinct colours and a legend.
KDE shows whether the distribution has:
- A single peak (unimodal) -- one dominant operating mode
- Two peaks (bimodal) -- two distinct operating conditions
- A long right tail -- occasional extreme defect events
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
# Left: overall distribution
sns.histplot(data=df, x="defects", bins=18, kde=True, color="steelblue", ax=axes[0])
axes[0].set_title("Overall Defect Count Distribution", fontsize=12, fontweight="bold")
axes[0].set_xlabel("Defects per Day")
axes[0].set_ylabel("Frequency")
# Right: distribution per machine
sns.histplot(data=df, x="defects", hue="machine_id", bins=16, kde=True, alpha=0.4, ax=axes[1])
axes[1].set_title("Defect Distribution per Machine", fontsize=12, fontweight="bold")
axes[1].set_xlabel("Defects per Day")
axes[1].set_ylabel("Frequency")
plt.suptitle("Seaborn histplot -- Defect Count Analysis", fontsize=13, y=1.02)
plt.tight_layout()
plt.savefig("sns_chart1_histplot.png", dpi=150, bbox_inches="tight")
plt.show()
Result Interpretation: histplot with KDE¶
- The overall distribution should show a right-skewed profile -- M-103's anomaly creates a long right tail.
- The per-machine distribution should show M-103's histogram shifted to the right.
- KDE curves make shape comparison across machines easier than reading raw bar heights.
Chart 2 -- boxplot and violinplot: Group Comparison¶
Concept and Code Explanation (Before Use)
Box plot -- shows five summary statistics:
- Horizontal line inside = median
- Box edges = 25th and 75th percentiles (IQR)
- Whiskers = 1.5 x IQR
- Dots beyond whiskers = outliers
Violin plot -- extends the box plot by adding the full KDE shape on both sides.
Use box plots for quick comparison in presentations.
Use violin plots for deeper distributional insights during analysis.
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
# Box plot
sns.boxplot(data=df, x="machine_id", y="yield_pct",
hue="machine_id", palette="tab10", legend=False, linewidth=1.2, ax=axes[0])
axes[0].axhline(99.0, color="red", linestyle="--", linewidth=1.2, label="Target 99%")
axes[0].set_title("Yield Rate by Machine -- Box Plot", fontsize=12, fontweight="bold")
axes[0].set_xlabel("Machine")
axes[0].set_ylabel("Yield Rate (%)")
axes[0].legend(fontsize=9)
# Violin plot
sns.violinplot(data=df, x="machine_id", y="yield_pct",
hue="machine_id", palette="tab10", legend=False, inner="quartile", ax=axes[1])
axes[1].axhline(99.0, color="red", linestyle="--", linewidth=1.2, label="Target 99%")
axes[1].set_title("Yield Rate by Machine -- Violin Plot", fontsize=12, fontweight="bold")
axes[1].set_xlabel("Machine")
axes[1].set_ylabel("Yield Rate (%)")
axes[1].legend(fontsize=9)
plt.suptitle("Box Plot vs Violin Plot -- Machine Yield Comparison", fontsize=13, y=1.02)
plt.tight_layout()
plt.savefig("sns_chart2_box_violin.png", dpi=150, bbox_inches="tight")
plt.show()
Result Interpretation: Box Plot vs Violin Plot¶
- M-103's box should appear lower on the y-axis (lower yield) with a longer lower whisker.
- The violin plot reveals whether M-103's distribution is bimodal (two bumps: one normal, one anomaly).
- Box plot outlier dots identify specific dates worth investigating.
Chart 3 -- Scatter Plot with Anomaly Highlighting¶
Concept and Code Explanation (Before Use)
sns.scatterplot() with hue= maps colours to a categorical variable automatically.
sns.regplot() overlays a linear regression line with a confidence interval.
Highlighting anomalies:
Overlay anomalous points in a contrasting colour to draw attention without hiding the rest of the data.
This visual annotation is more effective than a data table for operational reviewers.
anomaly_threshold = int(df["defects"].quantile(0.90))
df["is_anomaly"] = df["defects"] > anomaly_threshold
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
# Left: scatter coloured by machine, anomalies as red X
ax = axes[0]
sns.scatterplot(data=df[~df["is_anomaly"]], x="units", y="defects",
hue="machine_id", alpha=0.6, s=50, ax=ax)
sns.scatterplot(data=df[df["is_anomaly"]], x="units", y="defects",
color="red", s=100, marker="X", label="Anomaly", ax=ax)
ax.set_title("Units vs Defects -- Anomalies Highlighted", fontsize=12, fontweight="bold")
ax.set_xlabel("Units Produced")
ax.set_ylabel("Defects")
# Right: regression plot for M-103 only
ax2 = axes[1]
m103 = df[df["machine_id"] == "M-103"]
sns.regplot(data=m103, x="units", y="defects",
scatter_kws={"alpha": 0.6, "color": "darkorange"},
line_kws={"color": "red", "linewidth": 2}, ax=ax2)
ax2.set_title("M-103: Units vs Defects with Regression", fontsize=12, fontweight="bold")
ax2.set_xlabel("Units Produced")
ax2.set_ylabel("Defects")
r_m103 = m103["units"].corr(m103["defects"])
ax2.annotate(f"r = {r_m103:.3f}", xy=(0.05, 0.92), xycoords="axes fraction",
fontsize=10, color="red")
plt.suptitle("Scatter Analysis -- Trend and Anomaly Detection", fontsize=13, y=1.02)
plt.tight_layout()
plt.savefig("sns_chart3_scatter_anomaly.png", dpi=150, bbox_inches="tight")
plt.show()
print(f"Anomaly threshold (90th pct): {anomaly_threshold} defects")
print(f"Anomalous rows: {df['is_anomaly'].sum()} of {len(df)}")
Anomaly threshold (90th pct): 10 defects Anomalous rows: 7 of 90
Result Interpretation: Scatter with Anomaly Highlights¶
- Red X markers immediately direct attention to the anomalous records.
- A flat regression line on M-103 (r near 0) means the anomaly is not volume-driven.
annotate()places the correlation coefficient directly on the chart.
Chart 4 -- heatmap: Correlation and Cross-Tabulation¶
Concept and Code Explanation (Before Use)
A heatmap encodes numeric values as colour intensity.
Two common uses:
- Correlation matrix -- which variables move together? Use
df.corr()as input. - Pivot table heatmap -- how does a metric vary across two categorical dimensions?
Key arguments:
annot=True-- print the numeric value inside each cellfmt=".2f"-- number format for annotationscmap="coolwarm"-- blue for negative, red for positive correlation
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
# Correlation matrix
numeric_cols = ["units", "defects", "temp_c", "yield_pct"]
corr_matrix = df[numeric_cols].corr()
sns.heatmap(corr_matrix, annot=True, fmt=".2f", cmap="coolwarm",
vmin=-1, vmax=1, linewidths=0.5, ax=axes[0])
axes[0].set_title("Variable Correlation Matrix", fontsize=12, fontweight="bold")
# Pivot table: avg defects by machine x shift
df["shift"] = np.tile(["Morning","Afternoon","Night"], n)[:n]
pivot = df.pivot_table(values="defects", index="machine_id", columns="shift", aggfunc="mean")
sns.heatmap(pivot, annot=True, fmt=".1f", cmap="YlOrRd",
linewidths=0.5, ax=axes[1])
axes[1].set_title("Avg Defects: Machine x Shift", fontsize=12, fontweight="bold")
axes[1].set_xlabel("Shift")
axes[1].set_ylabel("Machine")
plt.suptitle("Heatmaps -- Correlation and Cross-Tabulation", fontsize=13, y=1.02)
plt.tight_layout()
plt.savefig("sns_chart4_heatmap.png", dpi=150, bbox_inches="tight")
plt.show()
Result Interpretation: Heatmap¶
defectsandyield_pctshould show a strong negative correlation (close to -1).- Temperature correlation near 0 means process temperature is not driving defects.
- The pivot table heatmap identifies the worst machine-shift combination in seconds.
Chart 5 -- lineplot with Confidence Band: Trend and Uncertainty¶
Concept and Code Explanation (Before Use)
sns.lineplot() with estimator="mean" and errorbar="sd" automatically:
- Plots the mean value per x-axis point per group
- Shades the area +-1 standard deviation as a confidence band
This shows both the central trend and the process variability at each point.
Overlapping confidence bands between two machines means their means are statistically indistinguishable.
df_line = df.copy()
df_line["week"] = df_line["date"].dt.isocalendar().week.astype(int)
fig, ax = plt.subplots(figsize=(12, 5))
sns.lineplot(
data=df_line, x="week", y="defects",
hue="machine_id", estimator="mean", errorbar="sd",
linewidth=2, alpha=0.9, ax=ax
)
ax.axvspan(28, 31, color="red", alpha=0.08, label="Anomaly period")
ax.set_title("Weekly Mean Defects by Machine (with +/-1 SD band)",
fontsize=12, fontweight="bold")
ax.set_xlabel("ISO Week Number")
ax.set_ylabel("Average Daily Defects")
ax.legend(title="Machine", fontsize=9, title_fontsize=9)
plt.tight_layout()
plt.savefig("sns_chart5_lineplot_ci.png", dpi=150, bbox_inches="tight")
plt.show()
print("All charts complete.")
All charts complete.
Result Interpretation: lineplot with Confidence Band¶
- Wide confidence bands indicate high week-to-week variation.
- M-103's line should rise noticeably during the anomaly weeks (highlighted in red).
axvspan()adds a semi-transparent shaded region -- effective for annotating known event periods.
About This Material¶
| Author | Prakash Ukhalkar |
| Role | Assistant Professor (MCA) · Researcher in Data Science and Machine Learning |
| GitHub | @prakash-ukhalkar |
| Repository | mfg-python-sql-training |
| License | MIT |
Provided for educational use in Python Data Analytics Phase 2 training programmes.
For questions, corrections, or contributions, open an issue or pull request on GitHub.