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

  1. Pass an int: s.diff(1).
  2. If the value comes in as a float, coerce only when whole: int(n) after verifying n.is_integer().
  3. 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

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


AI-assisted analysis of pandas-dev/pandas@71959b8cb9 (2026-08-07). Data as JSON: /api/errors/27a1fd4568d95f08. Report an issue: GitHub.