Skip to content

Fix pandas AttributeError: 'DataFrame' object has no attribute 'iteritems'

Tested with: pandas 1.4.4 (Python 3.10.21), 1.5.3, 2.0.0, 2.0.3 and 2.1.4 (Python 3.11.16), 2.1.4, 2.2.3 and 3.0.6 (Python 3.12.3), numpy 1.26.4 / 2.5.3, Linux. Last run 2026-09-27.

TL;DR:
  1. Replace .iteritems() with .items() on both DataFrame and Series. Same pairs, same order; it worked on every version tested, 1.4.4 through 3.0.6.
  2. It was removed in pandas 2.0.0. 1.5.3 still runs it with a FutureWarning, 1.4.4 runs it silently, and 2.0.0 onward raises AttributeError.
  3. If the traceback ends inside a library (for example seaborn 0.12.0), upgrade the library, do not edit it. seaborn 0.12.0 fails on pandas 2.0.3; 0.12.1 does not.
  4. Pinning pandas<2.0 only postpones the rename, and on Python 3.12 the pin failed to install at all (no 1.5.3 wheel).

You upgraded pandas, or cloned a project that installs a newer one, and code that used to work now raises:

AttributeError: 'DataFrame' object has no attribute 'iteritems'

Or on a Series:

AttributeError: 'Series' object has no attribute 'iteritems'

The code was written for pandas 1.x and is now running on 2.0 or later. The fix itself is a rename. The same release also removed append(), the squeeze argument and the silent text-column skipping in mean(), which usually live in the same old code, so check for those while you are here.


The Error and Which pandas Version Removed It

This is the real traceback from pandas 2.2.3 on Python 3.12.3:

Traceback (most recent call last):
  File "/tmp/iteritems/repro.py", line 61, in <module>
    for c, d in df.iteritems(): pass
                ^^^^^^^^^^^^
  File ".../site-packages/pandas/core/generic.py", line 6299, in __getattr__
    return object.__getattribute__(self, name)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'DataFrame' object has no attribute 'iteritems'. Did you mean: 'isetitem'?

Ignore the "Did you mean: 'isetitem'?" hint. isetitem() sets a column by position and has nothing to do with iteration. The hint came from Python 3.12: the same pandas 2.1.4 on Python 3.11.16 printed the error with no suggestion at all. For Series the message is 'Series' object has no attribute 'iteritems' with no hint on any version tested. The line number inside generic.py changes between releases (5989 on 2.0.x, 6204 on 2.1.4, 6299 on 2.2.3, 6194 on 3.0.6), so don't match on it.

import pandas as pd

print(pd.__version__)
# 1.4.x  -> iteritems() works, no warning
# 1.5.x  -> iteritems() works, FutureWarning
# 2.0.0+ -> AttributeError

df = pd.DataFrame({
    'temperature': [22.1, 19.8, 25.3],
    'humidity':    [0.55, 0.72, 0.41],
    'pressure':    [1013, 1009, 1017]
})

# Raises AttributeError on pandas >= 2.0
for col_name, col_data in df.iteritems():
    print(col_name, col_data.mean())

Reproduced: pandas 1.4 to 3.0

Each row ran in a fresh virtual environment with all warnings recorded. pandas 1.4.4, 1.5.3 and 2.0.x have no wheels for Python 3.12 (pip only offers 2.1.1 and later), so those ran on Python 3.10 or 3.11.

What ran1.4.41.5.32.0.0 / 2.0.32.1.4 / 2.2.33.0.6Fix that worked
df.iteritems()works, silentworks + FutureWarning: iteritems is deprecated and will be removed in a future version. Use .items instead.AttributeError: 'DataFrame' object has no attribute 'iteritems'same, plus Did you mean: 'isetitem'? on Python 3.12same as 2.2.3df.items()
s.iteritems()works, silentworks + same FutureWarningAttributeError: 'Series' object has no attribute 'iteritems'samesames.items()
df1.append(df2)works + FutureWarning: The frame.append method is deprecated ...sameAttributeError: 'DataFrame' object has no attribute 'append'samesamepd.concat([df1, df2])
df.mean() with a text columnreturns numeric means + FutureWarning (nuisance columns)returns numeric means + FutureWarning (numeric_only default)TypeError: Could not convert ['xyz'] to numericsameTypeError: Cannot perform reduction 'mean' with string dtypedf.mean(numeric_only=True)
pd.read_csv(..., squeeze=True)works + FutureWarningsameTypeError: read_csv() got an unexpected keyword argument 'squeeze'samesamepd.read_csv(...).squeeze("columns")
s.bool()worksworksworksworks + FutureWarning: Series.bool is now deprecated ...AttributeError: 'Series' object has no attribute 'bool's.item()
is_categorical_dtype(s)worksworksworksworks + DeprecationWarningstill works + Pandas4Warningisinstance(s.dtype, pd.CategoricalDtype)

The removal happened exactly at 2.0.0: 1.5.3 has iteritems, 2.0.0 does not. .items() returned identical output on all seven versions, so the rename is safe even if part of your team is still on 1.x.


Fix: Replace .iteritems() with .items()

.items() is the direct replacement on both DataFrame and Series. The only difference is the name.

On a DataFrame

import pandas as pd

df = pd.DataFrame({
    'temperature': [22.1, 19.8, 25.3],
    'humidity':    [0.55, 0.72, 0.41],
    'pressure':    [1013, 1009, 1017]
})

# Before: for col_name, col_data in df.iteritems():
for col_name, col_data in df.items():
    print(f"{col_name}: mean={col_data.mean():.2f}")

# Output (identical on 1.4.4 through 3.0.6):
# temperature: mean=22.40
# humidity: mean=0.56
# pressure: mean=1013.00

On a Series

import pandas as pd

s = pd.Series({'a': 10, 'b': 20, 'c': 30})

# Before: for idx, val in s.iteritems():
for idx, val in s.items():
    print(idx, val)

# a 10
# b 20
# c 30

DataFrame.items() yields (column_name, Series) pairs and Series.items() yields (index_label, value) pairs, the same as the removed methods.

Codebase-Wide Find and Replace

# List every call first
grep -rn '\.iteritems()' . --include='*.py'

# Replace across all Python files in the project
find . -name "*.py" -exec sed -i 's/\.iteritems()/.items()/g' {} +

# Verify nothing is left
grep -rn '\.iteritems()' . --include='*.py'

Check the grep output before running sed: a plain Python 2 dict.iteritems() in the same codebase would also be renamed, which is what you want on Python 3 anyway. Run it on your own code only, not on site-packages or a vendored virtualenv.

When the error comes from a library

If the last frame in the traceback is inside site-packages, the call is in a dependency. seaborn is a common one: seaborn 0.12.0 still contains plot_data.iteritems() in categorical.py and axisgrid.py, and on pandas 2.0.3 a wide-form sns.boxplot(data=df[["tip", "total"]]) raised AttributeError: 'DataFrame' object has no attribute 'iteritems'. seaborn 0.12.1 has no iteritems() calls and the same plot worked. Upgrade to at least seaborn 0.12.2, though: sns.pairplot on pandas 2.0.3 still failed on 0.12.0 and 0.12.1 with OptionError: No such keys(s): 'mode.use_inf_as_null', a different removed pandas option, and worked on 0.12.2.


Other APIs removed in the same upgrade

If you are moving a codebase from pandas 1.x to 2.x, iteritems() is rarely the only break. The table above lists the ones reproduced here; the fixes are below.

DataFrame.append() is gone: use pd.concat()

import pandas as pd

df1 = pd.DataFrame({'a': [1, 2], 'b': [3, 4]})
df2 = pd.DataFrame({'a': [5, 6], 'b': [7, 8]})

# Before (AttributeError on pandas 2.0+):
# combined = df1.append(df2, ignore_index=True)

combined = pd.concat([df1, df2], ignore_index=True)
print(combined)
#    a  b
# 0  1  3
# 1  2  4
# 2  5  7
# 3  6  8

Series.append() went at the same time, with the same fix.

Series.bool() is gone in 3.0: use .item(), .any() or .all()

bool(s) on a Series with more than one element was never allowed: it raised ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all(). on every version tested, 1.4.4 included. What changed is the .bool() method that the message suggests: no warning on 2.0.3, a FutureWarning on 2.1.4 and 2.2.3, and AttributeError on 3.0.6, even though the ValueError text still mentions it.

import pandas as pd

s = pd.Series([True, False, True])

if s.any():
    print("At least one True")

if s.all():
    print("All True")

# Single-element result: use .item() instead of .bool()
value = pd.Series([42])
if (value > 10).item():
    print("Greater than 10")

Chained assignment: use .loc[]

This one is often blamed on pandas 2.0, but 2.0 did not change it. On 1.4.4 through 2.2.3, df[df['category'] == 'A']['value'] = 99 emitted SettingWithCopyWarning and left df unchanged. On 3.0.6 it emits ChainedAssignmentError (a warning) and still leaves df unchanged. Copy-on-Write became the default in 3.0, not 2.0. The fix is the same on all of them:

import pandas as pd

df = pd.DataFrame({'category': ['A', 'B', 'A'], 'value': [10, 20, 30]})

df.loc[df['category'] == 'A', 'value'] = 99
print(df['value'].tolist())
# [99, 20, 99]

More detail in the SettingWithCopyWarning post.

Reductions no longer skip text columns

import pandas as pd

df = pd.DataFrame({'a': [1, 2, 3], 'b': ['x', 'y', 'z'], 'c': [4.0, 5.0, 6.0]})

# df.mean()
#   1.5.3: {'a': 2.0, 'c': 5.0} plus a FutureWarning
#   2.0.0 to 2.2.3: TypeError: Could not convert ['xyz'] to numeric
#   3.0.6: TypeError: Cannot perform reduction 'mean' with string dtype

print(df.mean(numeric_only=True))
# a    2.0
# c    5.0
# dtype: float64

While you are replacing loops: iterrows vs itertuples vs vectorized

items() loops over columns, which is cheap because there are few of them. Code that used iteritems() often also loops over rows, and that is where the time goes. Timings for computing price * qty on a 100,000-row DataFrame (best of 5 runs, Intel i5-7500, CPU only):

Methodpandas 2.2.3pandas 3.0.6
for _, r in df.iterrows(): r["price"] * r["qty"]2517 ms2242 ms
for r in df.itertuples(index=False): r.price * r.qty48.7 ms49.9 ms
df["price"] * df["qty"]0.34 ms0.28 ms
{c: s.mean() for c, s in df.items()} (2 columns)0.34 ms0.30 ms

All three row methods produced the same values. itertuples() was about 50 times faster than iterrows(), and the vectorized expression was over 100 times faster again. If you are touching the loop anyway, check whether it can be a column expression.


How to Check Your pandas Version and Pin It

import pandas as pd
from packaging.version import Version

print(pd.__version__)
if Version(pd.__version__) < Version("2.0.0"):
    print("pandas 1.x: iteritems() still exists")
else:
    print("pandas 2.x or later: use .items()")
pip show pandas

# Temporary pin while you fix the code
pip install "pandas>=1.5,<2.0"

# Then upgrade
pip install --upgrade pandas

That pin does not work on Python 3.12. The last 1.x release, 1.5.3, has no Python 3.12 wheel, so pip fell back to building it from source and stopped at Getting requirements to build wheel with ModuleNotFoundError: No module named 'pkg_resources'. On 3.12 the rename is the only real option.

If you maintain a package that calls .items() and other 2.x-only APIs, declare the floor so users get an install-time error instead of an AttributeError:

# pyproject.toml
[project]
dependencies = [
    "pandas>=2.0.0",
]

The FutureWarning You Should Have Caught Earlier

On pandas 1.5.3 every .iteritems() call printed:

FutureWarning: iteritems is deprecated and will be removed in a future version. Use .items instead.

pandas 1.5.0 was uploaded to PyPI on 2022-09-19 and 2.0.0 on 2023-04-03, so the warning had about six and a half months before the removal. To make warnings like this fail your tests, be careful with the filter. pandas attributes the warning to your file (the warning's filename was repro.py, not a pandas module), so a filter restricted to module="pandas" never matches. On 1.5.3 this ran with no error:

import warnings
import pandas as pd

# Does NOT turn the iteritems warning into an error:
warnings.filterwarnings("error", category=FutureWarning, module="pandas")

# Does, on pandas 1.5.3:
warnings.filterwarnings("error", category=FutureWarning)

for col, data in pd.DataFrame({'a': [1]}).iteritems():
    pass
# FutureWarning: iteritems is deprecated and will be removed in a future version. Use .items instead.

In pytest, the equivalent is filterwarnings = ["error::FutureWarning", "error::DeprecationWarning"] under [tool.pytest.ini_options]. Note that pandas 2.1 and 2.2 issued the is_categorical_dtype deprecation as a DeprecationWarning, and 3.0.6 uses Pandas4Warning, so a filter for FutureWarning alone misses those.


Worth doing while you are in here

Add the error::FutureWarning filter from the previous section to CI in the same commit as the rename. Series.bool() went the same way between 2.1 and 3.0, and is_categorical_dtype still warns on 3.0.6, so there is already a next one queued.