Skip to main content

📈 Line, Scatter, and Bar Charts

Master the fundamental building blocks of data visualization with Matplotlib

📚 What You'll Learn

By the end of this lesson, you will be able to:

⏱️ Estimated Time: 45–60 minutes

🎯 Project: Build a mini sales dashboard that combines a line trend, a scatter correlation, and a comparison bar chart from the same dataset.

🎨 The Art of Visual Storytelling

Imagine you're a data artist with three essential brushes: lines to show trends over time, dots to reveal relationships between variables, and bars to compare quantities. Each chart type is a different language for communicating insights!

In the real world, choosing the right visualization can mean the difference between confusion and clarity. A well-crafted chart can reveal patterns invisible in raw numbers, tell compelling stories, and drive data-informed decisions. Let's master these fundamental tools!

📊 Chart Type Selection Guide

graph TD A[What to Show?] --> B{Data Type} B --> C[Continuous Change] B --> D[Relationship] B --> E[Comparison] C --> F[Line Plot] D --> G[Scatter Plot] E --> H[Bar Chart] F --> I[Time Series
Trends
Progress] G --> J[Correlations
Clusters
Outliers] H --> K[Categories
Rankings
Parts of Whole] style A fill:#f9f,stroke:#333,stroke-width:2px style F fill:#3b82f6,stroke:#333,stroke-width:2px style G fill:#8b5cf6,stroke:#333,stroke-width:2px style H fill:#f59e0b,stroke:#333,stroke-width:2px

🎮 Interactive Chart Playground

Explore different chart types and their parameters in real-time:

20
30%

📈 Line Plots

Perfect for showing trends and changes over time

# Basic line plot
import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 10, 100)
y = np.sin(x)

plt.figure(figsize=(8, 6))
plt.plot(x, y)
plt.title('Simple Line Plot')
plt.xlabel('X axis')
plt.ylabel('Y axis')
plt.grid(True, alpha=0.3)
plt.show()

# Multiple lines with styles
y2 = np.cos(x)
plt.plot(x, y, 'b-', label='sin(x)', linewidth=2)
plt.plot(x, y2, 'r--', label='cos(x)', linewidth=2)
plt.legend()
plt.show()

📊 Scatter Plots

Ideal for revealing relationships and patterns between variables

# Basic scatter plot
import matplotlib.pyplot as plt
import numpy as np

x = np.random.randn(100)
y = 2 * x + np.random.randn(100) * 0.5

plt.figure(figsize=(8, 6))
plt.scatter(x, y, alpha=0.6)
plt.title('Scatter Plot')
plt.xlabel('X values')
plt.ylabel('Y values')
plt.grid(True, alpha=0.3)
plt.show()

# Advanced scatter with colors and sizes
colors = np.random.rand(100)
sizes = np.random.randint(20, 200, 100)
plt.scatter(x, y, c=colors, s=sizes, 
           alpha=0.6, cmap='viridis')
plt.colorbar(label='Color Scale')
plt.show()

📊 Bar Charts

Best for comparing quantities across categories

# Basic bar chart
import matplotlib.pyplot as plt

categories = ['A', 'B', 'C', 'D', 'E']
values = [23, 45, 56, 78, 32]

plt.figure(figsize=(8, 6))
plt.bar(categories, values, color='skyblue')
plt.title('Bar Chart')
plt.xlabel('Categories')
plt.ylabel('Values')
plt.show()

# Horizontal bar chart
plt.barh(categories, values, color='coral')
plt.title('Horizontal Bar Chart')
plt.show()

# Grouped bars
x = np.arange(len(categories))
width = 0.35
values2 = [30, 35, 40, 45, 50]

plt.bar(x - width/2, values, width, label='Series 1')
plt.bar(x + width/2, values2, width, label='Series 2')
plt.xticks(x, categories)
plt.legend()
plt.show()

🎯 Line Plot Mastery

Advanced techniques for professional line plots

# Customizing line plots
import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 10, 100)
y1 = np.sin(x)
y2 = np.cos(x)
y3 = np.sin(x) * np.exp(-x/10)

fig, ax = plt.subplots(figsize=(10, 6))

# Different line styles
ax.plot(x, y1, 'b-', linewidth=2, label='Solid')
ax.plot(x, y2, 'r--', linewidth=2, label='Dashed')
ax.plot(x, y3, 'g:', linewidth=2, label='Dotted')

# Add markers
x_markers = x[::10]
y_markers = y1[::10]
ax.plot(x_markers, y_markers, 'bo', markersize=8)

# Fill area under curve
ax.fill_between(x, 0, y3, alpha=0.3, color='green')

# Annotations
max_idx = np.argmax(y3)
ax.annotate('Peak', xy=(x[max_idx], y3[max_idx]),
           xytext=(x[max_idx]+1, y3[max_idx]+0.2),
           arrowprops=dict(arrowstyle='->', color='black'))

# Styling
ax.set_title('Advanced Line Plot Features', fontsize=16, fontweight='bold')
ax.set_xlabel('Time (seconds)', fontsize=12)
ax.set_ylabel('Amplitude', fontsize=12)
ax.grid(True, alpha=0.3, linestyle='--')
ax.legend(loc='upper right', frameon=True, shadow=True)

# Add secondary y-axis
ax2 = ax.twinx()
ax2.plot(x, y1*10, 'orange', linewidth=1, alpha=0.5)
ax2.set_ylabel('Secondary Scale', color='orange')
ax2.tick_params(axis='y', labelcolor='orange')

plt.tight_layout()
plt.show()

💫 Scatter Plot Enhancements

Create publication-quality scatter plots with advanced features

# Advanced scatter plots
import matplotlib.pyplot as plt
import numpy as np

# Generate data with clusters
np.random.seed(42)
n_points = 150

# Three clusters
cluster1_x = np.random.normal(2, 0.5, 50)
cluster1_y = np.random.normal(2, 0.5, 50)

cluster2_x = np.random.normal(5, 0.5, 50)
cluster2_y = np.random.normal(5, 0.5, 50)

cluster3_x = np.random.normal(3.5, 0.5, 50)
cluster3_y = np.random.normal(8, 0.5, 50)

fig, ax = plt.subplots(figsize=(10, 8))

# Plot clusters with different styles
ax.scatter(cluster1_x, cluster1_y, c='blue', marker='o', 
          s=100, alpha=0.6, label='Cluster A', edgecolors='black')
ax.scatter(cluster2_x, cluster2_y, c='red', marker='^', 
          s=100, alpha=0.6, label='Cluster B', edgecolors='black')
ax.scatter(cluster3_x, cluster3_y, c='green', marker='s', 
          s=100, alpha=0.6, label='Cluster C', edgecolors='black')

# Add regression line for one cluster
z = np.polyfit(cluster1_x, cluster1_y, 1)
p = np.poly1d(z)
x_line = np.linspace(cluster1_x.min(), cluster1_x.max(), 100)
ax.plot(x_line, p(x_line), "b--", alpha=0.8, linewidth=2)

# Add confidence ellipse
from matplotlib.patches import Ellipse
cov = np.cov(cluster2_x, cluster2_y)
eigenvalues, eigenvectors = np.linalg.eigh(cov)
angle = np.degrees(np.arctan2(*eigenvectors[:, 0][::-1]))
width, height = 2 * np.sqrt(eigenvalues)
ellipse = Ellipse((np.mean(cluster2_x), np.mean(cluster2_y)), 
                  width, height, angle=angle, 
                  facecolor='none', edgecolor='red', 
                  linewidth=2, linestyle='--', alpha=0.5)
ax.add_patch(ellipse)

# Styling
ax.set_title('Advanced Scatter Plot with Clusters', fontsize=16, fontweight='bold')
ax.set_xlabel('X Variable', fontsize=12)
ax.set_ylabel('Y Variable', fontsize=12)
ax.grid(True, alpha=0.3)
ax.legend(loc='upper left')

# Add text box with statistics
textstr = f'Total points: {n_points}\nCorrelation: 0.85'
props = dict(boxstyle='round', facecolor='wheat', alpha=0.5)
ax.text(0.05, 0.95, textstr, transform=ax.transAxes, fontsize=10,
       verticalalignment='top', bbox=props)

plt.tight_layout()
plt.show()

📊 Bar Chart Variations

Create stunning bar charts with advanced styling

# Advanced bar charts
import matplotlib.pyplot as plt
import numpy as np

# Data
categories = ['Product A', 'Product B', 'Product C', 'Product D', 'Product E']
quarters = ['Q1', 'Q2', 'Q3', 'Q4']
values = np.array([[20, 35, 30, 35],
                   [25, 32, 34, 20],
                   [15, 20, 35, 30],
                   [30, 25, 30, 35],
                   [10, 15, 25, 30]])

fig, axes = plt.subplots(2, 2, figsize=(12, 10))

# 1. Grouped bar chart
ax1 = axes[0, 0]
x = np.arange(len(categories))
width = 0.2
colors = ['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4']

for i, quarter in enumerate(quarters):
    ax1.bar(x + i * width, values[:, i], width, 
           label=quarter, color=colors[i])

ax1.set_title('Grouped Bar Chart', fontweight='bold')
ax1.set_xticks(x + width * 1.5)
ax1.set_xticklabels(categories, rotation=45, ha='right')
ax1.legend()
ax1.grid(axis='y', alpha=0.3)

# 2. Stacked bar chart
ax2 = axes[0, 1]
bottom = np.zeros(len(categories))
for i, quarter in enumerate(quarters):
    ax2.bar(categories, values[:, i], bottom=bottom, 
           label=quarter, color=colors[i])
    bottom += values[:, i]

ax2.set_title('Stacked Bar Chart', fontweight='bold')
ax2.legend()
ax2.grid(axis='y', alpha=0.3)

# 3. Horizontal bar chart with error bars
ax3 = axes[1, 0]
y_pos = np.arange(len(categories))
performance = [30, 45, 56, 78, 32]
error = [5, 3, 7, 2, 4]

bars = ax3.barh(y_pos, performance, xerr=error, 
                color='skyblue', ecolor='gray', capsize=5)

# Add value labels on bars
for i, (bar, val) in enumerate(zip(bars, performance)):
    ax3.text(val + 2, bar.get_y() + bar.get_height()/2, 
            f'{val}±{error[i]}', va='center')

ax3.set_yticks(y_pos)
ax3.set_yticklabels(categories)
ax3.set_title('Horizontal Bar with Error Bars', fontweight='bold')
ax3.set_xlabel('Performance Score')
ax3.grid(axis='x', alpha=0.3)

# 4. Bar chart with patterns
ax4 = axes[1, 1]
patterns = ['/', '\\', '|', '-', '+']
bars = ax4.bar(categories, performance)

for bar, pattern in zip(bars, patterns):
    bar.set_hatch(pattern)
    bar.set_edgecolor('black')
    bar.set_linewidth(1.5)

ax4.set_title('Bar Chart with Patterns', fontweight='bold')
ax4.set_ylabel('Values')
ax4.grid(axis='y', alpha=0.3)

# Color bars based on value
for bar, val in zip(bars, performance):
    if val > 50:
        bar.set_facecolor('#2ecc71')
    elif val > 30:
        bar.set_facecolor('#f39c12')
    else:
        bar.set_facecolor('#e74c3c')

plt.suptitle('Advanced Bar Chart Techniques', fontsize=16, fontweight='bold')
plt.tight_layout()
plt.show()

🌈 Style Gallery

Matplotlib comes with various built-in styles to quickly change the look of your plots:

# Apply different styles
import matplotlib.pyplot as plt

# List available styles
print(plt.style.available)

# Use a style
plt.style.use('seaborn')
# or
with plt.style.context('ggplot'):
    # Your plotting code here
    plt.plot([1, 2, 3], [4, 5, 6])

🌍 Real-World Example: Sales Dashboard

Let's create a comprehensive sales visualization combining all three chart types:

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from datetime import datetime, timedelta

# Generate sample sales data
np.random.seed(42)
dates = pd.date_range(start='2024-01-01', end='2024-12-31', freq='D')
n_days = len(dates)

# Sales data with trend and seasonality
trend = np.linspace(100, 150, n_days)
seasonal = 20 * np.sin(np.arange(n_days) * 2 * np.pi / 365)
noise = np.random.normal(0, 10, n_days)
daily_sales = trend + seasonal + noise

# Product categories
products = ['Electronics', 'Clothing', 'Food', 'Books', 'Sports']
product_sales = [45000, 32000, 28000, 18000, 22000]
product_growth = [15, -5, 8, 12, 20]  # YoY growth

# Regional data
regions = ['North', 'South', 'East', 'West']
regional_q1 = [25000, 22000, 28000, 20000]
regional_q2 = [27000, 24000, 30000, 23000]
regional_q3 = [30000, 26000, 32000, 25000]
regional_q4 = [32000, 28000, 35000, 27000]

# Customer satisfaction vs sales correlation
n_stores = 50
satisfaction = np.random.uniform(3, 5, n_stores)
store_sales = 20000 + 15000 * satisfaction + np.random.normal(0, 5000, n_stores)

# Create comprehensive dashboard
fig = plt.figure(figsize=(16, 10))
fig.suptitle('2024 Sales Performance Dashboard', fontsize=20, fontweight='bold')

# 1. Line plot - Daily sales trend
ax1 = plt.subplot(2, 3, 1)
ax1.plot(dates, daily_sales, color='#2E86AB', linewidth=1, alpha=0.7)

# Add 30-day moving average
window = 30
ma = pd.Series(daily_sales).rolling(window=window).mean()
ax1.plot(dates, ma, color='#A23B72', linewidth=2, label=f'{window}-day MA')

# Highlight max and min
max_idx = np.argmax(daily_sales)
min_idx = np.argmin(daily_sales)
ax1.plot(dates[max_idx], daily_sales[max_idx], 'go', markersize=10, label='Peak')
ax1.plot(dates[min_idx], daily_sales[min_idx], 'ro', markersize=10, label='Low')

ax1.set_title('Daily Sales Trend', fontweight='bold')
ax1.set_xlabel('Date')
ax1.set_ylabel('Sales ($K)')
ax1.legend()
ax1.grid(True, alpha=0.3)
ax1.tick_params(axis='x', rotation=45)

# 2. Bar chart - Product performance
ax2 = plt.subplot(2, 3, 2)
colors = ['#2ecc71' if g > 0 else '#e74c3c' for g in product_growth]
bars = ax2.bar(products, product_sales, color=colors, edgecolor='black', linewidth=1)

# Add value labels
for bar, val, growth in zip(bars, product_sales, product_growth):
    height = bar.get_height()
    ax2.text(bar.get_x() + bar.get_width()/2., height,
            f'${val/1000:.0f}K\n({growth:+d}%)',
            ha='center', va='bottom', fontsize=9)

ax2.set_title('Product Sales & Growth', fontweight='bold')
ax2.set_ylabel('Sales ($)')
ax2.set_ylim(0, max(product_sales) * 1.2)
ax2.grid(axis='y', alpha=0.3)

# 3. Scatter plot - Satisfaction vs Sales
ax3 = plt.subplot(2, 3, 3)
scatter = ax3.scatter(satisfaction, store_sales, 
                     c=store_sales, s=100, 
                     cmap='viridis', alpha=0.6, 
                     edgecolors='black', linewidth=1)

# Add trend line
z = np.polyfit(satisfaction, store_sales, 1)
p = np.poly1d(z)
ax3.plot(np.linspace(3, 5, 100), p(np.linspace(3, 5, 100)), 
        'r--', linewidth=2, alpha=0.8, label=f'Trend: R²=0.72')

ax3.set_title('Customer Satisfaction vs Store Sales', fontweight='bold')
ax3.set_xlabel('Customer Satisfaction (1-5)')
ax3.set_ylabel('Store Sales ($)')
ax3.legend()
ax3.grid(True, alpha=0.3)
plt.colorbar(scatter, ax=ax3, label='Sales ($)')

# 4. Grouped bar chart - Regional quarterly performance
ax4 = plt.subplot(2, 3, 4)
x = np.arange(len(regions))
width = 0.2

ax4.bar(x - 1.5*width, regional_q1, width, label='Q1', color='#FF6B6B')
ax4.bar(x - 0.5*width, regional_q2, width, label='Q2', color='#4ECDC4')
ax4.bar(x + 0.5*width, regional_q3, width, label='Q3', color='#45B7D1')
ax4.bar(x + 1.5*width, regional_q4, width, label='Q4', color='#96CEB4')

ax4.set_title('Regional Quarterly Sales', fontweight='bold')
ax4.set_xlabel('Region')
ax4.set_ylabel('Sales ($)')
ax4.set_xticks(x)
ax4.set_xticklabels(regions)
ax4.legend()
ax4.grid(axis='y', alpha=0.3)

# 5. Combined line and bar - Monthly comparison
ax5 = plt.subplot(2, 3, 5)
monthly_dates = pd.date_range(start='2024-01-01', end='2024-12-31', freq='M')
monthly_sales = pd.Series(daily_sales).resample('M', label='right', 
                                                 closed='right').sum()[1:]
monthly_target = [4200] * 12

ax5_bar = ax5.bar(range(12), monthly_sales, color='skyblue', 
                  alpha=0.7, label='Actual')
ax5.plot(range(12), monthly_target, 'r--', linewidth=2, 
         marker='o', label='Target')

# Color bars based on target achievement
for i, (bar, actual, target) in enumerate(zip(ax5_bar, monthly_sales, monthly_target)):
    if actual >= target:
        bar.set_facecolor('#2ecc71')
    else:
        bar.set_facecolor('#e74c3c')

ax5.set_title('Monthly Sales vs Target', fontweight='bold')
ax5.set_xlabel('Month')
ax5.set_ylabel('Sales ($K)')
ax5.set_xticks(range(12))
ax5.set_xticklabels(['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 
                     'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'])
ax5.legend()
ax5.grid(axis='y', alpha=0.3)

# 6. Pie chart bonus - Market share
ax6 = plt.subplot(2, 3, 6)
explode = [0.05 if s == max(product_sales) else 0 for s in product_sales]
colors_pie = plt.cm.Set3(np.linspace(0, 1, len(products)))

wedges, texts, autotexts = ax6.pie(product_sales, labels=products, 
                                    autopct='%1.1f%%', explode=explode,
                                    colors=colors_pie, startangle=90)

for autotext in autotexts:
    autotext.set_color('white')
    autotext.set_weight('bold')

ax6.set_title('Product Market Share', fontweight='bold')

# Add summary statistics
total_sales = sum(product_sales)
avg_satisfaction = np.mean(satisfaction)
total_stores = len(satisfaction)

fig.text(0.02, 0.02, 
         f'Total Annual Sales: ${total_sales:,.0f} | ' + 
         f'Avg Customer Satisfaction: {avg_satisfaction:.2f}/5.0 | ' + 
         f'Active Stores: {total_stores}',
         fontsize=12, ha='left', 
         bbox=dict(boxstyle='round', facecolor='wheat', alpha=0.5))

plt.tight_layout()
plt.show()

💡 Pro Tips for Great Visualizations

⚠️ Common Mistakes to Avoid

📓 Learning Journal

Keep a learning journal — digital or physical. After this lesson, take a few minutes to write down:

  • Key concepts you learned
  • Techniques that clicked for you
  • Questions or confusion points to revisit
  • Ideas you want to try
  • Your progress and feelings about learning this

✍️ This lesson's prompt: Pick a dataset you care about. Which of the three chart types — line, scatter, or bar — best reveals its most important story, and what would each of the other two hide or distort?

📝 Lesson Summary

🎓 Key Takeaways

  • Line plots show change over an ordered axis, scatter plots reveal relationships between two variables, and bar charts compare quantities across categories.
  • Each chart type has a simple Matplotlib call (plot, scatter, bar) that you extend with color, markers, labels, and legends.
  • Grouped and stacked bars, trend lines, and combined charts let you pack more meaning into one figure.
  • Good chart choices and honest axes matter as much as the data itself.

🎉 What You've Accomplished

You can now build the three workhorse chart types of data science and combine them into a coherent dashboard — the visual vocabulary you'll use in nearly every analysis.

❓ Common Questions at This Stage

When should I use a scatter plot instead of a line plot?

Use a scatter plot when you want to show the relationship between two variables where the points have no inherent order (for example, height vs. weight). Use a line plot when the x-axis is ordered — usually time — and connecting the points shows a meaningful progression.

What's the difference between grouped and stacked bar charts?

Grouped bars place each subcategory side by side so you can compare them directly, while stacked bars pile subcategories on top of each other to emphasize the total and each part's contribution to it.

My scatter plot is a solid blob of overlapping points — what do I do?

This is called overplotting. Reduce the marker size, add transparency with alpha, sample a subset of points, or switch to a density-based view like a 2D histogram or hexbin plot.

🔭 Looking Ahead

Once you can build individual charts, the next step is arranging several of them together — using subplots and figure composition to tell a fuller story on one canvas.

✅ Before the Next Lesson

🌟 Encouragement for the Journey

Every dashboard you admire is built from these same simple pieces. Master the basics here and you'll be able to visualize almost anything you can measure.