, ,

Plotting in Python with Matplotlib for Data Analysts (Data Analyst Series, Part 16)

Turn a pandas summary into a chart. Draw a bar, a line and a scatter with Matplotlib, label them, and save them to share, using the same sales data from Part 15.

Data Analyst Series · Part 16 of 22

A manager once scrolled past a perfectly correct table I had sent and asked, in the next breath, which region was doing best. The answer was right there in the numbers, and she still could not see it. That is the gap a chart closes. A table asks the reader to do the comparing; a chart does the comparing for them and hands over the conclusion.

In Part 15 you loaded a file, filtered it, and grouped revenue by region into a small clean summary. This part keeps the exact same data and turns that summary into a picture. You will draw a bar chart, a line, and a scatter with Matplotlib, the plotting library nearly every Python analyst reaches for first, and you will see how pandas can draw many of them for you in a single line.

Who this is for: You finished Part 15 and can load a CSV, filter with loc, and group with groupby. You have Python running, or a free browser notebook such as Google Colab, which ships with Matplotlib and pandas ready to import. No prior plotting experience is assumed. This part uses Matplotlib 3.11, released in June 2026, and pandas 3.0; older versions draw the same charts with the same code.

Key takeaways

Matplotlib draws the chart; pandas is a shortcut that calls Matplotlib for you. Learn both, because the shortcut is quick and the full library gives you control when the shortcut runs out.

Use the explicit style: create a figure and axes with fig, ax = plt.subplots(), then call methods on ax. It reads clearly and scales to charts with more than one panel.

Three charts cover most of the work: a bar for comparing categories, a line for change over time, and a scatter for the relationship between two numbers. Label every one and save it with savefig.

Two libraries, one job

Two names come up the moment you plot in Python, and it helps to know how they relate before you type anything. Matplotlib is the drawing engine, a library that turns numbers into shapes, axes and text on a canvas. pandas, the table library from Part 15, does not draw anything itself; its plot method is a thin wrapper that hands your DataFrame to Matplotlib and asks it to draw. So when you call a chart from pandas, Matplotlib is still doing the work underneath. That single fact removes most of the early confusion: you are always using Matplotlib, sometimes directly and sometimes through pandas.

The practical rule I follow is simple. Reach for the pandas shortcut when you want a quick look at data you have already shaped, because one short line gets a chart on screen. Reach for Matplotlib directly when you need to control the result, a specific colour, a second line on the same axes, a title and axis labels worded exactly your way, or two charts side by side. You will use both every week, and neither is more correct than the other. This part shows the direct route first, so the shortcut makes sense once you see what it is standing in for.

There is one more library you will hear about, Seaborn, which sits on top of Matplotlib and makes attractive statistical charts with less code. It is worth learning later, but not now. Get comfortable with the engine and its pandas shortcut first, because everything Seaborn produces is a Matplotlib figure you can then adjust with the same methods you are about to learn. Adding a third tool before the first two are steady just spreads your attention thin.

The parts of a Matplotlib figure

Almost every beginner mistake in Matplotlib comes from not knowing which object owns what, so learn the two that matter. The Figure is the whole canvas, the outer window that holds everything. The Axes, confusingly named because it is not a single axis, is one plot area sitting inside the figure, the rectangle where your bars or lines actually live, together with its x axis, y axis, title and labels. A figure can hold one Axes or a grid of them. When you type fig, ax = plt.subplots(), you are asking for a figure and one Axes in a single line, and then you draw on ax.

Hold that split in your head and the method names stop being arbitrary. Anything about the whole canvas, such as saving to a file, is a figure method, so it is fig.savefig. Anything about the plot itself, the bars, the title, the axis labels, is an Axes method, so it is ax.bar, ax.set_title, ax.set_xlabel. The diagram below labels each part on a real chart. Keep it in mind and you will know, without guessing, whether a command belongs to fig or to ax.

Figure and Axes, who owns whatThe Figure is the canvas. The Axes is the plot area inside it.Figure (fig)Axes (ax)ax.set_titleax.set_ylabelax.set_xlabelfig.savefig saves it all
The Figure holds the Axes. Commands about the canvas belong to fig; commands about the plot belong to ax.

Make your first bar chart

A bar chart compares a number across categories, and it is the honest default for a question like which region sold the most. We start from the grouped summary you built in Part 15, total revenue per region, and draw it directly with Matplotlib so every step is visible. You import the pyplot module as plt by convention, create a figure and one Axes, call ax.bar with the category names and their values, then add a title and labels before showing the result. Here are the numbers we are drawing.

regiontotal_revenueorders
West54002
North34002
South25002

The grouped revenue per region carried over from Part 15. These three numbers are the ones every chart below is built from.

import matplotlib.pyplot as plt

regions = ['West', 'North', 'South']
revenue = [5400, 3400, 2500]

fig, ax = plt.subplots()
ax.bar(regions, revenue, color='#ce242c')
ax.set_title('Total revenue per region')
ax.set_ylabel('Revenue')
ax.set_xlabel('Region')
plt.show()

Result: a window with three red bars, West tallest at 5400, then North, then South, with a title and both axes labelled. In a notebook the chart appears under the cell; plt.show() forces it to display in a plain script.

Common failure: nothing appears when you run this as a .py file and forget plt.show(). A notebook shows the figure automatically, but a script needs that final call to open the window. If the chart still does not show over a remote connection, save it to a file with savefig instead, which the last section covers.

The picture below is exactly what that code draws, redrawn here so you can check your own output against it. Notice the y axis starts at zero, which for a bar chart is not optional. You met this trap in Part 13: a bar whose axis starts above zero exaggerates small gaps into big ones. Matplotlib starts bar charts at zero by default, and you should leave it there.

Total revenue per regionWhat ax.bar draws from the three numbers above.02000400060005400West3400North2500SouthThe axis starts at zero, so the bar heights are honest to the numbers.
The bar chart the code produces. A zero baseline keeps the comparison between regions fair.

Let pandas draw it for you

Once your data is already in a DataFrame or a grouped Series, you rarely need to pull the values out by hand as we just did. pandas plots straight from the table with a plot method, and it labels the axes from your column and index names automatically, which saves several lines. The kind argument picks the chart: bar for a bar chart, line for a line, scatter for a scatter. Because pandas is calling Matplotlib underneath, you can capture the Axes it returns and keep tuning the result with the same ax methods from the last section.

import pandas as pd

sales = pd.read_csv('sales.csv')
by_region = sales.groupby('region')['revenue'].sum()

ax = by_region.plot(kind='bar', color='#ce242c', title='Revenue per region')
ax.set_ylabel('Revenue')
plt.show()

Result: the same bar chart as before, in three lines of plotting instead of six. pandas reads the region names from the group index and puts them on the x axis for you.

Common failure: expecting kind=’bar’ to sort the bars for you. It draws them in the order they sit in the Series, which for a groupby is alphabetical by region. If you want the tallest bar first, sort the data before plotting with sort_values(ascending=False), then call plot.

Worked example

Say a manager wants the regions ranked, tallest first, with revenue on the y axis. You already have every piece. Sort the grouped Series, then plot it: by_region.sort_values(ascending=False).plot(kind=’bar’).

That one line reorders West, North, South from highest to lowest and draws the ranked chart. The lesson is that shaping the data, not styling the chart, is what usually gets you the picture you want. Sort, filter or group first; plot last.

Show change over time with a line

When the x axis is time, a line chart beats bars, because the line makes the trend, the rise or fall from one day to the next, jump out. Our sample spans three days, so let us total revenue per day and draw it as a line. The pattern mirrors the bar chart: create the figure and Axes, call ax.plot with the dates and the daily totals, then label. A line implies the points connect in a meaningful order, so only use one when the x axis has a natural sequence like dates or months, never for unordered categories such as region names.

by_day = sales.groupby('date')['revenue'].sum()

fig, ax = plt.subplots()
ax.plot(by_day.index, by_day.values, marker='o', color='#ce242c')
ax.set_title('Revenue per day')
ax.set_ylabel('Revenue')
plt.show()

Result: a red line rising from 4000 on 2026-06-01 to 5000 on 2026-06-02, then falling to 2300 on 2026-06-03, with a dot marking each day. marker=’o’ draws the points so the reader sees the three real measurements, not just the line between them.

Common failure: dates read from a CSV arrive as plain text, so Matplotlib spaces them evenly rather than by real calendar distance. For three consecutive days it looks fine; across uneven gaps it misleads. Convert the column with pd.to_datetime(sales[‘date’]) first, and the axis then honours the true spacing between dates.

Revenue per dayA line fits time because the order of the points carries meaning.0200040006000400050002300Jun 1Jun 2Jun 3Revenue peaks on day two, then drops on day three.
The daily totals as a line. The connecting segments only make sense because the days run in order.

Compare two numbers with a scatter

A scatter plots one number against another, one dot per row, and answers the question of whether two measurements move together. In our data, do higher unit counts come with higher revenue? You met the danger of reading too much into that pattern in Part 12, where a rising cloud of points tempts you to claim one thing causes the other. The scatter shows the shape; it does not prove the cause. Here we plot units on the x axis and revenue on the y, each order a single dot.

clean = sales.dropna(subset=['units'])

fig, ax = plt.subplots()
ax.scatter(clean['units'], clean['revenue'], color='#ce242c')
ax.set_title('Units vs revenue per order')
ax.set_xlabel('Units')
ax.set_ylabel('Revenue')
plt.show()

Result: five dots rising from lower left to upper right, the order with 20 units sitting highest on revenue. We drop the row with a missing units value first, because a scatter cannot place a point with no x coordinate.

Common failure: feeding a scatter a column that still holds missing values. Matplotlib silently skips those points, so your chart shows fewer dots than you have rows and you may not notice. Drop or fill the gaps first, as in Part 15, so the count of dots matches the count of orders you expect.

Match the chart to the question: comparing a value across categories calls for a bar; tracking change over an ordered axis like time calls for a line; testing whether two numbers move together calls for a scatter. If you cannot say in one sentence what question the chart answers, you have picked the wrong chart, not the wrong colour.

Label it, then save it

A chart with no title and bare axes forces the reader to guess, and a guessing reader stops trusting you. Three methods fix that, and you have already seen them: ax.set_title gives the chart a plain sentence saying what it shows, ax.set_xlabel and ax.set_ylabel name the axes with their real units, and ax.legend labels each series when a chart holds more than one line or bar group. Treat these as part of drawing the chart, not an optional polish, because an unlabelled chart is a puzzle and a labelled one is an answer.

When the chart is ready to share, you save it to an image file rather than screenshotting the screen. fig.savefig writes a PNG or PDF, and two arguments matter: dpi controls the resolution, with 150 a good balance for a slide or document, and bbox_inches set to tight trims the surrounding whitespace so labels are not clipped. Save the file, drop it into your report or deck, and you have taken the analysis from a picture on your screen to something a colleague can open. The reference below lines up each method with the job it does.

fig, ax = plt.subplots()
ax.bar(regions, revenue, color='#ce242c')
ax.set_title('Total revenue per region')
ax.set_ylabel('Revenue')
fig.savefig('revenue.png', dpi=150, bbox_inches='tight')

Result: a file named revenue.png appears in your working folder, sharp enough for a slide and cropped tight to the chart. No window opens, because savefig writes to disk instead of the screen.

Common failure: calling plt.show() before fig.savefig. On some setups show clears the figure, so the saved file comes out blank. Save first, then show, and you always get the image you drew.

You want toMethodBelongs to
Draw barsax.barAxes
Draw a lineax.plotAxes
Draw dotsax.scatterAxes
Title the chartax.set_titleAxes
Name the axesax.set_xlabel, ax.set_ylabelAxes
Save to a filefig.savefigFigure

The core methods for this part. Everything about the plot lives on ax; only saving the whole canvas lives on fig.

The workflow from data to saved chart

Every chart in this part followed the same short path, and it is worth seeing the whole loop at once. You shape the data with the pandas moves from Part 15, decide which question you are answering and therefore which chart fits, draw it on an Axes, label it so it stands alone, and save it to a file. The step people skip is the second one, choosing the chart to match the question, and skipping it is how you end up with a line chart of unordered categories or a pie chart nobody can read. Decide the question first; the chart type falls out of it.

flowchart LR
  A[Shape data with pandas] --> B[Pick the chart for the question]
  B --> C[Draw on an Axes]
  C --> D[Add title and labels]
  D --> E[savefig to share]
The plotting loop. The same five steps produce a bar, a line or a scatter; only the chart choice changes.

My take

Beginners burn hours on styling, hunting for the perfect colour or a fancier chart type, when the win is almost always in the data and the labels. A plain red bar chart with an honest zero axis and a clear title persuades a manager better than a rainbow chart that hides its question. I keep a private rule: I do not touch a single style option until the chart already answers its question in one glance. Nine times in ten, it never needs the styling at all.

Get these three charts fluent first

Matplotlib can draw dozens of chart types and expose hundreds of options, and you can safely ignore nearly all of them for now. A bar for comparing categories, a line for change over time, and a scatter for the link between two numbers will carry the majority of your first year, and each one uses the same three steps: make a figure and Axes, call one drawing method, label and save. Get those reflexes steady and every fancier chart, the stacked bar, the histogram, the small grid of panels, is a short addition to a pattern you already own.

You can now take a grouped result from pandas and turn it into a chart a colleague can read without you standing next to them. Part 17 steps back from the tools to the thinking behind them: how to define the metrics and KPIs a chart should show in the first place, so the number you plot is the number that actually matters. Keep the Data Analyst guide open as your map, and revisit Part 13 if choosing between chart types still feels uncertain.

This week, open a Colab notebook, recreate the sales.csv from Part 15, and draw all three charts yourself: a bar of revenue per region, a line of revenue per day, and a scatter of units against revenue. Then save one with savefig and drop it into a document. Drawing them once by hand fixes the pattern far better than reading it does.

Data Analyst Series · Part 16 of 22
« Previous: Part 15  |  Guide  |  Next: Part 17 »

References

About The Author


Discover more from Journal of Intelligent Infrastructure – By Dr Pranay Jha

Subscribe to get the latest posts sent to your email.

Leave a Reply

Your email address will not be published. Required fields are marked *

Architect’s Toolkit

About the Author

Dr. Pranay Jha is a Cloud and AI Consultant with 18+ years of experience in hybrid cloud, virtualization, and enterprise infrastructure transformation. He specializes in VMware technologies, multi-cloud strategy, and Generative AI solutions. He holds a PhD in Computer Applications with research focused on Cloud and AI, has published multiple research papers, and has been a VMware vExpert since 2016 and a VMUG Community Leader.

Discover more from Journal of Intelligent Infrastructure - By Dr Pranay Jha

Subscribe now to keep reading and get access to the full archive.

Continue reading