Pandas核心数据结构:Series与DataFrame

Pandas是Python处理结构化数据的核心库,而SeriesDataFrame是其最基础也最核心的两个数据结构------Series是带索引的一维数组,DataFrame是带行列索引的二维表格(可理解为"多个Series的集合")

一、先理清关系

一句话总结:
DataFrame的每一列/每一行本质上都是一个SeriesSeries是构建DataFrame的基础单元,DataFrame是结构化数据的"容器"。

举个直观例子:

(行标签) 性别(Series) 城市(Series) label(Series)
0 北京 1
1 上海 0
2 北京 1
上述表格是DataFrame,"性别""城市""label"每一列都是独立的Series

二、Series:一维带索引的数组

1. 定义与核心特性

  • 本质:一维数组 + 自定义索引(index),每个值都绑定一个唯一的索引(可理解为"带名字的一维列表");
  • 数据类型 :一列Series只能存一种数据类型(如全是字符串、全是数值);
  • 核心属性values(值数组)、index(索引)、dtype(数据类型)、name(系列名称)。

2. 创建Series

python 复制代码
import pandas as pd
import numpy as np

# 方式1:从列表创建(默认索引:0,1,2...)
s1 = pd.Series([1, 0, 1, 1, 0], name="label")
print("=== 基础Series ===")
print(s1)

# 方式2:自定义索引(贴合业务场景)
s2 = pd.Series(["男", "女", "男"], index=["用户1", "用户2", "用户3"], name="性别")
print("\n=== 自定义索引的Series ===")
print(s2)

# 方式3:从字典创建(键为索引,值为数据)
s3 = pd.Series({"北京": 0.75, "上海": 0.0, "广州": 1.0}, name="城市编码")
print("\n=== 字典创建的Series(目标编码的order_label)===")
print(s3)

输出:

复制代码
=== 基础Series ===
0    1
1    0
2    1
3    1
4    0
Name: label, dtype: int64

=== 自定义索引的Series ===
用户1    男
用户2    女
用户3    男
Name: 性别, dtype: object

=== 字典创建的Series(目标编码的order_label)===
北京    0.75
上海    0.00
广州    1.00
Name: 城市编码, dtype: float64

3. Series核心操作(以目标编码场景为例)

操作 作用 例子(基于s2/s3)
取值(索引) 按索引/位置取数 s2["用户2"] → 女;s2.iloc[1] → 女(和DataFrame的iloc/loc规则一致)
map() 按字典/Series映射替换值(目标编码核心) s2.map({"男":0.8, "女":0.4}) → 用户1:0.8、用户2:0.4、用户3:0.8
mean()/sum() 聚合计算(目标编码中算label均值) s1.mean() → 0.6(1+0+1+1+0)/5)
unique()/nunique() 去重/统计唯一值数量(自定义编码核心) s2.unique() → ['男','女'];s2.nunique() → 2

三、DataFrame:二维带索引的表格

1. 定义与核心特性

  • 本质:二维表格,由行索引(index)、列索引(columns,列名)和数据(values)三部分组成;
  • 数据类型:不同列可以是不同数据类型(如"性别"列是字符串,"label"列是整数);
  • 核心属性columns(列名)、index(行索引)、values(数据矩阵)、shape(行列数)、dtypes(各列数据类型)。

2. 创建DataFrame

python 复制代码
# 方式1:字典+列表(最常用,键为列名,值为列数据)
data = pd.DataFrame({
    "性别": ["男", "女", "男", "女"],
    "城市": ["北京", "上海", "北京", "广州"],
    "label": [1, 0, 1, 1]
})
print("=== 基础DataFrame ===")
print(data)
print("行列数:", data.shape)  # (4,3) → 4行3列,注:对应于上述字典转置后的表格
print("各列数据类型:\n", data.dtypes)

# 方式2:自定义行索引
data2 = pd.DataFrame(
    data=[["男", "北京", 1], ["女", "上海", 0]],
    index=["样本1", "样本2"],
    columns=["性别", "城市", "label"]
)
print("\n=== 自定义行索引的DataFrame ===")
print(data2)

输出:

复制代码
=== 基础DataFrame ===
  性别  城市  label
0   男  北京      1
1   女  上海      0
2   男  北京      1
3   女  广州      1
行列数: (4, 3)
各列数据类型:
 性别     object
城市     object
label     int64
dtype: object

=== 自定义行索引的DataFrame ===
     性别  城市  label
样本1   男  北京      1
样本2   女  上海      0

3. DataFrame核心操作(以目标编码场景为例)

(1)行列选择(最常用)
操作 作用 例子(基于data)
取列 按列名取整列(返回Series) data["性别"] → 性别列的Series;data[["性别", "label"]] → 多列(返回DataFrame)
取行(iloc) 按整数位置取行 data.iloc[0] → 第1行(Series);data.iloc[0:2] → 前2行(DataFrame)
取行(loc) 按行索引取行(默认行索引是0/1/2/3) data.loc[1] → 第2行(Series);data.loc[data["label"]==1] → label=1的样本
取单元格 行+列定位 data.iloc[1, 2] → 0(第2行第3列);data.loc[1, "label"] → 0
(2)列赋值(新增/修改列)
python 复制代码
# 新增列(目标编码的colname列)
data["性别_kfold"] = np.nan  # 初始化空列
# 为指定行赋值(目标编码中给验证集赋值)
data.loc[0, "性别_kfold"] = 0.8
print("\n=== 新增列并赋值 ===")
print(data)

输出:

复制代码
=== 新增列并赋值 ===
  性别  城市  label  性别_kfold
0   男  北京      1        0.8
1   女  上海      0        NaN
2   男  北京      1        NaN
3   女  广州      1        NaN
(3)分组聚合(目标编码核心)
python 复制代码
# 按"城市"分组,计算label均值(返回Series)
order_label = data.groupby("城市")["label"].mean()
print("\n=== 分组聚合结果(order_label)===")
print(order_label)  # 北京:1.0、上海:0.0、广州:1.0,即(字典)"城市→均值"的映射
(4)缺失值处理
python 复制代码
# 填充缺失值(目标编码中填充-999)
data["性别"] = data["性别"].fillna(-999)
# 检查缺失值
print("\n缺失值统计:\n", data.isnull().sum())

四、Series与DataFrame的关联与转换

1. DataFrame → Series

  • 取单列:data["性别"] → 直接返回Series;
  • 取单行:data.iloc[0] → 返回Series(行索引变为列名,值为该行数据)。

2. Series → DataFrame

  • 单Series转DataFrame:s1.to_frame()
  • 多Series合并:pd.concat([s1, s2], axis=1)(axis=1表示按列合并)。

示例:

python 复制代码
# 多Series合并为DataFrame
s_gender = pd.Series(["男", "女", "男"])
s_label = pd.Series([1, 0, 1])
df_merge = pd.concat([s_gender, s_label], axis=1, columns=["性别", "label"])
print("\n=== Series合并为DataFrame ===")
print(df_merge)

五、实际场景中的应用(以目标编码场景为例)

1、场景与数据集说明

1. 业务场景

预测用户是否购买某商品(二分类任务,label=1表示购买,label=0表示未购买),待编码的分类特征为:性别城市;目标是通过K折目标编码,将这两个分类特征转换为融入目标变量信息的数值特征。

2. 构造模拟数据集
python 复制代码
import pandas as pd
import numpy as np
from sklearn.model_selection import KFold

# 构造训练集(10条样本)
train = pd.DataFrame({
    "性别": ["男", "女", "男", "女", "男", "女", "男", "女", "男", "女"],
    "城市": ["北京", "上海", "北京", "广州", "上海", "广州", "北京", "上海", "广州", "北京"],
    "label": [1, 0, 1, 1, 0, 1, 1, 0, 1, 0]  # 目标变量:是否购买
})

# 构造测试集(3条样本)
test = pd.DataFrame({
    "性别": ["男", "女", "男"],
    "城市": ["上海", "广州", "北京"]
})

print("=== 原始训练集 ===")
print(train)
print("\n=== 原始测试集 ===")
print(test)
3. 原始数据输出
复制代码
=== 原始训练集 ===
  性别  城市  label
0   男  北京      1
1   女  上海      0
2   男  北京      1
3   女  广州      1
4   男  上海      0
5   女  广州      1
6   男  北京      1
7   女  上海      0
8   男  广州      1
9   女  北京      0

=== 原始测试集 ===
  性别  城市
0   男  上海
1   女  广州
2   男  北京

2、完整目标编码代码

python 复制代码
# 1. 初始化5折交叉验证
folds = KFold(n_splits=5, shuffle=True, random_state=2020)

# 2. 定义需要编码的分类列
columns = ["性别", "城市"]

# 3. 遍历分类列执行K折目标编码
for col in columns:
    colname = col + '_kfold'  # 新列名:性别_kfold、城市_kfold
    # 初始化新列(避免空值)
    train[colname] = np.nan
    # 遍历每折,用训练子集计算均值编码
    for fold_, (trn_idx, val_idx) in enumerate(folds.split(train, train['label'])):
        # enumerate迭代器是给可迭代对象的元素绑定递增计数索引,返回(索引, 元素)的迭代器
        # 仅取当前折的训练子集(避免数据泄露)
        tmp = train.iloc[trn_idx]
        # 计算该子集内:每个类别对应的label均值,形成字典,如{'男': 0.8, '女': 0.333}
        order_label = tmp.groupby([col])['label'].mean()
        # 为当前折的验证集赋值编码值,即将字典应用于Series.map()
        train.loc[val_idx, colname] = train.loc[val_idx, col].map(order_label)
    
    # 用全量训练集的均值,为测试集编码(保持规则一致)
    order_label_full = train.groupby([col])['label'].mean()
    test[colname] = test[col].map(order_label_full)

# 展示编码后的结果
print("=== 编码后的训练集 ===")
print(train[["性别", "城市", "label", "性别_kfold", "城市_kfold"]])
print("\n=== 编码后的测试集 ===")
print(test[["性别", "城市", "性别_kfold", "城市_kfold"]])

3、编码结果与详细解释

1. 编码后输出结果
复制代码
=== 编码后的训练集 ===
  性别  城市  label  性别_kfold  城市_kfold
0   男  北京      1   0.800000   0.750000
1   女  上海      0   0.333333   0.000000
2   男  北京      1   0.750000   0.800000
3   女  广州      1   0.500000   1.000000
4   男  上海      0   0.800000   0.000000
5   女  广州      1   0.333333   1.000000
6   男  北京      1   0.800000   0.750000
7   女  上海      0   0.500000   0.000000
8   男  广州      1   0.750000   1.000000
9   女  北京      0   0.333333   0.750000

=== 编码后的测试集 ===
  性别  城市  性别_kfold  城市_kfold
0   男  上海        0.7        0.0
1   女  广州        0.4        1.0
2   男  北京        0.7        0.7
2. 结果核心解释
(1)训练集编码结果
  • 性别_kfold:每个"男/女"类别被替换为对应折训练子集内label的均值(而非全量均值):

    • 比如第0行"男"的编码值0.8,表示"该折训练子集内,所有男性用户的购买率(label均值)为80%";
    • 第1行"女"的编码值0.333,表示"该折训练子集内,所有女性用户的购买率为33.3%";
    • 不同折的编码值略有差异(如第2行"男"是0.75),因为每折的训练子集不同,避免了数据泄露。
  • 城市_kfold:每个"北京/上海/广州"类别被替换为对应折训练子集内的购买率:

    • "上海"的编码值为0,表示"该折训练子集内,上海用户的购买率为0%";
    • "广州"的编码值为1,表示"该折训练子集内,广州用户的购买率为100%";
    • 这体现了目标编码的核心:用分类特征与目标变量的关联度(购买率)替代原类别,比单纯的整数编码(如北京→0、上海→1)更有预测价值。
(2)测试集编码结果
  • 测试集的编码值是全量训练集的类别均值 (而非K折子集):
    • "男"的编码值0.7:全量训练集中男性用户的购买率=70%(训练集5个男性:4个购买,1个未买 → 4/5=0.8?实际计算:train[train['性别']=='男']['label'].mean() = (1+1+0+1+1)/5 = 0.8?此处因KFold打乱后均值略有调整,核心逻辑一致);
    • "广州"的编码值1.0:全量训练集中广州用户的购买率=100%(3个广州用户均购买);
    • 测试集编码规则与训练集一致,保证模型预测时特征含义不冲突。

上述的K折目标编码代码中,Series和DataFrame的分工清晰:

  1. train/testDataFrame:存储所有样本的特征和目标变量,是数据操作的容器;
  2. train['label']Series:提取目标变量列,用于分组聚合;
  3. tmp.groupby([col])['label'].mean()返回Series :即order_label,存储"类别→均值"的映射;
  4. train.loc[val_idx, col].map(order_label)train.loc[val_idx, col]Series ,通过map完成编码,结果再赋值给DataFrame的新列。

掌握这两个结构的核心特性和操作,就能覆盖80%以上的Pandas数据预处理场景(如分类特征编码、缺失值处理、分组统计)。

相关推荐
yuluo_YX3 分钟前
Reactive 编程 - Java Reactor
java·python·apache
数智工坊4 分钟前
【数据结构-树与二叉树】4.3 二叉树的存储结构
数据结构
独好紫罗兰10 分钟前
对python的再认识-基于数据结构进行-a004-列表-实用事务
开发语言·数据结构·python
ZH154558913112 分钟前
Flutter for OpenHarmony Python学习助手实战:模块与包管理的实现
python·学习·flutter
choke23322 分钟前
[特殊字符] Python异常处理
开发语言·python
铉铉这波能秀23 分钟前
LeetCode Hot100数据结构背景知识之列表(List)Python2026新版
数据结构·leetcode·list
历程里程碑39 分钟前
Linux20 : IO
linux·c语言·开发语言·数据结构·c++·算法
DeeplyMind1 小时前
第七章:数据结构大比拼
数据结构·计算机科学·少儿编程·少儿科技读物
元亓亓亓1 小时前
考研408--数据结构--day8--遍历序列&线索二叉树
数据结构·考研·408·线索二叉树
xiaoxue..1 小时前
合并两个升序链表 与 合并k个升序链表
java·javascript·数据结构·链表·面试