Unit 9 • Lesson 2

Date Parts

Overview

Year, month and weekday are columns you can group by.

Intermediate 20–25 min

What You Will Learn in This Lesson

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

  • Pull the year, month, day and weekday out of a datetime column.
  • Group by a date part.
  • Say why month alone is not enough across years.
  • Name a period so that it sorts correctly.

Why This Matters

"Which month is busiest" and "how did each month go" are different questions, and the difference is whether the year is part of the key. Grouping by month alone silently pools every January together, which is sometimes exactly right and sometimes the reason a chart makes no sense.

Step 1: Pulling a part out

Once a column is really a date, its parts are one attribute away through the .dt accessor.

1

The parts

Each is a whole column, computed for every row at once.

import pandas as pd

days = pd.to_datetime(pd.Series(["2024-01-05", "2024-06-18"]))
print(days.dt.year.tolist())
print(days.dt.month.tolist())
print(days.dt.day.tolist())
print(days.dt.day_name().tolist())
2

A period that sorts

to_period("M") gives a year-and-month value that orders correctly, unlike the number 1 which is shared by every January you have.

import pandas as pd

days = pd.to_datetime(pd.Series(["2023-12-31", "2024-01-01"]))
print(days.dt.month.tolist())
print(days.dt.to_period("M").astype(str).tolist())

Mini Practice #1: Every part

Try It Yourself

Print the year, month, day and weekday name for these two dates.

Press Run to see output

Step 2: Grouping by a part

"How many per month" is a group-by on a derived column, which you already know how to do.

1

Group on the accessor directly

No need to create the column first, though creating it is clearer when you want it in the output too.

import pandas as pd

df = pd.DataFrame({
    "day": pd.to_datetime(["2024-01-05", "2024-01-20", "2024-02-01"]),
    "n": [1, 2, 5],
})
print(df.groupby(df["day"].dt.month)["n"].sum())
2

Month alone pools the years

Two Januaries from different years land in the same bucket. Group on the period when the years are meant to stay apart.

import pandas as pd

df = pd.DataFrame({
    "day": pd.to_datetime(["2023-01-05", "2024-01-20"]),
    "n": [1, 9],
})
print(df.groupby(df["day"].dt.month)["n"].sum())
print(df.groupby(df["day"].dt.to_period("M"))["n"].sum())

Pooling is the right answer for "which month of the year is busiest" and the wrong one for "how has each month gone". The code looks almost identical, which is why it is worth stating which question you are asking.

Mini Practice #2: With and without the year

Try It Yourself

Group these by month and then by period, and satisfy yourself about which answers which question.

Press Run to see output

Step 3: When to Use It

Grouping by calendar

dt.year, dt.month and dt.day_name give keys for “per month” and “per weekday” questions.

df.groupby(df["date"].dt.day_name())["sales"].sum()

Filtering to a period

Compare a part — all Mondays, all of 2024 — without building strings.

When Not to Use It

Do not group by month number alone across several years. January 2023 and January 2024 end up in one group.

End-of-Lesson Exercises

Exercise 1: Totals by month number

Write by_month(df) that takes a table with a datetime day column and a numeric n, and returns a dictionary of month number to the total of n.

Group by df["day"].dt.month and sum n. The second hidden case is a warning about that: month alone ignores the year, which is sometimes what you want and sometimes a bug.

Press Run to try it, then Check my work to mark it.

Exercise 2: Totals that keep the year

Write by_period(df) returning a dictionary of "YYYY-MM" to the total of n — the same summary, with the years kept apart.

Group on df["day"].dt.to_period("M"), which orders correctly and keeps the year. str() each key on the way into the dictionary.

Press Run to try it, then Check my work to mark it.