Nov-20-2023, 04:47 AM
(This post was last modified: Nov-21-2023, 04:35 AM by deanhystad.)
This might do what you want. It splits the data into a bunch of localized peak valleys using your 10% change threshold. The localized peak valleys for [100, 99, 90, 79, 62, 94, 88, 75, 80, 72, 74, 87, 84, 90] are [100, 99, 90], [90, 79], [79, 62], [62, 94], [94, 88, 75], [75, 80, 72, 74, 87], [87, 84, 90]. This is done by the Trend class, a list with a special append() method.
The Trends class creates a list of trends. When a Trend is appended to the list, Trends combines the new trend with the previous trend. If both trends trend in the same direction, the new trend is absorbed into the previous trend. If the new trend changes direction, Trends searches for the peak or valley (depending on direction) in the new trend. This is the start of the new trend, and points prior to this are moved to the previous trend.
The Trends class creates a list of trends. When a Trend is appended to the list, Trends combines the new trend with the previous trend. If both trends trend in the same direction, the new trend is absorbed into the previous trend. If the new trend changes direction, Trends searches for the peak or valley (depending on direction) in the new trend. This is the start of the new trend, and points prior to this are moved to the previous trend.
class Trend(list): """A list of numbers that trend in the same direction.""" def __init__(self, value=None): super().__init__() self.direction = 0 super().append(value) def append(self, value): """Add number to Trend. Return direction of trend (-1, 0, 1).""" super().append(value) if value <= self[0] * 0.9 or value >= self[0] * 1.1: self.direction = 1 if self[-1] > self[0] else -1 return self.direction class Trends(list): """Convert list of numbers to a list of trends.""" def __init__(self, values): super().__init__() trend = Trend(values[0]) for value in values[1:]: if trend.append(value) != 0: self._add_trend(trend) trend = Trend(value) self._add_trend(trend) def _add_trend(self, trend): """Combine last trend into list""" prev = self[-1] if self else None if prev: i = trend.index(max(trend) if prev.direction == 1 else min(trend)) prev.extend(trend[1:i+1]) del trend[:i] if len(trend) > 1: super().append(trend) print(Trends([100, 99, 90, 79, 62, 94, 88, 75, 80, 72, 74, 87, 84, 90]))And as a generator without using classes:
def trends(numbers): prev = None trend = [] prev_dir = trend_dir = 0 for number in numbers: trend.append(number) if number > trend[0] * 1.1 or number < trend[0] * 0.9: trend_dir = 1 if number > trend[0] else -1 if prev: if trend_dir in (0, prev_dir): prev.extend(trend) else: elbow = min(trend) if trend_dir > 0 else max(trend) index = trend.index(elbow) prev.extend(trend[1:index+1]) yield prev prev = trend[index:] prev_dir = trend_dir else: prev = trend prev_dir = trend_dir trend = [number] if len(trend) > 1: if prev: prev.extend(trend[1:]) yield prev else: yield trend data = [100, 99, 90, 79, 62, 94, 88, 75, 80, 72, 74, 87, 84, 90] print(*trends(data))