数据框简介
探索 pandas 中 DataFrame 数据结构的基础知识。
在 pandas 中,一维的 Series 的二维对应物是 DataFrame。若要理解这一数据结构,应从其构建方式开始入手。
数据库与电子表格类比
如果将 DataFrame 视为按行组织的结构,接口使用起来会感觉不自然。许多表格数据结构都是按行组织的,这可能是因为电子表格和 CSV 文件通常以逐行方式处理。也可能是因为许多 OLTP 数据库默认就是按行存储的。而 DataFrame 通常用于分析目的,更应被理解为按列组织的结构,其中每一列都相当于一个 Series。
注意: 实际上,许多高度优化的分析型数据库(如用于 OLAP 多维立方体的数据库)也是按列存储的。以列式方式布局数据可以提升性能并减少资源消耗。同类型的数据列易于压缩。对某一列进行分析时,只需加载该列数据,而行式数据库则需要读取整个数据集才能获取一整列。
一个简单的 Python 版本
下面是一个尝试创建按列组织的表格型 Python 数据结构的简单实现。它使用基于 0 的整数索引,但这并非必须;索引也可以是字符串类型。每一列类似于之前开发的 Series 结构:
1df = { 2 'index': [0, 1, 2], 3 'cols': [ 4 { 'name': 'growth', 'data': [.5, .7, 1.2] }, 5 { 'name': 'Name', 'data': ['Paul', 'George', 'Ringo'] }, 6 ] 7} 8 9print(df)
通过索引访问行,通过列名访问列。以下是获取行和列的简单函数:
1def get_row(df, idx): 2 results = [] 3 value_idx = df['index'].index(idx) 4 for col in df['cols']: 5 results.append(col['data'][value_idx]) 6 return results 7 8print(get_row(df, 1))
接下来处理列:
1def get_col(df, name): 2 for col in df['cols']: 3 if col['name'] == name: 4 return col['data'] 5 6print(get_col(df, 'Name'))
注意: 一列可以提取为一个 Series。
pandas DataFrames
使用 pandas 的 DataFrame 对象,可以这样创建前面的结构:
1import pandas as pd 2 3df = pd.DataFrame({ 4 'growth': [.5, .7, 1.2], 5 'Name': ['Paul', 'George', 'Ringo'] }) 6 7print(df)
最左侧的值 0、1 和 2 是索引。有两个列:growth 和 Name。这个数据结构(如同 Series 一样)拥有数百个属性和方法。
一种访问行的方式是通过 iloc 属性进行位置索引:
1print(df.iloc[2])
列也可以通过多种方式访问。一种是直接通过对象索引列名:
1print(df['Name'])
注意: 列的类型是 pandas 的
Series实例。任何可应用于 Series 的操作都可以作用于列:
1print(type(df['Name'])) 2 3print(df['Name'].str.lower())
DataFrame 重写了 __getattr__ 方法,允许将列作为属性访问。这通常能正常工作,但如果列名与现有方法或属性冲突,或者列名包含空格等无效字符,则会失败:
1print(df.Name)
有些人建议永远不要使用属性访问来提取列,而应优先使用索引查找。虽然索引查找即使在列名不符合 Python 属性命名规则(如非字母数字或下划线)的情况下也能工作,但在使用 Jupyter 时推荐使用属性访问!
构建方式
DataFrames 可以从多种输入类型创建:
- 列(字典形式的列表)
- 行(字典列表)
- CSV 文件(
pd.read_csv) - NumPy 数组
- SQL、HDF5、Arrow 等格式
前面创建 df 的例子展示了如何从列构建 DataFrame。下面是通过行构建 DataFrame 的示例:
1df = pd.DataFrame([ 2{'growth': .5, 'Name': 'Paul'}, 3{'growth': .7, 'Name': 'George'}, 4{'growth': 1.2, 'Name': 'Ringo'}]) 5 6print(df)
类似地,以下是从 CSV 文件加载数据的示例(我们使用 StringIO 模拟文件内容):
1import pandas as pd 2from io import StringIO 3 4csv_file = StringIO("""growth,Name 5 .5,Paul 6 .7,George 7 1.2,Ringo""") 8 9df = pd.read_csv(csv_file) 10 11print(df)
pd.read_csv 函数会智能处理输入。如果传入的是 URL,它会自动下载文件。如果文件扩展名为 xz、bz2 或 zip,它会自动解压(可通过 compression='bz2' 参数显式指定对其他扩展名的解压)。
解析 CSV 文件后,pandas 会尽力为每列分配合适的类型。“尽力”意味着:若某列全为整数且无缺失值,则转换为 int64;含小数或缺失值的数值列转换为 float64;若包含非数值内容,则使用 object 类型。通常 object 表示该列是字符串类型数据,但也可能是混合类型的列,包含字符串和以浮点数形式存储的 NAN 值。
pd.read_csv 函数的一个参数是 dtypes,它接受一个字典,映射列名到对应类型。可使用的类型如下:
pandas 中的数据类型
| 类型 | 描述 |
|---|---|
| float64 | 浮点数。支持不同大小,如 float16、float32 或 float64。 |
| int64 | 整数。可在前面加 u 表示无符号整数。支持不同大小,如 int8、int16、int32 或 int64。不支持缺失值。 |
| Int64 | 可为空的整数。支持 <NA> 表示整数列中的缺失值。可在前面加 U 表示无符号。支持不同大小,如 Int16、Int32 或 Int64。 |
| datetime64[ns] | 日期时间类型。 |
| datetime64[ns, tz] | 带时区的日期时间类型。 |
| timedelta[ns] | 两个日期时间之间的差值。 |
| category | 用于指定分类列。 |
| object | 用于其他列,如字符串或 Python 对象。 |
| string | 用于文本数据。支持 <NA> 表示缺失值。 |
提示:
dtype参数在数据加载后更容易转换为多种类型。可以在每个列作为 Series 的基础上,使用astype方法或相关的to_*函数进行转换。
DataFrame 也可以从 NumPy 数组创建。此时需通过 columns 参数传入列名:
1import pandas as pd 2import numpy as np 3 4np.random.seed(42) 5ndf = pd.DataFrame(np.random.randn(10, 3), columns=['a', 'b', 'c']) 6 7print(ndf)
DataFrame 的轴
与只有一个轴的 Series 不同,DataFrame 有两个轴。它们通常被称为轴 0 和轴 1,分别对应“索引”(或“行”)轴和“列”轴:
1print(df.axes)
例如,我们可以使用标签 0 或 1 沿索引或列方向对 DataFrame 进行求和:
1print(df.sum(axis=0))
接下来处理列:
1print(df.sum(axis=1))
我们也可以明确写出轴名称。这是推荐的方法,因为可读性更强:
1print(df.sum(axis='index'))
接着处理列轴:
1print(df.sum(axis='columns'))
由于许多操作都接受 axis 参数,因此记住:0 代表索引,1 代表列非常重要:
1print(df.axes[0])
再看列轴:
1print(df.axes[1])
这里有一个帮助记忆轴编号的小技巧:回想一下 Series。它类似于一个带有索引的 DataFrame。轴 0 沿着索引方向。一个助记法是:数字 1 的形状像一列(轴 1 沿着列方向):
1import pandas as pd 2 3df = pd.DataFrame({'Score1': [None, None], 'Score2': [85, 90]}) 4print(df)
若要对每列求和,应沿索引轴(即行轴)向下求和(axis=0):
1print(df.apply(np.sum, axis=0))
若要对每行求和,则沿列轴横向求和(axis=1):
1print(df.apply(np.sum, axis=1))
总结
我们介绍了一个与 pandas 中 DataFrame 实现相似的 Python 数据结构,展示了其索引和列式特性。随后探讨了 DataFrame 的主要组成部分,以及列本质上就是 Series 对象。我们学习了多种构建 DataFrame 的方式,并最终了解了 DataFrame 的两个轴。