Skip to content

Python Pandas: How to Fix "IndexError: single positional indexer is out-of-bounds"

Learn how to diagnose and resolve the 'IndexError: single positional indexer is out-of-bounds' in Pandas when using .iloc for positional indexing, including dimension checks and safe access techniques.

The IndexError: single positional indexer is out-of-bounds is a fundamental error encountered when working with Pandas DataFrames (or Series) and attempting to access data using integer-based positional indexing via .iloc. This error unequivocally means you've tried to retrieve an element, row, or column using an integer position that simply doesn't exist within the dimensions of your DataFrame.

This guide will thoroughly explore why this IndexError occurs, demonstrate common scenarios that trigger it, and equip you with the knowledge to correctly use positional indexing, check DataFrame dimensions, and handle potential out-of-bounds access gracefully.

Understanding the "Out-of-Bounds" IndexError in Pandas

This IndexError specifically relates to positional indexing, which in Pandas is primarily handled by the .iloc accessor. It means the integer position you've supplied to .iloc is outside the valid range of indices for the rows or columns of your DataFrame.

Zero-Based Positional Indexing with .iloc

It's crucial to remember that Python and Pandas use zero-based indexing. This means:

  • For a DataFrame with N rows, the valid row indices are 0, 1, ..., N-1.
  • For a DataFrame with M columns, the valid column indices are 0, 1, ..., M-1.

Attempting to access df.iloc[N] for rows or df.iloc[:, M] for columns will result in the "out-of-bounds" error.

Let's use a sample DataFrame:

import pandas as pd

df = pd.DataFrame({
    'employee_name': ['Alice', 'Bob', 'Charlie', 'Diana'],  # 4 rows
    'department': ['HR', 'IT', 'Sales', 'HR'],              # 3 columns
    'salary': [70000, 85000, 92000, 72000]
})

print("Original DataFrame:")
print(df)

Output:

Original DataFrame:
  employee_name department  salary
0         Alice         HR   70000
1           Bob         IT   85000
2       Charlie      Sales   92000
3         Diana         HR   72000

This DataFrame has 4 rows (indices 0, 1, 2, 3) and 3 columns (indices 0, 1, 2).

Common Scenarios Causing the Error

Accessing a Non-Existent Column Index with .iloc

If you try to access a column by a position that is equal to or greater than the number of columns.

import pandas as pd

df = pd.DataFrame({
    'employee_name': ['Alice', 'Bob', 'Charlie', 'Diana'],  # 4 rows
    'department': ['HR', 'IT', 'Sales', 'HR'],              # 3 columns
    'salary': [70000, 85000, 92000, 72000]
})

try:
    non_existent_column = df.iloc[:, 3]
    print(non_existent_column)
except IndexError as e:
    print(f"Error accessing column: {e}")

Output:

Error accessing column: single positional indexer is out-of-bounds

Accessing a Non-Existent Row Index with .iloc

If you try to access a row by a position that is equal to or greater than the number of rows.

import pandas as pd

df = pd.DataFrame({
    'employee_name': ['Alice', 'Bob', 'Charlie', 'Diana'],  # 4 rows
    'department': ['HR', 'IT', 'Sales', 'HR'],              # 3 columns
    'salary': [70000, 85000, 92000, 72000]
})


try:
    non_existent_row_slice = df.iloc[[4]]
    print(non_existent_row_slice)
except IndexError as e:
    print(f"Error accessing row slice: {e}")

try:
    non_existent_single_row = df.iloc[4]
    print(non_existent_single_row)
except IndexError as e:
    print(f"Error accessing single row: {e}")

Output:

Error accessing row slice: positional indexers are out-of-bounds
Error accessing single row: single positional indexer is out-of-bounds

Preventing the Error: Verifying DataFrame Dimensions

Before attempting positional indexing, especially if the indices are dynamically generated or uncertain, it's wise to check the DataFrame's dimensions.

import pandas as pd

df = pd.DataFrame({
    'employee_name': ['Alice', 'Bob', 'Charlie', 'Diana'],  # 4 rows
    'department': ['HR', 'IT', 'Sales', 'HR'],              # 3 columns
    'salary': [70000, 85000, 92000, 72000]
})

num_rows = len(df) # Or len(df.index)
num_cols = len(df.columns) # Or df.shape[1]

print(f"DataFrame has {num_rows} rows (valid indices 0 to {num_rows - 1}).")
print(f"DataFrame has {num_cols} columns (valid indices 0 to {num_cols - 1}).")

rows, cols = df.shape
print(f"Using df.shape: Rows={rows}, Columns={cols}")

Output:

DataFrame has 4 rows (valid indices 0 to 3).
DataFrame has 3 columns (valid indices 0 to 2).
Using df.shape: Rows=4, Columns=3

Knowing these bounds can help you avoid specifying an out-of-range index.

Correct Positional Indexing Techniques with .iloc

Accessing Existing Rows and Columns by Position

Ensure your integer index is within the valid range 0 to length-1.

import pandas as pd

df = pd.DataFrame({
    'employee_name': ['Alice', 'Bob', 'Charlie', 'Diana'],  # 4 rows
    'department': ['HR', 'IT', 'Sales', 'HR'],              # 3 columns
    'salary': [70000, 85000, 92000, 72000]
})


first_column = df.iloc[:, 0]
print("First column ('employee_name'):")
print(first_column)
print()

third_row_slice = df.iloc[[2]]
print("Third row (index 2) as a slice:")
print(third_row_slice)
print()

third_row_series = df.iloc[2]
print("Third row (index 2) as a Series:")
print(third_row_series)
print()

bob_salary = df.iloc[1, 2]
print(f"Bob's salary (cell at [1, 2]): {bob_salary}") # Output: Bob's salary (cell at [1, 2]): 85000

Output:

First column ('employee_name'):
0      Alice
1        Bob
2    Charlie
3      Diana
Name: employee_name, dtype: object

Third row (index 2) as a slice:
  employee_name department  salary
2       Charlie      Sales   92000

Third row (index 2) as a Series:
employee_name    Charlie
department         Sales
salary             92000
Name: 2, dtype: object

Bob's salary (cell at [1, 2]): 85000

Accessing the Last Row or Column Using Negative Indexing

.iloc supports negative indexing, similar to Python lists, where -1 refers to the last element, -2 to the second last, and so on.

import pandas as pd

df = pd.DataFrame({
    'employee_name': ['Alice', 'Bob', 'Charlie', 'Diana'],  # 4 rows
    'department': ['HR', 'IT', 'Sales', 'HR'],              # 3 columns
    'salary': [70000, 85000, 92000, 72000]
})

last_column = df.iloc[:, -1]
print("Last column using -1:")
print(last_column)
print()

last_row_slice = df.iloc[[-1]] # As a DataFrame slice
print("Last row using -1 (as slice):")
print(last_row_slice)

Output:

Last column using -1:
0    70000
1    85000
2    92000
3    72000
Name: salary, dtype: int64

Last row using -1 (as slice):
  employee_name department  salary
3         Diana         HR   72000

Accessing the Last Row or Column Using Shape/Length

You can also use the DataFrame's dimensions to calculate the index of the last element.

import pandas as pd

df = pd.DataFrame({
    'employee_name': ['Alice', 'Bob', 'Charlie', 'Diana'],  # 4 rows
    'department': ['HR', 'IT', 'Sales', 'HR'],              # 3 columns
    'salary': [70000, 85000, 92000, 72000]
})


num_rows = df.shape[0]
num_cols = df.shape[1]

last_col_calculated = df.iloc[:, num_cols - 1]
print("Last column using shape:")
print(last_col_calculated)
print()

last_row_calculated_slice = df.iloc[[num_rows - 1]]
print("Last row using shape (as slice):")
print(last_row_calculated_slice)

Output:

Last column using shape:
0    70000
1    85000
2    92000
3    72000
Name: salary, dtype: int64

Last row using shape (as slice):
  employee_name department  salary
3         Diana         HR   72000

This is equivalent to using -1 but can be more explicit in some contexts.

Handling the IndexError Programmatically (Optional)

If you anticipate that an index might be out of bounds in some situations (e.g., user input, dynamic calculations), you can use a try-except block to catch the IndexError.

import pandas as pd

df = pd.DataFrame({
    'employee_name': ['Alice', 'Bob', 'Charlie', 'Diana'],  # 4 rows
    'department': ['HR', 'IT', 'Sales', 'HR'],              # 3 columns
    'salary': [70000, 85000, 92000, 72000]
})

column_to_access = 5 # This index is out of bounds for df

try:
    selected_column = df.iloc[:, column_to_access]
    print(f"Selected column {column_to_access}:\n{selected_column}")
except IndexError:
    print(f"Column index {column_to_access} is out of bounds for this DataFrame.")

Output:

Column index 5 is out of bounds for this DataFrame.

A Note on Empty DataFrames

If your DataFrame is empty (has 0 rows or 0 columns), any attempt to access data using .iloc with a non-empty slice or any integer index (even 0) will result in an IndexError.

import pandas as pd

df_empty_rows = pd.DataFrame(columns=['A', 'B'])    # 0 rows, 2 columns
df_empty_cols = pd.DataFrame(index=[0, 1])          # 2 rows, 0 columns
df_fully_empty = pd.DataFrame()                     # 0 rows, 0 columns

try:
    val = df_empty_rows.iloc[0, 0]                  # Accessing row 0 of an empty-row DataFrame
except IndexError as e:
    print(f"Error with df_empty_rows: {e}")         # single positional indexer is out-of-bounds

try:
    val = df_empty_cols.iloc[0, 0]                  # Accessing col 0 of an empty-col DataFrame
except IndexError as e:
    print(f"Error with df_empty_cols: {e}")         # single positional indexer is out-of-bounds

Output:

Error with df_empty_rows: index 0 is out of bounds for axis 0 with size 0
Error with df_empty_cols: index 0 is out of bounds for axis 0 with size 0

Always check if a DataFrame is empty (df.empty) before attempting positional access if its emptiness is a possibility.

Conclusion

The IndexError: single positional indexer is out-of-bounds in Pandas is a direct consequence of attempting to access data via .iloc with an integer position that falls outside the valid zero-based range of your DataFrame's rows or columns. To resolve and prevent this error:

  1. Understand Zero-Based Indexing: The first element is at index 0.
  2. Verify Dimensions: Before indexing, check df.shape, len(df.index), or len(df.columns) to know the valid range of indices.
  3. Use Correct Indices: Ensure the integer positions you use with .iloc are within 0 to N-1 (for N elements).
  4. Leverage Negative Indexing: Use .iloc[:, -1] or .iloc[-1] to reliably access the last column or row.

By adhering to these principles, you can confidently use positional indexing with .iloc and avoid out-of-bounds errors.

Navigation