Tablas que comparten una clave
Los datos reales rara vez llegan en una sola tabla. Tus posiciones están en una, el sector de cada símbolo en otra, los precios en una tercera. Lo que las conecta es una clave, aquí el símbolo. left.merge(right, on="clave") encuentra, para cada fila de left, las filas de right con la misma clave y las pone una al lado de otra. Es la misma idea que un join en SQL.
import pandas as pd
positions = pd.DataFrame({"symbol": ["AAPL", "MSFT", "TSLA", "GOOG"], "qty": [100, 50, 20, 10]})
reference = pd.DataFrame({"symbol": ["AAPL", "MSFT", "GOOG", "F"], "sector": ["tech", "tech", "tech", "auto"]})
merged = positions.merge(reference, on="symbol")
print(merged)
# symbol qty sector
# 0 AAPL 100 tech
# 1 MSFT 50 tech
# 2 GOOG 10 techinner, left, right y outer
TSLA desapareció del resultado anterior, y F también. El valor por defecto, how="inner", conserva solo las claves presentes en ambas tablas. Indica lo que quieres: how="left" conserva todas las filas de la tabla izquierda y rellena con NaN los valores que faltan a la derecha, how="right" hace lo contrario y how="outer" conserva las claves de ambas. Para enriquecer una tabla casi siempre quieres left, porque nunca pierde una fila en silencio.
# how="left" keeps every position; TSLA has no sector, so it gets NaN
left = positions.merge(reference, on="symbol", how="left")
print(left)
# symbol qty sector
# 0 AAPL 100 tech
# 1 MSFT 50 tech
# 2 TSLA 20 NaN
# 3 GOOG 10 tech
# how="outer" keeps the keys of both tables (sorted); quantities become floats because of the NaN
print(positions.merge(reference, on="symbol", how="outer"))
# symbol qty sector
# 0 AAPL 100.0 tech
# 1 F NaN auto
# 2 GOOG 10.0 tech
# 3 MSFT 50.0 tech
# 4 TSLA 20.0 NaNEncontrar lo que no coincide
Un NaN silencioso puede esconder un problema de datos, así que compruébalo. indicator=True añade una columna _merge que indica, para cada fila, si la clave se encontró en left_only, right_only o both. Filtrarla responde a preguntas como «¿qué posiciones no tienen precio?». Las claves duplicadas son la otra trampa: si una clave aparece dos veces a la derecha, cada fila coincidente de la izquierda se repite. Pasa validate="many_to_one" (o "one_to_one") y pandas lanza un error en vez de cambiar en silencio el número de filas.
# Which positions have no sector?
check = positions.merge(reference, on="symbol", how="left", indicator=True)
print(check)
# symbol qty sector _merge
# 0 AAPL 100 tech both
# 1 MSFT 50 tech both
# 2 TSLA 20 NaN left_only
# 3 GOOG 10 tech both
print(check.loc[check["_merge"] == "left_only", "symbol"].tolist()) # ['TSLA']# Duplicate keys repeat rows: AAPL has two quotes, so the AAPL position appears twice
quotes = pd.DataFrame({"symbol": ["AAPL", "AAPL", "MSFT"], "price": [184.25, 184.5, 410.1]})
print(len(positions.merge(quotes, on="symbol"))) # 3
# validate turns the surprise into an error
try:
positions.merge(quotes, on="symbol", validate="one_to_one")
except Exception as error:
print(type(error).__name__) # MergeErrorClaves con nombres distintos y columnas en conflicto
Cuando la clave tiene nombres distintos en las dos tablas, usa left_on="symbol", right_on="ticker"; se conservan ambas columnas. Cuando las dos tablas tienen una columna con el mismo nombre que no es la clave (aquí qty), pandas conserva las dos y añade los sufijos _x y _y. Elige otros más claros con suffixes=("_a", "_b"). Tras un outer join las cantidades que faltan son NaN y la columna es de coma flotante: usa fillna(0) y luego astype("int64") para recuperar enteros.
# Different key names: both columns are kept
book_a = pd.DataFrame({"symbol": ["AAPL", "MSFT"], "qty": [100, 50]})
book_b = pd.DataFrame({"ticker": ["MSFT", "TSLA"], "qty": [-20, 30]})
print(book_a.merge(book_b, left_on="symbol", right_on="ticker", how="outer", suffixes=("_a", "_b")))
# symbol qty_a ticker qty_b
# 0 AAPL 100.0 NaN NaN
# 1 MSFT 50.0 MSFT -20.0
# 2 NaN NaN TSLA 30.0
# Same key name, outer join, suffixes, then back to whole numbers
both = book_a.merge(book_b.rename(columns={"ticker": "symbol"}), on="symbol", how="outer", suffixes=("_a", "_b"))
both[["qty_a", "qty_b"]] = both[["qty_a", "qty_b"]].fillna(0).astype("int64")
print(both)
# symbol qty_a qty_b
# 0 AAPL 100 0
# 1 MSFT 50 -20
# 2 TSLA 0 30len() antes y después, usa how="left" cuando quieras conservar todas las filas y añade validate= cuando creas que las claves son únicas.left.merge(right, on="clave")empareja filas de dos tablas que comparten una clave;left_on/right_ongestionan nombres distintos.how="inner"(por defecto) conserva las claves de ambas,"left"todas las filas de la izquierda,"outer"todas las claves.indicator=Truemuestra lo que no coincide;validate=ysuffixes=protegen de claves duplicadas y nombres en conflicto.
A continuación: los rendimientos simples, que convierten una serie de precios en el cambio porcentual de un periodo al siguiente.
Lab: qué se califica
- Añadir el sector de cada operación con
attach_sector25 pts - Valorar las posiciones que tienen precio con
value_positions25 pts - Encontrar las posiciones sin precio con
unpriced_symbols25 pts - Unir dos libros en una sola tabla con
combine_books25 pts