Date Parts
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.
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())
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 YourselfPrint the year, month, day and weekday name for these two dates.
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.
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())
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 YourselfGroup these by month and then by period, and satisfy yourself about which answers which question.
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.
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.