user7468395
user7468395

Reputation: 1349

How to detect upward and downward trend with pandas

For the plot that is generated with the code below, I would like to get a signal generated via a pandas logic.

The output signal should change from -4 to -2, when the curve is +3 points higher (or more) than the last local minimum. It should change back from -2 to -4, when the curve is 2 points lower (or less) than the last local maximum.

Plot 1 shows the curve generated by code below. Plot 2 shows approximately, how the output signal should look like.

Plot 1: Plot 1

Plot 2: Plot 2

The code:

import matplotlib

matplotlib.use('QT5Agg')
import matplotlib.pyplot as plt
import numpy as np

a = np.arange(5)
b = np.arange(5, -4, -1)
c = np.arange(-4, 7, .5)
d = np.arange(7, 2, -1)
e = np.arange(2, 6, .2)
f = np.arange(6, -3, -1)
g = np.arange(-3, 2, .25)

r1 = np.append(a, b)
r2 = np.append(r1, c)
r3 = np.append(r2, d)
r4 = np.append(r3, e)
r5 = np.append(r4, f)
r6 = np.append(r5, g)

plt.rcParams['font.size'] = 6

fig, ax1 = plt.subplots()
ax1.plot(r6,'g-o',markersize=3)



plt.annotate('start upward', xy=(0,0), textcoords='data',)
plt.annotate('end upward', xy=(3,3), textcoords='data',)

plt.annotate('start downward', xy=(5,5), textcoords='data',)
plt.annotate('end downward', xy=(7,3), textcoords='data',)

plt.annotate('start upward', xy=(14,-4), textcoords='data',)
plt.annotate('end upward', xy=(20,-1), textcoords='data',)

plt.annotate('start downward', xy=(36,7), textcoords='data',)
plt.annotate('end downward', xy=(38,5), textcoords='data',)

plt.annotate('start upward', xy=(41,2), textcoords='data',)
plt.annotate('end upward', xy=(56,5), textcoords='data',)

plt.annotate('start downward', xy=(61,6), textcoords='data',)
plt.annotate('end downward', xy=(63,4), textcoords='data',)

plt.annotate('start upward', xy=(70,-3), textcoords='data',)
plt.annotate('end upward', xy=(82,0), textcoords='data',)

ax1.minorticks_on()
ax1.grid(b=True, which='major', color='g', linestyle='-')
ax1.grid(b=True, which='minor', color='y', linestyle='--')
plt.show()

Upvotes: 6

Views: 2865

Answers (2)

Quang Hoang
Quang Hoang

Reputation: 150745

I think you want this:

s = pd.Series(np.concatenate((a,b,c,d,e,f,g,)))

# is increasing
incr = s.diff().ge(0)

# shifted trend (local minima)
shifted = incr.ne(incr.shift())

# local max
local_max = shifted & (~incr)


# thresholding function
def thresh(x, threshold=3, step=2):
    ret = pd.Series([0]*len(x), index=x.index)
    t = x.min() + threshold
    ret.loc[x.gt(t)] = step
    return ret

signal = s.groupby(local_max.cumsum()).apply(thresh)
signal += s.min()

# draw
fig, ax = plt.subplots(figsize=(10,6))
s.plot(ax=ax)
signal.plot(drawstyle='steps', ax=ax)
plt.show()

Output:

enter image description here

Upvotes: 3

piRSquared
piRSquared

Reputation: 294258

IIUC:

s = pd.Series([0, 5, 0, -4, -1, 2, 4, 7, 2, 3, 4, 5, 6, -3, -2, -1, 0, 1, 2])

s.plot()
ax = s.diff().ge(0).mul(1).plot(drawstyle='steps', c='r', secondary_y=True)
ax.set_ylim(0, 8)

enter image description here

Upvotes: 3

Related Questions