KeyErrors in pandas groupby operations are frustrating because the error message often points at column names that look correct. The issue is usually one level removed — wrong name, wrong index, wrong group key, or a MultiIndex column you forgot about. Here are the four most common causes with exact error messages and fixes for each.
KeyError: 'category'
import pandas as pd
df = pd.DataFrame({
'category ': ['A', 'B', 'A', 'B'], # trailing space
'value': [10, 20, 30, 40]
})
# Fails — 'category' != 'category '
result = df.groupby('category')['value'].sum()
Whitespace in column names is invisible in most outputs. It is extremely common when reading CSVs with inconsistent formatting or copying column names from documentation.
import pandas as pd
df = pd.DataFrame({
'category ': ['A', 'B', 'A', 'B'],
'value': [10, 20, 30, 40]
})
# Diagnose: see exact column names with repr
print(repr(df.columns.tolist()))
# ['category ', 'value']
# Fix option 1: strip whitespace from all column names on load
df.columns = df.columns.str.strip()
# Fix option 2: strip when reading CSV
df = pd.read_csv('data.csv')
df.columns = df.columns.str.strip()
# Now this works
result = df.groupby('category')['value'].sum()
print(result)
Always use repr(df.columns.tolist()) rather than print(df.columns) when debugging — repr shows whitespace and special characters that print hides. For datetime-related column issues after parsing, see also pandas datetime parsing errors.
KeyError: 'date'
import pandas as pd
df = pd.DataFrame({
'value': [10, 20, 30, 40]
}, index=pd.to_datetime(['2024-01-01', '2024-01-01', '2024-01-02', '2024-01-02']))
df.index.name = 'date'
# Fails — 'date' is the index name, not a column
result = df.groupby('date')['value'].sum()
When a column is set as the index (via set_index() or from reading data with index_col), it is no longer in df.columns. Groupby looks in columns by default.
import pandas as pd
df = pd.DataFrame({
'value': [10, 20, 30, 40]
}, index=pd.to_datetime(['2024-01-01', '2024-01-01', '2024-01-02', '2024-01-02']))
df.index.name = 'date'
# Fix option 1: use level parameter to group by index level
result = df.groupby(level='date')['value'].sum()
# Fix option 2: reset the index first, then groupby as column
result = df.reset_index().groupby('date')['value'].sum()
# Fix option 3: pass the index directly
result = df.groupby(df.index)['value'].sum()
print(result)
To check whether a name refers to a column or an index level:
print("columns:", df.columns.tolist())
print("index names:", df.index.names)
KeyError: 'C'
import pandas as pd
df = pd.DataFrame({
'category': ['A', 'B', 'A', 'B'],
'value': [10, 20, 30, 40]
})
grouped = df.groupby('category')
# Fails — 'C' is not a group that exists
subset = grouped.get_group('C')
get_group() raises a KeyError immediately if the key does not exist in the grouped data. This also happens when the group key type does not match — e.g., passing integer 1 when the group key is string '1'.
import pandas as pd
df = pd.DataFrame({
'category': ['A', 'B', 'A', 'B'],
'value': [10, 20, 30, 40]
})
grouped = df.groupby('category')
# Check available groups before accessing
print(list(grouped.groups.keys())) # ['A', 'B']
# Safe access pattern
group_key = 'C'
if group_key in grouped.groups:
subset = grouped.get_group(group_key)
else:
print(f"Group '{group_key}' does not exist.")
subset = pd.DataFrame() # or handle differently
# Watch out for type mismatches
df2 = pd.DataFrame({'category': [1, 2, 1, 2], 'value': [10, 20, 30, 40]})
grouped2 = df2.groupby('category')
# This fails: '1' (str) vs 1 (int)
# grouped2.get_group('1')
# This works
grouped2.get_group(1)
KeyError: 'value'
import pandas as pd
df = pd.DataFrame({
'category': ['A', 'B', 'A', 'B'],
'value': [10, 20, 30, 40]
})
# agg with multiple functions creates a MultiIndex column
result = df.groupby('category')['value'].agg(['sum', 'mean'])
print(result.columns)
# Index(['sum', 'mean'], dtype='object')
# But with multiple columns and agg, you get a MultiIndex:
result2 = df.groupby('category').agg({'value': ['sum', 'mean']})
print(result2.columns)
# MultiIndex([('value', 'sum'), ('value', 'mean')], )
# Fails — 'value' is now a MultiIndex level, not a top-level key
print(result2['value_sum']) # KeyError
import pandas as pd
df = pd.DataFrame({
'category': ['A', 'B', 'A', 'B'],
'value': [10, 20, 30, 40],
'count': [1, 2, 3, 4]
})
result = df.groupby('category').agg({'value': ['sum', 'mean'], 'count': 'sum'})
print(result.columns)
# MultiIndex([('value', 'sum'), ('value', 'mean'), ('count', 'sum')])
# Fix option 1: access with tuple
print(result[('value', 'sum')])
# Fix option 2: flatten MultiIndex columns
result.columns = ['_'.join(col).strip() for col in result.columns.values]
print(result.columns)
# Index(['value_sum', 'value_mean', 'count_sum'])
print(result['value_sum'])
# Fix option 3: use named aggregations (pandas >= 0.25) — no MultiIndex
result3 = df.groupby('category').agg(
value_sum=('value', 'sum'),
value_mean=('value', 'mean'),
count_total=('count', 'sum')
)
print(result3.columns)
# Index(['value_sum', 'value_mean', 'count_total'])
Named aggregations (the third option) are the cleanest approach — they produce flat column names directly and make the output schema explicit and readable. Prefer them over dict-of-lists agg in any code that will be maintained.
A related confusion: when as_index=False is set, the groupby key becomes a regular column in the result. This changes how you access the output:
import pandas as pd
df = pd.DataFrame({
'category': ['A', 'B', 'A', 'B'],
'value': [10, 20, 30, 40]
})
# Default: category becomes the index
result_indexed = df.groupby('category')['value'].sum()
print(result_indexed.index) # Index(['A', 'B'], dtype='object', name='category')
# as_index=False: category stays as a column, result is a DataFrame
result_flat = df.groupby('category', as_index=False)['value'].sum()
print(result_flat.columns) # Index(['category', 'value'], dtype='object')
print(result_flat['category']) # works normally
as_index=False is equivalent to calling .reset_index() on the grouped result, but more explicit. Use it when you need the result as a regular DataFrame for merging or further processing.
One more source of KeyErrors after groupby: mixing up transform and apply. transform returns a result aligned to the original DataFrame index (same length). apply returns aggregated results. Using apply when you expect transform leads to shape mismatches when you try to assign back:
import pandas as pd
df = pd.DataFrame({
'category': ['A', 'B', 'A', 'B'],
'value': [10, 20, 30, 40]
})
# transform: result has same length as df — safe to assign back
df['group_mean'] = df.groupby('category')['value'].transform('mean')
print(df)
# category value group_mean
# 0 A 10 20.0
# 1 B 20 30.0
# 2 A 30 20.0
# 3 B 40 30.0
# apply with aggregation: length 2, can't assign back to df directly
group_sums = df.groupby('category')['value'].apply(sum)
# Use merge to bring it back
df = df.merge(group_sums.rename('group_sum'), on='category')