Chart Types
Different questions need different charts. "How did sales change over the year?" is a line chart. "Which city sold the most?" is a bar chart. "How are marks spread out?" is a histogram. Choosing the wrong form hides the answer even when the data is right.
In this lesson you will match common questions to the five charts data scientists use most, and draw each one with matplotlib using the practice datasets.
Which chart answers which question?
| Question | Chart | matplotlib |
|---|---|---|
| How does a value change over time? | Line chart | ax.plot() |
| How do categories compare? | Bar chart (horizontal for long names) | ax.bar(), ax.barh() |
| How is one numeric variable spread out? | Histogram | ax.hist() |
| Are two numeric variables related? | Scatter plot | ax.scatter() |
| How do spreads compare across groups? | Box plot | ax.boxplot() |
Pie charts are popular in office reports, but people judge angles poorly. With more than two or three slices, a sorted bar chart is almost always clearer.
Bar charts: comparing categories
A bar chart compares a quantity across categories. Sort the bars so the order carries meaning, and use horizontal bars (barh) when the labels are long.
import pandas as pd
import matplotlib.pyplot as plt
sales = pd.read_csv("sales.csv").drop_duplicates()
sales["revenue"] = sales["quantity"] * sales["unit_price"] * (1 - sales["discount"] / 100)
by_cat = (sales.groupby("category")["revenue"].sum() / 100000).sort_values()
fig, ax = plt.subplots(figsize=(7, 4))
ax.barh(by_cat.index, by_cat.values, color="#c2410c")
ax.set_title("Revenue by category, 2025")
ax.set_xlabel("₹ lakh")
plt.show()
print(by_cat.round(2))category Books 1.12 Sports 1.34 Groceries 1.78 Home & Kitchen 4.50 Electronics 5.03 Fashion 5.30 Name: revenue, dtype: float64
Six horizontal bars appear in order of size, with Fashion's the longest at the top and Books' the shortest at the bottom. Because barh draws the first value at the bottom, sorting from smallest to largest puts the biggest category on top, where readers look first. A bar's length is the message, so a bar chart's value axis must start at zero; matplotlib does that by default.
Histograms: how values are spread
A histogram splits a numeric range into bins (intervals) and draws a bar for how many values fall in each. It shows the shape of the data: where most values are, how wide the spread is, and whether one side has a longer tail.
import pandas as pd
import matplotlib.pyplot as plt
students = pd.read_csv("students.csv", na_values=["AB"]).drop_duplicates()
fig, ax = plt.subplots(figsize=(7, 4))
ax.hist(students["maths"].dropna(), bins=range(10, 101, 5), color="#2563eb", edgecolor="white")
ax.axvline(33, color="black", linestyle="--", label="Pass mark (33)")
ax.set_title("Maths marks, Class 10")
ax.set_xlabel("Marks")
ax.set_ylabel("Number of students")
ax.legend()
plt.show()
print((students["maths"] < 33).sum(), "students below the pass mark")38 students below the pass mark
The histogram is a hill centred around 55 to 60 marks, falling away on both sides, with a small group below the dashed pass-mark line and a thin tail reaching 100. bins=range(10, 101, 5) makes bins 5 marks wide, which matches how people think about marks; too few bins hide the shape and too many make it jagged. axvline() draws a vertical reference line. Histograms need dropna() because missing values cannot be placed in a bin.
Scatter plots: are two variables related?
A scatter plot draws one dot per row, with one variable on each axis. A cloud that slopes upward suggests that the variables rise together.
import pandas as pd
import matplotlib.pyplot as plt
students = pd.read_csv("students.csv", na_values=["AB"]).drop_duplicates()
students = students[students["attendance"] <= 100]
fig, ax = plt.subplots(figsize=(7, 4))
ax.scatter(students["attendance"], students["maths"], s=14, alpha=0.5)
ax.set_title("Attendance and maths marks")
ax.set_xlabel("Attendance (%)")
ax.set_ylabel("Maths marks")
plt.show()
print(students[["attendance", "maths"]].corr().round(2))attendance maths attendance 1.00 0.39 maths 0.39 1.00
The dots form a wide cloud that drifts upward from left to right: students with higher attendance tend to score higher, but with a lot of variation at every attendance level. s sets the dot size and alpha=0.5 makes dots half transparent, so places where many dots overlap look darker. The printed correlation puts a number on the relationship; you will learn exactly what it means in Correlation.
Box plots: comparing spreads across groups
A box plot summarises a distribution in five numbers: the box runs from the 25th to the 75th percentile with a line at the median, the whiskers reach the furthest values within 1.5 × IQR, and dots beyond them are possible outliers. Placing boxes side by side compares groups quickly.
import pandas as pd
import matplotlib.pyplot as plt
emp = pd.read_csv("employees.csv").drop_duplicates()
emp["department"] = emp["department"].str.strip().str.title().replace({"Engg": "Engineering", "Hr": "HR"})
order = emp.groupby("department")["salary"].median().sort_values().index
data = [emp.loc[emp["department"] == d, "salary"].dropna() / 100000 for d in order]
fig, ax = plt.subplots(figsize=(8, 4))
ax.boxplot(data, tick_labels=order)
ax.set_title("Salary by department")
ax.set_ylabel("₹ lakh per year")
plt.show()
print(emp.groupby("department")["salary"].median().sort_values().div(100000).round(2))department Support 5.34 Finance 7.48 Sales 7.74 HR 8.07 Engineering 10.11 Data Science 11.20 Name: salary, dtype: float64
Six boxes rise from left to right because they are ordered by median salary: Support on the left with the lowest and narrowest box, Data Science on the right with the highest. Engineering and Data Science have the tallest boxes, the widest spread of pay, while Sales and Finance show a few dots above their whiskers: unusually high earners in those departments. In matplotlib 3.10 the box names are passed with tick_labels; older code used labels, which still works but is deprecated.
Line charts with several series, and when not to use them
Line charts imply that the x-axis is continuous and ordered, usually time. Do not draw lines between categories that have no natural order, such as cities; use bars instead.
import pandas as pd
import matplotlib.pyplot as plt
sales = pd.read_csv("sales.csv", parse_dates=["order_date"]).drop_duplicates()
sales["month"] = sales["order_date"].dt.month
counts = sales.pivot_table(index="month", columns="category", values="order_id", aggfunc="count")
ax = counts[["Electronics", "Books", "Sports"]].plot(marker="o", figsize=(8, 4))
ax.set_title("Orders per month for three categories")
ax.set_xlabel("Month")
ax.set_ylabel("Orders")
ax.set_xticks(range(1, 13))
plt.show()The three lines tell three seasonal stories: Books are highest in February and March (exam season), Sports orders rise in April to June (summer holidays and cricket season), and Electronics shoots up in October and November with festive shopping. Three series on one chart is readable; with ten, the lines become spaghetti, and separate small charts work better, as shown in Subplots & Styling.
Common mistakes
- Pie charts with many slices. Use a sorted bar chart.
- Bar charts that do not start at zero. A cut axis makes small differences look huge. (Line charts may zoom in, because they show change, not size.)
- Lines between unordered categories. Use bars for cities, products or departments.
- Overplotting. Thousands of solid dots hide each other; use
alpha, smallers, or a histogram. - Forgetting
dropna()beforehist()orboxplot(). Missing values can cause errors or empty plots.
Key points
- Line for change over time, bar for comparing categories, histogram for a distribution, scatter for a relationship, box plot for comparing spreads.
- Sort bars by value and use
barh()for long labels; bar axes start at zero. - Choose histogram bins that match the data, such as 5-mark bins for exam marks.
- Use
alphaand small markers in scatter plots to show where points overlap. - In matplotlib 3.10, name box plot groups with
tick_labels.
Exercise
Using weather.csv, draw a histogram of Mumbai's daily rainfall for the days when it rained (more than 0 mm), with bins of 5 mm from 0 to 80. Add a title and axis labels. Then print how many rainy days Mumbai had and the median rainfall on those days.
Show solution
import pandas as pd
import matplotlib.pyplot as plt
weather = pd.read_csv("weather.csv")
rain = weather.loc[(weather["city"] == "Mumbai") & (weather["rainfall_mm"] > 0), "rainfall_mm"]
fig, ax = plt.subplots(figsize=(7, 4))
ax.hist(rain, bins=range(0, 81, 5), color="#0891b2", edgecolor="white")
ax.set_title("Rainfall on rainy days, Mumbai 2025")
ax.set_xlabel("Rainfall (mm)")
ax.set_ylabel("Days")
plt.show()
print(len(rain), "rainy days, median", rain.median(), "mm")97 rainy days, median 12.1 mm
The histogram is tallest at the left and falls away to the right: most rainy days bring a modest amount of rain, and a few bring very heavy downpours. This long right tail is called right skew, and it is why the median is a better summary than the mean for rainfall.
Quick Quiz
1. Which chart best shows how a variable is distributed?
A histogram counts how many values fall into each bin, showing the shape of the data.
2. Why should a bar chart's value axis start at zero?
Readers compare bar lengths; starting elsewhere distorts them.
3. What does alpha=0.5 do in a scatter plot?
Transparency reveals where many points overlap.
4. You want to compare salary spreads across six departments. Which chart fits best?
Box plots show the median, middle half and outliers of each group side by side.
Learn Data Science with a mentor
Free lessons get you started. Our Data Science & Machine Learning training adds real projects and code reviews from working developers.
- Project-based learning
- Mentor code reviews
- Free counselling call
