说明
《Python 教程》 持续更新中,提供建议、纠错、催更等加作者微信: gairuo123(备注:pandas教程)和关注公众号「盖若」ID: gairuo。跟作者学习,请进入 Python学习课程。欢迎关注作者出版的书籍:《深入浅出Pandas》 和 《Python之光》。
Python 的 portion /ˈpɔːʃ(ə)n/ 库来提供间隔的数据结构和操作。portion 以前以 python-intervals 的形式分发,但 python-intervals 不再支持 Python 3.5+ 和新增加更新。经常用于定义数字、时间、时长等数据的范围,同时判断一个标量是否在此范围内,各个范围的交集、差集等集合计算等。
pip install portion
更多参考:
一般导入 portion 库时设置别名为 P,然后用 P 的方法创建具有一定开闭性质的区间间隔对象。
import portion as P
# 开区间,不包含两边
P.open(1, 2) # (1,2)
# 闭区间,包含两边
P.closed(1, 2) # [1,2]
# 左开左闭,1<x<=2
P.openclosed(1, 2) # (1,2]
# 左闭右开,1<=x<2
P.closedopen(1, 2) # [1,2)
# 单一值
P.singleton(1) # [1]
# 空范围
P.empty() # ()
区间的边界可以是任意值,只要它们是可比较的,如字符和时间:
P.closed(1.2, 2.4)
# [1.2,2.4]
P.closed('a', 'z')
# ['a','z']
import datetime
P.closed(datetime.date(2011, 3, 15), datetime.date(2013, 10, 10))
# [datetime.date(2011, 3, 15),datetime.date(2013, 10, 10)]
使用 P.inf 和 -P.inf 作为上限或下限,支持无限和半无限间隔。 这两个对象支持与任何其他对象的比较。 当将无穷大用作下限或上限时,相应的边界会自动转换为开放边界。
P.inf > 'a', P.inf > 0, P.inf > True
# (True, True, True)
P.openclosed(-P.inf, 0)
# (-inf,0]
P.closed(-P.inf, P.inf) # 自动转换为开区间
# (-inf,+inf)
空间隔始终解析为(P.inf,-P.inf),无论提供的边界如何:
P.empty() == P.open(P.inf, -P.inf)
# True
P.closed(4, 3) == P.open(P.inf, -P.inf)
# True
P.openclosed('a', 'a') == P.open(P.inf, -P.inf)
# True
使用此库创建的间隔是间隔实例,间隔实例是原子间隔的析取,每个原子间隔代表一个间隔(例如[1,2])。 可以迭代间隔以访问基础原子间隔,并按其下限和上限进行排序。
list(P.open(10, 11) | P.closed(0, 1) | P.closed(20, 21))
# [[0,1], (10,11), [20,21]]
原子间隔也可以按位置进行检索:
(P.open(10, 11) | P.closed(0, 1) | P.closed(20, 21))[0]
# [0,1]
(P.open(10, 11) | P.closed(0, 1) | P.closed(20, 21))[-2]
# (10,11)
为方便起见,间隔会自动简化:
P.closed(0, 2) | P.closed(2, 4)
# [0,4]
P.closed(1, 2) | P.closed(3, 4) | P.closed(2, 3)
# [1,4]
P.empty() | P.closed(0, 1)
# [0,1]
P.closed(1, 2) | P.closed(2, 3) | P.closed(4, 5)
# [1,3] | [4,5]
注意,部分不支持离散间隔的简化(但是可以模拟它,请参阅#24)。 例如,即使[1,2]之间没有整数,将[0,1]与[2,3]组合也不会得到[0,3]。
间隔定义以下属性:
# 当且仅当间隔为空时,p.empty 为 True
P.closed(0, 1).empty # False
P.closed(0, 0).empty # False
P.openclosed(0, 0).empty # True
P.empty().empty # True
# 当且仅当间隔是单个(可能为空)间隔的析取时,p.atomic 才为 True
P.closed(0, 2).atomic # True
(P.closed(0, 1) | P.closed(1, 2)).atomic # True
(P.closed(0, 1) | P.closed(2, 3)).atomic # False
# p.enclosure 指包括当前原子间隔的最小原子间隔
(P.closed(0, 1) | P.open(2, 3)).enclosure
# [0,3)
可以通过其左,右,下和上属性分别访问间隔的左边界和右边界以及上下边界。 左右边界为 P.CLOSED 或 P.OPEN。 根据定义,P.CLOSED ==〜P.OPEN,反之亦然。
P.CLOSED, P.OPEN
# CLOSED, OPEN
x = P.closedopen(0, 1)
x.left, x.lower, x.upper, x.right
# (CLOSED, 0, 1, OPEN)
如果间隔不是原子间隔,则 left 和 lower 表示其外壳的下限,而 right 和 upper 表示其外壳的上限:
x = P.open(0, 1) | P.closed(3, 4)
x.left, x.lower, x.upper, x.right
# (OPEN, 0, 4, CLOSED)
您可以根据间隔的界限轻松检查某些间隔属性:
x = P.openclosed(-P.inf, 0)
# 检查间隔是否左右关闭
x.left == P.CLOSED, x.right == P.CLOSED
# (False, True)
# 检查间隔是否在左/右边界
x.lower == -P.inf, x.upper == P.inf
# (True, False)
# 检查单元素
x.lower == x.upper # False
# 交集
P.closed(0, 2).intersection(P.closed(1, 3))
P.closed(0, 2) & P.closed(1, 3)
# [1,2]
# 并集
P.closed(0, 2).union(P.closed(1, 3))
P.closed(0, 2) | P.closed(1, 3)
# [0,3]
# 补集
P.closed(0, 2).complement()
# (-inf,0) | (2,+inf)
~P.closed(0, 1)
# (-inf,0) | (1,+inf)
~(P.open(-P.inf, 0) | P.open(1, P.inf))
# [0,1]
~P.open(-P.inf, P.inf)
# ()
# 差集
P.closed(0,2).difference(P.closed(1,2))
P.closed(0,2) - P.closed(1,2)
# [0,1)
P.closed(0, 4) - P.closed(1, 2)
# [0,1) | (2,4]
# 包含
P.closed(0, 2).contains(2)
2 in P.closed(0, 2) # True
2 in P.open(0, 2) # False
P.open(0, 1) in P.closed(0, 2) # True
P.adjacent(other) 测试两个间隔是否相邻,即它们是否不重叠并且它们的并集形成单个原子间隔。 尽管此定义与原子间隔的通常邻接概念相对应,但它对非原子间隔的要求更高,因为它要求所有基本原子间隔都相邻(即,一个间隔填充了另一个原子间隔之间的间隙)。
P.closed(0, 1).adjacent(P.openclosed(1, 2))
# True
P.closed(0, 1).adjacent(P.closed(1, 2))
# False
(P.closed(0, 1) | P.closed(2, 3)).adjacent(P.open(1, 2) | P.open(3, 4))
# True
(P.closed(0, 1) | P.closed(2, 3)).adjacent(P.open(3, 4))
# False
P.closed(0, 1).adjacent(P.open(1, 2) | P.open(3, 4))
# False
P.overlaps(other) 测试两个时间间隔之间是否存在重叠。
P.closed(1, 2).overlaps(P.closed(2, 3))
# True
P.closed(1, 2).overlaps(P.open(2, 3))
# False
最后,间隔是可哈希的,只要它们的边界是可哈希的(并且我们为P.inf和-P.inf定义了哈希值)。
间隔之间的相等性可以使用 == 运算符检查:
P.closed(0, 2) == P.closed(0, 1) | P.closed(1, 2)
# True
P.closed(0, 2) == P.open(0, 2)
# False
此外,使用 >,> =,< 或 <= 可以比较间隔。 这些比较运算符的行为不同于通常的比较行为。 例如,如果 a 完全位于 b 的下边界的左侧,则 <b
成立,而如果 a 完全位于 b 的上边界的右侧,则 a>b
成立。
间隔是不可变的,但是提供了一种替换方法,可以根据当前间隔创建一个新间隔。 此方法接受四个可选参数 left,lower,upper 和 right:
i = P.closed(0, 2)
i.replace(P.OPEN, -1, 3, P.CLOSED)
# (-1,3]
i.replace(lower=1, right=P.OPEN)
# [1,2)
迭代间一个隔,会返回一个对间隔的值进行迭代的生成器。 显然,由于间隔是连续的,因此需要指定连续值之间的步长。 迭代从下限开始,在上限处结束,仅返回间隔中包含的值。
list(P.iterate(P.closed(0, 3), step=1))
# [0, 1, 2, 3]
list(P.iterate(P.closed(0, 3), step=2))
# [0, 2]
list(P.iterate(P.open(0, 3), step=2))
# [2]
如果某个间隔不是原子间隔,则从每个下限开始到每个上界结束,依次迭代所有基础原子间隔:
list(P.iterate(P.singleton(0) | P.singleton(3) | P.singleton(5), step=2)) # Won't be [0]
# [0, 3, 5]
list(P.iterate(P.closed(0, 2) | P.closed(4, 6), step=3)) # Won't be [0, 6]
# [0, 4]
更加复杂的操作可参考官方文档。
该库提供了 IntervalDict 类,这是一种类似 dict 的数据结构,用于存储和查询数据以及间隔,任何值都可以存储在这种数据结构中。
d = P.IntervalDict()
d[P.closed(0, 3)] = 'banana'
d[4] = 'apple'
d
# {[0,3]: 'banana', [4]: 'apple'}
间隔可以使用 repr 或 to_string 函数导出为字符串:
P.to_string(P.closedopen(0, 1))
# '[0,1)'
间隔也可以导出到带有 to_data 的 4 列列表,例如,以支持 JSON 序列化。 P.CLOSED和P.OPEN由布尔值True(包含)和False(不含)表示。
P.to_data(P.openclosed(0, 2))
# [(False, 0, 2, True)]
更新时间:2020-12-15 18:44:44 标签:间隔 python