Free Study Material

Data Visualization

Introduction

A table of numbers is precise, but a trend is often easier to actually see than to read off row by row. This chapter covers turning DataFrame data into charts, using Python's most widely used plotting library.

Concept

Matplotlib is Python's core plotting library, brought in with:

import matplotlib.pyplot as plt

A line chart connects data points in sequence with a line — the standard choice for showing a trend over an ordered sequence, like values changing across months.

plt.plot(x_values, y_values)
plt.show()

Worked Example

import matplotlib.pyplot as plt

months = ["Jan", "Feb", "Mar", "Apr"]
sales = [200, 240, 210, 300]

plt.plot(months, sales)
plt.xlabel("Month")
plt.ylabel("Sales")
plt.title("Monthly Sales")
plt.show()

plt.plot(months, sales) draws a line connecting each month's sales figure in sequence. xlabel(), ylabel(), and title() label the chart so it's readable on its own. plt.show() displays the finished chart — a line rising from Jan to Feb, dipping slightly at Mar, then rising again to its highest point at Apr, visually showing the same trend the raw numbers describe.

Note: plt.show() is what actually displays the chart — building it with plot()/xlabel()/etc. alone prepares it, but nothing appears until show() is called.

Explanation

A line chart's whole purpose is showing change over an ordered sequence — which is exactly why its x-axis should always be something naturally ordered, like months or years, rather than unordered categories, which the next topic covers with a more suitable chart type.

Summary

import matplotlib.pyplot as plt brings in Python's core plotting library. plt.plot(x, y) draws a line chart connecting data points in sequence — the right chart type for showing a trend over an ordered sequence like time. plt.show() displays the finished chart.

🛠️

Classroom Toolkit

System Update: We are initializing the Graphing Engine (Matplotlib).
Important: In the labs below, always use plt_show() instead of plt.show() to render the image.

import sys

# 1. Install Matplotlib if missing
if "matplotlib" not in sys.modules:
    print("Toolkit: Installing Graphing Engine...")
    import pyodide_js
    await pyodide_js.loadPackage("micropip")
    import micropip
    await micropip.install(['matplotlib', 'pandas', 'numpy'])
    print("Toolkit: Ready.")

# 2. The Magic Function to Render Graphs
import matplotlib.pyplot as plt
import io, base64

def plt_show():
    """Saves the plot to a buffer and prints it as an HTML image"""
    buf = io.BytesIO()
    plt.savefig(buf, format='png', bbox_inches='tight')
    buf.seek(0)
    img_str = base64.b64encode(buf.read()).decode('utf-8')
    print(f"<br><img src='data:image/png;base64,{img_str}' style='max-width:100%; border-radius:10px; box-shadow:0 4px 6px rgba(0,0,0,0.1);'/><br>")
    plt.clf() # Clear the canvas for the next plot

print(" Toolkit: Graphics System Loaded. You can now plot!")

1. Visualizing Trends (Line Chart)

A Line Chart is the "Hello World" of data visualization. It is best used for showing Trends over Time (e.g., Temperature changes, Stock prices, Viewer growth).

We use the library Matplotlib (specifically the `pyplot` module).

plt.plot(x, y)
import matplotlib.pyplot as plt

# Data
days = [1, 2, 3, 4, 5]
temp = [30, 32, 34, 33, 31]

# 1. Create the Plot
# x=days, y=temp
plt.plot(days, temp)

# 2. Render it using our custom tool
plt_show()

2. Adding Labels & Titles

A graph without labels is useless. You must tell the viewer what the X and Y axes represent.

  • plt.title("..."): Adds a main heading.
  • plt.xlabel("..."): Labels the horizontal axis (e.g., "Days").
  • plt.ylabel("..."): Labels the vertical axis (e.g., "Temperature").
  • plt.grid(True): Adds a grid for easier reading.
import matplotlib.pyplot as plt

days = [1, 2, 3, 4, 5]
temp = [30, 32, 34, 33, 31]

# Plot with Style
# color='red': Line color
# marker='o': Adds dots at data points
# linestyle='--': Makes the line dashed
plt.plot(days, temp, color='red', marker='o', linestyle='--')

# Add Labels
plt.title("Daily Temperature Report")
plt.xlabel("Day of Week")
plt.ylabel("Temperature (C)")
plt.grid(True)

# Render
plt_show()

Introduction

The last topic's line chart suits an ordered sequence like months. This topic covers the right chart for a genuinely different job: comparing separate, unordered categories against each other.

Concept

A bar chart represents each category as a separate bar, with height (or length) showing its value — built for comparing categories side by side, not for showing a trend.

plt.bar(categories, values)

Worked Example

import matplotlib.pyplot as plt

subjects = ["Physics", "Chemistry", "Maths"]
marks = [85, 78, 92]

plt.bar(subjects, marks)
plt.xlabel("Subject")
plt.ylabel("Marks")
plt.title("Marks by Subject")
plt.show()

plt.bar(subjects, marks) draws one bar per subject, each bar's height matching its mark — Maths's bar is visibly the tallest at 92, Chemistry's the shortest at 78. Unlike the line chart, there's no meaningful "order" connecting Physics to Chemistry to Maths — reordering the bars wouldn't change what the chart honestly represents.

Note: A bar chart and a line chart can plot the exact same numbers, but choosing the right one matters: connecting unordered categories with a line (as if Physics "leads to" Chemistry) would misleadingly suggest a trend that doesn't actually exist.

Explanation

The choice between a line chart and a bar chart isn't stylistic — it's about whether the x-axis categories have a genuine order. Months naturally progress; subjects don't. Using a bar chart for genuinely unordered categories is exactly what avoids implying a trend where none exists.

Summary

plt.bar(categories, values) draws a bar chart, one bar per category — the right choice for comparing unordered categories side by side, unlike a line chart, which implies (and should only be used for) a genuine ordered sequence.

🛠️

Classroom Toolkit

System Update: We are initializing the Graphing Engine (Matplotlib).
Important: In the labs below, always use plt_show() instead of plt.show() to render the image.

import sys

# 1. Install Matplotlib if missing
if "matplotlib" not in sys.modules:
    print("Toolkit: Installing Graphing Engine...")
    import pyodide_js
    await pyodide_js.loadPackage("micropip")
    import micropip
    await micropip.install(['matplotlib', 'pandas', 'numpy'])
    print("Toolkit: Ready.")

# 2. The Magic Function to Render Graphs
import matplotlib.pyplot as plt
import io, base64

def plt_show():
    """Saves the plot to a buffer and prints it as an HTML image"""
    buf = io.BytesIO()
    plt.savefig(buf, format='png', bbox_inches='tight')
    buf.seek(0)
    img_str = base64.b64encode(buf.read()).decode('utf-8')
    print(f"<br><img src='data:image/png;base64,{img_str}' style='max-width:100%; border-radius:10px; box-shadow:0 4px 6px rgba(0,0,0,0.1);'/><br>")
    plt.clf() # Clear the canvas for the next plot

print("Toolkit: Graphics System Loaded. You can now plot!")

1. Comparing Groups (Bar Chart)

A Bar Chart uses rectangular bars to represent data. The height of the bar corresponds to the value.

Syntax:

plt.bar(categories, values)
  • categories: The names (X-axis), e.g., ['A', 'B', 'C'].
  • values: The heights (Y-axis), e.g., [10, 20, 15].
  • color: You can set a list of colors to make each bar distinct.
import matplotlib.pyplot as plt

# Data
langs = ['Python', 'Java', 'C++']
students = [50, 30, 20]

# Create Bar Chart
# color='teal' makes all bars teal color
plt.bar(langs, students, color='teal')

# Labels
plt.title("Most Popular Languages")
plt.xlabel("Language")
plt.ylabel("Number of Students")

plt_show()
import matplotlib.pyplot as plt

# Data
langs = ['Python', 'Java', 'C++']
students = [50, 30, 20]

# Create Bar Chart
# color='teal' makes all bars teal color
plt.bar(langs, students, color='teal')

# Labels
plt.title("Most Popular Languages")
plt.xlabel("Language")
plt.ylabel("Number of Students")

plt_show()

2. Horizontal Bars (barh)

If you have long names for your categories (e.g., "Introduction to Computer Science"), they won't fit well on the bottom X-axis.
In this case, we use a Horizontal Bar Chart.

Syntax: Simply add an 'h'.

plt.barh(categories, values)
import matplotlib.pyplot as plt

subjects = ['Maths', 'Physics', 'Computer Science', 'English']
marks = [85, 70, 95, 80]
# List of colors for each bar
my_colors = ['red', 'blue', 'green', 'orange']

# Note: For horizontal bars, we use barh()
plt.barh(subjects, marks, color=my_colors)

plt.title("Term 1 Report Card")
plt.xlabel("Marks Obtained") # Note: X-axis is now Marks
plt.ylabel("Subjects")       # Note: Y-axis is now Subjects

plt_show()

Introduction

A bar chart compares categories you already named. This topic covers a chart that instead shows how a set of numeric values is distributed — how many fall into each range.

Concept

A histogram groups numeric data into equal-width ranges called bins, and draws one bar per bin, with height showing how many values fall inside it.

plt.hist(values, bins=n)

Worked Example

import matplotlib.pyplot as plt

marks = [45, 52, 67, 71, 74, 78, 81, 85, 88, 91, 95]

plt.hist(marks, bins=5)
plt.xlabel("Marks")
plt.ylabel("Number of Students")
plt.title("Distribution of Marks")
plt.show()

plt.hist(marks, bins=5) splits the full range of marks (45 to 95) into five equal-width ranges, and draws one bar per range, its height showing how many students' marks fall inside it — revealing the overall shape of the data (are most students clustered around the middle, or spread evenly?), which a plain list of numbers doesn't show at a glance.

Note: A histogram looks like a bar chart, but represents something genuinely different — a bar chart's bars are separate, named categories; a histogram's bars are numeric ranges of one single variable, and are always drawn touching, since the ranges are continuous.

Explanation

A histogram answers a question a simple average can't: two classes could have the exact same average mark, one with every student clustered tightly around it, the other with marks spread from very low to very high — a histogram makes that difference immediately visible, where a single average number hides it completely.

Summary

plt.hist(values, bins=n) groups numeric data into equal-width ranges and shows how many values fall in each — revealing a dataset's overall shape and spread, something a single average or a category-based bar chart can't show.

🛠️

Classroom Toolkit

System Update: We are initializing the Graphing Engine (Matplotlib).
Important: In the labs below, always use plt_show() instead of plt.show() to render the image.

import sys

# 1. Install Matplotlib if missing
if "matplotlib" not in sys.modules:
    print("Toolkit: Installing Graphing Engine...")
    import pyodide_js
    await pyodide_js.loadPackage("micropip")
    import micropip
    await micropip.install(['matplotlib', 'pandas', 'numpy'])
    print("Toolkit: Ready.")

# 2. The Magic Function to Render Graphs
import matplotlib.pyplot as plt
import io, base64

def plt_show():
    """Saves the plot to a buffer and prints it as an HTML image"""
    buf = io.BytesIO()
    plt.savefig(buf, format='png', bbox_inches='tight')
    buf.seek(0)
    img_str = base64.b64encode(buf.read()).decode('utf-8')
    print(f"<br><img src='data:image/png;base64,{img_str}' style='max-width:100%; border-radius:10px; box-shadow:0 4px 6px rgba(0,0,0,0.1);'/><br>")
    plt.clf() # Clear the canvas for the next plot

print("Toolkit: Graphics System Loaded. You can now plot!")

1. Understanding Histograms

A Histogram answers the question: "How many people fall into this range?"
It takes a list of numbers (like Ages) and groups them into buckets (called Bins).

  • Bar Chart: Gaps between bars. Compare Categories (Apple vs Banana).
  • Histogram: No gaps. Shows Distribution (Ages 0-10, 10-20...).
plt.hist(data, bins=5)
import matplotlib.pyplot as plt

# Data: Ages of people
ages = [22, 55, 62, 45, 21, 22, 34, 42, 42, 4, 99, 102, 110, 120, 121]

# Create Histogram
# bins: How many buckets to create
plt.hist(ages, bins=5, color='purple', edgecolor='black')

plt.title("Age Distribution")
plt.xlabel("Age Groups")
plt.ylabel("Number of People (Frequency)")

plt_show()

2. Controlling the Bins

The most important setting in a Histogram is bins.

  • Integer (e.g., bins=5): Python automatically creates 5 equal-sized buckets.
  • List (e.g., bins=[0, 18, 50, 100]): You define the exact cut-off points (0-18, 18-50, 50-100). This is very useful for specific groups like "Child", "Adult", "Senior".
import matplotlib.pyplot as plt

ages = [10, 15, 20, 25, 30, 45, 50, 55, 90, 95]

# Custom Cut-off points (Bins)
custom_bins = [0, 18, 40, 80, 120]

plt.hist(ages, bins=custom_bins, color='orange', edgecolor='black')

plt.title("Age Distribution (Custom Groups)")
plt.xlabel("Age Ranges")
plt.ylabel("Count")

plt_show()

Introduction

Every chart so far compared values against each other. This topic covers a chart with a different job: showing how one whole splits into its parts.

Concept

A pie chart represents a whole as a circle, sliced into wedges — each wedge's size proportional to what share of the total it represents.

plt.pie(values, labels=category_names)

Worked Example

import matplotlib.pyplot as plt

streams = ["Science", "Commerce", "Arts"]
students = [120, 90, 60]

plt.pie(students, labels=streams)
plt.title("Students by Stream")
plt.show()

plt.pie(students, labels=streams) draws one wedge per stream, each wedge's size proportional to its share of the total 270 students — Science's wedge (120 of 270) is visibly the largest, closest to half the circle; Arts's (60 of 270) the smallest.

Note: A pie chart only genuinely makes sense when the values represent parts of one meaningful whole that together add up to something complete — comparing unrelated totals (like this year's Science students against last year's total school enrolment) in a pie chart would be misleading, since they aren't parts of the same whole.

Explanation

A pie chart trades precision for an intuitive, at-a-glance sense of proportion — reading an exact value off a wedge is harder than reading one off a bar chart's height, which is exactly why a pie chart works best with a small number of categories, and exact figures are often labelled directly on or beside each wedge.

Summary

plt.pie(values, labels=names) draws a pie chart — a circle split into wedges proportional to each category's share of one meaningful whole. It suits a small number of categories that genuinely sum to a complete total, favouring intuitive proportion over exact readability.

🛠️

Classroom Toolkit

System Update: We are initializing the Graphing Engine (Matplotlib).
Important: In the labs below, always use plt_show() instead of plt.show() to render the image.

import sys

# 1. Install Matplotlib if missing
if "matplotlib" not in sys.modules:
    print("Toolkit: Installing Graphing Engine...")
    import pyodide_js
    await pyodide_js.loadPackage("micropip")
    import micropip
    await micropip.install(['matplotlib', 'pandas', 'numpy'])
    print("Toolkit: Ready.")

# 2. The Magic Function to Render Graphs
import matplotlib.pyplot as plt
import io, base64

def plt_show():
    """Saves the plot to a buffer and prints it as an HTML image"""
    buf = io.BytesIO()
    plt.savefig(buf, format='png', bbox_inches='tight')
    buf.seek(0)
    img_str = base64.b64encode(buf.read()).decode('utf-8')
    print(f"<br><img src='data:image/png;base64,{img_str}' style='max-width:100%; border-radius:10px; box-shadow:0 4px 6px rgba(0,0,0,0.1);'/><br>")
    plt.clf() # Clear the canvas for the next plot

print("Toolkit: Graphics System Loaded. You can now plot!")

1. Understanding Pie Charts

A Pie Chart is a circular graph divided into slices. The arc length of each slice is proportional to the quantity it represents.

Syntax:

plt.pie(values, labels=labels)
  • values: The numerical data (e.g., [10, 20, 30]).
  • labels: The names for each slice (e.g., ['Rent', 'Food', 'Savings']).
import matplotlib.pyplot as plt

# Data
expenses = [500, 300, 200]
items = ['Food', 'Books', 'Movies']

# Create Pie Chart
plt.pie(expenses, labels=items)

plt.title("Monthly Expenses")
plt_show()

2. Formatting the Slice

To make a Pie Chart professional, we often need to "pull out" a specific slice to highlight it, or show the exact percentage numbers.

Parameter Function
explode A list of decimals (e.g., [0, 0.2, 0]). Pulls a slice out from the center.
autopct Format string (e.g., '%1.1f%%') to display percentages automatically.
import matplotlib.pyplot as plt

# Data
modes = ['Bus', 'Car', 'Walk', 'Cycle']
counts = [40, 20, 10, 30]

# Explode Configuration: 
# The list matches the order of data. 0 = Normal, 0.1 = Pulled out slightly.
# Here, 'Car' is the 2nd item, so we explode the 2nd value.
my_explode = [0, 0.2, 0, 0] 

# Create Pie Chart with bells and whistles
plt.pie(counts, labels=modes, explode=my_explode, autopct='%1.1f%%', shadow=True)

plt.title("How Students Travel to School")
plt_show()

Introduction

This closing topic of the unit covers three finishing touches: labelling multiple series on one chart, saving a chart to a file instead of only displaying it, and building up a chart across several plot calls.

Concept

  • Legend — a key identifying which line/bar corresponds to which label, added with plt.legend() after giving each plot() call a label=.
  • plt.savefig("filename.png") — saves the current chart to an image file, instead of (or in addition to) displaying it.
  • Multiple plot() calls — calling plt.plot() more than once before plt.show() draws every one of them on the same chart, layered together.

Worked Example

import matplotlib.pyplot as plt

months = ["Jan", "Feb", "Mar"]
science_scores = [78, 82, 88]
commerce_scores = [70, 75, 74]

plt.plot(months, science_scores, label="Science")
plt.plot(months, commerce_scores, label="Commerce")
plt.legend()
plt.title("Average Scores by Stream")
plt.savefig("scores_chart.png")
plt.show()

The two plt.plot() calls draw two separate lines on the same chart, each with its own label=. plt.legend() then adds a key showing which line is Science and which is Commerce — without it, two unlabeled lines would be impossible to tell apart. plt.savefig("scores_chart.png") writes the finished chart to an image file before plt.show() displays it.

Note: savefig() must be called before show() — on some setups, show() can clear the chart from memory afterward, leaving nothing left for savefig() to save if called second.

Explanation

Layering multiple plot() calls onto one chart, each labelled and distinguished by a legend, is exactly how a single chart compares more than one series at once — Science versus Commerce's trend over the same months, read together rather than as two separate, harder-to-compare charts.

Summary

Giving each plot() call a label= and then calling plt.legend() distinguishes multiple lines on one chart. plt.savefig("file.png"), called before show(), saves the chart to an image file. Multiple plot() calls before one show() layer their lines onto the same chart together.

🛠️

Classroom Toolkit

System Update: Initializing Pandas and Matplotlib for the final Unit 1 review.

import sys
if "matplotlib" not in sys.modules:
    import pyodide_js
    await pyodide_js.loadPackage("micropip")
    import micropip
    await micropip.install(['matplotlib', 'pandas', 'numpy'])

import matplotlib.pyplot as plt
import pandas as pd
import io, base64

def plt_show():
    buf = io.BytesIO()
    plt.savefig(buf, format='png', bbox_inches='tight')
    buf.seek(0)
    img_str = base64.b64encode(buf.read()).decode('utf-8')
    print(f"<br><img src='data:image/png;base64,{img_str}' style='max-width:100%; border-radius:10px; box-shadow:0 4px 6px rgba(0,0,0,0.1);'/><br>")
    plt.clf()

print(" Toolkit: Ready for Final Unit 1 Review.")

1. Vector Math & Iteration

Two small but important concepts remaining:

  • Series Math (Alignment): When adding two Series (S1 + S2), Pandas matches them by Index Label, not position. If a label is missing in one, you get NaN.
  • Iteration: To loop through a DataFrame row-by-row, we use df.iterrows(). It gives you the Index and the Row Data for every step of the loop.
import pandas as pd

# 1. Series Math (Alignment)
term1 = pd.Series([90, 80, 70], index=['Amit', 'Sumit', 'Ravi'])
term2 = pd.Series([95, 85], index=['Amit', 'Sumit']) # Ravi is missing

print("--- Term 1 + Term 2 ---")
# Ravi will be NaN because he doesn't exist in Term 2
total = term1 + term2
print(total)

# 2. Iteration (df.iterrows)
df = pd.DataFrame({'Name': ['Amit', 'Sumit'], 'Marks': [90, 85]})

print("\n--- Generating Report Cards (Loop) ---")
for index, row in df.iterrows():
    print(f"Student: {row['Name']} | Score: {row['Marks']}")

2. Legends & Saving

When plotting multiple lines, you need a Legend key to tell them apart.

  • label='...': Add this inside `plt.plot()` to name the line.
  • plt.legend(): Call this at the end to make the key appear.
  • plt.savefig('name.png'): Saves the graph as an image file on your computer.
import matplotlib.pyplot as plt

days = [1, 2, 3, 4]
class_a = [30, 35, 40, 38]
class_b = [25, 30, 32, 30]

# Plotting two lines with 'label'
plt.plot(days, class_a, color='blue', label='Class A')
plt.plot(days, class_b, color='red', label='Class B')

# Add Labels & Legend
plt.title("Class Performance Comparison")
plt.xlabel("Test Number")
plt.ylabel("Average Score")

# This enables the box that says "Blue=Class A, Red=Class B"
plt.legend()

# In a real desktop app, you would use: plt.savefig('my_chart.png')
plt_show()

Want the full course?

Unlock all chapters, video lectures, and SQL practice exercises.

Get Full Access