Dates and Time Series with pandas

Learn to work with dates and times in pandas. Parse dates from text without mixing up day and month, build a time index and select any day, month or time of day, and compare values with earlier ones. Generate regular date ranges, move dates by calendar rules and work with months, quarters and fiscal years as periods. Resample data to daily, weekly or monthly totals, fill gaps, smooth series with rolling, expanding and exponentially weighted windows, and handle time zones and daylight saving time. Examples use pandas 3 and everyday data such as energy readings, temperatures, store sales and website visits.

IntermediateLesson 6 of 8 in Data Analysis with Python Course

What you will learn

  1. Store moments and durations in pandas' datetime types, read dates from text without mixing up day and month, and pull out calendar parts such as month and weekday.
    • Work with timestamps, durations and calendar parts
      Use Timestamp, Timedelta and the datetime64 data type, calculate the time between events, and extract parts such as year, hour and weekday with the .dt accessor.
    • Parse dates from text and format them for output
      Convert text and numbers to datetimes with pd.to_datetime, choose formats so day and month are never swapped, deal with values that cannot be parsed, and turn datetimes back into readable text.
  2. Build a clean, sorted time index, select any day, month or time window with it, and compare each value with earlier values.
    • Build and check a time index
      Turn a datetime column into a sorted DatetimeIndex, handle duplicate timestamps, and find gaps and the frequency of a series.
    • Select rows by date and time of day
      Select whole years, months, days and ranges with date strings, filter by weekday or clock time, and avoid the slips that return wrong rows.
    • Compare values over time with shifts, differences and percent changes
      Lag and lead values, compute changes and growth rates, and understand how pandas aligns two time series by date.
  3. Create regular sequences of dates, move dates by calendar rules such as "one month later" or "next business day", and work with whole months, quarters and fiscal years as periods.
    • Create regular date ranges with frequency aliases
      Generate sequences of dates and times with pd.date_range, choose the right frequency alias, and get the first and last dates right.
    • Move dates with calendar-aware offsets
      Tell fixed durations from calendar rules, and use DateOffset, month-end, business-day and other offsets to move dates correctly.
    • Work with whole months, quarters and fiscal years as periods
      Represent spans of time with Period and PeriodIndex, convert between timestamps and periods, and handle quarters of a fiscal year.
  4. Aggregate detailed data into daily, weekly, monthly or quarterly totals and averages, fill in a finer time grid, and resample several series at once.
    • Downsample to totals, averages and other summaries
      Use resample to aggregate data into coarser time bins, choose the right aggregation, and read bin edges and labels correctly.
    • Upsample and fill gaps in a time series
      Move data to a finer time grid, fill new slots by carrying values forward or back or by interpolating, and expose missing dates.
    • Resample several series at once with groups
      Resample a long table with many stores, sensors or customers per group, and reshape the result for comparison.
  5. Compute moving averages and other window statistics over a number of rows or a span of time, and running totals and weighted averages that follow the whole history.
    • Compute moving statistics over a fixed number of rows
      Use rolling(n) for moving averages, sums and other statistics, control incomplete windows, and apply windows per group.
    • Use time-based windows for irregular data
      Define windows by a span of time such as "7D", understand which observations each window includes, and choose between row-based and time-based windows.
    • Track running totals, peaks and exponentially weighted averages
      Use expanding windows for cumulative statistics and drawdowns, and exponentially weighted means that give recent values more weight.
  6. Attach the right time zone to timestamps, convert between zones, combine data from different zones, and deal with the hours that daylight saving time skips or repeats.
    • Localize timestamps and convert between time zones
      Tell naive from aware timestamps, attach a zone with tz_localize, convert with tz_convert, and combine data recorded in different zones.
    • Handle daylight saving time in time series
      Recognize skipped and repeated local hours, localize them deliberately, and resample and shift correctly on days that have 23 or 25 hours.

Time Series with pandas study guide

6 chapters

1

Turn dates and times into data pandas can calculate with

Dates arrive as strings, numbers and clock readings, but analysis needs a consistent representation. Learn to turn those inputs into moments and durations, check failed conversions and read useful calendar parts. The examples distinguish a stored datetime from its display text, so later sorting, selection and arithmetic use calendar meaning. Small details such as month/minute format codes and storage resolution can change a result substantially.

1.1 Work with timestamps, durations and calendar parts

A date is more useful when pandas knows it is a date. A time series ties observations to moments or spans of time. Hourly energy readings form a regular series; orders arriving at uneven intervals form an irregular one. Text such as "12/31/2024" can sort after "03/01/2025", even though December came first. Consistent full ISO strings sort correctly, but datetime values also support calendar selection and arithmetic.

Choose the right time object

Python supplies datetime, date and timedelta. In pandas, a Timestamp holds one moment, a datetime column has a datetime64[unit] data type, and a DatetimeIndex holds datetime row labels. NaT means a missing time. Detect it with pd.isna(); subtracting a missing time produces another missing time.

The unit in datetime64[us] is microseconds, the storage resolution. Other units include seconds (s), milliseconds (ms) and nanoseconds (ns). pandas 3 normally parses text at microsecond resolution, using nanoseconds when the text needs them. Finer resolution uses more of the integer range: nanosecond dates extend only from 1677 to 2262.

Calculate elapsed time
import pandas as pd
start = pd.Timestamp("2025-03-10 18:00")
end = pd.Timestamp("2025-03-14 09:30")
elapsed = end - start
hours = elapsed / pd.Timedelta(hours=1)

elapsed is 3 days, 15 hours and 30 minutes. The three days contribute 72 hours, so hours is 87.5. elapsed.total_seconds() gives 315,000. Its .days attribute gives only 3; it is the whole-day component, not the complete duration in days. Build durations with pd.Timedelta(hours=36) or parse several with pd.to_timedelta(["1h", "90min"]).

Subtracting a timestamp from a whole datetime column produces durations row by row. Use .dt.days for whole-day components or divide the duration column by a unit Timedelta for fractional totals.

Read calendar parts and round times

On a column, .dt.year, .dt.month, .dt.hour and .dt.quarter extract calendar parts. .dt.day_of_week numbers Monday as 0 through Sunday as 6; .dt.day_name() gives a name. March 14, 2025 is Friday, numbered 4. An index uses these attributes directly: df.index.month. The spellings day_of_week and day_of_year replace older spellings deprecated in pandas 3.1.

A weekday can become a grouping key: df.groupby(df["timestamp"].dt.day_name())["kwh"].mean(). Grouping by month number combines that month across years; a monthly period keeps each year separate.

For 09:37, .dt.floor("h") gives 09:00, .dt.ceil("h") gives 10:00, and .dt.round("15min") gives 09:30. .dt.normalize() sets clocks to midnight while keeping datetime values. .dt.date instead produces Python date objects and loses datetime-column features.

Keep numeric units explicit

astype("int64") returns counts in the column's own unit. A microsecond count is 1,000 times smaller than the nanosecond count for the same moment. If an external calculation needs nanoseconds, use s.dt.as_unit("ns").astype("int64"); a Timestamp or index uses .as_unit("ns") directly. For elapsed time, Timedelta arithmetic avoids that unit trap.

Key Takeaways

  • A Timestamp is a moment; a Timedelta is elapsed time.
  • Use unit Timedeltas to obtain full durations, including fractions.
  • Calendar attributes use .dt on columns and direct access on indexes.
  • Missing times are NaT.
  • Integer datetime counts depend on storage resolution.

1.2 Parse dates from text and format them for output

Parsing gives text a calendar meaning. Before converting a date column, establish its format and check whether it contains offsets, invalid entries or missing values. ISO 8601 writes the year first, as in "2025-03-14T09:30:00", and avoids day/month ambiguity.

State the source format

"03/04/2025" can mean March 4 or April 3. pd.to_datetime("03/04/2025") reads month first; dayfirst=True reads day first. A known format is clearer:

raw = pd.Series(["14/03/2025 18:45", "15/03/2025 09:00"])
parsed = pd.to_datetime(raw, format="%d/%m/%Y %H:%M")

%Y is a four-digit year, %m a month and %d a day. %H is a 24-hour clock hour, %M a minute and %S a second. Use %I with %p for a 12-hour clock and AM/PM. %b and %B read abbreviated and full month names. Uppercase %M means minutes, so it cannot replace lowercase %m for months.

Without format, pandas infers a format from the first non-missing entry and applies it to the column. With "03/04/2025" first, a later "13/04/2025" fails under month-first inference. Reordering the rows can change the inferred format. format="ISO8601" handles ISO variants. format="mixed" parses entries individually, but cannot settle an ambiguous source convention; clean or verify that convention first.

Inspect failed conversions
raw = pd.Series(["2025-01-15", "not a date", None])
parsed = pd.to_datetime(raw, format="%Y-%m-%d", errors="coerce")
failed = parsed.isna() & raw.notna()

The result contains January 15 and two NaT values. parsed.isna().sum() is 2 because it includes the original missing entry. failed.sum() is 1 because only "not a date" was present and failed. Inspect raw[failed] before deciding to repair or remove it. Coercion keeps a load running, but also hides typos such as an impossible month. The default errors="raise" stops on invalid input; pandas 3 removed errors="ignore".

Parse files and numeric timestamps

pd.read_csv("meter.csv", parse_dates=["timestamp"], index_col="timestamp") requests datetime parsing and a time index. Check the resulting dtypes after loading. Text columns in pandas 3 use the str dtype; older versions commonly show object.

Join separate date and time strings with a space before parsing. Columns named year, month and day can be assembled directly with pd.to_datetime(df[["year", "month", "day"]]).

For Unix counts, specify the unit. pd.to_datetime(1735689600, unit="s", utc=True) gives midnight UTC on January 1, 2025. The millisecond count is 1735689600000 with unit="ms". Using the wrong unit changes the interpreted moment.

Format only for presentation

parsed.dt.strftime("%d %b %Y") writes text such as "15 Jan 2025". A Timestamp's .isoformat() writes an ISO string. Named months and weekdays follow the system locale; the displayed examples use English. Keep datetimes during analysis and format at export, because strings no longer support date arithmetic.

Key Takeaways

  • Prefer unambiguous ISO dates when controlling the source.
  • Pass a known parsing format explicitly.
  • Separate invalid inputs from values that were already missing.
  • Match Unix counts to their seconds or milliseconds unit.
  • Format to text at the end of the analysis.
  • %m means month; %M means minute.
2

Index, select and compare data by time

A time index makes dates usable as row labels, but the labels need careful checks before comparison. Sort observations, decide what duplicate timestamps mean and expose missing dates. Then select calendar periods or recurring clock windows and compare values with earlier ones. The central distinction is between row position and actual time: a previous row can be several days earlier, and two series combine by matching timestamps.

2.1 Build and check a time index

A useful time index is parsed, ordered and checked. A table can contain several events at one moment, missing days or out-of-order batches. Each condition changes what later calculations mean.

Build datetime row labels
df["timestamp"] = pd.to_datetime(df["timestamp"], format="%Y-%m-%d %H:%M")
df = df.set_index("timestamp").sort_index()

set_index() moves the column into the row labels; it does not parse text. Inspect df.index.dtype for a datetime dtype and df.index.is_monotonic_increasing for chronological order. Equal neighboring times are allowed in an increasing index, so this check does not establish uniqueness.

A DatetimeIndex supports date selection and resampling. Text labels do not become a time index merely because they look like dates. You can also leave timestamps in a column: df.resample("D", on="timestamp") uses that column without changing the index. A PeriodIndex is another time-series representation when rows describe spans such as months.

Decide what repeated times mean

df.index.is_unique reports whether every label is different. df.index.duplicated() marks repeated labels after the first. Suppose two readings, 3 and 5, share January 1 and a reading of 4 belongs to January 2. Averaging duplicate readings with df.groupby(level=0).mean() produces 4.0 on both dates.

That operation is a decision about the data. If rows are separate purchases, summing may be appropriate. If an import duplicated a record, keeping the last copy with df[~df.index.duplicated(keep="last")] may be appropriate. Do not delete valid transactions solely to make an index unique. Operations that place one value at every target label, including reindex() and asfreq(), require unique source labels.

Distinguish stored and inferred frequency

An index created by pd.date_range() normally carries .freq. Parsed timestamps often have .freq equal to None, even when they fall on consecutive days. pd.infer_freq(df.index) examines at least three timestamps and returns an alias such as "D" or "ME" if it finds a regular pattern. Irregular spacing returns None.

Frequency metadata is not a completeness audit. To look for absent daily rows, build the calendar you expect:

calendar = pd.date_range(df.index.min(), df.index.max(), freq="D")
missing = calendar.difference(df.index)

A missing day has no row. It differs from a present row containing NaN. A calendar bounded by the observed minimum and maximum cannot reveal missing dates outside that range; use the planned reporting start and end when those are known.

Prepare for later operations

Sorting makes slices predictable and is required for time-based rolling windows. Missing rows matter even when a calculation succeeds: shift(1) means one row back, and rolling(7) means seven rows. Neither checks that each row represents one day. For a long table, also keep the series key, such as sensor or store, so later operations can run separately per series.

Key Takeaways

  • Parse before setting the time index, and sort it.
  • Check ordering and uniqueness separately.
  • Handle duplicate labels according to what their rows represent.
  • Compare with a full calendar to find absent dates.

2.2 Select rows by date and time of day

Once the index contains sorted datetimes, date strings become useful selectors. Their precision controls how much time they represent: a year, a month, a day or a particular minute.

Select a whole calendar period

For daily data from January through March 2025, ts.loc["2025"] selects all 90 days. ts.loc["2025-02"] selects February's 28 days. On hourly data, a day string selects that whole day, not just midnight.

Use .loc on a DataFrame too:

february = df.loc["2025-02", "kwh"]
spring_onward = df.loc["2025-03-01":]

df["2025-02"] instead asks for a column with that name. The presence of a time index does not change square-bracket column selection into row selection.

Read the endpoint precisely

A label slice includes both endpoints. ts.loc["2025-01-30":"2025-02-02"] includes January 30 and 31 and February 1 and 2. A position slice such as ts.iloc[0:4] stops before position 4.

The end of a partial-string slice includes the entire named period. On hourly data, df.loc[:"2025-06-01"] includes all of June 1. df.loc[:"2025-06-01 00:00"] stops at midnight. Likewise a slice ending in "2025-02" includes the whole of February. Choosing a more precise string changes the boundary, so write the cutoff to match the question.

ts.truncate(before="2025-03-01") keeps March 1 onward. Its after= argument trims the other end. Sorting first avoids failures that can occur when date-string slices meet unordered labels.

Select recurring calendar conditions

Calendar attributes produce boolean masks:

weekends = df[df.index.day_of_week >= 5]
evening_march = df[(df.index.month == 3) & (df.index.hour >= 18)]

Monday is 0, so Saturday and Sunday are 5 and 6. Each condition needs parentheses when combined with & or |. With a datetime column, use df["timestamp"].dt.day_of_week instead of an index attribute. The weekday mask selects a repeated calendar pattern; it is different from selecting one continuous date interval.

Select a clock window on every date

df.between_time("09:00", "17:00") selects those hours each day. Both ends are included by default, giving nine rows in a complete hourly day. inclusive="left" includes 09:00 and excludes 17:00, giving eight. df.at_time("12:00") selects exactly noon each day.

A clock window can cross midnight. between_time("22:00", "06:00") selects late-night and early-morning rows across the index. It does not select one uninterrupted interval between two particular dates. On an aware index, date-string selection uses the index's local zone; convert the index before selecting a reporting day in a different zone.

Key Takeaways

  • A partial date string selects its whole period.
  • Label slices include the endpoint; position slices exclude the stop.
  • Use .loc for date selection on a DataFrame.
  • Calendar masks and clock windows select recurring patterns.
  • Check endpoint precision and time zone before filtering.

2.3 Compare values over time with shifts, differences and percent changes

A change needs a clear comparison point. "Previous" can mean the previous observed row, the previous calendar day or the same month a year ago. Choose that meaning before computing a lag or growth rate.

Put earlier values beside current ones

For visitor counts 200, 210 and 189 on March 3–5, s.shift(1) gives NaN, 200 and 210. It moves values down one row while keeping the labels. The new first slot is missing because no earlier observation exists. shift(-1) gives 210, 189 and NaN: a lead that brings the next value to the current row.

df["previous"] = df["visitors"].shift(1)
df["change"] = df["visitors"].diff()
df["growth"] = df["visitors"].pct_change()

diff() subtracts the preceding value, giving NaN, 10 and −21. pct_change() divides the current value by the previous one and subtracts 1, giving NaN, 0.05 and −0.10. These are fractions: 0.05 means 5%, not 0.05%. Floating-point output can have tiny extra digits; round for display after calculating.

Choose the lag in rows or in time

pct_change(7) compares with seven rows earlier. It is the same weekday a week earlier only when there is exactly one row per day with no gaps. On a complete monthly series, pct_change(12) gives year-over-year change.

shift(1, freq="D") moves the timestamps forward one day and leaves the values attached to those moved labels. It keeps every value. Arithmetic with the original then aligns by date:

previous_day = s.shift(1, freq="D")
daily_change = s - previous_day

If the source has observations on Monday and Thursday only, plain shift(1) compares Thursday with Monday. A date shift offers a comparison with Wednesday; if Wednesday is absent, Thursday's daily change is missing. Filling that gap is a separate decision about the data.

Expect arithmetic to align labels

Two Series combine by timestamp, not by row position. Add values 200, 210 and 189 on March 3, 4 and 5 to values 1 and 1 on March 4 and 7. Only March 4 has two observations, so its sum is 211. The union of dates is March 3, 4, 5 and 7; the other results are missing.

Use s.add(other, fill_value=0) when an absent observation genuinely means zero, such as a verified event count. It is not a general repair for missing sensor readings.

Restart comparisons per series

Sort a long table by its series key and date, then calculate within groups. df.groupby("store")["sales"].pct_change() compares each store only with itself. For store A selling 100 then 110 and store B selling 50 then 40, the growth rates are missing, 0.10, missing and −0.20. A plain column-wide calculation would compare B's first sale with A's last sale. That numerical result would describe a switch of stores, not growth over time.

Key Takeaways

  • A lag moves earlier values beside later ones.
  • pct_change() returns a fraction; multiply by 100 to express a percentage.
  • Row shifts and date shifts answer different questions.
  • Arithmetic aligns timestamps, not positions.
  • Compute changes separately within each series.
3

Generate dates and use frequencies, offsets and periods

Calendar rules make repeated date work reliable. Generate a complete range from a frequency, move to the next month end or business day, and label whole months or quarters with periods. Each representation answers a different question: a duration counts elapsed time, an offset follows a calendar rule, and a period names a span. Reading anchors and endpoints carefully prevents off-by-one errors and fiscal-year mix-ups.

3.1 Create regular date ranges with frequency aliases

A regular date range is a calendar you can generate instead of typing every label. It can label test data, define an expected reporting grid or reveal missing observations after reindexing.

Specify a start and a rule
ends = pd.date_range("2025-01-01", periods=4, freq="ME")

The four timestamps are January 31, February 28, March 31 and April 30. "ME" means month end, so the first date is the first month end on or after the start. A regular frequency can follow a calendar rule even when its elapsed gaps vary in length.

Normally give two of start, end and periods, plus freq. Without an explicit frequency, the default is one calendar day. periods is a count of timestamps, not a count of gaps: four hourly timestamps contain three gaps.

Read common aliases
RuleAliasExample use
Calendar or business dayD, BDaily dates or Monday–Friday dates
Week on an anchor dayW, W-MONSundays or Mondays
Month boundaryMS, MEFirst or last calendar day
Quarter boundaryQS, QEQuarter start or end
Year boundaryYS, YEYear start or end
Fixed clock intervalh, min, s, msHours through milliseconds

Multiples express wider intervals: "15min", "2h" and "90min". Starting at 08:00 with four "90min" periods gives 08:00, 09:30, 11:00 and 12:30. "1h30min" expresses the same spacing.

pandas 3 timestamp frequencies use "ME", "QE", "YE", "BME", "h", "min", "s", "ms" and "us". Older aliases "M", "Q", "Y" or "A", "BM", "H", "T", "S", "L" and "U" no longer work in these timestamp operations. Monthly periods still use "M" because a period describes a span.

Follow anchors and business days

An anchored range does not force the supplied start into its output. Starting January 15, 2025 with freq="MS" gives February 1 first. In January 2025, "W-MON" gives January 6, 13, 20 and 27; plain "W" gives Sundays. "WOM-3FRI" means the third Friday of each month.

"B" skips weekends, but it does not know local holidays. March 2025 contains 21 Monday–Friday dates. A custom calendar can exclude a supplied holiday:

pd.bdate_range("2025-03-01", "2025-03-31",
               freq="C", holidays=["2025-03-17"])

That range contains 20 dates. The holiday list is explicit; a country is not inferred from a time zone or language.

Count endpoints carefully

Matching start and end points are included by default. Hourly from January 1 at midnight through January 2 at midnight gives 25 timestamps. Use periods=24 for exactly 24 hourly readings, or inclusive="left" to exclude the end. normalize=True resets the start and end clocks to midnight. Add tz="Asia/Tokyo" when the generated labels must carry a zone.

Key Takeaways

  • Frequency aliases express clock intervals or calendar anchors.
  • An anchored range begins at the first matching date.
  • Business-day ranges need explicit holidays.
  • Count timestamps and endpoint inclusion separately.
  • Use current pandas 3 aliases for timestamp frequencies.

3.2 Move dates with calendar-aware offsets

"A month later" is a calendar instruction. "Thirty days later" is elapsed time. The results can differ because months and years do not all contain the same number of days.

Separate duration from calendar movement

A Timedelta adds a fixed amount of elapsed time. A DateOffset follows calendar fields. From January 31, 2025:

t = pd.Timestamp("2025-01-31")
t + pd.DateOffset(months=1)   # February 28
t + pd.Timedelta(days=30)    # March 2

For the DateOffset, first move to February, then keep day 31 if it exists. It does not, so pandas clips to February's last valid day. From January 31, 2024, the result is February 29 because it is a leap year. pd.Timedelta(months=1) is invalid: a month has no single fixed duration.

The same distinction applies to years. From January 1, 2024, 365 elapsed days end on December 31, 2024. pd.DateOffset(years=1) ends on January 1, 2025. Use the unit named by the real question instead of substituting a convenient number of days.

Move to a calendar anchor

pd.offsets.MonthEnd(1) moves to the next month end. Starting January 15, 2025 gives January 31. Starting on January 31 gives February 28. The count of one means advance to an anchor, so an already matching date moves too.

MonthEnd(0) moves a date forward to its current month end but leaves an existing month end unchanged. It is useful for labeling every observation by the end of its month:

month_labels = df.index + pd.offsets.MonthEnd(0)

An offset also has rollforward() and rollback(). For January 15, a MonthEnd's nearest forward anchor is January 31 and its nearest backward anchor is December 31, 2024. These operations select the nearest valid anchor in a direction.

Move through business days

March 14, 2025 is Friday. Adding pd.offsets.BDay(1) gives Monday, March 17. Adding one elapsed day gives Saturday, March 15. A standard business-day rule skips weekends, with holidays requiring an explicitly supplied custom calendar.

Frequency strings are short names for offset objects: "ME" corresponds to MonthEnd, "B" to BusinessDay and "W-MON" to a Monday-anchored week. This lets the same calendar meaning drive a range, resampling rule or date shift.

Keep the intended clock across daylight saving time

In pandas 3, pd.offsets.Day(1) means one calendar day and keeps the local clock time. pd.Timedelta(days=1) adds exactly 24 hours. From noon on March 29, 2025 in Amsterdam, the calendar offset ends at noon March 30, after only 23 elapsed hours. The Timedelta ends at 13:00. A recurring local appointment and a measured equipment runtime therefore need different operations.

Key Takeaways

  • Months and years are calendar rules, not fixed durations.
  • A month offset clips to the target month’s last valid day.
  • MonthEnd(1) advances an existing month end; MonthEnd(0) keeps it.
  • Business-day offsets skip weekends.
  • A calendar day and 24 elapsed hours can differ.

3.3 Work with whole months, quarters and fiscal years as periods

A monthly sales total belongs to a whole month. Labeling it with one timestamp is possible, but a Period states its span directly. A period combines a value with a frequency, so "2025-03" with frequency "M" means all of March 2025.

Represent and move spans
march = pd.Period("2025-03", freq="M")
months = pd.period_range("2025-01", "2025-04", freq="M")

months is a four-item PeriodIndex with dtype period[M]. March's start_time is March 1 at midnight; its end_time is March 31 at 23:59:59.999999 at microsecond resolution. An end is the final moment, not midnight at the start of the final day.

Period arithmetic moves in the period's own unit. march + 1 is April 2025. June minus March at monthly frequency gives an offset of three month ends, whose .n is 3. A monthly period also knows .days_in_month: February 2025 has 28 days.

Convert moments to spans and back
monthly_labels = df.index.to_period("M")
column_labels = df["date"].dt.to_period("M")
start_labels = monthly_labels.to_timestamp()
end_labels = monthly_labels.to_timestamp(how="end")

Every timestamp in March maps to the same monthly period. Converting that period back gives its start by default or its end when requested. This does not recover an observation's original day and clock time: the conversion to a period discarded that detail.

Keep the alias families separate. A monthly span uses "M" in Period and period_range. A month-end timestamp uses "ME" in date_range and datetime resampling. The two names describe different kinds of labels.

Change a period's frequency

A period's .asfreq() selects a containing or contained period at a different frequency. march.asfreq("D", how="start") gives March 1 as a daily period; how="end" gives March 31. A yearly period converted to "M" with how="start" gives January.

This selects a boundary period; it does not distribute a monthly total among days. For a PeriodIndex series, resampling can aggregate monthly spans to quarterly spans. Counts of the days in each month of 2025 sum to quarter totals 90, 91, 92 and 92.

Read fiscal quarter labels

A fiscal quarter is named within a fiscal year whose end month is explicit. With "Q-JUN", fiscal 2025 ends in June 2025. Its first quarter therefore runs July–September 2024:

q = pd.Period("2025Q1", freq="Q-JUN")

With "Q-MAR", May 10, 2025 falls in 2026Q1 because that fiscal year ends in March 2026. The year in a fiscal label need not match the calendar year of its first day. For timestamp filters, selecting times before the next period starts is often clearer than constructing the last fractional second of the current one.

Key Takeaways

  • Periods describe spans and move in their own frequency unit.
  • Converting to a period discards finer timestamp detail.
  • Period and timestamp frequency aliases differ.
  • Fiscal years are labeled by their ending year.
4

Change the frequency of a time series with resampling

Changing frequency changes the question asked of the observations. Detailed readings can become daily totals or monthly averages; a sparse series can become a finer grid with empty slots. Choose aggregations and fill methods from the meaning of the values, then check the time-bin boundaries. When a table contains several stores or sensors, keep their identities in the grouping so the resulting histories remain separate.

4.1 Downsample to totals, averages and other summaries

Resampling answers questions such as "How much energy was used each day?" or "What was the average temperature each month?" It groups observations into time bins and then applies an aggregation.

Choose a statistic that fits the quantity
daily = df.resample("D").agg({"kwh": "sum", "temp_c": "mean"})

Energy in kWh and rainfall in mm add over time. Temperature is a level, so a mean describes it more usefully than a sum. last() suits a closing balance, while max() finds a peak. Daily sales can be summed for monthly total sales or averaged for average sales per observed day; the question decides.

resample() returns a Resampler until you call an aggregation such as sum(), mean(), count() or agg(). Use on="date" when the datetimes are in a column instead of the index.

Read calendar bins and partial coverage

A daily series of ones from January 1 through April 30, 2025 gives monthly totals 31, 28, 31 and 30 with resample("ME").sum(). "MS" gives the same monthly totals with start labels. Quarter-end resampling gives 90 for January–March and 30 for a second quarter containing April only. A June 30 label does not establish that May and June were observed.

Weekly "W" bins end on Sunday. Daily values 1 through 14 starting Monday, March 3 sum to 28 for the week labeled March 9 and 77 for the week labeled March 16. "W-MON" ends on Monday; the first bin then contains March 3 alone. Read the weekday anchor before treating a label as a week's start.

Separate membership from labeling

closed controls which boundary belongs to a bin. label controls the timestamp printed on the result. For most rules, including "5min", "h", "D" and "MS", both default to "left".

Values 0 through 9, one per minute from 00:00, produce two default five-minute sums: 10 labeled 00:00 and 35 labeled 00:05. The first bin contains 00:00 through 00:04. With closed="right", label="right", results are 0 at 00:00, 15 at 00:05 and 30 at 00:10. The middle bin contains 00:01 through 00:05. Changing only the label would not change membership.

End rules such as "ME", "QE", "YE", "W" and "BME" default to right closure and right labels. Fixed-interval bins normally align from midnight of the first day, so 15-minute boundaries are 00:00, 00:15 and so on.

Distinguish empty bins from real zeros

An empty day's sum() is 0, count() is 0 and mean() is NaN. Use sum(min_count=1) to preserve missingness when no reading is available. A recorded zero and an absent reading then remain distinguishable.

For several summaries of one series, .ohlc() gives open, high, low and close: the first, maximum, minimum and last observed value in each bin. Readings 10, 12 and 11 give open 10, high 12, low 10 and close 11. Open and close depend on time order, not on numerical size.

Key Takeaways

  • Choose aggregations from the meaning of the values.
  • closed sets membership; label sets the displayed time.
  • Weekly labels normally name the week’s ending day.
  • Check partial first and last bins.
  • sum(min_count=1) preserves empty bins as missing.
  • OHLC means first, maximum, minimum and last.

4.2 Upsample and fill gaps in a time series

A finer grid creates slots, not measurements. Before filling them, decide whether each value represents a state, a count, a smooth measurement or a total over a span.

Expose the new slots
s = pd.Series([10.0, 16.0],
              index=pd.to_datetime(["2025-06-01", "2025-06-04"]))
daily = s.asfreq("D")

The daily result is 10, NaN, NaN, 16. s.resample("D").asfreq() gives the same grid here. Values are kept at matching timestamps; the new dates contain no original observation.

Fill according to meaning
MethodResult on June 1–4Suitable meaning
ffill()10, 10, 10, 16A state holds until changed
bfill()10, 16, 16, 16A later value is used for earlier slots
interpolate()10, 12, 14, 16Change is estimated along a straight line

A thermostat setting holds until someone changes it, so forward fill is useful. A smoothly changing water level can justify interpolation. Zero suits an event count only when the missing record means no event. A sensor outage does not establish a zero measurement.

The interpolation example uses a regular daily grid. The total change is 6 across three one-day steps, so each step adds 2. The two interior values are estimates, not newly recovered measurements.

Backward fill uses future information. It cannot describe what an observer knew at each earlier moment. Forward fill respects that time direction, but long gaps can still make a carried state stale. daily.ffill(limit=1) fills one slot after a reading, giving 10, 10, NaN, 16 in this example.

Use explicit reporting boundaries

asfreq("D") spans the first through last source timestamp. Reindex when the calendar must extend further:

calendar = pd.date_range("2025-06-01", "2025-06-05", freq="D")
complete = s.reindex(calendar)

June 5 is now visible too. fill_value=0 in reindexing fills newly created labels, which is useful for verified zero-event days. It does not replace missing values that already existed in the source. Both reindexing and asfreq() need unique source labels.

Keep sampling separate from aggregation

asfreq() selects values at target timestamps. It does not summarize the observations between them. On 48 hourly values numbered 0 through 47, asfreq("D") selects midnight readings 0 and 24. resample("D").sum() instead gives 276 and 852 by adding every hourly value in each day.

A monthly total cannot be forward-filled onto every day and then summed: that would count the same total repeatedly. An equal daily allocation requires dividing by the month's number of days. That is an allocation assumption, distinct from carrying a state or estimating a smooth measurement.

Key Takeaways

  • A finer grid adds empty slots, not observations.
  • Choose filling from the quantity’s meaning and time direction.
  • Limit filling when long gaps should remain visible.
  • asfreq() selects points; resampling with aggregation summarizes bins.

4.3 Resample several series at once with groups

A long table can hold many series in one value column. A store key distinguishes stores, and a sensor key distinguishes sensors. Group by that identity as well as time when the question asks for a separate history for each series.

Combine a series key with a time rule
monthly = df.groupby([
    "store", pd.Grouper(key="date", freq="ME")
])["sales"].sum()

pd.Grouper places a time-bin rule inside groupby. key= names a datetime column; level= can name a datetime index level. The result has one index level for store and another for month end. Selecting the numeric column keeps the aggregation focused on sales.

Suppose store A sells 100 on January 5, 150 on January 20 and 120 on February 3. Store B sells 80 on January 11, 90 on February 14 and 60 on February 25. The grouped result is A: January 250, February 120; B: January 80, February 150. Resampling the entire table without the store key would combine the stores into January 330 and February 270.

Resample within each group

A second form makes the date the index first:

monthly = (df.set_index("date")
             .groupby("store")["sales"]
             .resample("ME").sum())

It produces the same totals for observed store-month combinations. The two forms differ when a store has no rows in an intervening month. If A appears in January and March only, group-wise resampling creates a February bin between A's first and last date; ordinary sum() gives zero there. Grouping by store and a Grouper retains the combinations that contain rows, so A's February combination is absent.

An absent combination, an empty bin summarized as zero and a missing measurement are different data states. Decide which one the report should show. Use explicit reindexing when every store must share a calendar wider than its observed range. Use min_count=1 with a sum when an empty bin should stay missing.

Reshape for comparison

monthly.unstack("store") moves the store level into columns and leaves months in rows. In the example, January has A = 250 and B = 80; February has A = 120 and B = 150. A missing store-month combination becomes NaN; unstack("store", fill_value=0) replaces missing combinations with zero when that meaning is appropriate.

Keep years separate when needed

A time Grouper at "ME" keeps January 2024 and January 2025 separate. Grouping by df["date"].dt.month keeps only month numbers and merges both Januaries. The first form answers "What happened each month?" The second supports a seasonal comparison such as average January sales across years. The choice is about the time identity you need to preserve.

Key Takeaways

  • Group by series identity and time to preserve separate histories.
  • Per-group resampling and a time Grouper differ for empty months.
  • unstack() makes series easy to compare in columns.
  • Month numbers combine years; monthly time bins preserve them.
5

Smooth and summarize data with rolling and expanding windows

Window statistics describe how a series changes without reducing it to one overall number. A rolling calculation follows recent rows or recent elapsed time, an expanding calculation retains the history, and an exponentially weighted mean gradually favors newer observations. Work through which values enter each result, how missing data affect it and where the result is labeled. These choices determine what a smoothed value can tell you.

5.1 Compute moving statistics over a fixed number of rows

A rolling window repeatedly summarizes a moving group of rows. At each new row, the window gains the current value and eventually drops its oldest one. The window length controls how much short-term variation is smoothed.

Work one window at a time

For values 4, 8, 6, 10 and 12:

s = pd.Series([4, 8, 6, 10, 12])
s.rolling(3).mean()

The first full window is [4, 8, 6], totaling 18 and averaging 6. The next is [8, 6, 10], averaging 8. The last is [6, 10, 12], averaging about 9.333. Default results are NaN, NaN, 6, 8 and 9.333 because the first two rows do not have three observations yet.

Control data requirements and placement

min_periods sets the required count of non-missing values. For a row-count window it defaults to the full window size. With min_periods=1, the early means use one value and then two: 4 and 6. Their smaller samples make them less stable than the later three-value means.

Missing values still occupy row positions. In [4, NaN, 6, 10, 12], a window of three rows containing the missing value has only two valid observations. min_periods=2 permits the mean of [4, NaN, 6], which is 5; the default does not.

Results normally belong to the window's final row. With center=True, the same three full-window means appear at the middle rows, giving NaN, 6, 8, 9.333, NaN. A centered value uses later observations relative to its label. Use trailing windows to describe what was known at each moment.

Summarize more than the mean

The same windows support sum(), max(), min() and std(). pandas uses sample standard deviation by default: [4, 8, 6] has standard deviation 2. A custom function can compute a range:

s.rolling(3).apply(lambda v: v.max() - v.min(), raw=True)

The full-window ranges are 4, 4 and 6. raw=True passes array values to the function. Two aligned series can also share a window: a.rolling(3).corr(b) measures correlation across the last three paired observations, so the relationship can vary over time.

Choose a meaningful length and grouping

With one complete daily row per day, a seven-row average contains every weekday once and averages out a fixed repeating weekday component. A twelve-row average does the same for a fixed annual component on complete monthly data. Longer windows smooth more but respond later to turns; a trailing n-row average has roughly half a window of delay, about (n − 1) / 2 rows.

Rows are not days. Seven readings with gaps can cover more than a week. Use a time window when the question names elapsed time. In a long table, sort each series in time and run windows within its group: df.groupby("store")["sales"].rolling(2).sum(). That prevents a window from crossing from one store's data into another's.

Key Takeaways

  • A row window counts rows, including positions with missing values.
  • min_periods counts valid observations.
  • Centered windows include later values relative to their labels.
  • Longer windows smooth more and respond more slowly.
  • Apply windows separately within each series.
  • Rolling methods can summarize spread and relationships as well as levels.

5.2 Use time-based windows for irregular data

Irregular events call for windows defined by time. "Orders in the past three days" should keep the same duration even when there are many orders one day and none the next.

Define an elapsed-time window
s = pd.Series([5, 3, 4, 6], index=pd.to_datetime([
    "2025-05-01", "2025-05-02", "2025-05-05", "2025-05-06"
]))
s.rolling("3D").sum()

The results are 5, 8, 4 and 10. Unlike rolling(3), the string "3D" defines a span of three days. The number of rows inside that span can vary. Sort the DatetimeIndex first; time windows require monotonic times. A DataFrame can use a sorted datetime column with on="date".

Test the boundary before adding

By default the window includes the current time and excludes the instant exactly three days earlier. At May 5 midnight, the interval is after May 2 midnight through May 5 midnight. The May 2 reading is exactly on the excluded boundary, so only May 5's value of 4 contributes.

At May 6, the window begins just after May 3 and includes values 4 and 6, totaling 10. With closed="both", May 5's window also includes May 2 and totals 7. A boundary choice can change a result even when the data and duration stay the same.

A two-row window on this series gives NaN, 8, 7 and 10. At May 5 it includes the previous row from May 2. That answers "the last two readings", a different question from "the last three days".

Set an evidence requirement

A time window's min_periods defaults to 1. The first observation can therefore produce a result immediately. s.rolling("3D").count() gives 1, 2, 1 and 2 readings, exposing the changing support for the result. A mean based on one reading and a mean based on many are both valid calculations but have different data support.

With min_periods=2, the sums are NaN, 8, NaN, 10. The duration has not changed; the calculation now requires at least two non-missing observations inside that duration. A wider time interval does not itself guarantee more observations.

Use fixed lengths and separate series

Time-based rolling requires a fixed duration such as "30min", "24h" or "7D". A month-end rule such as "1ME" has variable length and cannot define this kind of rolling window. Use a stated fixed length such as "30D" if that is the intended rule, or aggregate into monthly bins first when the question concerns calendar months.

For separate customer histories, df.groupby("customer").rolling("30D", on="date")["amount"].sum() keeps each customer's transactions together. Times must be ordered within each customer. The result adds the group key as an index level, preserving which series each window describes.

Key Takeaways

  • Time windows select by duration and can contain different numbers of rows.
  • The default left edge is excluded and the current time is included.
  • Time-window min_periods defaults to 1.
  • Calendar months cannot serve as fixed rolling durations.

5.3 Track running totals, peaks and exponentially weighted averages

Some questions need the whole history: "How much has accumulated?" or "What is the highest value so far?" Others should favor recent observations without abruptly discarding the past. Expanding and exponentially weighted calculations serve these different aims.

Grow the history one row at a time

For values 4, 8, 6, 10 and 12, s.expanding().mean() gives 4, 6, 6, 7 and 8. The fourth result uses all four values: their total is 28 and their mean is 7. expanding().sum() gives 4, 12, 18, 28 and 40, matching cumsum() on this complete series. expanding().max() gives 4, 8, 8, 10 and 12, matching cummax().

Expanding windows default to one required observation. They keep every earlier observation with equal weight. Late in a long series, a new value contributes only a small part of that entire history, so a running mean reacts slowly.

Running totals can restart at a boundary:

ytd = df.groupby(df.index.year)["sales"].cumsum()
by_store = df.groupby("store")["sales"].cumsum()

Sort within the relevant history before accumulating. A plain cumulative sum over interleaved stores mixes them.

Measure distance below a running peak

For 100, 120, 90, 110 and 130, cummax() gives 100, 120, 120, 120 and 130. Compute drawdown with s / s.cummax() - 1. At 90 the reference is the earlier peak of 120, so the drawdown is −0.25, or 25% below the peak. Comparing 90 with the first value of 100 would answer a different question. A new peak has drawdown zero.

Give recent values more weight

An exponentially weighted moving average, or EWMA, reduces the weight of older values by a constant factor. The smoothing factor follows α=2/(span+1), so span=3 gives 0.5. For adjust=False, the update is:

yt=αxt+(1−α)yt−1

Here, xt is the new observation, yt−1 is the previous smoothed value and yt is the updated result. The first smoothed value equals the first observation. Then each new value receives weight 0.5 and the previous smoothed value receives weight 0.5:

s.ewm(span=3, adjust=False).mean()

For 4, 8, 6, 10 and 12, the results are 4, 6, 6, 8 and 10. The final update averages the new observation 12 and the previous smoothed value 8. It does not average only the two most recent raw observations.

Read the adjustment setting

The default adjust=True normalizes by the sum of available weights. For the same data, the early results are 4, about 6.667, 6.286, 8.267 and 10.194. This is a different startup calculation from the recursive form; the difference fades as more history becomes available.

Choose one decay argument: span, alpha, com or halflife. An EWMA has no hard cutoff where an old row suddenly disappears. A rolling mean has that cutoff, while an expanding mean keeps all earlier values equally weighted. Choose according to how much influence recent changes should have.

Key Takeaways

  • Expanding windows use the entire history to each row.
  • Group cumulative calculations when histories must restart.
  • Drawdown compares a value with its running peak.
  • EWMA weights decay gradually with age.
  • adjust=False uses the previous smoothed value in its recursive update.
  • The default adjustment changes early weighted means.
6

Handle time zones and daylight saving time

A local clock reading identifies a moment only when its time zone is known. Attach the recording zone, convert actual moments between zones and keep calculations aligned in UTC. Local reports then need the right calendar boundaries, including the hours that daylight saving changes skip or repeat. Separating localization from conversion makes it possible to combine records from several locations without silently shifting their meaning.

6.1 Localize timestamps and convert between time zones

A clock reading needs a location to identify a moment. "2025-07-01 09:00" is a naive timestamp. Adding "Europe/Amsterdam" makes it aware: in July it has offset +02:00 and represents 07:00 UTC. UTC is the shared time reference and has no daylight saving clock changes.

Attach the recording zone
local = pd.Timestamp("2025-07-01 09:00")
aware = local.tz_localize("Europe/Amsterdam")

tz_localize() keeps 09:00 and assigns its zone meaning. Use the location where the clock reading was recorded. Calling the same reading UTC would describe a different moment, two hours later in this example.

Use IANA names such as "Europe/Amsterdam", "America/New_York" and "Asia/Kolkata". Named regional zones retain historical offset and daylight saving rules. A fixed +01:00 offset does not. Short names are inconsistent: "EST" is fixed at −05:00, while "CET" follows seasonal changes. pandas 3 uses Python's zoneinfo representation.

Convert an already identified moment

aware.tz_convert("UTC") gives 07:00 UTC. Converting that same instant to New York gives 03:00, to Kolkata 12:30, to Tokyo 16:00 and to Sydney 17:00 on July 1. The clock changes, but elapsed-time identity stays the same.

OperationRequired inputWhat stays fixed
tz_localize(zone)Naive timeLocal clock reading
tz_convert(zone)Aware timeActual moment

Converting a naive value raises an error because no starting zone is known. Localizing an aware value also raises an error; use conversion for that operation. On columns, use .dt.tz_localize() and .dt.tz_convert(). On indexes use the methods directly, or the DataFrame's index-oriented methods.

Read offset-bearing text and combine zones

A trailing "Z" in ISO text means UTC. For text carrying different offsets, parse with utc=True:

raw = pd.Series(["2025-03-01 10:00+01:00",
                 "2025-04-01 10:00+02:00"])
common = pd.to_datetime(raw, utc=True)

These become 09:00 and 08:00 UTC. Convert afterward to a regional zone for local reporting. Mixed-offset parsing without utc=True raises in pandas 3.

Two aware series align by actual moments even when their zone labels differ. Amsterdam readings at 09:00 and 10:00 and London readings at 08:00 and 09:00 in July refer to matching instants. Their arithmetic result uses UTC labels. Ordering comparisons between naive and aware timestamps fail; equality returns false.

Remove a zone deliberately

tz_convert(None) first expresses an aware value in UTC and then removes the zone. Amsterdam's 09:00 becomes naive 07:00 in July. tz_localize(None) removes the zone while keeping naive 09:00. These are different outputs. Keep calculation data aware in UTC, then convert before reporting or grouping by a local day.

Key Takeaways

  • Localize naive clock readings in their recording zone.
  • Convert aware timestamps to display the same moment elsewhere.
  • Regional IANA names retain daylight saving rules.
  • Parse mixed offsets into a common UTC representation.
  • Removing a zone can preserve UTC time or local clock time.

6.2 Handle daylight saving time in time series

Local clocks can skip or repeat an hour. Those changes affect parsing, elapsed durations and daily totals. Named time zones let pandas distinguish the actual moments behind local clock readings.

Recognize a skipped local time

Amsterdam advanced from 02:00 to 03:00 on March 30, 2025. The clock reading 02:30 never occurred. Localizing it without a policy raises an error:

pd.Timestamp("2025-03-30 02:30").tz_localize(
    "Europe/Amsterdam", nonexistent="shift_forward"
)

The result is 03:00+02:00, the first valid time after the gap. nonexistent="NaT" preserves the problem as missing. "shift_backward" moves to the last valid moment before the gap. Shifting a record changes its timestamp, so retain the original input and document the chosen policy in the analysis.

Distinguish two repeated readings

On October 26, 2025 Amsterdam moved from 03:00 back to 02:00. The local reading 02:30 occurred once with offset +02:00 and once with +01:00. They are an hour apart in UTC.

ambiguous=True selects the daylight saving occurrence; ambiguous=False selects standard time. ambiguous="NaT" marks an unresolved reading as missing. A boolean list can choose per row. ambiguous="infer" can use an ordered sequence containing the repeated hour, such as 01:30, 02:00, 02:30, 02:00, 02:30, 03:00. A lone repeated clock reading does not provide enough ordering evidence to infer which occurrence was intended.

Count hours in the local day

The spring transition day has 23 hours and the autumn transition day has 25. A complete hourly series resampled to local days therefore has varying counts. To report average hourly energy, divide a daily sum by the actual number of valid hourly readings, or take their mean. Dividing every total by 24 misstates the transition days.

A partial dataset can also create short bins. A UTC series starting March 29 at 00:00 begins in Amsterdam at 01:00, so its first local day lacks midnight even before the transition day. Check the reporting boundaries as well as the zone rules.

Distinguish same clock time from equal elapsed time
t = pd.Timestamp("2025-03-29 12:00", tz="Europe/Amsterdam")
t + pd.offsets.Day(1)       # March 30 at 12:00
t + pd.Timedelta(days=1)    # March 30 at 13:00

The calendar offset preserves noon and crosses only 23 elapsed hours. The Timedelta adds 24 hours. In autumn, a local calendar day can instead contain 25 elapsed hours.

Choose the reporting zone before grouping

Resampling follows the index's zone. To total use by Amsterdam's local date, first convert an aware UTC index to "Europe/Amsterdam", then resample to "D". Resampling in UTC first creates different day boundaries that cannot be repaired merely by relabeling the result.

Transition dates differ by region: New York changed on March 9 and November 2 in 2025; Sydney ended summer time on April 6 and began it on October 5. Tokyo and Kolkata have no daylight saving changes. Keep raw moments in UTC and use the named local zone for calendar reports.

Key Takeaways

  • Skipped and repeated hours need explicit localization policies.
  • An ambiguous local time can refer to two distinct UTC moments.
  • Complete local days can contain 23 or 25 hours.
  • Convert to the reporting zone before grouping by local day.
  • Distinguish clock changes from incomplete data coverage.

Time Series with pandas examples

6 Walkthroughs

1

Turn a text log into usable dates

11 steps
Parse a small log, inspect failures and calculate durations without losing the time units.
  1. 1
    IntroductionStart with a date column

    A meter log arrives with dates written as text. You need dates pandas can compare, elapsed time between records and readable labels for a report. That means a Timestamp for each moment, a Timedelta for elapsed time and formatted text only at the end.

    Work through the conversion before calculating. A string can look like a date while still lacking calendar behavior, and an ambiguous format can produce a plausible date with the wrong meaning.

  2. 2
    ExampleRead the source convention

    The log documents day-first dates and a 24-hour clock. One entry is "14/03/2025 18:45". Its format is:

    format="%d/%m/%Y %H:%M"
    

    Read the codes in order: day, month, year, hour and minute. %m and %M differ by case because they represent different fields. The date is March 14; the time is 18:45.

  3. 3
    ExplanationApply one known format

    Convert the column with the documented format:

    parsed = pd.to_datetime(raw,
        format="%d/%m/%Y %H:%M", errors="coerce")
    

    The format applies to every non-missing entry. Coercion turns invalid inputs into NaT. Without a known format, pandas infers from the first non-missing row; that makes a source containing ambiguous slashed dates vulnerable to the wrong interpretation.

  4. 4
    ChallengeCount actual failures

    A column contains "14/03/2025 18:45", "bad" and None. After coercion, two results are NaT. How many present values failed parsing, and what mask finds them?

    ---reveal---

    One present value failed: "bad". Use parsed.isna() & raw.notna(). The original None was already missing, so counting every NaT would overstate conversion failures.

  5. 5
    ExplanationIdentify moments and durations

    A Timestamp represents one moment, and a datetime column holds many moments. A Timedelta represents elapsed time.

    Subtract a start Timestamp from an end Timestamp to obtain a duration. Add a Timedelta to move a moment by a fixed amount. NaT marks missing times, so arithmetic involving a missing start or end also produces a missing duration.

  6. 6
    ExampleSubtract the two moments

    Take these timestamps:

    start = pd.Timestamp("2025-03-10 18:00")
    end = pd.Timestamp("2025-03-14 09:30")
    elapsed = end - start
    

    The result is 3 days 15:30. First count the three whole days, then the remaining 15 hours 30 minutes. Keeping this as a Timedelta lets the next step choose the desired reporting unit without discarding that remainder.

  7. 7
    ExampleConvert the full duration to hours

    Three days contribute 72 hours. The remainder contributes 15.5 hours, so the total is 87.5 hours.

    hours = elapsed / pd.Timedelta(hours=1)
    

    elapsed.days gives only 3. It describes the whole-day component, so multiplying that component by 24 would lose 15.5 hours. total_seconds() gives 315,000 and also includes the complete duration.

  8. 8
    ChallengeChoose a useful calendar feature

    A report needs average kWh by weekday across many weeks. Which calendar feature can supply the grouping key, and does a column need .dt?

    ---reveal---

    Use df["timestamp"].dt.day_name() as the grouping key, then average kwh. A datetime column needs .dt; a DatetimeIndex exposes day_name() directly. This groups recurring weekdays, not separate calendar dates.

  9. 9
    TipCheck units before integer export

    A datetime dtype such as datetime64[us] stores counts in microseconds. A direct integer cast returns those counts, even if older code expects nanoseconds.

    For a column, explicitly request s.dt.as_unit("ns").astype("int64") when nanosecond counts are required. For duration comparisons, keep Timedelta arithmetic so the unit travels with the value.

  10. 10
    ChallengePrepare the final report

    You have parsed dates and computed elapsed hours. The report needs dates such as "14 Mar 2025". Should you format the date column before calculating, and what expression produces the label?

    ---reveal---

    Finish datetime calculations first, then use parsed.dt.strftime("%d %b %Y") for display labels. Formatting returns text. The abbreviated month name follows the system locale; this example uses English.

  11. 11
    RecapChoose representation by purpose

    Use datetime values for calendar selection and arithmetic, Timedeltas for elapsed lengths and formatted strings for final labels. Parsing is the point where source conventions become calendar meaning.

    Before passing a converted column onward, inspect failed non-missing inputs, its dtype and any required output units. That gives later analysis a clear time representation to work with.

2

Build a clean history and compare the right dates

13 steps
Check an event index, select reporting intervals and distinguish row shifts from calendar comparisons.

Save this lesson to follow every Walkthrough step by step.

  1. 1
    IntroductionGive every row a clear time identity
  2. 2
    ExampleParse, index and sort
  3. 3
    TipTreat repeated events by meaning
  4. 4
    ChallengeExpose an absent date
  5. 5
    ExplanationRead the precision of a selector
  6. 6
    ChallengeSelect a recurring work window
  7. 7
    ExamplePlace the predecessor beside each value
  8. 8
    Key InsightA growth fraction is not a percentage label
  9. 9
    ExplanationMove dates for a calendar comparison
  10. 10
    ChallengeTrace label alignment
  11. 11
    ConnectionKeep each store’s predecessor separate
  12. 12
    ChallengeRepair a daily growth report
  13. 13
    RecapCheck labels before interpreting change
3

Build calendars and label complete spans

12 steps
Generate ranges, move calendar dates and interpret period labels including fiscal quarters.

Save this lesson to follow every Walkthrough step by step.

  1. 1
    IntroductionDecide what a date should represent
  2. 2
    ExampleGenerate month-end dates
  3. 3
    TipMatch the alias to the calendar
  4. 4
    ChallengeCount a range’s endpoints
  5. 5
    ExampleMove one month from January 31
  6. 6
    ExplanationUnderstand an already matched anchor
  7. 7
    ChallengeChoose business time or elapsed time
  8. 8
    ExplanationGive monthly totals span labels
  9. 9
    ExampleConvert a date to a month and back
  10. 10
    ChallengeRead a fiscal label
  11. 11
    ChallengeChoose monthly moments or monthly spans
  12. 12
    RecapLet the calendar rule carry the meaning
4

Make daily and monthly reports from observations

13 steps
Choose aggregations, trace bin boundaries, fill a finer grid and keep separate series grouped.

Save this lesson to follow every Walkthrough step by step.

  1. 1
    IntroductionAsk what the new frequency should mean
  2. 2
    ExampleAggregate two quantities differently
  3. 3
    ExplanationTrace a five-minute bin
  4. 4
    ChallengeMove the boundary observation
  5. 5
    TipAudit empty and partial bins
  6. 6
    ExampleCreate a finer daily grid
  7. 7
    ExampleFill one gap in three ways
  8. 8
    ChallengePreserve what was known
  9. 9
    Key InsightA sampled point is not a total
  10. 10
    ExampleKeep stores in their own bins
  11. 11
    ChallengeExplain an empty store-month
  12. 12
    ChallengeRepair a comparison table
  13. 13
    RecapRead what each output row claims
5

Follow the observations inside each window

16 steps
Compare row windows, elapsed-time windows, running history and exponential weighting.

Save this lesson to follow every Walkthrough step by step.

  1. 1
    IntroductionChoose how much history matters
  2. 2
    ExampleBuild the first full row window
  3. 3
    ExampleSlide forward one row
  4. 4
    ChallengeCount valid values inside positions
  5. 5
    TipPlace results in time honestly
  6. 6
    ExplanationUse elapsed time for irregular events
  7. 7
    ExampleTrace the excluded left edge
  8. 8
    ChallengeChange the boundary and the evidence count
  9. 9
    Key InsightA month is not a rolling duration
  10. 10
    ExampleRetain the whole history
  11. 11
    ExampleCompare a value with its earlier peak
  12. 12
    ExampleUpdate a recursive weighted mean
  13. 13
    ExplanationRead the adjustment setting
  14. 14
    ChallengeDistinguish weighting from a recent pair
  15. 15
    ChallengeSelect a report’s memory
  16. 16
    RecapIdentify the window before reading the result
6

Match moments across zones and clock changes

12 steps
Localize and convert timestamps, resolve skipped or repeated hours and form correct local-day bins.

Save this lesson to follow every Walkthrough step by step.

  1. 1
    IntroductionSeparate the clock from the moment
  2. 2
    ExplanationLocalize a naive reading
  3. 3
    ExampleConvert one event to several clocks
  4. 4
    TipRetain regional rules
  5. 5
    ChallengeChoose how a zone is removed
  6. 6
    ExplanationHandle a skipped spring reading
  7. 7
    ExplanationSeparate two autumn occurrences
  8. 8
    ChallengeResolve the repeated clock reading
  9. 9
    ExampleCount a complete local day
  10. 10
    ExampleDistinguish tomorrow’s noon from 24 hours
  11. 11
    ChallengeRepair the local daily report
  12. 12
    RecapKeep the instant and reporting boundary clear

Time Series with pandas practice questions

50 Practice Questions in 5 sets

Set 1: Parse, select and summarize dates10 Practice Questions

Question 1 of 10

A task starts at "2025-08-12 22:15" and ends at "2025-08-14 04:45". How many elapsed hours does timestamp subtraction give?
  1. 24.0 hours
  2. 6.5 hours
  3. 30.5 hours
  4. 54.5 hours
Show answerHide answer
Answer: C
The duration is one whole day plus 6 hours 30 minutes, totaling 30.5 hours. Reading only the Timedelta’s .days component would keep 24 hours and lose the remaining 6.5 hours. Divide the duration by pd.Timedelta(hours=1) to include the full length.

Question 2 of 10

A source defines "07/11/2025 16:20" as November 7 at 16:20. What explicit format parses that convention?
  1. "%m/%d/%Y %H:%M"
  2. "%d/%m/%Y %H:%M"
  3. "%d/%m/%Y %I:%M"
  4. "%Y/%m/%d %H:%M"
Show answerHide answer
Answer: B
The source puts day before month, so %d/%m/%Y preserves November 7. %H:%M reads the 24-hour time and minutes. Month-first parsing would produce July 11, a valid date with the wrong meaning, which is why a successful conversion alone is not enough.

Question 3 of 10

An index contains May 3, May 1 and May 2, 2025, once each. The dates are already parsed. What prepares it for reliable date-range selection?
  1. Sort the index into chronological order.
  2. Drop repeated labels before selecting.
  3. Replace the index with month numbers.
  4. Forward-fill the numeric observations.
Show answerHide answer
Answer: A
The labels are datetime values and unique, but they arrive out of order. sort_index() establishes the chronological order needed for dependable time selection. Removing duplicates would not address the problem because the stated index contains no repeated timestamp.

Question 4 of 10

A sorted index has one hourly row throughout July 8, 2025. How many rows does .loc["2025-07-08 09:00":"2025-07-08 12:00"] select?
  1. 3 rows
  2. 12 rows
  3. 24 rows
  4. 4 rows
Show answerHide answer
Answer: D
The label slice includes 09:00, 10:00, 11:00 and 12:00, for four rows. Treating the end like an .iloc stop would omit 12:00 and give three. Datetime label slicing includes both endpoints when matching rows are present.

Question 5 of 10

What is the first timestamp in pd.date_range("2025-08-19", periods=3, freq="MS")?
  1. August 1, 2025
  2. September 1, 2025
  3. August 19, 2025
  4. August 31, 2025
Show answerHide answer
Answer: B
The MS rule requires month starts, so the first matching date on or after August 19 is September 1. August 1 is already before the supplied start. The range does not include August 19 merely because that date was passed as the starting boundary.

Question 6 of 10

A reminder is dated January 30, 2025 and must move one calendar month later. What date does pd.DateOffset(months=1) produce?
  1. March 1, 2025
  2. March 2, 2025
  3. February 28, 2025
  4. February 27, 2025
Show answerHide answer
Answer: C
Moving to February preserves day 30 only if that day exists. February 2025 ends on day 28, so the offset clips to February 28. Adding 30 elapsed days would reach March 1, but a fixed duration is a different instruction from one calendar month.

Question 7 of 10

A sensor records temperatures of 18, 24 and 21 °C during one day. The report asks for the mean of the recorded temperatures. What daily result should resampling give?
  1. 21 °C
  2. 63 °C
  3. 24 °C
  4. 18 °C
Show answerHide answer
Answer: A
The mean is the sum of the three observations divided by three, giving 21 °C. Summing to 63 treats a temperature level as an additive quantity and does not answer the requested question. A maximum of 24 would report the peak instead of the mean.

Question 8 of 10

A device setting is 12 on September 1 and changes to 18 on September 4. It holds between recorded changes. What daily values should a filled grid show for September 1–4?
  1. 12, 14, 16, 18
  2. 12, 18, 18, 18
  3. 12, 0, 0, 18
  4. 12, 12, 12, 18
Show answerHide answer
Answer: D
Forward fill carries the last known setting until the next change, producing 12 on September 2 and 3. Linear interpolation would create intermediate settings of 14 and 16, but the stated device does not gradually change between records. The quantity’s meaning selects the fill method.

Question 9 of 10

For s = pd.Series([3, 9, 6, 12]), what is the final value of s.rolling(3).mean()?
  1. 6.0
  2. 9.0
  3. 7.5
  4. 12.0
Show answerHide answer
Answer: B
The final three-row window contains 9, 6 and 12, whose mean is 9. The value 7.5 averages the entire four-row history, which would fit an expanding mean. A rolling window drops 3 when the fourth observation enters the window.

Question 10 of 10

A naive clock reading is "2025-07-15 10:00" recorded in Amsterdam. A UTC log has events at 08:00 and 10:00. Which event matches the reading, and how should the reading be prepared?
  1. Localize to Amsterdam, convert to UTC; match 08:00.
  2. Localize to UTC, convert to Amsterdam; match 10:00.
  3. Localize to Amsterdam, remove its zone; match 10:00.
  4. Localize to UTC, remove its zone; match 08:00.
Show answerHide answer
Answer: A
Amsterdam is two hours ahead of UTC in July, so its 10:00 identifies 08:00 UTC. First localization assigns the recording location; conversion then preserves that actual moment in UTC. Localizing the original clock reading as UTC would identify a different event two hours later.
Set 2: Compare histories and window results10 Practice Questions

Save this lesson to see every answer and practice every set.

Question 1 of 10

What does .dt.day_of_week return for a Sunday observation?
  1. 0
  2. 1
  3. 7
  4. 6
Show answerHide answer

Save this lesson to see this answer.

Question 2 of 10

Visits are 320 on Monday, 400 on Tuesday and 360 on Wednesday. What does Wednesday’s pct_change() report?
  1. −0.10
  2. −0.125
  3. −40.0
  4. 0.90
Show answerHide answer

Save this lesson to see this answer.

Question 3 of 10

An organization uses fiscal quarters with freq="Q-SEP". What span does pd.Period("2026Q1", freq="Q-SEP") describe?
  1. January–March 2026
  2. July–September 2026
  3. October–December 2025
  4. October–December 2026
Show answerHide answer

Save this lesson to see this answer.

Question 4 of 10

An event series has 5 at 10:00, 8 at 10:05 and 2 at 10:09. What is the 10:05-labeled value of resample("5min", closed="right", label="right").sum()?
  1. 13
  2. 8
  3. 10
  4. 15
Show answerHide answer

Save this lesson to see this answer.

Question 5 of 10

Sensor A reports 6 in January and 9 in March; sensor B reports 4 in February. A table must show every sensor for January–March and retain unobserved months as missing. What workflow fits?
  1. Resample all sensors monthly, then copy totals into each sensor column.
  2. Group by month number only, then forward-fill the missing sensor labels.
  3. Aggregate by sensor and month, then reindex the full sensor-month grid.
  4. Resample per sensor with default sums, then replace every zero with missing.
Show answerHide answer

Save this lesson to see this answer.

Question 6 of 10

For [2, NaN, 8, 14], what are the last two results of rolling(3, min_periods=2).mean()?
  1. 5.0 and 11.0
  2. NaN and NaN
  3. 6.0 and 12.0
  4. 5.0 and 8.0
Show answerHide answer

Save this lesson to see this answer.

Question 7 of 10

Values are 7 on April 1, 4 on April 3 and 9 on April 6, all at midnight. What is the April 6 value of rolling("3D").sum()?
  1. 13
  2. 9
  3. 20
  4. 11
Show answerHide answer

Save this lesson to see this answer.

Question 8 of 10

A series is 80, 100, 75 and 90. What drawdown does the fourth row have using s / s.cummax() - 1?
  1. 0.20
  2. −0.25
  3. 0.125
  4. −0.10
Show answerHide answer

Save this lesson to see this answer.

Question 9 of 10

A logger emits "2025-01-10 09:00+01:00" and "2025-07-10 09:00+02:00". The report must preserve instants and show Amsterdam clock time. What sequence fits pandas 3?
  1. Parse with utc=True, then convert to Europe/Amsterdam.
  2. Parse with utc=True, then localize to Europe/Amsterdam.
  3. Strip the offsets, then localize both readings to UTC.
  4. Parse without offsets, then convert the naive results to Amsterdam.
Show answerHide answer

Save this lesson to see this answer.

Question 10 of 10

An Amsterdam meter records 2 kWh in each actual hour of October 26, 2025, with complete coverage. What daily total and average hourly use should its local-day report show?
  1. 48 kWh and 2 kWh per recorded hour
  2. 50 kWh and about 2.083 kWh per recorded hour
  3. 50 kWh and 2 kWh per recorded hour
  4. 46 kWh and 2 kWh per recorded hour
Show answerHide answer

Save this lesson to see this answer.

Set 3: Choose the right calendar operation10 Practice Questions

Save this lesson to see every answer and practice every set.

Question 1 of 10

A column is ["2025-09-02", None, "2025-15-02", "bad"]. It is parsed with format="%Y-%m-%d", errors="coerce". How many present source values failed conversion?
  1. 1
  2. 2
  3. 3
  4. 4
Show answerHide answer

Save this lesson to see this answer.

Question 2 of 10

A sorted daily index contains November 2, 3 and 5, 2025. What does a complete November 1–6 calendar reveal that a min-to-max calendar does not?
  1. The internal missing date November 4.
  2. The repeated date November 3.
  3. The weekday names of November 2 and 5.
  4. The absent boundary dates November 1 and 6.
Show answerHide answer

Save this lesson to see this answer.

Question 3 of 10

A full hourly index covers December 1–3, 2025. How many rows are selected by .loc["2025-12-02":"2025-12-03"]?
  1. 48 rows
  2. 25 rows
  3. 24 rows
  4. 47 rows
Show answerHide answer

Save this lesson to see this answer.

Question 4 of 10

How many timestamps are in pd.date_range("2025-06-10", "2025-06-11", freq="6h") with default endpoint inclusion?
  1. 4
  2. 6
  3. 5
  4. 24
Show answerHide answer

Save this lesson to see this answer.

Question 5 of 10

Two readings occur on February 4 at 08:00 and February 21 at 17:00, 2025. Their index is converted to monthly periods and then back with .to_timestamp(). What happens to labels and uniqueness?
  1. Both retain their original days, so labels stay unique.
  2. Both become February 28 at midnight, so labels repeat.
  3. They become February 1 and March 1, so labels stay unique.
  4. Both become February 1 at midnight, so labels repeat.
Show answerHide answer

Save this lesson to see this answer.

Question 6 of 10

Water-level readings are 20 cm on May 10 and 32 cm on May 13. After a daily grid and linear interpolation, what is the May 12 estimate?
  1. 28 cm
  2. 24 cm
  3. 26 cm
  4. 32 cm
Show answerHide answer

Save this lesson to see this answer.

Question 7 of 10

A store has sales of 14 in January and 22 in March, with no February row. How do default monthly sums differ between per-store resampling and grouping by store plus pd.Grouper?
  1. Per-store resampling omits February; the Grouper form includes it as 0.
  2. Per-store resampling includes February as 0; the Grouper form omits it.
  3. Both include February as NaN because no sale was observed.
  4. Both include February as 18 by interpolating between sales.
Show answerHide answer

Save this lesson to see this answer.

Question 8 of 10

Orders have amounts 8 at June 1 12:00, 12 at June 2 12:00 and 20 at June 4 12:00. What are the final sum and count for rolling("3D", min_periods=2) with default boundaries?
  1. 32 and 2
  2. 40 and 3
  3. 20 and 1
  4. NaN and 1
Show answerHide answer

Save this lesson to see this answer.

Question 9 of 10

For readings 6, 18 and 10, what are the third recursive EWMA and third expanding mean, using ewm(span=3, adjust=False)?
  1. 14 and about 11.333
  2. 11 and 12
  3. 11 and about 11.333
  4. 10 and about 11.333
Show answerHide answer

Save this lesson to see this answer.

Question 10 of 10

Two Amsterdam records both read October 26, 2025 at 02:15. One is localized with ambiguous=True, the other with ambiguous=False. What is their separation after conversion to UTC?
  1. They identify the same moment in UTC.
  2. The first is 1 hour earlier than the second.
  3. The first is 1 hour later than the second.
  4. The first is 2 hours earlier than the second.
Show answerHide answer

Save this lesson to see this answer.

Set 4: Diagnose time-series calculations10 Practice Questions

Save this lesson to see every answer and practice every set.

Question 1 of 10

A parsed column has dtype datetime64[us]. An export requires nanosecond integer counts. What column expression supplies those counts?
  1. s.dt.as_unit("ns").astype("int64")
  2. s.astype("int64")
  3. s.dt.as_unit("ms").astype("int64")
  4. s.dt.normalize().astype("int64")
Show answerHide answer

Save this lesson to see this answer.

Question 2 of 10

A UTC event count is stored as the Unix value 1735689600000 in milliseconds. What parsing arguments preserve its stated unit and zone?
  1. unit="s", utc=True
  2. unit="us", utc=True
  3. unit="ms", utc=True
  4. unit="ms", utc=False
Show answerHide answer

Save this lesson to see this answer.

Question 3 of 10

Series a has 30 on August 1 and 50 on August 4. You compute a - a.shift(1, freq="D"). What values appear on August 2 and August 4?
  1. −30 and 20
  2. 30 and 50
  3. NaN and 20
  4. NaN and NaN
Show answerHide answer

Save this lesson to see this answer.

Question 4 of 10

For a timestamp already on April 30, 2025, what do adding MonthEnd(0) and adding MonthEnd(1) produce, respectively?
  1. May 31 and May 31
  2. April 30 and May 31
  3. April 30 and April 30
  4. April 30 and May 30
Show answerHide answer

Save this lesson to see this answer.

Question 5 of 10

A May 2025 monthly period is converted with .asfreq("D", how="end") and then .to_timestamp() with defaults. What timestamp results?
  1. May 31, 2025 at 00:00
  2. May 1, 2025 at 00:00
  3. May 31, 2025 at 23:59:59.999999
  4. June 1, 2025 at 00:00
Show answerHide answer

Save this lesson to see this answer.

Question 6 of 10

A range uses freq="B" from Friday, September 12 through Monday, September 15, 2025. No custom holidays are supplied. What dates appear?
  1. September 12, 13, 14 and 15
  2. September 13 and September 14
  3. September 12 only
  4. September 12 and September 15
Show answerHide answer

Save this lesson to see this answer.

Question 7 of 10

Hourly kWh observations cover a complete day, including its midnight reading. A daily report uses asfreq("D"). Why is the reported value not the daily energy total?
  1. It sums all hourly readings but labels the result at midnight.
  2. It takes the daily mean and multiplies it by the observed count.
  3. It selects the midnight reading and omits the other hourly contributions.
  4. It fills missing hours before selecting the largest hourly reading.
Show answerHide answer

Save this lesson to see this answer.

Question 8 of 10

Daily values on June 1–7 are 2, 6, 10, 14, 18, 22 and 26. What are the results labeled June 4 for rolling(5, center=True).mean() and default trailing rolling(5).mean(), respectively?
  1. 10 and 10
  2. 14 and NaN
  3. 14 and 10
  4. 18 and 14
Show answerHide answer

Save this lesson to see this answer.

Question 9 of 10

A table is sorted by store and date. Store A has sales 5, 7; store B has sales 20, 3. The report needs within-store running totals and within-store recent two-row totals. What are B’s two output sequences?
  1. Running: 32, 35; recent: 27, 23.
  2. Running: 20, 23; recent: 20, 23.
  3. Running: 12, 35; recent: NaN, 35.
  4. Running: 20, 23; recent: NaN, 23.
Show answerHide answer

Save this lesson to see this answer.

Question 10 of 10

An aware timestamp is July 20, 2025 at 14:00 in Amsterdam. What naive clocks result from tz_convert(None) and tz_localize(None), in that order?
  1. 12:00 and 14:00
  2. 14:00 and 12:00
  3. 14:00 and 14:00
  4. 16:00 and 14:00
Show answerHide answer

Save this lesson to see this answer.

Set 5: Combine time rules in complete analyses10 Practice Questions

Save this lesson to see every answer and practice every set.

Question 1 of 10

A source sends ["2025-12-02", "03/12/2025"] and documents day-first slashed dates. What interpretation should cleaning preserve?
  1. December 2 and March 12, 2025
  2. February 12 and December 3, 2025
  3. February 12 and March 12, 2025
  4. December 2 and December 3, 2025
Show answerHide answer

Save this lesson to see this answer.

Question 2 of 10

Two valid transactions share an exact timestamp and have amounts 11 and 17. A report needs one total per timestamp before reindexing. How should the duplicate labels be handled?
  1. Keep the last transaction, giving 17.
  2. Group by timestamp and sum to 28.
  3. Average both transactions, giving 14.
  4. Keep the first transaction, giving 11.
Show answerHide answer

Save this lesson to see this answer.

Question 3 of 10

A sorted hourly UTC table must report Tokyo activity from 09:00 inclusive to 17:00 exclusive on every local date. What operation order and clock-window inclusion fit?
  1. Convert to Tokyo, then use between_time(..., inclusive="left").
  2. Filter UTC 09:00–17:00, then convert the selected rows to Tokyo.
  3. Convert to Tokyo, then use between_time(..., inclusive="both").
  4. Remove the UTC zone, then localize to Tokyo and include both ends.
Show answerHide answer

Save this lesson to see this answer.

Question 4 of 10

Store A has 40 then 60 units sold; store B has 90 then 72, in chronological order within each store. What are B’s growth fractions with grouped versus plain column-wide pct_change()?
  1. Grouped: 0.50, −0.20; plain: NaN, −0.20.
  2. Grouped: NaN, −0.25; plain: 1.25, −0.25.
  3. Grouped: NaN, −0.20; plain: 0.50, −0.20.
  4. Grouped: 0.50, −0.25; plain: NaN, −0.25.
Show answerHide answer

Save this lesson to see this answer.

Question 5 of 10

A technician schedules an inspection for 08:45 local time in Amsterdam. From October 25, 2025 at 08:45, what operation advances to the next day’s same clock time in pandas 3?
  1. Add pd.Timedelta(hours=24).
  2. Add pd.offsets.Day(1).
  3. Add pd.Timedelta(hours=23).
  4. Add pd.offsets.MonthEnd(0).
Show answerHide answer

Save this lesson to see this answer.

Question 6 of 10

A table spans January 2024 and January 2025 for several stores. The report needs one total per store per distinct calendar month. What grouping keys retain the needed identities?
  1. Store and df["date"].dt.month.
  2. pd.Grouper(key="date", freq="ME") alone.
  3. Store and pd.Grouper(key="date", freq="ME").
  4. Store and df["date"].dt.day_of_week.
Show answerHide answer

Save this lesson to see this answer.

Question 7 of 10

An irregular customer log needs spending in the preceding 30 elapsed days, including the current event and excluding the instant exactly 30 days earlier. What calculation fits after sorting within customers?
  1. df.groupby("customer").rolling("30D", on="date")["amount"].sum()
  2. df.groupby("customer")["amount"].rolling(30).sum()
  3. df.groupby("customer").rolling("30D", on="date", closed="both")["amount"].sum()
  4. df.rolling("30D", on="date")["amount"].sum()
Show answerHide answer

Save this lesson to see this answer.

Question 8 of 10

An hourly UTC series completely covers Amsterdam’s March 30, 2025 local day. What are that local day’s UTC start, UTC end (exclusive) and number of hours?
  1. March 30 at 00:00; March 31 at 00:00; 24 hours.
  2. March 29 at 22:00; March 30 at 23:00; 25 hours.
  3. March 29 at 23:00; March 30 at 23:00; 24 hours.
  4. March 29 at 23:00; March 30 at 22:00; 23 hours.
Show answerHide answer

Save this lesson to see this answer.

Question 9 of 10

A daily energy series has readings 4 on October 2 and 7 on October 4, 2025. Missing records indicate unknown use. What should October 3 contain after resample("D").sum(min_count=1)?
  1. 0 kWh
  2. 5.5 kWh
  3. NaN
  4. 4 kWh
Show answerHide answer

Save this lesson to see this answer.

Question 10 of 10

Rainfall observations cover every day from July 1 through August 10, 2025. Monthly sums are 93 mm and 50 mm. What are the means per observed day, and what can be concluded about August’s full-month total?
  1. July: 3 mm; August: 5 mm; full August must be higher.
  2. July: 3 mm; August: 5 mm; full August remains unknown.
  3. July: 3 mm; August: about 1.61 mm; full August is lower.
  4. July: 93 mm; August: 50 mm; full August remains unknown.
Show answerHide answer

Save this lesson to see this answer.

Stuck on your own question? Homework Help

Key Terms

85 Key Terms, each with its definition

A

Ambiguous local time
An ambiguous local time is a repeated clock reading that can identify two distinct moments with different offsets during a backward clock change.
The ambiguous local time Amsterdam 02:30 on October 26, 2025 has two different UTC meanings.
Anchored frequency
An anchored frequency selects specified calendar points, starting with the first matching point on or after the supplied boundary.
An anchored frequency of month starts begins September 1 when its start boundary is August 19.
asfreq
Frequency conversion with asfreq() places a series on a new grid by selecting matching timestamps, without aggregating intervening observations.
Daily asfreq() on hourly data retains midnight readings without adding the day’s values.
Aware timestamp
An aware timestamp carries time-zone information, identifying an actual moment that can be displayed in another zone without changing the instant.
The aware timestamp Amsterdam 09:00+02:00 identifies the same July event as UTC 07:00.

B

Backward fill
Backward fill replaces missing values with the next known later value, bringing information from the future into earlier empty slots.
Backward fill would apply tomorrow’s balance to a date when that information was unavailable.
Bin closure
Bin closure specifies which boundary of a resampling interval belongs to that interval, deciding where exact-edge observations are counted.
Right bin closure assigns an exact 10:05 reading to the interval ending at that time.
Bin label
A bin label is the timestamp assigned to an aggregated time interval, which identifies the result without determining its membership.
A June 30 bin label can name a quarter containing observations from April only.
Business-day frequency
A standard business-day frequency generates Monday–Friday dates, skipping weekends while requiring explicit custom rules for holidays.
A business-day frequency includes Friday and Monday while skipping the intervening weekend.

C

Calendar day
A calendar day advances the local date while preserving the clock reading, so its elapsed length can change across daylight saving transitions.
Across a spring clock change, one calendar day can move noon through only 23 elapsed hours.
Calendar gap
A calendar gap is an expected time label with no corresponding row, which can be found by comparing observations with a complete calendar.
A calendar gap on Wednesday is absent from a daily index containing only Tuesday and Thursday.
Calendar mask
A calendar mask is a boolean filter built from date parts such as weekday or month, selecting rows that satisfy a recurring calendar condition.
A calendar mask using weekday numbers 5 and 6 keeps weekend readings across the dataset.
Centered window
A centered window places its output at the middle of the contributing observations, so later values can contribute relative to the result label.
A centered window labeled Tuesday can use observations recorded on Wednesday and Thursday.
Clock-time window
A clock-time window selects observations by their time of day across dates, with explicit choices about whether each endpoint is included.
A clock-time window of 22:00–06:00 keeps late-night and early-morning activity on each date.
Coercion
Date coercion replaces inputs that cannot be parsed with NaT, allowing conversion to finish while leaving invalid values marked missing.
After coercion, compare missing results with present source entries to locate failed dates.
Cumulative sum
A cumulative sum adds each new observation to the preceding total, producing a running amount that can restart within separate groups.
Grouping sales by year before a cumulative sum starts a fresh year-to-date amount each January.

D

Date range
A date range is a sequence of datetime labels generated from boundaries, a count and a frequency rule, often used as an expected calendar.
A date range of 24 hourly labels can define the target index for one complete ordinary day.
DateOffset
A DateOffset moves a timestamp according to calendar fields or rules, allowing months and years to vary in their elapsed lengths.
A DateOffset of one month clips January 31 to the final valid day of February.
DatetimeIndex
A DatetimeIndex is a pandas index of datetime row labels that supports calendar selection and provides a basis for time-based calculations.
A DatetimeIndex made from event times allows .loc to select a whole reporting month.
Daylight saving time
Daylight saving time changes local clock offsets seasonally, producing a skipped hour when clocks advance and a repeated hour when they retreat.
Daylight saving time shortens Amsterdam’s spring transition day while UTC continues without a jump.
Downsampling
Downsampling moves from a finer time frequency to a coarser one, usually combining several observations with a chosen aggregation.
Downsampling daily rainfall to monthly sums retains millimeters as the total’s unit.
Drawdown
A drawdown is a value’s fractional fall from its running peak, calculated as the current value divided by that peak, minus one.
A value of 90 after a peak of 120 has drawdown −0.25, meaning it is 25% below that peak.
Duplicate timestamp
A duplicate timestamp is a row label shared by more than one observation, which can represent separate valid events or repeated records.
A duplicate timestamp at 14:00 can label two purchases that both belong in a sales total.

E

Endpoint inclusion
Endpoint inclusion determines whether matching start and end boundaries appear in a generated date range, affecting the number of labels.
Default endpoint inclusion gives 25 hourly labels from one midnight through the next.
Expanding window
An expanding window begins at the first observation and grows through the current row, retaining all earlier valid observations with equal weight.
An expanding window gives a new reading less influence after a long history has accumulated.
Exponentially weighted moving average
An exponentially weighted moving average smooths a series by reducing older observations’ weights by a constant factor with age.
An exponentially weighted moving average with alpha 0.5 updates a prior mean of 6 to 8 for a new 10.

F

Fiscal quarter
A fiscal quarter is a three-month reporting span within a fiscal year defined by an ending month that can differ from December.
Under Q-JUN, the first fiscal quarter of 2025 covers July through September 2024.
Fixed-duration window
A fixed-duration window has one elapsed length, such as 24 hours or 30 days, unlike calendar spans whose lengths depend on their dates.
A fixed-duration window of 30 days cannot be replaced by a variable-length calendar-month rule.
Forward fill
Forward fill replaces missing values with the most recent earlier known value, fitting a state that remains in effect until it changes.
Forward fill carries a device setting through later days until its next recorded change.
Fractional change
A fractional change is the current value divided by its comparison value, minus one, giving growth relative to that earlier value.
A fractional change of −0.15 means the current amount is 15% below its comparison value.
Frequency alias
A frequency alias is a compact string naming a repeat rule for datetime operations, such as hourly intervals or month-end calendar anchors.
The frequency alias "15min" generates quarter-hour labels, while "ME" generates month ends.

G

Grouped resampling
Grouped resampling applies a time-frequency operation separately within each series identity, preserving independent histories in a long table.
Grouped resampling keeps one sensor’s monthly bins separate from neighboring sensors’ readings.

I

IANA zone name
An IANA zone name identifies a regional time-zone rule set, usually in Area/City form, including historical and seasonal offset changes.
The IANA zone name "Europe/Amsterdam" supplies different offsets in winter and summer.
Inclusive label slice
An inclusive label slice retains matching labels at both its start and its end, unlike a position slice that excludes the stop position.
An inclusive label slice from 10:00 through 13:00 keeps four hourly rows when all are present.
Inferred frequency
An inferred frequency is a regular spacing rule detected from observed timestamps, even when the index carries no stored frequency metadata.
An inferred frequency can reveal a monthly pattern in three consecutive month-end timestamps.
ISO 8601
ISO 8601 is a date and time notation that puts year before month and day, making complete dates unambiguous across local conventions.
The ISO 8601 date "2025-08-17" has the same date meaning in day-first and month-first settings.

L

Lag
A lag is an earlier value brought alongside a later observation for comparison, with its distance defined in rows or in calendar time.
A one-row lag can reach several days back when a sensor has gaps between its readings.
Lead
A lead is a later value brought alongside an earlier observation, making the following value available at the current row label.
A lead from shift(-1) lets a table compare each event with the next recorded event.
Linear interpolation
Linear interpolation estimates missing values along a straight line between known values, using equal increments on an equally spaced grid.
Linear interpolation estimates 12 and 14 between daily endpoints of 10 and 16 three days apart.
Local-day bin
A local-day bin groups moments by date in the index’s named zone, so its elapsed length can differ from a UTC day across clock changes.
A local-day bin must be formed after converting an hourly index to the time zone used for reporting.
Localization
Time-zone localization assigns a recording zone to naive clock readings while preserving their displayed local hours and minutes.
Localization assigns Tokyo meaning to a naive 09:00 reading before it is converted to UTC.
Localization policy
A localization policy specifies how skipped or repeated local readings are resolved, such as choosing an occurrence, marking missing or shifting.
Retaining raw clock text lets an analyst inspect readings changed by a shifting localization policy.

M

min_periods
The min_periods argument sets how many valid values a window needs before producing a result, ignoring missing cells for that count.
A three-row window with one missing cell satisfies min_periods=2 but not min_periods=3.
Mixed-format parsing
Mixed-format parsing interprets each date string separately, allowing different layouts in one column after their meanings have been checked.
Mixed-format parsing still needs a known day/month convention for each slashed source date.
Monotonic index
A monotonically increasing time index keeps every timestamp at or after the preceding one, permitting equal labels while preserving order.
A monotonic index can still have two valid purchases sharing the same timestamp.
MonthEnd offset
A MonthEnd offset moves a timestamp to a calendar month-end anchor, with its count controlling whether an existing anchor advances.
A MonthEnd offset with count 0 keeps April 30, while count 1 moves it to May 31.
MultiIndex
A MultiIndex is an index with several label levels, such as store and month, allowing a result to retain more than one grouping identity.
A sales MultiIndex can identify both store A and its January total in the same row label.

N

Naive timestamp
A naive timestamp has no attached time zone, giving a clock reading without identifying where that local reading was recorded.
The naive timestamp "2025-07-01 09:00" needs a location before matching it to a UTC event.
NaT
NaT is pandas’ missing-time marker for datetimes and durations, detected with missing-value checks such as pd.isna().
An invalid date coerced during parsing becomes NaT, alongside any already missing inputs.
Nonexistent local time
A nonexistent local time is a clock reading skipped by a forward offset change, requiring a policy when naive data are localized.
The nonexistent local time Amsterdam 02:30 on March 30, 2025 needs a localization policy.
Normalization
Datetime normalization sets the clock part of a datetime value to midnight while retaining its datetime representation for analysis.
Datetime normalization gives events midnight labels while keeping their values usable as datetimes.

O

OHLC
OHLC is an aggregation returning the first, highest, lowest and last observations in each time bin, named open, high, low and close.
Daily OHLC separates the final sensor reading from the highest reading reached that day.
Open left boundary
An open left boundary excludes observations exactly at the start of a time interval, even when the current right boundary is included.
An open left boundary makes default three-day rolling at May 5 exclude May 2 at midnight.

P

Parsing format
A parsing format specifies how text fields represent date and time components through codes for fields such as year, month and minute.
The parsing format "%d/%m/%Y" reads "08/11/2025" as November 8 instead of August 11.
Partial-string indexing
Partial-string indexing selects all timestamps matching a date string’s precision, such as every observation within one specified month.
Partial-string indexing with "2025-09" keeps hourly observations throughout September.
Period
A Period is a span of time defined by a label and frequency, such as one month or quarter, instead of an individual recorded moment.
A monthly Period can label a March sales total without selecting an arbitrary day inside March.
Period boundary
A period boundary marks the start or final moment of a span and is chosen when converting a period label into a timestamp.
Choosing the start period boundary gives midnight at the beginning of the month.
Period frequency conversion
Period frequency conversion maps a period to another frequency, using a chosen start or end boundary when selecting a finer span.
Period frequency conversion with how="end" can select a month’s final daily period.
PeriodIndex
A PeriodIndex is a pandas index whose labels are periods with a shared frequency, suited to observations describing repeated time spans.
Adding one monthly period to a PeriodIndex advances every label by one whole calendar month.

R

Resampling
Resampling organizes a time series at a new frequency, allowing observations to be summarized into coarser bins or placed on a finer grid.
Resampling hourly energy readings with daily sums gives one energy total for each day.
Resolution
Datetime resolution is the unit used for stored time counts, such as microseconds or nanoseconds, and determines their precision and range.
Casting a column with microsecond resolution to integers gives microsecond counts from the epoch.
Rollback
An offset’s rollback() method finds the nearest valid calendar anchor on or before a date, keeping a date already on that anchor.
A MonthEnd rule’s rollback() moves January 15 to December 31 of the preceding year.
Rollforward
An offset’s rollforward() method finds the nearest valid calendar anchor on or after a date, keeping a date already on that anchor.
A MonthEnd rule’s rollforward() moves January 15 to January 31 in the same year.
Rolling correlation
Rolling correlation measures the relationship between two series using a moving set of paired observations, revealing changes over time.
Rolling correlation can show two sensors moving together in one interval but less closely in another.
Rolling window
A rolling window is a moving selection of rows or times used to compute a statistic repeatedly as the series progresses.
A three-row rolling window drops its oldest row when a new row enters the calculation.
Running peak
A running peak is the highest value reached from the start through the current observation, remaining unchanged until a higher value arrives.
A running peak stays at 120 when a series falls to 90, until a higher value arrives.

S

Smoothing factor
The smoothing factor alpha controls the new observation’s weight in a recursive exponentially weighted mean, with the rest on the prior mean.
A smoothing factor of 0.5 gives equal weight to the new reading and the previous smoothed value.
Smoothing lag
Smoothing lag is the delayed response of a moving average to changes in direction, which generally increases when more older values are retained.
Smoothing lag makes a long trailing average respond slowly after a sudden increase in readings.
strftime
The strftime method turns datetime values into display text using format codes, changing their representation from datetimes to strings.
A report can use strftime to show "17 Aug 2025" after completing its datetime calculations.

T

Time bin
A time bin is an interval used to group observations during resampling, with boundary membership and an output label defined separately.
A time bin ending at 10:05 can contain readings after 10:00 through 10:05.
Time Grouper
A time Grouper is a pandas grouping object that supplies time-bin rules inside groupby, alongside other keys such as store or sensor.
A store key plus a monthly time Grouper preserves a separate monthly history for each store.
Time series
A time series is a collection of observations tied to points or spans of time, allowing values to be studied in their time order.
An hourly energy-use time series lets an analyst compare demand at different times of day.
Time-based window
A time-based window selects observations within a fixed elapsed duration relative to each timestamp, allowing its number of rows to vary.
A three-day time-based window may contain one customer order or several, depending on timing.
Time-zone conversion
Time-zone conversion expresses an already aware moment in another zone, preserving the actual instant while changing its local clock display.
Time-zone conversion changes Amsterdam noon to the UTC clock appropriate for that date.
Timedelta
A Timedelta is a fixed duration that can be added to a timestamp or obtained by subtracting two moments from each other.
A Timedelta of 36 hours represents one day plus twelve hours, not just one whole day.
Timestamp
A Timestamp is a pandas object representing one moment, with calendar fields and an optional time zone for identifying its location.
Subtracting an event’s start Timestamp from its end Timestamp gives its elapsed duration.
Timestamp alignment
Timestamp alignment matches observations by their datetime labels during Series arithmetic, instead of matching them by row position.
Timestamp alignment leaves an unmatched date missing when two store histories are added.
Trailing window
A trailing window ends at the current row and uses observations at or before its label, fitting reports of information available at that time.
A daily trailing window smooths recent energy use without relying on tomorrow’s readings.

U

Unix timestamp
A Unix timestamp counts time units from January 1, 1970 at midnight UTC, so interpreting its number requires knowing the unit.
A millisecond Unix timestamp must use unit="ms", even if a seconds count looks similar.
Unstacking
Unstacking moves an index level into columns, creating a wider table in which missing combinations are represented as missing cells by default.
Unstacking the store level places each store’s monthly total alongside the others in columns.
Upsampling
Upsampling moves observations to a finer time grid and introduces empty slots where no original observation exists at the new timestamps.
Upsampling three-day readings to daily labels creates interior dates without observations.
UTC
UTC is Coordinated Universal Time, the shared time reference used to align moments across zones without seasonal daylight saving clock changes.
Converting regional event logs to UTC allows their actual moments to be matched consistently.
UTC offset
A UTC offset states how far a local clock is ahead of or behind UTC at a moment, but a fixed offset alone supplies no seasonal rules.
The UTC offset +05:30 puts a local clock five hours and thirty minutes ahead of UTC.

W

Week-of-month frequency
A week-of-month frequency selects a specified weekday occurrence within each month, such as the third Friday for a recurring calendar event.
The week-of-month frequency "WOM-3FRI" creates recurring dates on each month’s third Friday.
Window count
A window count reports how many non-missing observations contribute inside a moving window, showing the data support behind its statistic.
A window count reveals when one moving mean uses a single reading and another uses ten.

#

.dt accessor
The .dt accessor exposes datetime and duration features on a pandas column, including calendar fields, rounding and unit conversion.
The .dt accessor in readings["time"].dt.hour supplies the recorded hour for grouping.

About this lesson

By StudeliReport a mistake

Make your own Study Materials

Turn your notes, slides and documents into Study Guides, Practice Questions, Flashcards and 6 more tools.

Get Started