Understanding Data Visualization (matplotlib)
Data visualization translates observations into positions, lengths, colors, and other visible marks. The analytical task is to choose a representation that makes a comparison meaningful. Before writing matplotlib code, state a question such as βHow did daily orders change?β or βWhich shop converted the largest fraction of visits into purchases?β These require different measurements, even when they use the same underlying records.
Matplotlib is a widely used Python plotting library, but not every Python visualization runs on it. The supplied pandas guide includes chart visualization, direct Matplotlib usage, and alternative plotting backends. Pandas can help prepare and summarize tabular data; matplotlib provides control over the resulting figure. Treat preparation and presentation as separate steps so that an attractive chart does not conceal an incorrect calculation.
Choose the chart according to the variables and comparison. A line chart connects observations along an ordered coordinate, often time; those connections suggest continuity between measurements. A bar chart compares values across categories using length. A scatter plot shows paired numerical observations and can reveal associations, clusters, or unusual points, but not establish causation. A histogram groups numerical observations into intervals and counts them; changing interval boundaries can change the apparent distribution.
Matplotlibβs explicit object interface starts with `fig, ax = plt.subplots()`. The Figure is the overall container; an Axes is a plotting region with its own coordinates, labels, and marks. Despite its plural-looking name, one Axes can contain one chart. Methods such as `ax.plot`, `ax.bar`, `ax.scatter`, and `ax.hist` add different marks. Methods such as `ax.set_xlabel` explain what their positions mean. Keeping an explicit `ax` reference helps avoid accidentally modifying the wrong chart.
Interpretation also depends on units, denominators, and scale. Purchases and purchase rates answer different questions. Bar charts normally need a zero baseline because readers compare lengths. A restricted vertical range can help reveal small changes in a line chart, but must be clear. Color should distinguish meaningful groups rather than decorate individual observations. Label the population, measurement period, and units whenever their absence could change the conclusion. No styling choice repairs missing observations, inconsistent definitions, or a sample too limited to support the intended claim.
Practical walkthrough
Start with a concrete question: which of three shops had the highest purchase rate during one reporting period? Assume each record contains a shop name, visit count, and purchase count. Here, a purchase means a visit with a purchase, counted once; otherwise purchases divided by visits might not represent the fraction of visits that converted.
Inspect the inputs before considering styling. Verify that counts are nonnegative, each shop appears once, and visits are positive. A zero denominator produces an undefined rate, not a zero rate. If several rows describe one shop, add purchases and visits separately before dividing. Averaging row-level percentages would incorrectly give small and large groups equal influence.
Compute each rate as purchases divided by visits. Keep the numerical result as a fraction and format it as a percentage only for display. Sorting by rate supports ranking; alphabetical order supports finding a known shop. Neither order changes the values, but each emphasizes a different reading task.
For a finished chart, use shop names as the categorical positions in `ax.bar` and the unformatted fractions as heights. Start the vertical scale at zero, label it βVisits with a purchase (%)β, and use percentage tick formatting that treats 1.0 as 100%. Include visit counts in an accompanying table or annotation so readers can see the denominators.
The example below checks these chart inputs without rendering or saving a plot. It uses matplotlibβs formatting machinery and prints exact results, making arithmetic and labels independently verifiable. The same validated values can subsequently supply a bar chart.
Worked example
from matplotlib.ticker import StrMethodFormatter
# Illustrative counts from one reporting period.
rows = [("A", 100, 20), ("B", 40, 12), ("C", 200, 30)]
assert len({shop for shop, _, _ in rows}) == len(rows)
assert all(visits > 0 and 0 <= purchases <= visits
for _, visits, purchases in rows)
prepared = [(shop, visits, purchases, purchases / visits)
for shop, visits, purchases in rows]
prepared.sort(key=lambda row: (-row[3], row[0]))
percent = StrMethodFormatter("{x:.1%}")
print("Shop | Purchases / visits | Rate")
for shop, visits, purchases, rate in prepared:
print(f"{shop} | {purchases} / {visits} | {percent(rate)}")
total_visits = sum(row[1] for row in prepared)
total_purchases = sum(row[2] for row in prepared)
print(f"Overall | {total_purchases} / {total_visits} | "
f"{percent(total_purchases / total_visits)}")
Expected output
Shop | Purchases / visits | Rate
B | 12 / 40 | 30.0%
A | 20 / 100 | 20.0%
C | 30 / 200 | 15.0%
Overall | 62 / 340 | 18.2%
Shop B has the highest observed rate, while shop C has the largest purchase count. Thus, a rate chart and a count chart would produce different rankings. The overall rate is 62 divided by 340, not the unweighted average of the three percentages. StrMethodFormatter turns fractions into percentage labels without changing the numerical values used for chart heights. The assertions catch basic inconsistencies in this small demonstration; production validation should use explicit error handling. These illustrative counts do not establish that shop B is intrinsically better: customer mix, reporting conditions, and variation across periods could affect the observed differences substantially.
Common mistakes and how to avoid them
Connecting unordered categories with a line.
Use bars or unconnected points for shop comparisons. Reserve connecting lines for an order whose adjacent positions have meaning, and sort time observations before drawing.
Formatting fractions as ordinary numbers and labeling them percentages.
Keep rates numeric and apply percentage formatting consistently. A fraction of 0.30 should appear as 30%, not 0.30%, and should not be multiplied twice.
Treating missing measurements as zero.
Distinguish an observed zero from an unavailable value. Preserve gaps or explicitly identify missing categories; explain any exclusions instead of silently inventing measurements.
Truncating a bar chart to exaggerate differences.
Start bar lengths at zero. If small differences are the focus, consider a clearly labeled point chart and disclose the displayed range rather than distorting lengths.
Review questions
Why retain an Axes reference instead of relying entirely on pyplot's current chart?
An explicit reference identifies the plotting region being modified. This makes labels, limits, and marks easier to manage when a figure contains several charts.
Why is the overall purchase rate not the mean of the three shop rates?
The shops have different visit counts. Dividing total purchases by total visits weights each shop by its denominator and gives every visit equal influence.
How does a histogram differ from a categorical bar chart?
A histogram groups numerical observations into intervals, whose boundaries affect the result. A categorical bar chart displays values for named groups without numerical interval boundaries.
What can the example establish about shop B?
B has the highest recorded purchase fraction in this period. Without further evidence, the comparison cannot establish a causal advantage or reliably predict future rankings.