aopstudio 的个人博客

记录精彩的程序人生

AOP=art of programming=编程的艺术=程艺
  menu
41 文章
0 浏览
8 当前访客
ღゝ◡╹)ノ❤️

Pandas用Series构建DataFrame:到底是行还是列?

在数据分析的世界里,pandas.DataFrame 是最常用的数据结构之一。然而,许多初学者甚至中级用户都会遇到一个常见的困惑:为什么在构造DataFrame时,有时候 Series 变成一列,有时候却变成一行?为什么Pandas的Series底层由NumPy数组提供支撑,但是NumPy ndarray和 Pandas DataFrame在行和列的定义上有时候看起来是“反过来的”?

本文将系统梳理 DataFrame 的构造逻辑,揭示其背后的设计哲学,并强调一个至关重要的概念:样本 vs. 特征。掌握这些原则,你将不再被“按行还是按列”所困扰。


一、核心概念:样本 vs. 特征 —— 符合人类直觉的表格设计

在数据分析中,样本(Sample)特征(Feature) 的区别是理解表格结构的基础。

核心概念:

概念含义在表格中的位置
样本(Sample)一个独立的观测或实例(如一个人、一次实验)一行
特征(Feature)描述样本的属性或变量(如身高、体重、温度)一列

为什么这样设计符合人类直觉?

想象你有一张 Excel 表:

身高(cm)体重(kg)年龄
张三1757025
李四1685530
王五1808022
  • 每一行是一个(样本)
  • 每一列是一个属性(特征)

这正是 DataFrame 的设计哲学:行是样本,列是特征

现实世界中的普遍实践

这种设计不仅符合直觉,更是现实工作流的体现:

  • Excel 中的数据录入:当你收集新数据时,通常是在表格底部追加一行(如新增一个员工、一次销售记录),而不是在右侧追加一列。列名(如“姓名”、“部门”、“薪资”)一旦定义,通常不会轻易变动,它们代表了数据的结构和模式。
  • 关系型数据库(如 MySQL):在数据库表中,每一行代表一条记录(record),每一列代表一个字段(field)。新增数据意味着插入一条新记录(一行),而修改表结构(如添加新字段)是相对少见且需要谨慎操作的变更。

这说明:“列是稳定的结构,行是动态的数据” 是一种广泛接受的数据组织范式。

Series 本身没有“行/列”方向

在代码中,因为我们只能“横着写”数据,所以并不清楚Series到底是行还是列:

s = pd.Series([1, 2, 3])

这个 Series 本身既不是行也不是列——它只是一个一维带标签的数组

Series 的“方向”(是行还是列)完全取决于它被如何使用

  • 当你把它放进 {'A': s} 这样的字典里 → Pandas 认为它是一个特征 → 成为一列
  • 当你把它放进 [s1, s2] 这样的列表里 → Pandas 认为它是一个样本 → 成为一行

二、核心原则:输入结构决定组织方式

Pandas 在构造 DataFrame 时,并不是随意决定数据如何排列的。它会根据你传入的数据结构,自动推断数据的“语义角色”。这个过程遵循一个简单而强大的原则:

“字典是列,列表是行”

让我们通过几个典型场景来理解这一点。


三、场景一:用字典构造 —— 按“列”组织(特征优先)

当你使用 字典(dict 构造 DataFrame 时,Pandas 会认为你是在定义多个“变量”或“特征”。

情况 1:{'列名': Series}

import pandas as pd

s1 = pd.Series([1, 2, 3])
s2 = pd.Series([4, 5, 6])

df = pd.DataFrame({'A': s1, 'B': s2})

结果:

   A  B
0  1  4
1  2  5
2  3  6
  • 'A', 'B' 是列名
  • s1 → A 列,s2 → B 列
  • 每个 Series 成为一列

语义:你有两个变量 A 和 B,每个变量是一组观测值(即两个特征)。

情况 2:{'列名': 字典}

data = {
    'A': {'a': 1, 'b': 4},
    'B': {'a': 2, 'b': 5}
}
df = pd.DataFrame(data)

结果:

   A  B
a  1  2
b  4  5
  • 外层键 'A', 'B' → 列名(特征名)
  • 内层键 'a', 'b' → 行索引(样本标签)
  • 自动对齐,缺失值填 NaN

这是最常见的“列优先”构造方式,完全符合“列是特征”的直觉。

自动对齐与 NaN 填充

DataFrame 的核心是一个规则的二维矩形结构。当使用字典构造时,如果不同列的行索引不完全一致,Pandas 会自动将所有列的索引并集作为最终的行索引,并在缺失位置填充 NaN,确保结构完整。

例如:

data2 = {
    'A': {'a': 1, 'b': 4},
    'B': {'a': 2, 'b': 5},
    'C': {'a': 3, 'c': 6}  # 'C' 缺少 'b',但有 'c'
}
df = pd.DataFrame(data2)

结果:

     A    B    C
a  1.0  2.0  3.0
b  4.0  5.0  NaN
c  NaN  NaN  6.0

Pandas 会自动对齐索引,用 NaN 填补空缺,形成一个完整的矩形网格,让你无需手动处理不完整数据。


四、场景二:用列表构造 —— 按“行”组织(样本优先)

当你使用 列表(list 构造 DataFrame 时,Pandas 会认为你是在记录多个“样本”或“观测”。

情况 1:[Series, Series]

df = pd.DataFrame([s1, s2])

结果:

   0  1  2
0  1  2  3
1  4  5  6
  • s1 → 第 0 行(样本1),s2 → 第 1 行(样本2)
  • s1 的索引 [0,1,2] → 成为列名(特征名)
  • 每个 Series 成为一行(一个样本)

语义:你有两个样本,每个样本有三个特征。

情况 2:[列表, 列表]

df = pd.DataFrame([
    [1, 2, 3],
    [4, 5, 6]
])

结果同上。

列表的每个元素(子列表)成为一行(一个样本)。

列表构造的“规则矩形”特性:处理不等长的 Series

当列表中的 Series 长度不一致时,Pandas 会以所有 Series 索引的并集作为最终的列索引,并用 NaN 填充缺失值,确保 DataFrame 保持规则的矩形结构。

s1 = pd.Series([1, 2, 3])  # 索引: [0, 1, 2]
s2 = pd.Series([4, 5])     # 索引: [0, 1]

df_pandas = pd.DataFrame([s1, s2])

结果:

     0    1    2
0  1.0  2.0  3.0
1  4.0  5.0  NaN

关键洞察

  • 所有 Series 的索引被合并:{0, 1, 2} → 成为最终的列名。
  • s2 缺少索引 2 对应的值 → 在 df_pandas.loc[1, 2] 处自动填入 NaN
  • 这与字典构造的行为一致,再次体现了 DataFrame 的“智能对齐”和“规则矩形”设计原则。

五、混合使用字典和列表:更灵活的构造方式

Pandas 允许你混合使用字典和列表,实现更复杂的构造逻辑。

情况 1:列表中包含字典(按行构造,但每行是字典)

data = [
    {'Name': 'Alice', 'Age': 25, 'City': 'Beijing'},
    {'Name': 'Bob', 'Age': 30, 'City': 'Shanghai'},
    {'Name': 'Charlie', 'Age': 22, 'City': 'Guangzhou'}
]

df = pd.DataFrame(data)

结果:

      Name  Age       City
0    Alice   25    Beijing
1      Bob   30   Shanghai
2  Charlie   22  Guangzhou
  • 每个字典代表一个样本(一行)
  • 字典的键成为列名(特征)
  • 完美符合“行是样本,列是特征”

这是从 API 或 JSON 数据创建 DataFrame 的常见方式。

情况 2:字典中包含列表(按列构造)

data = {
    'Name': ['Alice', 'Bob', 'Charlie'],
    'Age': [25, 30, 22],
    'City': ['Beijing', 'Shanghai', 'Guangzhou']
}

df = pd.DataFrame(data)

结果同上。

两种方式结果一致,只是组织视角不同。


六、二维列表与 NumPy 数组:结构优先的构造方式

当使用二维列表或 NumPy 数组时,Pandas 和 NumPy 的行为完全一致:结构决定一切

1. 二维列表构造 DataFrame

data_2d = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
]

df = pd.DataFrame(data_2d, 
                  index=['Row1', 'Row2', 'Row3'], 
                  columns=['ColA', 'ColB', 'ColC'])

结果:

      ColA  ColB  ColC
Row1     1     2     3
Row2     4     5     6
Row3     7     8     9
  • 外层列表的每个元素 → 一行
  • 内层列表的每个元素 → 该行的一个值

2. NumPy 二维数组构造 DataFrame

import numpy as np

arr = np.array([
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
])

df_numpy = pd.DataFrame(arr, 
                        index=['Sample1', 'Sample2', 'Sample3'], 
                        columns=['FeatureX', 'FeatureY', 'FeatureZ'])

结果:

           FeatureX  FeatureY  FeatureZ
Sample1           1         2         3
Sample2           4         5         6
Sample3           7         8         9

与 NumPy 的一致性

# NumPy 数组
np_arr = np.array(data_2d)
print(np_arr)
# 输出:
# [[1 2 3]
#  [4 5 6]
#  [7 8 9]]

关键点:对于二维列表/数组:

  • 外层结构 → 行
  • 内层结构 → 列
  • Pandas 和 NumPy 在此完全一致,都是“结构优先”而非“语义优先”

七、为什么感觉 NumPy 和 Pandas 有的时候行和列是“反过来”的?

许多人在对比以下代码时会感到困惑:

# Pandas:字典 → 列(特征)
df = pd.DataFrame({'A': s1, 'B': s2})  # s1 是列(特征)

# NumPy:列表 → 行(样本)
arr = np.array([s1.values, s2.values])  # s1.values 是行(样本)

看起来“反了”?其实不然。

  • Pandas 关注语义{'A': s1} 表示“变量 A 的数据是 s1” → 应为列(特征)
  • NumPy 关注结构[s1.values, s2.values] 是一个列表 → 每个元素成为行(样本)

当你用

pd.DataFrame([s1, s2])

时,Pandas 和 NumPy 的行为就一致了:列表的每个元素都成为一行(一个样本)


八、最佳实践

最佳实践:

  1. 想定义特征(变量)? → 用 dict{'feature_name': data}
  2. 想添加样本(观测)? → 用 list[sample1, sample2]
  3. 从 JSON/API 获取数据? → 用 [dict1, dict2] 列表
  4. 从 NumPy 数组转换? → 直接 pd.DataFrame(arr)
  5. 处理不完整或不等长数据? → 信任 Pandas 的自动对齐和 NaN 填充
  6. 不确定时,问自己: “这个数据是代表一个样本还是一个特征?”

九、总结

pandas.DataFrame 的构造逻辑看似复杂,实则遵循一套清晰、一致的原则:

  1. 核心设计: 每个样本代表一行,每个特征代表一列,这完全符合人类对表格的直觉。
  2. 现实映射: 这种设计与 Excel 的数据追加习惯和 MySQL 等关系型数据库的表结构一致:列(特征)是稳定的结构,行(样本)是动态的数据
  3. 输入结构决定行为:
    • 字典 → 特征(列)
    • 列表 → 样本(行)
    • 二维结构 → 外层是行,内层是列
  4. 语义优先于结构: Pandas 理解“变量”和“样本”的区别
  5. Series 无固有方向: 单独的 Series 只是一维数组,它的“行/列”角色由构造方式决定。
  6. 规则矩形结构: DataFrame 会自动对齐索引,用 NaN 填充缺失值,确保数据是一个完整的二维网格。

掌握这些原则,你就能游刃有余地构造和理解 DataFrame,不再被“按行还是按列”所困扰。

记住:Pandas 不是“复杂”,而是“智能”。它用 dictlist 这样的“结构信号”,理解你的数据意图。


作者:aopstudio撰写初稿,Qwen进行整理和润色,aopstudio进行校对
撰写日期:2025年8月19日


标题:Pandas用Series构建DataFrame:到底是行还是列?
作者:aopstudio
地址:https://neusoftware.top/articles/2025/08/19/1755613405359.html