From separate inputs to a portfolio value
A useful pandas workflow has four jobs: read data, check its meaning, transform it and produce a result. The arithmetic is often the shortest part.
This self-contained example checks the matching contract before computing values:
import pandas as pd
holdings = pd.DataFrame({"instrument": ["A", "B"], "quantity": [2, 3]})
quotes = pd.DataFrame({"instrument": ["B", "A"], "price_usd": [50, 100]})
def value_book(holdings: pd.DataFrame, quotes: pd.DataFrame) -> pd.DataFrame:
"""Value one position per instrument using one complete mark per holding."""
if holdings["instrument"].isna().any() or quotes["instrument"].isna().any():
raise ValueError("An instrument identifier is missing")
book = holdings.merge(
quotes, on="instrument", how="left", validate="one_to_one"
)
if book[["quantity", "price_usd"]].isna().any().any():
raise ValueError("A held quantity or price is missing")
return book.assign(value_usd=book["quantity"] * book["price_usd"])
book = value_book(holdings, quotes)
print(book.set_index("instrument")["value_usd"].to_dict())
# {'A': 200, 'B': 150}
print(f"Portfolio value: USD {book['value_usd'].sum():.2f}")
# Portfolio value: USD 350.00validate="one_to_one" rejects repeated instrument keys on either side; it does not prove that a quote exists for every holding. The separate missing-value check catches an absent or missing mark before the sum can conceal it. Unheld instruments in the quote table are deliberately excluded. Null identifiers are rejected because pandas can match null merge keys to each other. Merge referenceassign returns a new DataFrame with the value column. Multiplying two columns applies the operation across their rows; it does not need a handwritten loop over instruments. The new column remains attached to the instrument identifiers. pandas also supports index alignment when combining labelled Series, a related mechanism with its own rules. Assign referenceThe example uses small integer inputs, so the displayed products are exact. It is a teaching workflow, not a complete feed validator: real inputs also need numeric-type, finite-value, range, currency and timestamp checks. Fixed-width integer columns can overflow at large magnitudes, and floating-point columns require a rounding policy. A matching identifier alone does not establish a current, tradable or economically comparable price.
What else belongs in pandas?
| Task | Typical pandas operation | Decision the analyst still owns |
|---|---|---|
| Read a file | read_csv, read_parquet | Column types, missing-value codes and source provenance |
| Select observations | .loc, Boolean filters | Which instruments and dates belong in the sample |
| Combine tables | merge, concat | Keys, join policy and expected match counts |
| Summarise or reshape | groupby, pivot | Aggregation rule and treatment of duplicate observations |
| Work through time | resample, rolling | Calendar, observation order, window and information timing |
Keep the version with the result
.loc assignment on that table rather than chained indexing. String inference also changed in 3.0, and date-range construction no longer universally defaults to nanosecond units. Record versions and inspect dtypes; specify required string, timestamp-unit and timezone contracts where they matter. Migration guide, 3.0 release notesSources
- pandas documentation — library scope and the documentation version checked.
- What kind of data does pandas handle? — DataFrame, Series and column-oriented operations.
- DataFrame.merge — left joins, uniqueness validation and null-key matching.
- DataFrame.assign — a new table containing derived columns.
- Migration Guides — Copy-on-Write and default string dtype changes.
- What's new in pandas 3.0 — changed date-range unit inference.