pandas-dev/pandas · error · ValueError

cannot convert float NaN to integer

Error message

cannot convert float NaN to integer

What it means

Raised in _field_to_int64 when a float-typed year/quarter/day/etc field contains NaN. Casting NaN to int64 would produce a garbage ordinal (a huge negative integer), so pandas explicitly mirrors the scalar Period constructor's error. It guards _range_from_fields' vectorized path.

Source

Thrown at pandas/core/arrays/period.py:1633

        ordinals = libperiod.period_ordinals_from_fields(
            _field_to_int64(arrays[0]),
            _field_to_int64(arrays[1]),
            _field_to_int64(arrays[2]),
            _field_to_int64(arrays[3]),
            _field_to_int64(arrays[4]),
            _field_to_int64(arrays[5]),
            base,
        )

    return ordinals, freq


def _field_to_int64(values) -> np.ndarray:
    values = np.asarray(values)
    if values.dtype.kind == "f" and np.isnan(values).any():
        # Match the error raised by the scalar Period constructor; casting
        #  NaN to int64 would otherwise silently produce garbage ordinals.
        raise ValueError("cannot convert float NaN to integer")
    return values.astype(np.int64, copy=False)


def _make_field_arrays(*fields) -> list[np.ndarray]:
    length = None
    for x in fields:
        if isinstance(x, (list, tuple, np.ndarray, ABCSeries)):
            if length is not None and len(x) != length:
                raise ValueError("Mismatched Period array lengths")
            if length is None:
                length = len(x)

    # error: Argument 2 to "repeat" has incompatible type "Optional[int]"; expected
    # "Union[Union[int, integer[Any]], Union[bool, bool_], ndarray, Sequence[Union[int,
    # integer[Any]]], Sequence[Union[bool, bool_]], Sequence[Sequence[Any]]]"
    return [
        (
            np.asarray(x)

View on GitHub (pinned to 71959b8cb9)

Solutions

  1. Drop or fill NaN rows before building the period range: df = df.dropna(subset=['year']).
  2. Convert the year column to a nullable Int64 and fill: df['year'] = df['year'].astype('Int64').fillna(0).astype(int).
  3. Filter to non-null fields: mask = df[['year','quarter']].notna().all(axis=1).

Example fix

// before
rng = pd.period_range(year=df['year'], quarter=df['quarter'], freq='Q')
// after
clean = df.dropna(subset=['year','quarter'])
rng = pd.period_range(year=clean['year'].astype(int), quarter=clean['quarter'].astype(int), freq='Q')
Defensive patterns

Strategy: validation

Validate before calling

import pandas as pd
import numpy as np

def period_range_safe(year, quarter, freq='Q'):
    y = pd.Series(year)
    q = pd.Series(quarter)
    mask = y.notna() & q.notna()
    return pd.period_range(year=y[mask].astype(int), quarter=q[mask].astype(int), freq=freq)

Type guard

def no_nan_float_field(field) -> bool:
    import numpy as np
    arr = np.asarray(field, dtype=float)
    return not np.isnan(arr).any()

Try / catch

try:
    rng = pd.period_range(year=year, quarter=quarter, freq='Q')
except ValueError as e:
    if 'cannot convert float NaN' in str(e):
        clean = pd.DataFrame({'y': year, 'q': quarter}).dropna()
        rng = pd.period_range(year=clean['y'].astype(int), quarter=clean['q'].astype(int), freq='Q')
    else:
        raise

Prevention

When it happens

Trigger: period_range(year=[2020, np.nan], quarter=[1,2], freq='Q'); a year Series with missing values passed to period_range; float columns (e.g. years stored as float64 because of NaNs) reaching the period constructor.

Common situations: CSV years parsed as float because of empty cells; joins/groupbys introducing NaNs into year/quarter columns; nullable integer columns upcast to float by an operation.

Related errors


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