由带标签的列组成的表
DataFrame 是二维表格:由一组列(每列是一个 Series)构成,这些列共享同一个行标签索引。它是市场数据分析的主力:每个代码或每一天一行,每个字段(价格、成交量、收益率)一列。可以用列的字典 pd.DataFrame({"price": [...], "volume": [...]}, index=symbols) 创建,也可以用行字典的列表创建。每一列有自己的 dtype(价格是 float64,成交量是 int64),不像 NumPy 数组所有元素共用一个 dtype。
import pandas as pd
df = pd.DataFrame(
{"price": [184.25, 410.2, 171.0], "volume": [200, 50, 300]},
index=["AAPL", "MSFT", "GOOG"],
)
print(df)
# price volume
# AAPL 184.25 200
# MSFT 410.20 50
# GOOG 171.00 300
print(df.shape, df.columns.tolist()) # (3, 2) ['price', 'volume']
print(df.dtypes.astype(str).to_dict()) # {'price': 'float64', 'volume': 'int64'}
rows = [{"symbol": "AAPL", "price": 184.25}, {"symbol": "MSFT", "price": 410.2}]
print(pd.DataFrame(rows)) # from a list of row dictionaries
# symbol price
# 0 AAPL 184.25
# 1 MSFT 410.20列、新增列与筛选行
df["price"] 是一列,即一个 Series;用列表的 df[["price"]] 则是只有一列的 DataFrame。新列来自对列的运算:df.assign(notional=df["price"] * df["volume"]) 返回带有额外一列的新表,df 保持不变(而写 df["notional"] = ... 会修改 df 本身)。对某列的布尔条件可以筛选行,df[df["volume"] >= 100];df.sort_values("volume", ascending=False) 对行排序,.head(n) 保留前 n 行。这些都是对整列一次性操作:有列操作可用时,千万别用 iterrows() 逐行循环。
import pandas as pd
df = pd.DataFrame(
{"price": [184.25, 410.2, 171.0], "volume": [200, 50, 300]},
index=["AAPL", "MSFT", "GOOG"],
)
bigger = df.assign(notional=df["price"] * df["volume"]) # a new frame
print(bigger)
# price volume notional
# AAPL 184.25 200 36850.0
# MSFT 410.20 50 20510.0
# GOOG 171.00 300 51300.0
print(df.columns.tolist()) # ['price', 'volume'] df is unchanged
print(df[df["volume"] >= 100]) # rows that pass the test
# price volume
# AAPL 184.25 200
# GOOG 171.00 300
print(df.sort_values("volume", ascending=False).head(2)) # the two biggest volumes
# price volume
# GOOG 171.00 300
# AAPL 184.25 200汇总与查看数据
信任一张表之前一定要先看看它。df.head() 显示前几行,df.shape 给出 (行数, 列数),df.dtypes 列出每列的类型,df.isna().sum() 统计每列的缺失值,df.describe() 汇总数值列(计数、均值、标准差、最小值、四分位数、最大值)。df.mean() 这样的列方法每列给出一个值;df.sum(axis=1) 每行给出一个值。本应是数字的列,dtype 却是 object 或 str,通常说明文本混进了该列。
import pandas as pd
df = pd.DataFrame(
{"price": [184.25, 410.2, 171.0], "volume": [200, 50, 300]},
index=["AAPL", "MSFT", "GOOG"],
)
print(df.describe().loc[["count", "mean", "max"]])
# price volume
# count 3.00 3.000000
# mean 255.15 183.333333
# max 410.20 300.000000
print(df.mean().to_dict()) # {'price': 255.15, 'volume': 183.33333333333334}
print(df.sum(axis=1).tolist()) # [384.25, 460.2, 471.0] one value per row
print(df.isna().sum().to_dict()) # {'price': 0, 'volume': 0}
# Changing a copy never changes the original (pandas 3 copies on write)
cheap = df[df["price"] < 200].copy()
cheap.loc["AAPL", "price"] = 0.0
print(df.loc["AAPL", "price"]) # 184.25import pandas as pd
rows = [
{"symbol": "AAPL", "price": 184.25, "volume": 200},
{"symbol": "MSFT", "price": 410.2, "volume": 50},
]
df = pd.DataFrame(rows).set_index("symbol") # make a column the row labels
print(df)
# price volume
# symbol
# AAPL 184.25 200
# MSFT 410.20 50
print(df.rename(columns={"price": "px"}).columns.tolist()) # ['px', 'volume']
print(df.drop(columns="volume").columns.tolist()) # ['price']
print(df.reset_index().columns.tolist()) # ['symbol', 'price', 'volume']assign、布尔筛选和 sort_values,它们都会返回新表。经典的 bug 是把新列写进参数,这会悄悄把该列加到调用者的表里。实验的测试会比较参数调用前后的内容,原因正在于此。pd.DataFrame({列: 值}, index=labels);df["c"]是 Series,df[["c"]]是 DataFrame;每列有自己的 dtype。assign向新表添加列;df[mask]筛选行;sort_values(...).head(n)取前 n 行。- 先查看:
shape、dtypes、head()、describe()、isna().sum();绝不修改调用者的表。
下一节:用 loc 和 iloc 选择数据,按标签或按位置精确选取行与列的两种方式。
实验:评分内容
- 用
make_frame构建价格与成交量表25 分 - 用
add_notional添加成交金额列25 分 - 用
filter_liquid只保留流动性好的行25 分 - 用
top_by按任意列给行排名25 分