Skip to content

Index alignment

How matching data by asset labels prevents a portfolio calculation from assigning the right numbers to the wrong holdings.

Index alignment matches observations by their labels before combining data. In pandas, arithmetic between two Series matches index labels rather than merely pairing the first value with the first value. For a quant, this is the difference between multiplying each asset's return by its own portfolio weight and producing a plausible number for the wrong holdings. Here, index means data labels, not a market benchmark.

The same assets, in a different order

Consider two fictional assets over one holding period. The beginning-of-period weights are A: 60% and B: 40%. Their simple returns over that period are A: +10% and B: −5%. Suppose the return data arrive in A, B order, while the weights are stored in B, A order.

The portfolio return, before costs and with no intervening trades or cash flows, is
Rp=0.60(0.10)+0.40(−0.05)=0.04=4%.R_p=0.60(0.10)+0.40(-0.05)=0.04=4\%.

Pairing stored positions instead gives 0.40(0.10)+0.60(−0.05)=0.01=1%0.40(0.10)+0.60(-0.05)=0.01=1\%. Both inputs contain two numbers. Equal lengths establish neither equal asset order nor equal asset identity.

Returns arrive as A +10%, B −5%, while weights arrive as B 40%, A 60%. Matching labels gives contributions +6 and −2 percentage points, totalling 4%. Pairing stored positions gives +4 and −3 percentage points, totalling an incorrect 1%.
Returns arrive as A +10%, B −5%, while weights arrive as B 40%, A 60%. Matching labels gives contributions +6 and −2 percentage points, totalling 4%. Pairing stored positions gives +4 and −3 percentage points, totalling an incorrect 1%.
Original fictional example. Beginning-of-period weights and same-period simple returns; contributions and totals are shown in percentage points and percent respectively. The positional calculation attaches each weight to the other asset.
import pandas as pd returns = pd.Series([0.10, -0.05], index=["A", "B"]) weights = pd.Series([0.40, 0.60], index=["B", "A"]) print(f"Label-aligned: {(returns * weights).sum():.1%}") # Label-aligned: 4.0% raw_result = (returns.to_numpy() * weights.to_numpy()).sum() print(f"Raw positional: {raw_result:.1%}") # Raw positional: 1.0%
to_numpy() returns values without the asset index. NumPy array multiplication then pairs positions. The conversion is a boundary at which you must already know that the orders agree.

Make the matching rule explicit

For this portfolio calculation, require exactly one non-missing value per asset on each side. Continuing the example:

if not returns.index.is_unique or not weights.index.is_unique: raise ValueError("Asset labels must be unique") if set(returns.index) != set(weights.index): raise ValueError("Asset universes differ") ordered_weights = weights.reindex(returns.index) if returns.isna().any() or ordered_weights.isna().any(): raise ValueError("A return or weight is missing") checked = (returns.to_numpy() * ordered_weights.to_numpy()).sum() print(f"Checked positional: {checked:.1%}") # Checked positional: 4.0%
reindex rearranges weights into the return index's order. Checking the label sets first prevents an extra weight from being silently discarded. Checking uniqueness first prevents a repeated asset label from making the intended one-to-one match ambiguous. pandas permits duplicate labels, but operations such as reindexing a non-unique source can raise an error; duplicate labels are not automatically aggregated.
For deliberate partial overlaps, left.align(right, join="inner") keeps shared labels; join="outer" keeps their union and introduces missing values where a counterpart is absent. Choose that policy explicitly. An inner alignment can remove a held asset from a portfolio calculation. Filling a missing return with zero asserts that the asset did not move; it does not recover the observation.
Ordinary arithmetic between differently indexed Series retains the union and marks unmatched results as missing. A subsequent .sum() skips missing values by default, so a seemingly reasonable total can conceal an incomplete calculation. Inspect missing data before reducing the result to one number.
With a DataFrame, labels also live on columns. Specify whether an explicit alignment should match rows, columns or both. Asset labels cannot compensate for matching the wrong date, currency or return horizon. In a backtest, a same-date label also does not establish that a weight or signal was available before trading; that requires a separate look-ahead bias check.

The examples were executed with Python 3.12.14 and pandas 2.2.3. The linked documentation was checked against pandas 3.1.0; it is a separate documentation version, not the runtime used here.

For a longer worked introduction, the pandas lesson covers labelled tables, returns and missing or duplicated observations. A public preview is available; the full lesson requires membership.

Sources

Last updated
  • pandas
  • data integrity
  • portfolio computations
← Back to the glossaryBack to top ↑