Skip to content
Aabha AI Academy
Skip to reading

Python · Module 9

Charts that answer a question

Outcome: create an interpretable chart and describe its limits. Prerequisite: Module 8. Profile: Charts · Matplotlib. Time: about 2 hours.

Start with the question

A chart is a compressed argument about data. Decide the question first, then choose marks and labels that help answer it. A bar chart compares categories. A line chart shows an ordered sequence such as measurements over time. A scatter plot shows paired numeric values without proving that one causes the other. A histogram groups numeric observations into bins to show a distribution.

Our question is narrow: how many recorded practice minutes belong to each topic in six synthetic sessions? The counts are Python 80, Data 70 and Charts 30. A bar chart makes these category totals easy to compare. It does not establish learning quality, effort, mastery or the effect of a teaching method.

A bar's length represents a magnitude, so a zero baseline is the sensible default for this chart. Cropping the baseline can exaggerate modest differences. A line chart's scale may serve a different purpose, but it still needs clear units and an honest range. Avoid 3D effects, crowded legends and color choices that carry meaning you have not explained.

Matplotlib's two useful objects

A Figure is the overall canvas. An Axes is a plotting area within it. fig, ax = plt.subplots(...) creates both. Plot through ax, set labels and titles explicitly, and keep the Figure when you want to save a file. figsize is measured in inches; output pixels also depend on resolution. layout="constrained" helps leave room for labels, but you must still inspect the rendered result.

Worked example

# Profile: Charts · Matplotlib
import matplotlib.pyplot as plt

topics = ["Python", "Data", "Charts"]
minutes = [80, 70, 30]
fig, ax = plt.subplots(figsize=(6, 3.5), layout="constrained")
ax.bar(topics, minutes, color=["#2563eb", "#0d9488", "#d97706"])
ax.set(title="Practice time by topic (synthetic sessions)",
       xlabel="Topic", ylabel="Minutes", ylim=(0, 90))
ax.spines[["top", "right"]].set_visible(False)
for topic, value in zip(topics, minutes):
    ax.text(topic, value + 2, str(value), ha="center")
fig.savefig("practice_minutes.png", dpi=160)
# The lab captures the chart and offers a download. Locally, open the PNG.
# For an interactive local window, optionally call plt.show() afterward.

Download this worked script

The chart should show three bars of heights 80, 70 and 30, with the exact values printed above them. The vertical label states minutes; the title declares the data synthetic. Removing the top and right borders is cosmetic, not a statistical operation. The baseline still starts at zero.

The lab's Charts profile has Matplotlib and NumPy; do not assume it also includes pandas. The required example uses simple Python lists so it runs in that profile. In the capstone you will first calculate a summary in the Data profile, then paste the verified summary into the separate Charts script. The local route can combine all three packages in one process.

The worked label loop uses zip(topics, minutes) to pair one topic and one height on each iteration: Python with 80, Data with 70, Charts with 30. These lists must have equal lengths for this task; zip otherwise stops at the shorter list, which could hide an unmatched label or value.

Choose a chart and handle changed values

Choose a chart for each new question before seeing the answer: compare counts across three product categories; show daily recorded durations across a week of actual dates; inspect whether paired session duration and exercise count vary together. State what the chart can show and one conclusion it cannot support.

The earlier upper limit 90 fits the current largest value 80. Change that value to 140 and predict what will be clipped. A reusable chart needs a limit based on current data. The following expanded loops convert cents into display units and find the largest value. Starting the largest value at zero is appropriate because these validated subtotals are non-negative. max(1.0, largest * 1.2) leaves 20% headroom and a nonzero range for all-zero or empty input.

# Profile: Charts · Matplotlib. Choose a safe display range from current data.
import matplotlib.pyplot as plt

def chart_values(labels, cents):
    if len(labels) != len(cents):
        raise ValueError("each label needs one value")
    values = []
    for amount in cents:
        values.append(amount / 100)
    largest = 0.0
    for value in values:
        if value > largest:
            largest = value
    upper = max(1.0, largest * 1.2)
    fig, ax = plt.subplots(figsize=(6, 3.5), layout="constrained")
    ax.bar(labels, values, color="#2563eb")
    ax.set(title="Synthetic accepted subtotal", xlabel="Category",
           ylabel="Fictional currency units", ylim=(0, upper))
    if len(values) == 0:
        ax.text(0.5, 0.5, "No accepted records", transform=ax.transAxes,
                ha="center")
    total = sum(cents) / 100
    print(f"Subtotal: {total:.2f}; y-axis upper bound: {upper}")
    return fig, ax

fig, ax = chart_values(["Pens", "Paper"], [14000, 2000])
assert ax.get_ylim()[1] > 140
fig.savefig("changed_values.png", dpi=160)
# Run again with labels=[], cents=[], or with two zero values. Explain each.

Download this worked example

For the empty-message label, transform=ax.transAxes makes (0.5, 0.5) the center of the plotting area: each coordinate runs from 0 to 1 across that area, independently of the data scale. You can reuse this display line without changing the data.

The changed values 140 and 20 have a displayed total of 160.00 and an upper bound of 168. The format :.2f displays two decimal places; it does not change the numeric value. The capstone's shorter [amount / 100 for amount in cents] is a list comprehension equivalent to the conversion loop. Its max(values, default=0) returns zero only when the collection is empty; it is the compact version of the non-negative maximum loop shown here.

Try empty labels/values and then two zero values. Empty means there are no accepted records; zero-valued bars mean accepted records exist but their subtotal is zero. The code labels the empty case explicitly instead of treating it as observed zero activity. Keep these meanings separate in your caption. The chart checks and the independent data-validation checks answer different questions.

Save and inspect

fig.savefig("practice_minutes.png", dpi=160) writes the figure. The Aabha lab captures charts as downloadable images; check Chart output and keep the image separately from your downloaded code. Locally, open the saved PNG. plt.show() is optional for an interactive local window; the lab uses a non-interactive backend, so calling it there can warn even when the chart is captured. A saved file is not automatically a complete deliverable: inspect it for clipped text, missing fonts, wrong labels and unreadable size.

A chart needs a text alternative. Describe the main values, units, ordering and any exclusions in a short paragraph. Do not rely on color alone to distinguish categories. A reader should be able to understand the essential result even if the image does not load.

Interpret cautiously

A supported description is: “In these synthetic records, Python accounts for 80 minutes, Data 70 and Charts 30. These are recorded practice durations, not measurements of learning.” An unsupported conclusion is: “Python teaching is more effective.” No outcomes, controls or comparison design were supplied.

If a table excludes invalid rows, the chart title or caption must say so. If some dates have no valid records, do not quietly interpret them as true zero-activity days. When sorting categories, decide whether magnitude or an existing logical order best serves the question. Explain a tie rather than inventing a meaningful difference.

Practice

  • 9A: recreate the three-bar chart in exercises/09_charts.py, including title, both axis labels and a zero baseline.
  • 9B: add exact value labels and choose enough headroom that nothing is clipped. Save or download the chart and visually inspect it at normal reading size.
  • 9C: write a two-sentence interpretation and a text alternative. Include the synthetic-data limit and avoid causal or learning-gain claims. As an optional extension, make a line chart only if you have actual ordered dates rather than merely replacing category bars with connected points.

Exit check

Why is a bar chart appropriate here? What would a cropped baseline imply visually? Why does a Matplotlib script running without an exception not prove the chart is readable? Which claim can the data support, and which important question remains unanswered?

Further reading

Read the separate answers and explanations

Reading progress

11 lessons remain open to guests. Marking a lesson read records reading only; it does not award assessment credit or a certificate.

Device reading marks require browser storage. Reading is always available.

Sign in or create an account to save separate account progress. Your current page is kept.