In [1]:
import numpy as np
import pandas as pd
In [2]:
import doctest
In [3]:
def detect_heads(v, height) -> 'list of tuple (i,j,k)':
""" pi < pj > pk, such that |pj - pi| > hight and |pj - pk| > hight
i < j < k, i and k the first index that makes a head
>>> detect_heads([1,2,3,2,1], 1)
[(1, 2, 3)]
>>> detect_heads([1,2,3,2,1], 2)
[(0, 2, 4)]
>>> detect_heads([1,2,3,2,1], 3)
[]
>>> detect_heads([5,4,3,2,1], 1)
[]
>>> detect_heads([1,2,3,4,5], 1)
[]
>>> detect_heads([1,2,3,4,3], 1)
[(2, 3, 4)]
>>> detect_heads([1,2,3,2,3,2,3], 1)
[(1, 2, 3), (3, 4, 5)]
>>> detect_heads([1,2,1,2,3,4,1], 3)
[(0, 5, 6)]
"""
assert height > 0
if len(v) < 2:
return []
i, j, k = 0, None, None
result = []
for idx, p in enumerate(v):
if j:
if p <= v[j] - height:
result.append((i, j, idx))
i, j, k = idx, None, None
elif p > v[j]:
j = idx
while p >= v[i+1] + height:
i = i + 1
continue
elif p < v[i]:
i = idx
elif p >= v[i] + height:
j = idx
while p >= v[i+1] + height:
i = i + 1
return result
doctest.run_docstring_examples(detect_heads, globals(), verbose=False)
In [4]:
def detect_vallies(v, height) -> 'list of tuple (i,j,k)':
""" pi > pj < pk, such that |pj - pi| > hight and |pj - pk| > hight
i < j < k, i and k the first index that makes a valley
>>> detect_vallies([3,2,1,2,3], 1)
[(1, 2, 3)]
>>> detect_vallies([3,2,1,2,3], 2)
[(0, 2, 4)]
>>> detect_vallies([3,2,1,2,3], 3)
[]
>>> detect_vallies([5,4,3,2,1], 1)
[]
>>> detect_vallies([1,2,3,4,5], 1)
[]
>>> detect_vallies([4,3,2,1,2], 1)
[(2, 3, 4)]
>>> detect_vallies([3,2,1,2,1,2,1], 1)
[(1, 2, 3), (3, 4, 5)]
>>> detect_vallies([4,3,4,3,2,1,4], 3)
[(0, 5, 6)]
"""
return detect_heads([-i for i in v], height)
doctest.run_docstring_examples(detect_vallies, globals(), verbose=False)
In [59]:
def find_head_heights(v: list, peaks_idx: list) -> 'peak_heights':
"""
Returns the height of each peaks_idx[i]
Height is defined as v[i] - max(min_left, min_right)
There's no checks on `peaks_idx`!
>>> find_head_heights([1,2,3,2,1,2,3,2,1,2,3,2,1,2,3], [2,6,10])
[2, 2, 2]
>>> find_head_heights([1,9,3,6,2,7,5], [1,3,5])
[6, 3, 2]
>>> find_head_heights([1,9,1,8], [1])
[8]
>>> find_head_heights([1], [])
[]
>>> find_head_heights([1,9,3,6,2,7,5], [1,3,6])
Traceback (most recent call last):
ValueError: Height <= 0 detected at i=2, probably the input `peaks_idx` is wrong
"""
peak_heights =[float('nan') for i in peaks_idx]
n = len(peaks_idx)
for i in range(n):
min_left = min(v[peaks_idx[i-1] if i > 0 else 0:peaks_idx[i]])
min_right = min(v[peaks_idx[i]:peaks_idx[i+1] if i < n-1 else len(v)])
peak_heights[i] = v[peaks_idx[i]] - max(min_right, min_left)
if peak_heights[i] <= 0:
raise ValueError(f'Height <= 0 detected at i={i}, probably the input `peaks_idx` is wrong')
return peak_heights
doctest.run_docstring_examples(find_head_heights, globals(), verbose=False)
In [88]:
def detect_chain_of_heads(v, min_heights, max_heights=None, height=None) -> "list of head's center":
""" Returns that last sequence that matches min_heights & max_heights
>>> detect_chain_of_heads([1,4,1,4,1,4], [3], height=1000)
[3]
>>> detect_chain_of_heads([1,4,1,4,1,4], [3,3], height=1000)
[1, 3]
>>> detect_chain_of_heads([1,9,1,8,1,7,1], [8, 7, 6], [9, 8, 7])
[1, 3, 5]
>>> detect_chain_of_heads([1,9,1,8,1,7,2], [8, 7, 6], [9, 8, 7])
[]
"""
if max_heights is None:
if height is None:
raise ValueError('max_heights and height can\'t both be None')
max_heights = [h + height for h in min_heights]
assert len(min_heights) == len(max_heights)
assert min(min_heights) > 0
if not min_heights:
return list()
heads = [i[1] for i in detect_heads(v, min(min_heights))]
head_heights = find_head_heights(v, heads)
if len(heads) < len(min_heights):
return []
for i in range(len(heads)-1, len(min_heights) - 2, -1):
match = True
for k in range(len(min_heights)):
z = len(min_heights) - 1 - k
if not (min_heights[z] <= head_heights[i - k] <= max_heights[z]):
match = False
break
if match:
break
return heads[i - len(min_heights) + 1:i + 1]
doctest.run_docstring_examples(detect_chain_of_heads, globals(), verbose=False)
In [ ]:
doctest.testmod(verbose=0)
In [89]:
for _ in range(3):
print('hi')
In [92]:
k = 3
while k-=1 > 0:
print('k')
In [ ]: