Skip to content

Python Pandas for Finance: Data Structures That Make Sense

pandas is the foundation of Python quant finance. Understanding its data structures—Series, DataFrame, Panel—transforms messy market data into analysis-ready arrays.

7 of 8 ~1 min

Python’s pandas library is the foundation of quantitative finance in Python. Almost every quant workflow—data cleaning, feature engineering, factor analysis, backtesting—uses pandas. Understanding its data structures deeply makes everything else faster.

The Three Core Data Structures

Series: One-Dimensional Labeled Array

A Series is a single column of data with an index:

import pandas as pd
import numpy as np

# Price series
prices = pd.Series([100.5, 101.2, 100.8, 102.1],
                  index=pd.to_datetime(['2024-01-02', '2024-01-03',
                                        '2024-01-04', '2024-01-05']))

# Daily returns
returns = prices.pct_change()  # Automatic element-wise operation

DataFrame: Two-Dimensional Table

A DataFrame is a table with rows (observations) and columns (variables):

# Multi-asset price DataFrame
prices_df = pd.DataFrame({
    'AAPL': [185.5, 186.2, 187.1],
    'MSFT': [374.2, 375.8, 378.1],
    'GOOGL': [141.8, 142.3, 143.9]
}, index=pd.to_datetime(['2024-01-02', '2024-01-03', '2024-01-04']))

Panel: Three-Dimensional (Deprecated)

pandas Panel (3D: item × major axis × minor axis) is deprecated. Use xarray for multi-dimensional data, or MultiIndex DataFrames.

Finance-Specific Operations

Returns and Log Returns

def compute_returns(prices):
    """Simple returns: (P_t - P_{t-1}) / P_{t-1}"""
    return prices.pct_change()

def compute_log_returns(prices):
    """Log returns: ln(P_t / P_{t-1})"""
    return np.log(prices / prices.shift(1))

Log returns add across time: log_return_2d = log_return_1d + log_return_2d. Simple returns average differently. Use log returns for time-series aggregation.

Resampling for Different Frequencies

# Daily to monthly (last day of month)
monthly_prices = prices.resample('M').last()

# Daily to monthly (average)
monthly_avg = prices.resample('M').mean()

# Business quarter end
quarterly = prices.resample('Q').last()

# Annual
annual = prices.resample('Y').last()

Rolling Windows

# 20-day moving average
ma_20 = prices.rolling(window=20).mean()

# 20-day rolling volatility
vol_20 = prices.pct_change().rolling(window=20).std() * np.sqrt(252)

# Exponentially weighted moving average
ewma = prices.ewm(halflife=10).mean()

Multi-Index for Panel Data

For multi-asset, multi-date data, use MultiIndex:

# Create multi-index from tuples
index = pd.MultiIndex.from_tuples([
    ('2024-01-02', 'AAPL'),
    ('2024-01-02', 'MSFT'),
    ('2024-01-03', 'AAPL'),
    ('2024-01-03', 'MSFT')
], names=['date', 'asset'])

returns = pd.Series([0.012, 0.008, -0.003, 0.005], index=index)

# Pivot to wide format
returns_wide = returns.unstack(level='asset')

Common Pitfalls

Alignment bug. When you combine two Series with different indexes, pandas aligns by index. If Apple’s data has a holiday that Microsoft doesn’t, the result has NaN. Always dropna() or ffill() before calculations.

Mutation. Many pandas operations return new objects by default, but some (like df.sort_values()) can mutate in place. Be explicit: use .copy() when you need to preserve the original.

SettingWithCopyWarning. Chained indexing like df[df['col'] > 0]['new_col'] = value produces a warning. Use df.loc or df.iloc instead:

# Correct
df.loc[df['col'] > 0, 'new_col'] = value

# Wrong (produces warning, may not work)
df[df['col'] > 0]['new_col'] = value

References

  • McKinney, Python for Data Analysis, Ch. 5-10. O’Reilly, 2023. ISBN: 978-1-492-07231-9.
  • VanderPlas, Python Data Science Handbook, Ch. 3. O’Reilly, 2016. ISBN: 978-1-491-91205-8.