LAG & LEAD (shift, diff)

Python · GroupBy & SQL-Style Analytics

Theory

s.shift(1) moves values down one row, so each row sees the previous row's value - SQL's LAG(). shift(-1) moves them up, giving the next row's value - LEAD(). The first row (for lag) or last row (for lead) has no neighbour and gets NaN; fill_value=0 changes that. s.diff() is s - s.shift(1), the change from the previous row, and s.pct_change() is the relative change, (current - previous) / previous.

As with all window-style work, sort first, usually by date. To stop values leaking between groups, call the method on a groupby: df.groupby('city')['amount'].shift(1) gives the previous amount within the same city only - LAG(amount) OVER (PARTITION BY city ORDER BY month). groupby(...).diff() and groupby(...).pct_change() work the same way. Typical uses: month-over-month growth, days since previous order (date.diff().dt.days), and spotting when a value changed (s != s.shift()).

Why we use it

Comparisons with the previous period are at the heart of reporting: growth versus last month, gap between orders, change in price. shift/diff/pct_change do this without self-joins or loops.

When to use it

Use shift for raw previous/next values, diff for absolute change and pct_change for growth rates. Always sort by time first and use groupby for per-entity series. For averages over several previous rows, use rolling instead.

How it works

  1. Sort by key and time
  2. Group by the partition key
  3. shift(1) brings the previous row's value alongside
  4. First row of the group? Yes: No previous value: NaN. No: Compute diff / pct_change.
  5. Previous, change and growth columns

Syntax

s.shift(1)   # LAG
s.shift(-1)  # LEAD
s.diff(); s.pct_change(fill_method=None)
df.groupby('key')['col'].shift(1) / .diff() / .pct_change(fill_method=None)

Example

df = df.sort_values(['city', 'month'])
df['prev'] = df.groupby('city')['amount'].shift(1)
df['growth_%'] = df.groupby('city')['amount'].pct_change(fill_method=None) * 100

Key points

Python topics

Getting Started

Variables & Data Types

Operators

Strings

Lists

Tuples

Sets

Dictionaries

Control Flow

Loops

Functions & OOP

Working with Data

Tooling & Errors

File Handling

Exception Handling

NumPy Library

Pandas Basics

Data Cleaning

Combining DataFrames (SQL Joins & Unions)

GroupBy & SQL-Style Analytics

Reshaping & Workflow

Data Visualization

Machine Learning (Scikit-Learn)

Advanced Python & AI

T
Tech Town
Log InSign Up Free
Home SQL Python DSA Power BI Snowflake Databricks Spark Gen AI Practice Problems Contest Blog

Python for Data Analysts

From basics to business insights — the complete step-by-step Python roadmap. Click any topic in the sidebar to open it right here.

{{ sectionCount }}
Sections
{{ topicCount }}
Topics
Free
Forever
Hands-on practice Practice Python Online 141 Python questions for data analysts — strings, records, dates, statistics and regex — with hidden tests and instant verdicts. Start practising →
Section {{ sec.num }}

{{ sec.label }}

Python  /  {{ current.sectionLabel }}  /  {{ current.name }}

{{ current.name }}

Theory

{{ current.def }}

Why we use it

{{ rich.why }}

When to use it

{{ rich.when }}

Diagram

{{ rich.dg.caption }}

{{ tb.title }}
{{ c.v }}
{{ rich.dg.op }}
➜
{{ rich.dg.output.title }}
{{ c.v }}
{{ nd.label }}
{{ nd.sub }}
{{ nd.label }}
{{ nd.sub }}
{{ rich.dg.left.title }}
{{ it }}
{{ rich.dg.right.title }}
{{ it }}
Flowchart — how it works
{{ nd.text }}
{{ nd.text }}
◇ {{ nd.text }}
Yes → {{ nd.yes }}
No → {{ nd.no }}
Syntax
{{ current.syntax }}
Example
{{ current.example }}
▶ Try It Live — runs real Python 3.12 in your browser (first run loads the engine)
{{ liveOutput }}
{{ liveError }}
Key Points — click to check off as you learn
  • {{ pt.mark }} {{ pt.text }}

Share this topic