这里需要用的集合的知识,一般python集合会用intervals库
或portion库
,这里我们用portion库
需要安装:
pip install portion
算法思路
- 得到序列的极值,根据极值(极大值、极小值)划分区间,在一个区间里的数就是单调的,这样可以简化计算
- 一个区间一个区间的判断
- 有可能第一个区间插入目标区间一半,另一个区间没捅穿又出来了,类似于“W”类型,因此我们使用一个记录过往区间的变量
cul_portion
,记录之前走过的区间
import numpy as np
import portion
import scipy.signal as sg
def monotonic(x):
"""判断单调"""
dx = np.diff(x)
return np.all(dx <= 0) or np.all(dx >= 0)
def check_cross_interval(values: np.ndarray, target_interval: portion.Interval) -> int:
"""
看序列值横穿整个目标区间的次数
:param values: 一个序列值
:param target_interval: 目标区间
:return: 次数
"""
if len(values) < 2: # 值小于2
return 0
if monotonic(values): # 单调
value_portion = portion.closed(values.min(), values.max())
if target_interval in value_portion:
return 1
else:
return 0
single_index = np.append(sg.argrelmin(values)[0], sg.argrelmax(values)[0])
single_index.sort()
# 第一个区间
cross_num = 0 # 穿过的次数
first_portion = portion.closed(min(values[0], values[single_index[0]]), max(values[0], values[single_index[0]]))
if target_interval in first_portion:
cross_num += 1
# 中间所有的区间
cul_portion = portion.empty() # 用于不断迭代合并的区间
last_index = single_index[0]
for index in single_index[1:]:
interval_portion = portion.closed(
min(values[last_index], values[index]), max(values[last_index], values[index]))
last_index = index
cul_portion = cul_portion.union(interval_portion)
if target_interval in cul_portion:
cross_num += 1
cul_portion = portion.empty()
# 最后一个区间
last_index = portion.closed(min(values[-1], values[single_index[-1]]), max(values[-1], values[single_index[-1]]))
if target_interval in last_index:
cross_num += 1
return cross_num
if __name__ == '__main__':
array = np.array([1, 2, 3, 2, -1, 2, 3, 0, 2, -1])
across_counts = check_cross_interval(array, portion.closed(0, 1))
print(across_counts) # 5次