Skip to content

pandas dt.quarter

Four buckets a year, numbered from January — which is not where a lot of organizations start counting.

What it does

Have you ever handed someone a quarterly chart and been told that their Q1 is your Q2? .dt.quarter is a small attribute with one big assumption baked into it, and this page is mostly about how to take that assumption back out.

.dt.quarter returns the quarter of the year as an int32, 1 through 4: January through March is 1, October through December is 4. It is an attribute, so no parentheses; the rest of the numeric family and that rule are on dt.year.

Official documentation: Series.dt.quarter and Series.dt.to_period.

A worked example, on real data

US retail sales, not seasonally adjusted, monthly since 1992, from FRED. Four years of it is 48 rows:

import pandas as pd

url = 'https://fred.stlouisfed.org/graph/fredgraph.csv?id=RSXFSN'

df = (pd
      .read_csv(url, parse_dates=['observation_date'])
      .rename(columns={'observation_date': 'month', 'RSXFSN': 'sales'})
      .loc[lambda df_: df_['month'].dt.year.between(2022, 2025)])

df.groupby(df['month'].dt.quarter)['sales'].sum().div(1000).round().astype(int)
month
1    6646
2    7271
3    7245
4    7611
Name: sales, dtype: int64

Four rows, billions of dollars, and a Q4 peak that is entirely real — this is the holiday quarter. It is also four years pooled into four numbers, and it cannot tell you whether 2025 was better than 2022. That is the same collapse that dt.month covers at length; the cure is the same, and to_period writes it in one line:

(df
 .groupby(df['month'].dt.to_period('Q'))['sales']
 .sum().div(1000).round().astype(int)
 .tail(6))
month
2024Q3    1820
2024Q4    1944
2025Q1    1736
2025Q2    1887
2025Q3    1897
2025Q4    1999
Freq: Q-DEC, Name: sales, dtype: int64

Note the Freq: Q-DEC on that last line. Pandas is telling you the assumption: these are quarters of a year that ends in December.

Getting a fiscal quarter

Most large US retailers close their books on the Saturday nearest January 31, so their fiscal 2026 runs from February 2025 through January 2026 and their Q4 is November, December and January. The US federal government ends its year on September 30. Australia ends in June.

You do not need arithmetic for any of that. Change the anchor on the period and ask it for its quarter:

(df
 .assign(calendar=lambda df_: df_['month'].dt.quarter,
         fiscal=lambda df_: df_['month'].dt.to_period('Q-JAN').dt.quarter,
         fy=lambda df_: df_['month'].dt.to_period('Q-JAN').astype(str))
 [['month', 'sales', 'calendar', 'fiscal', 'fy']]
 .tail(5))
         month   sales  calendar  fiscal      fy
403 2025-08-01  640803         3       3  2026Q3
404 2025-09-01  611277         3       3  2026Q3
405 2025-10-01  642315         4       3  2026Q3
406 2025-11-01  640201         4       4  2026Q4
407 2025-12-01  716924         4       4  2026Q4

October 2025 is calendar Q4 and fiscal Q3, and the fiscal label already says 2026 while the calendar still says 2025. 'Q-SEP' gives you the federal version, 'Q-JUN' the Australian one, and 'Q-DEC' is the default you have been using all along. The dt.month page works through the annual form of this, 'Y-SEP', along with the Period arithmetic for files that are already labeled with a fiscal year.

Mistakes people make

Assuming the reader's fiscal year is yours. A bare Q3 on a chart axis is ambiguous in any room with a finance person in it. Print the anchored period — 2026Q3 with Freq: Q-JAN — or say which calendar you used.

Grouping by .dt.quarter alone across several years. Four tidy rows, and they average a trend away. Group by to_period('Q'), or by year and quarter together.

Parentheses. .dt.quarter() raises TypeError: 'Series' object is not callable, because Pandas already computed the quarters and handed you a Series.

Expecting 'Q' to work as a resample rule. It does not any more, the way 'M' does not; the offset alias is 'QE' for quarter end or 'QS' for quarter start. to_period('Q') still takes the bare letter, because period aliases and offset aliases are separate vocabularies.

Where it shows up in Bamboo Weekly

Two Bamboo Weekly solutions genuinely use .dt.quarter, and both are free to read. (A third, Bamboo Weekly #45, mentions it in passing as an alternative to .dt.month.)

Bamboo Weekly #66: Pittsburgh is the trap, demonstrated. Asked for one bar per quarter across a decade of 311 calls, the obvious move is to swap dt.year for dt.quarter in a pivot_table — and the chart that comes back has four bars and answers a different question.

Bamboo Weekly #63: Ukraine aid is the fix, in the form I use most: groupby on a list of three keys — dt.year, then dt.quarter, then the category — followed by unstack to get one line per type of aid. Year first, so the quarters never collapse.

Practice it

Work through a dt.quarter exercise, with instant feedback and no signup required: practice.lernerpython.com/bamboo-weekly/dt-quarter/

Go deeper

dt.month is the page for anchored periods and fiscal-year arithmetic, dt.year for the rest of the numeric accessor, and to_datetime for getting a real datetime column in the first place. Quarterly results usually go on to pct_change or resample. The Pandas user guide's time series chapter lists every anchored offset, Q-JAN through Q-DEC included.

More Pandas videos on Python and Pandas with Reuven Lerner.

Bamboo Weekly is the practice. If you want the structured version — full courses with downloadable Jupyter notebooks, plus live Pandas office hours when you get stuck — that is LernerPython+Data. A paid Bamboo Weekly subscription is included with it.

See it on real data

Below are the 2 Bamboo Weekly exercises that use dt.quarter on real-world data — try each one, then study the worked solution.

Part of the Pandas Methods Index. See also practice by skill.