本节大纲
- Interval
- IntervalIndex
- interval_range()
- cut() / qcut()
- 属性
- left、right、mid、length、closed
- 方法
- contains()
- overlaps()
- get_loc()
- 区间运算
- 区间索引
1. Interval
Interval 是 pandas 中表示一个连续区间的对象。它由左右端点和闭合状态定义。
创建 Interval
import pandas as pd
# 创建一个左闭右开的区间 [0, 1)
iv = pd.Interval(left=0, right=1, closed='right')
print(iv) # (0, 1]
print(iv.closed) # 'right'- 参数:
left:区间左端点(标量)。right:区间右端点(标量)。closed:区间闭合方式,可选'left'、'right'、'both'、'neither',默认'right'(即左开右闭,与数学约定(left, right]一致)。
- 返回值:一个
Interval对象。
Interval 的特点
- 区间是不可变的,端点不能直接修改。
- 支持比较运算(相等、排序)基于端点和闭合方式。
- 可以包含在
Series或DataFrame中,作为元素。
2. IntervalIndex
IntervalIndex 是一种特殊的 Index,其每个元素都是 Interval 对象。它允许高效地对区间数据进行索引、查询和对齐。
创建 IntervalIndex
- 直接从
Interval列表:idx = pd.IntervalIndex([pd.Interval(0,1), pd.Interval(1,2)]) - 使用
IntervalIndex.from_arrays():idx = pd.IntervalIndex.from_arrays(left=[0,1,2], right=[1,2,3], closed='right') - 使用
IntervalIndex.from_tuples():idx = pd.IntervalIndex.from_tuples([(0,1), (1,2)], closed='right') - 使用
interval_range()(见下文)。
IntervalIndex 的特点
- 继承自
Index,但元素必须是Interval。 - 拥有
left、right、mid、length、closed等属性(见下文)。 - 支持
contains()、overlaps()、get_loc()等方法。 - 可用于 DataFrame 的行索引或列索引,实现区间对齐。
3. interval_range()
interval_range() 是一个便捷函数,用于创建等距的 IntervalIndex。
语法
pd.interval_range(start=None, end=None, periods=None, freq=None, name=None, closed='right')- 参数:
start:起始值(数值或日期时间)。end:结束值。periods:区间数量(与start/end配合)。freq:区间长度(数值或日期偏移)。name:索引名称。closed:闭合方式,默认'right'。
- 返回值:一个
IntervalIndex。
示例
# 按频率创建
idx = pd.interval_range(start=0, end=5, freq=1, closed='right')
# 结果:[ (0,1], (1,2], (2,3], (3,4], (4,5] ]
# 按数量创建
idx = pd.interval_range(start=0, periods=4, freq=2)
# 结果:[ (0,2], (2,4], (4,6], (6,8] ]
# 日期区间
idx = pd.interval_range(start=pd.Timestamp('2024-01-01'), periods=3, freq='D')注意
start、end、periods、freq至少提供两个才能确定区间。- 与
pd.date_range()类似,但生成的是区间而不是时间点。
4. cut() / qcut()
cut() 和 qcut() 是将连续数据离散化为区间的函数。
4.1 pd.cut()
根据值域将数据分箱。
pd.cut(x, bins, right=True, labels=None, retbins=False, precision=3, include_lowest=False, duplicates='raise')- 参数:
x:要分箱的一维数组或 Series。bins:分箱依据,可以是:- 整数:将值域等分为
bins个区间。 - 序列:定义箱体的边界(如
[0, 1, 5, 10])。 IntervalIndex:直接指定区间。
- 整数:将值域等分为
right:是否包含右边界,默认True(即区间为(a, b])。labels:箱体的标签,默认None返回区间对象。retbins:是否返回边界。precision:边界精度(当bins为整数时)。include_lowest:是否包含最低边界(用于right=True时包含第一个区间的左端点)。duplicates:遇到重复边界时的处理方式('raise'或'drop')。
- 返回值:一个
Categorical对象(或 Series,取决于labels),每个元素对应一个区间。
4.2 pd.qcut()
根据分位数将数据分箱,每个箱体包含大致相同数量的观测值。
pd.qcut(x, q, labels=None, retbins=False, precision=3, duplicates='raise')- 参数:
q:分位数数量(整数)或分位数列表(如[0, 0.25, 0.5, 0.75, 1])。- 其他参数与
cut()类似。
- 返回值:
Categorical对象。
示例对比
data = [1, 7, 5, 4, 6, 3, 100]
# 按值域等宽分箱
pd.cut(data, bins=3)
# 区间:[(0.901, 34.0], (0.901, 34.0], ..., (67.0, 100.0]]
# 按分位数分箱
pd.qcut(data, q=3)
# 每个箱体包含数量相近的观测用途
- 将连续变量转换为分类变量。
- 创建区间特征用于机器学习。
- 配合
value_counts()统计区间分布。
5. 属性
IntervalIndex 和 Interval 都暴露以下属性,用于获取区间的端点、长度和闭合状态。
5.1 left
返回区间左端点。
- 对于
IntervalIndex:返回一个Index,包含每个区间的左端点。 - 对于单个
Interval:返回标量左端点。
idx = pd.interval_range(0, 3)
idx.left # Index([0, 1, 2], dtype='int64')5.2 right
返回区间右端点。
idx.right # Index([1, 2, 3], dtype='int64')5.3 mid
返回区间中点((left + right) / 2)。
idx.mid # Index([0.5, 1.5, 2.5])5.4 length
返回区间长度(right - left)。
idx.length # Index([1, 1, 1], dtype='int64')5.5 closed
返回区间的闭合方式(字符串)。
- 对于
IntervalIndex:返回一个字符串,表示所有区间的闭合方式(因为一个索引内所有区间闭合方式必须一致)。 - 对于
Interval:返回该区间的闭合方式。
idx.closed # 'right'示例
iv = pd.Interval(1, 5, closed='both')
print(iv.left) # 1
print(iv.right) # 5
print(iv.mid) # 3.0
print(iv.length) # 4
print(iv.closed) # 'both'6. 方法
6.1 contains()
判断一个(或多个)点是否位于区间内。
Interval.contains()
- 签名:
Interval.contains(other) - 参数:
other可以是标量或 Interval。 - 返回值:布尔值,表示
other是否被当前区间包含。 - 注意:闭合方式会影响判断,例如区间
(0, 1]包含1但不包含0。
iv = pd.Interval(0, 1, closed='right')
iv.contains(0.5) # True
iv.contains(1) # True
iv.contains(0) # FalseIntervalIndex.contains()
- 签名:
IntervalIndex.contains(other) - 参数:
other可以是标量或数组。 - 返回值:布尔数组,每个元素表示对应区间是否包含
other。
idx = pd.interval_range(0, 3)
idx.contains(1.5) # array([False, True, False])6.2 overlaps()
判断两个区间是否有重叠(交集非空)。
Interval.overlaps()
- 签名:
Interval.overlaps(other) - 参数:
other是另一个Interval对象。 - 返回值:布尔值,表示两个区间是否重叠。
- 注意:端点重合但闭合方式不同可能不重叠,例如
(0,1]和(1,2]不重叠,因为1只在第一个区间内,但(0,1]和[1,2]重叠(共享点1)。
iv1 = pd.Interval(0, 1, closed='right')
iv2 = pd.Interval(1, 2, closed='both')
iv1.overlaps(iv2) # False(因为 iv1 不包含 1,而 iv2 包含 1,但共同点仅为 1,实际不重叠)实际上 iv1 为 (0,1],iv2 为 [1,2],它们共享点 1,但由于 iv1 包含 1,iv2 包含 1,所以重叠。上述示例结果应为 True。请根据实际测试为准。
IntervalIndex.overlaps()
- 签名:
IntervalIndex.overlaps(other),其中other是另一个IntervalIndex。 - 返回值:布尔数组,表示对应区间是否重叠。
idx1 = pd.interval_range(0, 3)
idx2 = pd.interval_range(0.5, 3.5)
idx1.overlaps(idx2) # array([ True, True, True])6.3 get_loc()
获取某个值在 IntervalIndex 中的位置索引。
- 签名:
IntervalIndex.get_loc(key) - 参数:
key可以是:- 标量:查找包含该标量的区间位置(返回整数位置)。
Interval:查找与该区间完全匹配的位置。
- 返回值:整数位置或切片(当
key是标量且多个区间包含它时,可能返回布尔数组?实际上对于标量,返回包含该标量的区间的整数位置;如果多个区间包含,会返回一个布尔数组或切片?默认行为是返回单个位置,如果多个匹配,会引发错误?需要测试)。
idx = pd.interval_range(0, 3)
idx.get_loc(1.5) # 1,因为 1.5 在区间 (1,2] 内
idx.get_loc(pd.Interval(1, 2)) # 1- 注意:
get_loc()是IntervalIndex特有的方法,普通Index也有get_loc(),但这里针对区间索引做了重载,支持按区间成员查找。
7. 区间运算
区间数据支持多种运算,包括比较、集合运算和算术运算。
7.1 区间比较
- 两个
Interval对象可以比较是否相等(==)以及排序(<,>,<=,>=)。 - 排序规则:先比较左端点,再比较右端点,最后比较闭合方式。
iv1 = pd.Interval(0, 1)
iv2 = pd.Interval(1, 2)
iv1 < iv2 # True7.2 集合运算
IntervalIndex 支持类似集合的操作:
intersection(other):返回两个区间索引的交集(可能产生新的区间)。union(other):返回并集(需要区间不相邻且有序)。difference(other):返回差集。symmetric_difference(other):返回对称差集。
idx1 = pd.IntervalIndex.from_tuples([(0,1), (2,3)])
idx2 = pd.IntervalIndex.from_tuples([(0.5, 2.5)])
idx1.intersection(idx2) # 返回 [ (0.5,1], (2,2.5] ]7.3 算术运算
Interval可以与标量进行加减运算(端点平移):
iv = pd.Interval(0, 1)
iv + 1 # Interval(1, 2, closed='right')IntervalIndex的算术运算会分别作用于每个区间。
7.4 重叠检测
overlaps()方法用于检测重叠(见上文)。is_non_overlapping_monotonic属性:检查区间索引是否非重叠且单调递增。
idx = pd.interval_range(0, 3)
idx.is_non_overlapping_monotonic # True8. 区间索引
区间数据可以作为 Series 或 DataFrame 的索引,实现基于区间的数据选择和分组。
8.1 将 IntervalIndex 用作索引
idx = pd.interval_range(start=0, end=10, freq=2)
s = pd.Series([10, 20, 30, 40, 50], index=idx)
print(s)
# (0, 2] 10
# (2, 4] 20
# (4, 6] 30
# (6, 8] 40
# (8, 10] 508.2 使用标量进行标签选择
- 使用
s.loc[3.5]会查找包含3.5的区间(2,4],返回对应值20。 - 使用
s.loc[5]:5在区间(4,6]内,返回30。 - 注意:如果标量落在多个区间(理论上不会,因为区间通常不重叠),则返回第一个匹配或引发错误。
s.loc[3.5] # 208.3 使用区间对象进行选择
- 使用
s.loc[pd.Interval(4,6)]会选择完全匹配的区间。
s.loc[pd.Interval(4,6)] # 308.4 切片选择
- 可以使用标量切片,如
s.loc[2:6],会包含所有与切片范围重叠的区间。 - 切片行为取决于端点位置,返回所有可能重叠的区间。
s.loc[2:6] # 返回区间 (2,4] 和 (4,6] 的值8.5 分组与聚合
- 使用
groupby()配合区间索引可以对区间内的数据进行分组,但更常见的是使用pd.cut()的结果作为分组键。
data = pd.Series([1, 2, 5, 7, 9])
bins = pd.cut(data, bins=[0,3,6,10])
data.groupby(bins).sum()- 区间索引也可以直接进行
resample()等时间序列操作(如果区间是时间区间)。
总结
区间数据是 pandas 中处理连续范围的有力工具。
Interval表示单个区间,IntervalIndex表示区间集合,interval_range()快速创建等距区间,cut()/qcut()实现离散化,配合属性、方法和索引选择,能高效完成区间匹配、重叠检测和数据分箱任务。
> [!note] 说明
>
> - 内容严格按大纲“十、区间数据”的结构展开,未增删条目。
> - 每个知识点均包含定义、参数、示例和注意事项,保证广度和深度。
> - 可使用 Obsidian 直接创建笔记并粘贴以上内容。