A Series is a one-dimensional labelled data structure in pandas: each value has an index label, and the Series has a data type (
dtype). In quant work, it can hold prices indexed by instrument or returns indexed by date. Labels let calculations match the intended observations even after their row order changes.This is different from a mathematical series, which sums terms. A pandas Series is also not automatically a time series: its index might contain tickers, scenarios or ordinary integers rather than dates.
Labels carry meaning; positions carry order
Think of a Series as one column of values together with an index. A DataFrame has two labelled axes and can hold several columns with different dtypes. A Series has one value axis and one dtype; an
object dtype can nevertheless contain mixed Python objects. Its index labels are separate from that value dtype.Use
.loc[label] to select by label and .iloc[position] to select by zero-based position. Integer labels are still labels to .loc; do not infer that label 0 means the first observation. These explicit accessors also avoid the changing historical meaning of bare integer indexing with s[0].Reordering should not change the pairing
Suppose fictional instruments A and B have prices of 100 and 50 under a common USD-per-unit convention. We want a weighted price summary with dimensionless coefficients 0.6 for A and 0.4 for B. The weights arrive in the opposite row order:
import pandas as pd
prices = pd.Series({"A": 100.0, "B": 50.0}, name="price_usd")
weights = pd.Series({"B": 0.4, "A": 0.6}, name="weight")
assert prices.index.is_unique and weights.index.is_unique
assert set(prices.index) == set(weights.index)
assert prices.notna().all() and weights.notna().all()
print(prices.ndim, prices.shape, str(prices.dtype))
# 1 (2,) float64
print(prices.loc["A"], prices.iloc[0])
# 100.0 100.0
print(f"Label matched: {prices.dot(weights):.1f}")
# Label matched: 80.0
print(f"Position matched: {prices.to_numpy() @ weights.to_numpy():.1f}")
# Position matched: 70.0For these unique matching indexes,
Series.dot aligns the labels before taking the dot product:Converting both Series to NumPy arrays removes their labels. The second product instead computes . It is valid positional arithmetic with the wrong pairing for this task. Before crossing into an array-based model, establish and check a common observation order.
Labels preserve the intended pairing even when the two Series use different row orders. All prices use a common USD-per-unit convention; the weights are dimensionless.
The result is a weighted price summary in the stated convention. It is not a portfolio return: the inputs are prices. Nor does it establish portfolio market value: that requires quantities held and instrument-specific valuation conventions. Labels protect identity, but cannot supply economic meaning or validate units.
A successful calculation can still be incomplete
Elementwise arithmetic between differently indexed Series normally retains the union of labels and introduces missing values where a partner is absent. A reduction can then hide the problem because
.sum() skips missing values by default:import pandas as pd
prices = pd.Series({"A": 100.0, "B": 50.0})
weights = pd.Series({"A": 0.6, "C": 0.4})
contributions = prices * weights
print(contributions.isna().to_dict())
# {'A': False, 'B': True, 'C': True}
print(f"Default sum: {contributions.sum():.1f}")
# Default sum: 60.0
print(f"No skipping: {contributions.sum(skipna=False):.1f}")
# No skipping: nanThe 60 is only A's contribution. It is not a complete summary of the requested universe. Choose a missing-data policy deliberately; do not fill absent prices or weights with zero just to obtain a number. The first example checks matching labels, uniqueness and nonmissing inputs before calculation.
Index labels can repeat. With duplicates, a scalar label selection may return several observations, and some operations such as reindexing can fail. If one row must represent one instrument, enforce uniqueness rather than treating the index as an automatically validated key.
The examples were executed with Python 3.12.14 and pandas 2.2.3. Current pandas 3.x documentation describes Copy-on-Write semantics that differ from older defaults. A Series is mutable; assigning another variable to it does not copy it. For edits, target the intended object directly with
.loc, and do not rely on changing a selected Series to mutate a parent DataFrame.Continue with financial data
The pandas lesson develops labelled tables, return calculations and data checks. Tabular and Time-Indexed Data extends alignment to calendars and resampling. Both have public previews; full lessons require membership.
Sources
- pandas: Intro to data structures, Series, dtypes and label alignment.
- pandas: Indexing and selecting data,
.locand.iloc. - pandas.Series.dot, labelled dot-product contract.
- pandas 2.2.3: Series.sum,
skipnaand missing values. - pandas: Duplicate Labels, selection and reindexing consequences.
- pandas: Copy-on-Write migration, pandas 3.0 changes.