σμimplied vol · KKmax(S−K,0)udβρᵢⱼfat tailsPnL · drawdownyield · TdSₜ = μSₜ dt + σSₜ dWₜC = S·N(d₁) − K·e⁻ʳᵀ·N(d₂)σₚ² = wᵀΣwd₁ = [ln(S/K) + (r + σ²⁄2)T] ⁄ σ√T∂V/∂t + ½σ²S²·∂²V/∂S² + rS·∂V/∂S − rV = 0Sharpe = (Rₚ − R_f) ⁄ σₚVaR₉₉ = μ − 2.33σρ = Cov(X,Y) ⁄ σₓσᵧE[Rₚ] = Σ wᵢ·E[Rᵢ]φ(x) = e^(−x²/2) ⁄ √2πE[Rᵢ] = R_f + βᵢ(E[Rₘ] − R_f)dXₜ = θ(μ − Xₜ)dt + σ dWₜσ²ₜ = ω + α·ε²ₜ₋₁ + β·σ²ₜ₋₁C − P = S − K·e⁻ʳᵀΔ = ∂V/∂S Γ = ∂²V/∂S²p = (e^(rΔt) − d) ⁄ (u − d)rₜ = ln(Pₜ ⁄ Pₜ₋₁)E[Xₜ₊₁ | ℱₜ] = Xₜf* = (bp − q) ⁄ bz = (x − μ) ⁄ σP(N=k) = λᵏe^(−λ) ⁄ k!Θ = −∂V/∂Tν = S·φ(d₁)·√Tmin wᵀΣw s.t. wᵀμ = μ*∫Σ∂∞λ
σμimplied vol · KKmax(S−K,0)udβρᵢⱼfat tailsPnL · drawdownyield · TdSₜ = μSₜ dt + σSₜ dWₜC = S·N(d₁) − K·e⁻ʳᵀ·N(d₂)σₚ² = wᵀΣwd₁ = [ln(S/K) + (r + σ²⁄2)T] ⁄ σ√T∂V/∂t + ½σ²S²·∂²V/∂S² + rS·∂V/∂S − rV = 0Sharpe = (Rₚ − R_f) ⁄ σₚVaR₉₉ = μ − 2.33σρ = Cov(X,Y) ⁄ σₓσᵧE[Rₚ] = Σ wᵢ·E[Rᵢ]φ(x) = e^(−x²/2) ⁄ √2πE[Rᵢ] = R_f + βᵢ(E[Rₘ] − R_f)dXₜ = θ(μ − Xₜ)dt + σ dWₜσ²ₜ = ω + α·ε²ₜ₋₁ + β·σ²ₜ₋₁C − P = S − K·e⁻ʳᵀΔ = ∂V/∂S Γ = ∂²V/∂S²p = (e^(rΔt) − d) ⁄ (u − d)rₜ = ln(Pₜ ⁄ Pₜ₋₁)E[Xₜ₊₁ | ℱₜ] = Xₜf* = (bp − q) ⁄ bz = (x − μ) ⁄ σP(N=k) = λᵏe^(−λ) ⁄ k!Θ = −∂V/∂Tν = S·φ(d₁)·√Tmin wᵀΣw s.t. wᵀμ = μ*∫Σ∂∞λ
uantCraft AcademySkills
Ctrl K
AO
+1.2k Learners
LoginJoin ↗
Skills
AO
+1.2k Learners
Login
  • English
  • Français
  • Español
  • العربية
  • Português
  • Deutsch
  • 日本語
  • 中文
Python skill›Module 2 · NumPy & pandas for market data›Topic 2.5·~40 minBeginner

pandas Series

Skill positionModule 2 of 11 · Topic 5 of 20
2.5 pandas Series
—Lab score— / 100Total XP—Streak—

An array with labels

A pandas Series is a one-dimensional array in which every value has a label, kept in the Series' index. Instead of remembering that position 1 is Microsoft, you ask for prices["MSFT"]. A Series has a name, a dtype like a NumPy array, and it supports the same whole-array arithmetic, so prices * 2, prices.mean() and prices > 180 all work. Import pandas by convention as import pandas as pd; it is built on NumPy, which you met in the previous topics.

import pandas as pd

prices = pd.Series([184.25, 410.2, 171.0], index=["AAPL", "MSFT", "GOOG"], name="price")
print(prices)
# AAPL    184.25
# MSFT    410.20
# GOOG    171.00
# Name: price, dtype: float64

print(prices["AAPL"], prices.iloc[1])    # 184.25 410.2   by label, by position
print(prices.index.tolist())             # ['AAPL', 'MSFT', 'GOOG']
print(prices[prices > 180])              # keeps AAPL and MSFT, labels intact
print(prices.mean(), prices.idxmax())    # 255.15 MSFT

# A dict also builds a Series: keys become the index
print(pd.Series({"AAPL": 1, "MSFT": 2}))

Alignment: operations match labels, not positions

When you combine two Series, pandas aligns them by label first. Labels present in only one of them give NaN (not a number, the marker for a missing value) in the result, and the result's index is the union of both indexes. This is exactly what you want for positions held in different accounts. Use a.add(b, fill_value=0) to treat a missing label as zero instead of producing NaN. Reading by label also differs from NumPy: s["MSFT"] is by label, while s.iloc[1] is by position, and a label that does not exist raises KeyError.

import pandas as pd

a = pd.Series([100, 200], index=["AAPL", "MSFT"])
b = pd.Series([50, 10], index=["MSFT", "GOOG"])

print(a + b)
# AAPL      NaN
# GOOG      NaN
# MSFT    250.0

print(a.add(b, fill_value=0))
# AAPL    100.0
# GOOG     10.0
# MSFT    250.0

# Selecting by a list of labels, and asking for one that is missing
prices = pd.Series([184.25, 410.2], index=["AAPL", "MSFT"])
print(prices.reindex(["MSFT", "TSLA"]).tolist())   # [410.2, nan]

Sorting, ranking and rebasing

s.sort_values(ascending=False) orders by value and carries the labels with it, s.sort_index() orders by label, and s.abs() takes absolute values. Ordering plus slicing finds the biggest movers: r.abs().sort_values(ascending=False).index[:3] gives the three labels with the largest absolute return. To compare assets that trade at different price levels, rebase each to 100 at the start: s / s.iloc[0] * 100, which keeps the index and name. Missing values (NaN) are skipped by sum, mean and friends; s.isna() marks them.

import pandas as pd

r = pd.Series([0.01, -0.03, 0.02], index=["x", "y", "z"])
print(r.abs().sort_values(ascending=False))
# y    0.03
# z    0.02
# x    0.01
# dtype: float64

prices = pd.Series([184.25, 410.2, 171.0], index=["AAPL", "MSFT", "GOOG"], name="price")
print(prices / prices.iloc[0] * 100)           # rebased to 100 at the first value
# AAPL    100.000000
# MSFT    222.632293
# GOOG     92.808684
# Name: price, dtype: float64

gappy = pd.Series([1.0, None, 3.0])
print(gappy.sum(), gappy.mean(), gappy.isna().tolist())   # 4.0 2.0 [False, True, False]
import pandas as pd

# A stray space and a lower-case letter make "the same" symbol two labels
raw = pd.Series([1, 2], index=["AAPL", "aapl "])
print(raw.index.is_unique)                     # True: they look different

raw.index = raw.index.str.strip().str.upper()  # clean the labels
print(raw.index.is_unique)                     # False: now both are "AAPL"
print(raw["AAPL"].tolist())                    # [1, 2]   a duplicate label returns a Series

merged = raw.groupby(level=0).sum()            # combine duplicates (groupby comes soon)
print(merged.to_dict())                        # {'AAPL': 3}
Labels are data too: duplicates and typosAlignment is only as good as the labels. "AAPL" and "aapl " are different labels, so a typo silently produces NaN rows; clean labels (strip, upper) before combining. An index can contain duplicates, and then s["AAPL"] returns a Series instead of a value, so check s.index.is_unique for data that should have one value per label. Operations return new Series, leaving the original untouched, which is the behaviour the lab tests check.
  • pd.Series(values, index=labels, name=...); read by label s["A"] or position s.iloc[0].
  • Operations align on labels (union index, NaN for missing); a.add(b, fill_value=0) treats a gap as 0.
  • sort_values, abs, idxmax, s / s.iloc[0] * 100; results are new Series and the original stays unchanged.

Next up: pandas DataFrames, tables made of Series that share one index.

Lab · what's graded

  • Label prices by symbol with price_series25 pts
  • Add two accounts by label with total_exposure25 pts
  • Find the biggest movers with top_movers25 pts
  • Put every price on a base of 100 with rebase_to_10025 pts
Explorer · lab-38-1
▾ lab-38-1
…
bash · in-browser
learner@quantcraft:~/lab-38-1$
Python not loaded yetPythonLn 1, Col 1Spaces: 4UTF-8

Reset this topic?

Everything in lab-38-1 goes back to the starter files and the terminal is cleared. Your current solution will be lost — this can't be undone.

Will be reset
  • main.py → starter code
  • Terminal history and test output
  • Unsubmitted changes
Will be kept
  • Best lab score
  • Your XP and streak
  • Lecture progress and notes

Log in to run your code.

Every lecture is free to read. Create an account or log in to run code, run tests and submit this lab.

  • In-browser IDE with a bash terminal — nothing to install
  • Instant grading, XP and your place on the skill
Create an accountLog in
OR
Continue with GitHub