W3docs

Matplotlib Line Plots in Python — Complete Guide

Learn to create and customize line plots in Python with Matplotlib. Covers colors, styles, markers, multiple lines, annotations, fill, and saving.

Matplotlib's plot() function is the workhorse for line charts — graphs that connect data points with a continuous line to show trends over time or across a continuous variable. This chapter covers everything from your first sine-wave plot to professional touches like annotations, shaded regions, and saving publication-ready files.

Before diving in, make sure Matplotlib is installed:

pip install matplotlib

If you are new to the library, see the Matplotlib Introduction and Getting Started chapters first.

When to Use a Line Plot

Use a line plot when:

  • You want to show trends over time (stock prices, temperature over months, training loss over epochs).
  • Your x-axis represents a continuous or ordered variable (time, distance, frequency).
  • You need to compare multiple series that share the same x-axis.

Avoid line plots for unordered categories — a bar chart is clearer there. For the relationship between two independent numerical variables without implied order, consider a scatter plot instead.

Creating a Basic Line Plot

The minimal signature is plt.plot(x, y). Both x and y can be Python lists or NumPy arrays.

import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 10, 100)   # 100 evenly-spaced points from 0 to 10
y = np.sin(x)

plt.plot(x, y)

plt.xlabel('Time (s)')
plt.ylabel('Amplitude')
plt.title('Sine Wave')

plt.tight_layout()
plt.show()

np.linspace(start, stop, num) returns num evenly-spaced values — a convenient way to generate smooth curves without writing a loop. plt.tight_layout() adjusts spacing so axis labels are never clipped; it is a good habit to include it before every show() or savefig() call.

Changing Line Color and Style

Using the Format String Shorthand

Pass a format string '[color][marker][linestyle]' as the third positional argument:

import matplotlib.pyplot as plt
import numpy as np

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

plt.plot(x, y, 'r--')   # red dashed line

plt.xlabel('x')
plt.ylabel('sin(x)')
plt.title('Red Dashed Sine Wave')
plt.tight_layout()
plt.show()

Common color letters: b blue, g green, r red, c cyan, m magenta, y yellow, k black, w white.

Common linestyle codes: - solid (default), -- dashed, -. dash-dot, : dotted.

Using Named Parameters

Named parameters are more readable and give you finer control:

import matplotlib.pyplot as plt
import numpy as np

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

plt.plot(
    x, y,
    color='steelblue',
    linestyle='--',
    linewidth=2,
)

plt.xlabel('x')
plt.ylabel('sin(x)')
plt.title('Styled Line')
plt.tight_layout()
plt.show()
ParameterWhat it controlsExample values
colorLine color'red', '#3498db', (0.2, 0.6, 0.8)
linestyleDash pattern'-', '--', '-.', ':'
linewidthThickness in points1 (default), 2, 3
alphaTransparency 0–10.5 for 50% opacity

Adding Markers to Data Points

Markers draw a symbol at each data point, which is useful when the raw data has few points and you want to show them explicitly.

import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 2 * np.pi, 9)   # only 9 points — markers are visible
y = np.sin(x)

plt.plot(
    x, y,
    color='darkorange',
    linewidth=1.5,
    marker='o',          # circle marker
    markersize=8,
    markerfacecolor='white',
    markeredgewidth=1.5,
)

plt.xlabel('x')
plt.ylabel('sin(x)')
plt.title('Sine Wave with Markers')
plt.tight_layout()
plt.show()

Common marker codes: 'o' circle, 's' square, '^' triangle-up, 'D' diamond, '+' plus, 'x' cross, '*' star.

See the Matplotlib Markers chapter for the full list and advanced formatting options.

Plotting Multiple Lines

Call plt.plot() more than once before plt.show(). Each call adds a new line to the same axes. Pass a label argument and then call plt.legend() to generate a legend automatically.

import matplotlib.pyplot as plt
import numpy as np

x  = np.linspace(0, 10, 200)
y1 = np.sin(x)
y2 = np.cos(x)
y3 = np.sin(x) * np.exp(-x / 5)   # damped sine

plt.plot(x, y1, label='sin(x)', linewidth=1.5)
plt.plot(x, y2, label='cos(x)', linewidth=1.5, linestyle='--')
plt.plot(x, y3, label='damped sin(x)', linewidth=1.5, linestyle='-.')

plt.xlabel('x')
plt.ylabel('y')
plt.title('Multiple Lines')
plt.legend()

plt.tight_layout()
plt.show()

Matplotlib cycles through its default color palette automatically, so you do not need to specify different colors for each line unless you want to override them.

Controlling Axis Limits

plt.xlim() and plt.ylim() set the visible range of each axis. Pass (min, max) to zoom in or out:

import matplotlib.pyplot as plt
import numpy as np

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

plt.plot(x, y)

plt.xlim(0, 5)     # show only the first half
plt.ylim(-1.2, 1.2)

plt.xlabel('x')
plt.ylabel('sin(x)')
plt.title('Sine Wave (Zoomed In)')
plt.tight_layout()
plt.show()

To let Matplotlib determine one bound automatically while fixing the other, use None as a placeholder: plt.xlim(None, 5) leaves the lower bound automatic.

Adding a Grid

plt.grid(True) adds a light grid that makes it easier to read values off the chart. You can target only the major or minor ticks and control the style:

import matplotlib.pyplot as plt
import numpy as np

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

plt.plot(x, y, color='steelblue', linewidth=2)

plt.grid(True, linestyle='--', color='grey', alpha=0.5)

plt.xlabel('x')
plt.ylabel('sin(x)')
plt.title('Sine Wave with Grid')
plt.tight_layout()
plt.show()

See the Matplotlib Grid chapter for a full discussion of grid customization.

Annotating Points

plt.annotate() draws a text label with an optional arrow pointing at a specific data coordinate. This is useful for calling out peaks, troughs, or significant events.

import matplotlib.pyplot as plt
import numpy as np

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

plt.plot(x, y, color='steelblue', linewidth=1.5)

# Annotate the first peak (approx. x = pi/2)
plt.annotate(
    'peak',
    xy=(np.pi / 2, 1),            # point to annotate
    xytext=(np.pi / 2 + 1, 1.1),  # position of the text
    arrowprops=dict(arrowstyle='->', color='black'),
    fontsize=10,
)

plt.xlabel('x')
plt.ylabel('sin(x)')
plt.title('Annotated Sine Wave')
plt.tight_layout()
plt.show()

Filling Between Lines

plt.fill_between() shades the region between two curves (or between a curve and a constant). This is common for showing confidence intervals or uncertainty bands.

import matplotlib.pyplot as plt
import numpy as np

x    = np.linspace(0, 10, 200)
y    = np.sin(x)
# Simulate an upper and lower confidence bound
upper = y + 0.3
lower = y - 0.3

plt.plot(x, y, color='steelblue', linewidth=2, label='mean')
plt.fill_between(x, lower, upper, alpha=0.2, color='steelblue', label='±0.3 band')

plt.xlabel('x')
plt.ylabel('Amplitude')
plt.title('Line with Confidence Band')
plt.legend()
plt.tight_layout()
plt.show()

The alpha argument controls how transparent the shading is — values around 0.20.3 usually work well so the line remains clearly visible.

Using the Object-Oriented API

All examples above use the plt.* stateless interface, which is convenient for single-plot scripts. For more complex figures (multiple subplots, embedded plots), use the object-oriented API where you work with explicit Figure and Axes objects:

import matplotlib.pyplot as plt
import numpy as np

x  = np.linspace(0, 10, 200)
y1 = np.sin(x)
y2 = np.cos(x)

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

ax.plot(x, y1, label='sin(x)', color='steelblue', linewidth=1.5)
ax.plot(x, y2, label='cos(x)', color='darkorange', linewidth=1.5, linestyle='--')

ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_title('OO-style Line Plot')
ax.legend()
ax.grid(True, linestyle=':', alpha=0.6)

plt.tight_layout()
plt.show()

The OO API mirrors the plt.* calls with ax.set_* prefixes for labels and titles. It is the preferred style when you need to arrange multiple plots — see Matplotlib Subplots for details.

Saving a Line Plot to a File

Use plt.savefig() before plt.show(). Specify the file format through the extension:

import matplotlib.pyplot as plt
import numpy as np

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

plt.plot(x, y, color='steelblue', linewidth=2)
plt.xlabel('x')
plt.ylabel('sin(x)')
plt.title('Saved Line Plot')
plt.tight_layout()

plt.savefig('sine_wave.png', dpi=150)    # raster PNG at 150 dpi
plt.savefig('sine_wave.svg')             # vector SVG (ideal for web/print)

plt.show()

Common format options: png, pdf, svg, eps. Use svg or pdf when you need a scalable, print-ready image. Call savefig() before show() — after show() the figure is cleared and savefig() would produce a blank image.

Controlling Figure Size

The default figure is 6.4 × 4.8 inches at 100 dpi. Override it with figsize=(width_in, height_in):

import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 10, 200)

fig, ax = plt.subplots(figsize=(10, 4))   # wide and short
ax.plot(x, np.sin(x), label='sin', linewidth=1.5)
ax.plot(x, np.cos(x), label='cos', linewidth=1.5, linestyle='--')

ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_title('Wide Figure')
ax.legend()

plt.tight_layout()
plt.show()

Quick Reference

TaskCode
Basic line plotplt.plot(x, y)
Red dashed line (shorthand)plt.plot(x, y, 'r--')
Named style paramsplt.plot(x, y, color='steelblue', linestyle='--', linewidth=2)
Add markersplt.plot(x, y, marker='o', markersize=6)
Multiple lines with legendcall plt.plot() twice; plt.legend()
Set axis limitsplt.xlim(0, 5) / plt.ylim(-1, 1)
Add gridplt.grid(True, linestyle='--', alpha=0.5)
Annotate a pointplt.annotate('text', xy=(x0, y0), xytext=(xt, yt), arrowprops={...})
Shade between curvesplt.fill_between(x, y_lower, y_upper, alpha=0.2)
Save to fileplt.savefig('file.png', dpi=150)
Set figure sizeplt.subplots(figsize=(10, 4))
Was this page helpful?