pandas-dev/pandas · error · ValueError
periods must be an integer
Error message
periods must be an integer
What it means
The diff() and shift() functions require the periods argument (n) to be an integer. This check accepts Python ints, numpy integers, and floats that are whole numbers (e.g., 3.0), but rejects fractional floats, strings, None, and other non-integer types. The check was added in GH#56607 to catch invalid period values early before they cause confusing downstream behavior.
Solutions
- Explicitly cast n to int: df.diff(int(n)).
- Validate n is integer-valued before calling: if isinstance(n, float) and not n.is_integer(): raise ValueError(...).
- Ensure computed period values use integer division (//) rather than float division (/).
Example fix
# before periods = len(df) / num_groups # may produce a float df.diff(periods) # after periods = len(df) // num_groups # integer division df.diff(periods)
Defensive patterns
Strategy: validation
Validate before calling
def safe_diff(series_or_df, periods, axis=0):
if isinstance(periods, float):
if not periods.is_integer():
raise ValueError(f"periods must be integer, got {periods}")
periods = int(periods)
elif not isinstance(periods, (int, np.integer)):
raise ValueError(f"periods must be integer, got {type(periods).__name__}")
return series_or_df.diff(periods, axis=axis) Type guard
def is_valid_periods(n) -> bool:
if isinstance(n, (int, np.integer)):
return True
if isinstance(n, float) and n.is_integer():
return True
return False Try / catch
try:
result = df.diff(periods)
except ValueError as e:
if "periods must be an integer" in str(e):
result = df.diff(int(periods))
else:
raise Prevention
- Always cast periods to int before calling diff or shift.
- Use integer division (//) when computing periods from lengths.
- Validate dynamically-computed periods with isinstance(n, (int, np.integer)).
When it happens
Trigger: Calling df.diff(n) or s.shift(n) where n is a float like 1.5, a string like "2", None, or NaN. Passing a periods value computed from a calculation that can produce non-integer results (e.g., division). Using a variable that is dynamically typed and may hold a non-integer.
Common situations: Computing shift periods from a formula (e.g., len(df) / num_groups) that yields a float, then passing it directly to diff(). Receiving a periods value from user input or configuration as a string. Passing np.nan as periods when a computed window size is undefined.
Related errors
- cannot diff on axis=
- by_row= not allowed
- cannot broadcast result
- cannot combine transform and aggregation operations
- cannot perform both aggregation and transformation…
AI-assisted analysis of pandas-dev/pandas@3b7651241d (2026-08-11).
Data as JSON: /api/errors/27a1fd4568d95f08.
Report an issue: GitHub.
Appendix: source
Thrown at pandas/core/algorithms.py:1521
----------
arr : ndarray or ExtensionArray
n : int
number of periods
axis : {0, 1}
axis to shift on
stacklevel : int, default 3
The stacklevel for the lost dtype warning.
Returns
-------
shifted
"""
# added a check on the integer value of period
# see https://github.com/pandas-dev/pandas/issues/56607
if not lib.is_integer(n):
if not (is_float(n) and n.is_integer()):
raise ValueError("periods must be an integer")
n = int(n)
na = np.nan
dtype = arr.dtype
is_bool = is_bool_dtype(dtype)
if is_bool:
op = operator.xor
else:
op = operator.sub
if isinstance(dtype, NumpyEADtype):
# NumpyExtensionArray cannot necessarily hold shifted versions of itself.
arr = arr.to_numpy()
dtype = arr.dtype
if not isinstance(arr, np.ndarray):
# i.e ExtensionArray
if hasattr(arr, f"__{op.__name__}__"):View on GitHub (pinned to 3b7651241d)