Pandas - Read Excel File (read_excel)

The `read_excel()` function is your primary tool for importing Excel data into pandas DataFrames. At minimum, you only need the file path:

Key Insights

  • read_excel() handles both .xls and .xlsx formats automatically, with openpyxl as the default engine for modern Excel files and xlrd for legacy formats
  • Sheet selection supports multiple methods: index position, sheet name, or lists for reading multiple sheets simultaneously into a dictionary of DataFrames
  • Advanced parameters like usecols, skiprows, and dtype provide granular control over which data gets loaded and how it’s interpreted, critical for handling large files efficiently

Basic Excel File Reading

The read_excel() function is your primary tool for importing Excel data into pandas DataFrames. At minimum, you only need the file path:

import pandas as pd

# Read the first sheet
df = pd.read_excel('sales_data.xlsx')
print(df.head())

For legacy .xls files, pandas automatically switches to the xlrd engine. For .xlsx files, openpyxl is used by default. Install the required engine if you encounter errors:

pip install openpyxl  # For .xlsx files
pip install xlrd       # For .xls files

You can explicitly specify the engine:

df = pd.read_excel('data.xlsx', engine='openpyxl')

Working with Multiple Sheets

Excel workbooks typically contain multiple sheets. The sheet_name parameter controls which sheets to read:

# Read a specific sheet by name
df = pd.read_excel('financial_report.xlsx', sheet_name='Q4_Sales')

# Read by index (0-based)
df = pd.read_excel('financial_report.xlsx', sheet_name=0)

# Read multiple sheets into a dictionary
dfs = pd.read_excel('financial_report.xlsx', sheet_name=['Q1_Sales', 'Q2_Sales'])
print(dfs['Q1_Sales'].head())

# Read all sheets
all_sheets = pd.read_excel('financial_report.xlsx', sheet_name=None)
for sheet_name, data in all_sheets.items():
    print(f"{sheet_name}: {len(data)} rows")

The dictionary approach is particularly useful when processing multiple related datasets:

# Combine quarterly data
dfs = pd.read_excel('annual_report.xlsx', sheet_name=['Q1', 'Q2', 'Q3', 'Q4'])
annual_df = pd.concat(dfs.values(), ignore_index=True)

Selecting Specific Columns and Rows

Loading entire spreadsheets wastes memory. Use usecols and skiprows to read only what you need:

# Read specific columns by name
df = pd.read_excel('employees.xlsx', usecols=['Name', 'Department', 'Salary'])

# Read columns by letter (Excel-style)
df = pd.read_excel('employees.xlsx', usecols='A,C,E')

# Read a range of columns
df = pd.read_excel('employees.xlsx', usecols='A:D')

# Use a callable for complex logic
df = pd.read_excel('employees.xlsx', 
                   usecols=lambda x: 'salary' in x.lower() or 'bonus' in x.lower())

Skip header rows, footer rows, or specific rows:

# Skip first 3 rows (often metadata or titles)
df = pd.read_excel('report.xlsx', skiprows=3)

# Skip specific rows by index
df = pd.read_excel('report.xlsx', skiprows=[0, 2, 5])

# Skip footer rows
df = pd.read_excel('report.xlsx', skipfooter=2)

# Combine: skip header and use a specific row as column names
df = pd.read_excel('report.xlsx', header=2, skipfooter=1)

Handling Headers and Column Names

Excel files often have non-standard header arrangements:

# No header row - pandas assigns default column names
df = pd.read_excel('data.xlsx', header=None)
print(df.columns)  # Int64Index([0, 1, 2, 3], dtype='int64')

# Specify custom column names
df = pd.read_excel('data.xlsx', header=None, 
                   names=['Product', 'Quantity', 'Price', 'Total'])

# Multi-level headers (rows 0 and 1 as headers)
df = pd.read_excel('data.xlsx', header=[0, 1])

# Use a different row as header
df = pd.read_excel('data.xlsx', header=3)

Data Type Control

Explicit data type specification prevents parsing errors and reduces memory usage:

# Specify dtypes for columns
dtypes = {
    'Product_ID': str,
    'Quantity': int,
    'Price': float,
    'Active': bool
}
df = pd.read_excel('inventory.xlsx', dtype=dtypes)

# Keep specific columns as strings (prevent auto-conversion)
df = pd.read_excel('data.xlsx', dtype={'ZIP_Code': str, 'Account_Number': str})

# Handle dates explicitly
df = pd.read_excel('transactions.xlsx', parse_dates=['Transaction_Date', 'Ship_Date'])

# Custom date parsing
df = pd.read_excel('data.xlsx', 
                   parse_dates=['Date'],
                   date_parser=lambda x: pd.to_datetime(x, format='%d/%m/%Y'))

Handling Missing Data

Control how pandas interprets missing or null values:

# Specify additional NA values
df = pd.read_excel('data.xlsx', na_values=['N/A', 'Missing', '--', 'TBD'])

# Different NA values per column
na_dict = {
    'Sales': ['N/A', 'No Data'],
    'Region': ['Unknown', 'TBD']
}
df = pd.read_excel('data.xlsx', na_values=na_dict)

# Keep default NA values and add more
df = pd.read_excel('data.xlsx', na_values=['Custom_NA'], keep_default_na=True)

# Disable default NA value interpretation
df = pd.read_excel('data.xlsx', keep_default_na=False)

Working with Index Columns

Set one or more columns as the DataFrame index during import:

# Use first column as index
df = pd.read_excel('data.xlsx', index_col=0)

# Use a named column as index
df = pd.read_excel('employees.xlsx', index_col='Employee_ID')

# Multi-level index
df = pd.read_excel('sales.xlsx', index_col=[0, 1])  # Region and Store_ID

# Combine with other parameters
df = pd.read_excel('data.xlsx', 
                   sheet_name='Summary',
                   index_col='Date',
                   parse_dates=True)

Performance Optimization for Large Files

Reading large Excel files can be slow. Apply these strategies:

# Read only necessary columns
df = pd.read_excel('large_file.xlsx', 
                   usecols=['ID', 'Amount', 'Date'],
                   dtype={'ID': 'int32', 'Amount': 'float32'})

# Read in chunks by row ranges
chunk_size = 10000
df_chunk1 = pd.read_excel('large_file.xlsx', nrows=chunk_size)
df_chunk2 = pd.read_excel('large_file.xlsx', 
                          skiprows=range(1, chunk_size + 1), 
                          nrows=chunk_size)

# Use converters for complex transformations during read
converters = {
    'Amount': lambda x: float(x.replace('$', '').replace(',', ''))
}
df = pd.read_excel('financial.xlsx', converters=converters)

For extremely large files, consider converting to CSV or Parquet format first:

# One-time conversion
df = pd.read_excel('huge_file.xlsx')
df.to_parquet('huge_file.parquet')

# Future reads are much faster
df = pd.read_parquet('huge_file.parquet')

Handling Excel Formulas and Formatting

read_excel() reads calculated values, not formulas:

# Reads the result of =SUM(A1:A10), not the formula
df = pd.read_excel('calculations.xlsx')

# Merged cells: pandas reads the top-left value
df = pd.read_excel('formatted.xlsx')

# To preserve formatting, use openpyxl directly
from openpyxl import load_workbook
wb = load_workbook('formatted.xlsx')
ws = wb.active

# Extract with formatting
for row in ws.iter_rows(values_only=False):
    for cell in row:
        print(f"{cell.value} - Font: {cell.font.name}")

Error Handling and Validation

Robust code handles common Excel reading errors:

import pandas as pd
from pathlib import Path

def safe_read_excel(filepath, sheet_name=0, **kwargs):
    try:
        if not Path(filepath).exists():
            raise FileNotFoundError(f"File not found: {filepath}")
        
        df = pd.read_excel(filepath, sheet_name=sheet_name, **kwargs)
        
        if df.empty:
            raise ValueError("DataFrame is empty")
        
        return df
    
    except Exception as e:
        print(f"Error reading Excel file: {e}")
        return None

# Usage
df = safe_read_excel('data.xlsx', 
                     sheet_name='Sales',
                     usecols=['Date', 'Amount'],
                     parse_dates=['Date'])

if df is not None:
    print(f"Successfully loaded {len(df)} rows")

The read_excel() function provides comprehensive control over Excel data import. Combine parameters strategically to handle real-world messy spreadsheets while maintaining performance and memory efficiency.

Liked this? There's more.

Every week: one practical technique, explained simply, with code you can use immediately.