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.
Pairing stored positions instead gives . Both inputs contain two numbers. Equal lengths establish neither equal asset order nor equal asset identity.
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.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.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.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.
Sources
- pandas: Vectorized operations and label alignment with Series — automatic matching by label and the union of indexes.
- pandas: Series.to_numpy — conversion to an array of values.
- pandas: Series.reindex — conforming data to a target index.
- pandas: Series.align — explicit inner, outer, left and right matching policies.
- pandas: Duplicate Labels — uniqueness checks and operations affected by duplicates.
- pandas: Series.sum — missing-value exclusion by default.