pandas-dev/pandas · error · ValueError
periods must be an integer
Error message
periods must be an integer
What it means
Raised by pandas.core.algorithms.diff when the periods argument n is not an integer. diff shifts by n positions then subtracts; a non-integral shift count is meaningless, so only integer-valued types (or floats that represent whole numbers like 2.0) are accepted.
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 71959b8cb9)
Solutions
- Pass an int: s.diff(1).
- If the value comes in as a float, coerce only when whole: int(n) after verifying n.is_integer().
- Validate the input type at your boundary before diff.
Example fix
# before s.diff(1.5) # after s.diff(int(1))
Defensive patterns
Strategy: validation
Validate before calling
import numpy as np
def safe_diff(s, n):
if isinstance(n, float):
if not n.is_integer():
raise ValueError('periods must be an integer')
n = int(n)
elif not isinstance(n, (int, np.integer)):
raise ValueError('periods must be an integer')
return s.diff(n) Type guard
import numpy as np
def is_integer_periods(n) -> bool:
if isinstance(n, (int, np.integer)):
return True
return isinstance(n, float) and n.is_integer() Prevention
- Pass ints to diff periods; coerce whole-number floats to int first.
- Parse config values as int before diff.
- Validate the type at your boundary.
When it happens
Trigger: s.diff(1.5), df.diff(periods='1'), or passing a float NaN/None-derived n that is neither an integer nor a whole-number float; calling diff with a config value parsed as a float.
Common situations: Period values read from JSON/config as strings or floats; computations that produce fractional period counts; np.float64 values that are not whole numbers.
Related errors
- Value must be a nonnegative integer or None
- cannot diff {type(arr).__name__} on axis={axis}
- No such keys(s): {pat!r}
- {k} is not a valid identifier
- {k} is a python keyword
AI-assisted analysis of pandas-dev/pandas@71959b8cb9 (2026-08-07).
Data as JSON: /api/errors/27a1fd4568d95f08.
Report an issue: GitHub.