What’s new in 3.1.0 (Month XX, 2026)#

These are the changes in pandas 3.1.0. See Release notes for a full changelog including other versions of pandas.

Enhancements#

enhancement1#

enhancement2#

Other enhancements#

  • Period now supports f-string formatting via __format__, e.g. f"{period:%Y-%m}" (GH 48536)

  • DataFrameGroupBy.agg() now allows for the provided func to return a NumPy array (GH 63957)

  • DataFrameGroupBy.transform() now accepts list-like and dict arguments similar to GroupBy.agg(), and supports NamedFunc (GH 58318)

  • Series.to_json() now supports serializing custom ExtensionArrays (by correctly using the _values_for_json method of an ExtensionArray) (GH 65047)

  • Timestamp.round(), Timestamp.floor(), and Timestamp.ceil() now officially accept Timedelta arguments (GH 63687)

  • Added NamedFunc, an alias to NamedAgg for a more semantically accurate name when used with non-aggregation functions; either can accept arbitrary functions (GH 65164)

  • ExtensionArray.map() now calls ExtensionArray._cast_pointwise_result() to retain the dtype backend, e.g. Arrow-backed arrays now preserve their Arrow dtype through map (GH 57189, GH 62164)

  • read_csv() now supports dtype="complex64" and dtype="complex128" with the C engine, enabling round-tripping of complex-number columns written by DataFrame.to_csv() (GH 9379)

  • Added ExtensionArray.count() (GH 64450)

  • Added ExtensionArray.sort() for in-place sorting of ExtensionArray (GH 64977)

  • Added Index.replace() method to support value replacement functionality similar to Series.replace() (GH 19495)

  • Added reduction methods as public API on pandas-implemented extension arrays where applicable (GH 63512)

  • Display formatting for float sequences in DataFrame cells now respects the display.precision option (GH 60503).

  • Improved the precision of float parsing in read_csv() (GH 64395)

  • Improved the string repr of pd.core.arrays.SparseArray (GH 64547)

  • Improved type inference of comparison and arithmetic operators on Series and DataFrame for static type checkers (e.g. ser == "a" is now inferred as Series instead of Any) (GH 40762)

  • MSVC is no longer required to build on Windows, and build errors when using the MinGW compiler have been fixed (GH 63160)

Notable bug fixes#

These are bug fixes that might have notable behavior changes.

notable_bug_fix1#

notable_bug_fix2#

Backwards incompatible API changes#

Increased minimum versions for dependencies#

Some minimum supported versions of dependencies were updated. If installed, we now require:

Package

Minimum Version

Required

Changed

X

X

For optional libraries the general recommendation is to use the latest version. The following table lists the lowest version per library that is currently being tested throughout the development of pandas. Optional libraries below the lowest tested version may still work, but are not considered supported.

Package

Minimum Version

Changed

X

See Dependencies and Optional dependencies for more.

Other API changes#

  • DataFrameGroupBy.sum() and DataFrameGroupBy.mean() on float dtypes with sorted grouping keys may differ from prior versions in the last few floating-point digits, due to a faster summation algorithm that does not use Kahan compensation (GH 65103)

  • DataFrame.to_hdf() no longer writes a pandas_version attribute into the HDF5 file. The value was hardcoded to "0.15.2" and was only used internally to detect files written by pandas versions older than 0.10.1 (GH 62792)

  • APIs that accept an engine="numba" parameter with engine_kwargs will no longer pass through a nopython argument to numba.jit. This argument has had no effect since numba 0.59.0 (GH 64483).

  • Removed the freq and freqstr attributes from DatetimeArray and TimedeltaArray. Frequency is now stored only on DatetimeIndex and TimedeltaIndex; access Series.dt.freq or wrap the array in an Index to retrieve a frequency. The check_freq keyword on testing.assert_extension_array_equal() for these array types has also been removed (GH 24566).

Deprecations#

Performance improvements#

Bug fixes#

Categorical#

  • Bug in Categorical.__repr__() where the values and categories lines could exceed display.width (GH 12066)

  • Bug in Categorical.map() where mapping with a defaultdict and na_action=None would bypass the default factory by using dict.get, causing NA values to be replaced with NaN instead of the mapper’s default value (GH 62710)

  • Bug in CategoricalIndex.union() and CategoricalIndex.intersection() giving incorrect results when the two indexes have the same unordered categories in different orders (GH 55335)

  • Bug in Index.fillna() raising TypeError when filling with a tuple value (e.g. on object-dtype or CategoricalIndex with tuple categories) (GH 37681)

Datetimelike#

  • Bug in ArrowExtensionArray where adding a DateOffset to a date32[pyarrow] or date64[pyarrow] Series raised an ArrowTypeError (GH 57168)

  • Bug in DatetimeIndex constructor raising ValueError when passing equivalent but not equal frequencies (e.g. QS-FEB vs QS-MAY) (GH 61086)

  • Bug in DatetimeIndex raising AttributeError when comparing against Arrow date types (date32, date64) (GH 62051)

  • Bug in Timestamp constructor where passing np.str_ objects would fail in Cython string parsing (GH 48974)

  • Bug in Timestamp constructor where strings with a negative year of fewer than 4 digits (e.g. "-111-01-01") silently dropped the leading "-" and were parsed as a positive year; BC dates with 1-4 digit years now parse correctly, matching numpy.datetime64 (GH 55954)

  • Bug in Timestamp constructor, Timedelta constructor, to_datetime(), and to_timedelta() with non-round float input and unit failing to raise when the value is just outside the representable bounds (GH 57366)

  • Bug in api.types.infer_dtype() returning "date" or "mixed" instead of "datetime" / "timedelta" for lists of Timestamp/Timedelta values mixed with pd.NA (GH 53023)

  • Bug in date_range() where inclusive="left" and inclusive="right" returned a single-element result instead of empty when start equals end (GH 55293)

  • Bug in date_range() where inclusive parameter failed to filter endpoints when only start and periods or end and periods were specified (GH 46331)

  • Bug in date_range() where periods=1 with offsets that disallow n=0 (e.g. offsets.LastWeekOfMonth, offsets.FY5253) raised ValueError (GH 41563)

  • Bug in date_range() where calendar-based offsets (e.g. MS, ME, QS, YS) could exclude the last offset boundary when end’s time-of-day was earlier than start’s (GH 35342)

  • Bug in to_datetime() and to_timedelta() on ARM platforms where round float values outside the int64 domain (e.g. float(2**63)) could silently produce incorrect results instead of raising (GH 64619)

  • Bug in to_datetime() and to_timedelta() where uint64 values greater than int64 max silently overflowed instead of raising OutOfBoundsDatetime or OutOfBoundsTimedelta (GH 60677)

  • Bug in to_datetime() when using a low time resolution unit, higher resolution in origin is now preserved instead of silently dropped (e.g. unit="D" with microsecond precision origin) (GH 63419)

  • Bug in DataFrame.replace() and Series.replace() raising AssertionError instead of OutOfBoundsDatetime when replacing with a datetime value outside the datetime64[ns] range (GH 61671)

  • Bug in DataFrame.to_string() and Series.to_string() where na_rep was ignored for datetime and timedelta columns, always displaying NaT (GH 55426)

  • Bug in DatetimeArray.isin() and TimedeltaArray.isin() where mismatched resolutions could silently truncate finer-resolution values, leading to false matches (GH 64545)

  • Bug in Series.dt.isocalendar() with a pyarrow-backed datetime or date dtype not preserving the original index, resetting it to a default RangeIndex (GH 65894)

  • Bug in adding non-nano DatetimeIndex with non-vectorized offsets (e.g. CustomBusinessDay, CustomBusinessMonthEnd) having a sub-unit offset parameter incorrectly truncating the result or raising AttributeError (GH 56586)

  • Bug in subtracting BusinessHour (or CustomBusinessHour) from a Timestamp giving incorrect results when the subtraction would land exactly on the business-hour opening time (GH 33682)

Timedelta#

  • Bug in TimedeltaIndex.resolution raising when the index has no frequency (GH 65186)

  • Bug in DateOffset where DateOffset(1) and DateOffset(days=1) returned different results near daylight saving time transitions (GH 61862)

  • Bug in Timedelta constructor where keyword arguments (e.g. days=365000) that exceeded nanosecond int64 bounds raised OutOfBoundsTimedelta instead of falling back to a coarser resolution (GH 46587)

  • Bug in to_timedelta() where passing np.str_ objects would fail in Cython string parsing (GH 48974)

  • Bug in Series.sum() on an overflowing timedelta64 series raising a plain ValueError instead of OutOfBoundsTimedelta (GH 43178)

  • Bug in Series.dt.seconds and Series.dt.microseconds with ArrowDtype durations returning the Series.dt.components field values (e.g. 0-59 for seconds, or 0 for microseconds on a "ms" unit) instead of the totals within each day and second respectively, inconsistent with NumPy-backed timedeltas (GH 63470)

Timezones#

Numeric#

Conversion#

  • Bug in DataFrame constructor where NaT in a TimedeltaIndex row was incorrectly inferred as datetime64 instead of timedelta64 (GH 23985)

  • Bug in DataFrame constructor where constructing from a list of uniform-dtype arrays (e.g. pyarrow, CategoricalDtype, nullable dtypes) lost the dtype (GH 49593)

  • Bug in pd.array() silently converting NaN to a nonsensical integer when given float data containing NaN and a NumPy integer dtype (GH 41724)

  • Fixed pandas.array() to preserve mask information when converting NumPy masked arrays, converting masked values to missing values (GH 63879)

  • Fixed bug in DataFrame constructor where mutating the result could corrupt the source Series or Index when built with dtype="str" and infer_string=False (GH 63936)

  • Fixed bug in DataFrame.from_records() where exclude was ignored when data was an iterator and nrows=0 (GH 63774)

Strings#

Interval#

  • Bug in IntervalArray and IntervalIndex constructors unnecessarily upcasting sub-64-bit numeric dtypes (e.g. float32, int32) to 64-bit (GH 45412)

  • Bug in cut() and other operations building an IntervalIndex engine raising TypeError on 32-bit platforms when there were more than 100 intervals (GH 44075, GH 23440)

Indexing#

Missing#

MultiIndex#

I/O#

  • read_csv() with memory_map=True and an in-memory buffer (e.g. BytesIO) now raises a clear ValueError instead of a cryptic UnsupportedOperation: fileno (GH 45630)

  • read_sql_table() now raises an informative NotImplementedError (instead of one with no message) when passed a DBAPI connection such as sqlite3, and reading from a URI string without a usable sqlalchemy install now raises a clearer ImportError (GH 41237)

  • Fixed bug in read_csv() with the c engine where an embedded \r followed by a space in an unquoted field could cause an infinite re-parsing loop, producing spurious rows or a buffer overflow (GH 51141)

  • Fixed bug in read_excel() where usage of skiprows could lead to an infinite loop (GH 64027)

  • Fixed bug where read_html() parsed nested tables incorrectly when using html5lib or bs4 flavors (GH 64524)

  • Fixed memory leak in read_csv() (GH 19941)

  • Fixed segfault when instantiating the internal pandas._libs.parsers.TextReader with no arguments; it now raises TypeError (GH 53131)

  • Fixed read_json() with lines=True and chunksize to respect nrows when the requested row count is not a multiple of the chunk size (GH 64025)

  • HDFStore.put() and HDFStore.append() now support storing Series and DataFrame columns with PeriodDtype in both "fixed" and "table" formats (GH 41978)

  • Bug in DataFrame.__repr__() raising TypeError for a column with a NumPy structured dtype (e.g. produced by DataFrame.from_records() from a structured ndarray) (GH 55011)

  • Bug in DataFrame.__repr__() where horizontally truncated output could exceed the terminal width by up to 4 characters (GH 32461)

  • Bug in DataFrame.to_stata() raising KeyError when column names require renaming and convert_dates is specified for a different column (GH 60536)

  • Bug in DataFrame.to_string() where formatters dict was applied to wrong columns when output was horizontally truncated via max_cols (GH 35410)

  • Fixed read_json() with lines=True and nrows=0 to return an empty DataFrame (GH 64025)

  • DataFrame.to_hdf() now raises a clear NotImplementedError when writing a column or Index of an unsupported extension dtype (such as IntervalDtype, SparseDtype, or the nullable integer/float/boolean dtypes), instead of a low-level AttributeError or PyTables TypeError (GH 26144, GH 38305, GH 42070)

  • read_hdf() can again read fixed-format files written by very old pandas versions (<=0.15.x) that stored a freq attribute on non-datetimelike indexes, which previously failed with a TypeError or ValueError (GH 33186)

  • DataFrame.to_hdf() with format="fixed" now compresses object dtype (e.g. string) columns when complib/complevel are given; previously the compression settings were silently ignored for these columns, producing much larger files (GH 45286)

  • HDFStore.put(), HDFStore.append(), and DataFrame.to_hdf() now emit a UserWarning instead of silently doing nothing when writing an empty DataFrame or Series with format='table' or append=True (GH 13016)

  • HDFStore.select() and read_hdf() now warn when a nested where of the form "(A & B) | (C & D)" over indexed columns may return incorrect results because of an upstream PyTables bug, suggesting writing with index=False or running the OR branches as separate queries (GH 50598)

  • HDFStore.select() now raises a clear ValueError with a workaround, instead of an opaque too many inputs error, when a where expression has too many comparisons for a query against indexed columns (GH 39752)

  • HDFStore.select() now raises an informative NotImplementedError instead of a cryptic KeyError when a column selection such as where="columns=['A']" is combined with iterator=True or chunksize; pass the columns argument instead (GH 12953)

  • HDFStore.select() now raises an informative NotImplementedError when a where clause contains an arithmetic expression such as "(A % 3) == 0", instead of an opaque PyTables TypeError; arithmetic in where filters is not supported (GH 41100)

  • Bug in HDFStore.select() where a where query on a categorical data column for a value that is not one of the categories incorrectly matched rows with missing (NaN) values (GH 22977)

  • Fixed MemoryError in HDFStore.select() when iterating large tables with chunksize and no where filter (GH 15937)

  • Fixed bug in read_hdf() raising on files written by older pandas versions whose freq index attribute could not be decoded; the freq is now dropped with a warning instead of corrupting the index (GH 35917)

  • Fixed bug in read_hdf() where a categorical column containing a category equal to the nan_rep string (e.g. the default "nan") raised ValueError: operands could not be broadcast together instead of reading that category back as NaN (GH 21741)

  • Fixed bug in read_hdf() where the literal string "nan" in a string Index was incorrectly converted to NaN on read, even when a custom nan_rep was supplied (GH 9604)

  • Fixed bug in DataFrame.to_hdf() and HDFStore.put() where writing an object to a key silently deleted any nested keys stored beneath it (GH 17267)

  • Fixed bug in DataFrame.to_hdf() raising TypeError when the index had a non-tick DateOffset freq (e.g. DateOffset(years=1)) (GH 45790)

  • Fixed bug in DataFrame.to_hdf() with format="table" where a TimedeltaIndex was reconstructed as a PeriodIndex (when freq was set) or an integer Index (otherwise) on read-back (GH 21466)

  • Fixed DataFrame.to_hdf() and Series.to_hdf() to round-trip a CategoricalIndex in both "fixed" and "table" formats; previously raised AssertionError (GH 33909, GH 16118)

  • Bug in Series.to_json() with date_format="iso" where a timezone-aware datetime Series was serialized without the trailing Z marker, losing the timezone information that is retained for an equivalent DatetimeIndex or DataFrame column (GH 65744)

  • Fixed bug in DataFrame.to_parquet() (pyarrow engine) where a local file path was opened twice, once by pandas and again by pyarrow, wasting a syscall and silently truncating output to 0 bytes on filesystems that finalize a file’s contents on close (GH 65810)

  • Fixed bug in HDFStore.get_storer() where .shape reported a phantom row for a fixed-format Series or DataFrame stored with no rows (GH 37235)

  • Fixed bug in HDFStore.remove() where a where clause selecting on more than 31 values (e.g. "index in [...]") deleted every row in the table instead of only the matching rows (GH 17567)

  • Fixed bug in HDFStore.select() where passing where as a list of conditions referencing caller-scope variables failed on Python 3.12+ due to PEP 709 inlining list comprehension stack frames (GH 64881)

  • Fixed bug in HDFStore.select() with format="table" where reading a frame with a string Index could crash with a bus error on strict-alignment platforms such as 32-bit ARM (GH 54396)

  • Storing a DataFrame or Series with a MultiIndex level named 'index' via HDFStore.put() or HDFStore.append() with format='table' now raises a clear ValueError instead of an opaque reshape error (GH 6208)

  • The PerformanceWarning emitted by DataFrame.to_hdf() for object columns now names only the columns that cannot be mapped to a c-type, instead of every object column sharing the same block (GH 28460)

  • Writing a DataFrame with format='table' and a column named 'index' as a data_columns entry (including data_columns=True) now raises a clear ValueError instead of an opaque reshape error (GH 41437)

Period#

  • Bug in Period constructor where passing np.str_ objects would fail in Cython string parsing (GH 48974)

  • Bug in Period.strftime() where unknown format directives (e.g. "%Q") silently produced platform-dependent output and crashed the Python process on Windows; an Invalid format string ValueError is now raised on all platforms (GH 53562)

Plotting#

Groupby/resample/rolling#

Reshaping#

  • Bug in concat() raising InvalidIndexError when keys or the concatenated objects’ index was an overlapping IntervalIndex (GH 64825)

  • Bug in merge() where merging on a MultiIndex containing NaN values mapped NaN keys to the last level value instead of NaN (GH 64492)

  • Bug in DataFrame.melt() where var_name colliding with an id_vars column or value_name silently overwrote the affected column data instead of raising (GH 65654)

  • Bug in DataFrame.pivot_table() with margins=True raising TypeError when values has an ExtensionDtype that cannot hold NA (e.g. IntervalDtype with an integer subtype) and no columns were specified (GH 55484)

  • Bug in Index.union() where the result could be unsorted when both inputs were monotonic increasing but disjoint, when sort was not False (GH 54646)

  • Fixed bug in Series.sort_values() where ignore_index=True had no effect on an already-sorted Series (GH 65833)

  • In pivot_table(), when values is empty, the aggregation will be computed on a Series of all NA values (GH 46475)

Sparse#

  • Bug in Series.mean() with skipna=False ignoring missing values for SparseDtype-backed Series (GH 65478)

  • Bug in SparseArray.astype() where converting a datetime64 SparseArray with NaT fill value to "Sparse[int64]" silently replaced the fill value with 0 instead of iNaT (GH 49631)

  • Bug in SparseArray.mean() raising a TypeError when called with the skipna argument (GH 65478)

  • Bug in indexing a SparseArray with an out-of-bounds integer with the value of the length of the array returning the fill value instead of raising an IndexError (GH 64183).

ExtensionArray#

Styler#

Other#

Contributors#