Matplotlib & Seaborn
Theme
Palette
Download
Caption (tap to copy)
📖 Deep dive (full written explanation)
This final post is a field guide to the plotting traps that hurt most: the ones that don't raise an error. Broken code is easy — it crashes and you fix it. The dangerous bugs render a perfectly normal-looking chart that misleads you or your audience. Each trap here comes paired with a one-line fix.
The cover names the core insight: a chart that runs fine can still lie, and those quiet lies are the expensive ones.
The truncated y-axis is the most common chart distortion. On a bar chart, the length of the bar is what encodes the value, so starting the axis at 90 instead of 0 makes a 2% difference look enormous. The reader's eye reads bar length as magnitude, and you've broken that contract.
For bar charts specifically, the axis should begin at zero. For line charts the rule is softer, since they encode value by position rather than length, but you should still be deliberate. Truncation isn't always malicious, but it always misleads.
The fix is one line: set the y-limit to include zero. Here ax.set_ylim(0, 110) anchors the bars at the baseline so their lengths are honestly proportional to their values. The commented-out (90, 110) is the trap to avoid.
The broader habit is to be conscious of your axis limits rather than accepting whatever auto-scaling produces, because auto-scaling optimizes for filling the frame, not for honest comparison.
Overplotting is the trap of too many points. Pour tens of thousands of fully-opaque markers onto a scatter plot and they overlap into a solid blob — the data is all there, but the density structure is invisible. You can't see where points concentrate because every point looks the same as a thousand stacked beneath it.
This is especially insidious because the chart looks complete. Nothing errors; you just can't see the pattern you came to find.
Two fixes, both shown. Transparency via alpha lets overlapping points sum visually, so dense regions read darker and you recover the density structure; shrinking marker size with s helps too. Alternatively, abandon individual points entirely and switch to a density view — here sns.histplot with both x and y draws a 2D histogram that shows concentration directly.
The choice depends on scale: alpha works into the tens of thousands, but for truly massive data a density or hexbin view is the honest move.
Figures piling up in a loop is a memory trap. If you call plt.plot inside a loop without creating a fresh figure, everything either lands on one shared Axes, or Matplotlib opens a new figure each iteration that never closes. Those open figures accumulate in memory, and a script generating hundreds of charts can slow to a crawl or run out of memory.
The warning sign is a batch-plotting script that gets progressively slower or triggers memory warnings — the classic symptom of leaked figures.
The fix has two parts. Inside the loop, create a fresh fig, ax with plt.subplots so each chart is isolated. After saving, call plt.close(fig) to release that figure's memory before the next iteration. Without the close, every figure stays alive until the process ends.
This pattern — create, draw, save, close — is the standard shape for any script that produces many charts in a loop, and it keeps memory flat regardless of how many you generate.
Mixing the pyplot and object-oriented styles is the subtle trap from the mechanics post made concrete. You build a figure explicitly with fig, ax = plt.subplots(), then reflexively reach for plt.title. That call targets whatever Matplotlib considers the 'current' Axes — which, with multiple subplots, may not be the one you meant. The symptom is a title or label appearing on the wrong panel, and it runs without error, which is exactly why it's confusing to diagnose.
The compare diagram puts the wrong and right approaches side by side. On the left, the mixed style: explicit subplots followed by plt.title, which lands on the wrong Axes. On the right, the consistent object-oriented style: ax.set_title always targets the Axes you're holding. The rule is simple — once you've created Axes explicitly, address them explicitly. Pick a lane and stay in it for the whole figure.
Colorblind-unsafe palettes make charts unreadable for a meaningful fraction of viewers. Red-green pairs, common in defaults and in red-means-bad conventions, are exactly the combination many colorblind viewers cannot distinguish. Rainbow colormaps add a second problem: they aren't perceptually uniform, so equal data steps look like unequal color jumps, distorting magnitude.
The fixes are cheap and well-known: perceptually uniform colormaps like viridis for continuous data, and colorblind-safe categorical palettes. There's no good reason to ship the unsafe defaults.
The fix is one argument. sns.set_palette("colorblind") switches the categorical palette globally to a colorblind-safe set. For heatmaps and other continuous color, cmap="viridis" gives a perceptually uniform map where equal data differences map to equal perceived color differences.
Setting these once at the top of a notebook means every chart you make afterward is accessible by default, which is the right way to handle it — make the safe choice automatic rather than per-chart.
Choosing the wrong chart type is the trap of fighting your data with the wrong form. A pie chart with a dozen slices makes ordering impossible. A line plot over unordered categories implies a sequence that doesn't exist. A 3D bar chart for two numbers adds confusion for no information.
The discipline is to start from the question. Distribution questions want histograms or box plots. Comparisons want bars. Relationships want scatter plots. Composition wants stacked bars or, sparingly, a pie with few slices. The chart type is a decision driven by the question, not a default you accept.
These tips are the honesty checklist to run before you ship any chart. Bars start at zero. Use alpha or a density view when points crowd. Close figures in loops. Use one API style per figure. Choose colorblind-safe palettes. And match the chart type to the question.
Run this list and your charts stay both honest and readable — the two qualities that separate a useful visualization from a misleading one.
This closes the day and the topic. Across five posts you've covered what Matplotlib and Seaborn are, why visualization matters, how the plotting engine works, how to build real charts, and how to avoid the traps. That's a complete, working command of the two libraries. The next topic in the series arrives tomorrow.