Alexis Moreno
Alexis Moreno

Reputation: 13

Coloring a specific portion of the plot based on condition

I have data that is pretty cyclical in nature but not quite exactly Here is what part of the data looks like . I want to color all the points that are monotonically increasing one color, call it color A, then I want to color all the points that decreasing one color ,call it B, and then the next points that are increasing color C and then color B for when we decrease. The data is essentially a running total of X until Y and then reset X back to 0 and keep counting. Data is in a pandas df and I am using matplotlib to do this graphing.

Any advice would be much appreciated!

Upvotes: 1

Views: 130

Answers (1)

filippo
filippo

Reputation: 5294

Let's start with a sample dataset, hoping it mimicks yours well enough:

y = np.linspace(0, 10000, 1000, endpoint=True)
for i in np.sort(np.random.randint(1000, size=10)):
   y[i:] = y[i:] - y[i]

enter image description here

Now we need to identify the monotonically increasing segments. You can find where the first discrete derivative becomes negative and use cumulative sum to get a discrete category/color for each group of points.

It's easier done than said:

c = np.cumsum((np.diff(y) < 0))

Now c represents the color of our segments, its shape is (999,) but we want it to be (1000,) like y, so

c = np.hstack([c[0], c])

Now you can use plt.scatter with c as color to plot each segment with a different shade:

plt.scatter(np.arange(len(y)), y, marker='.', c=plt.cm.tab20(c))

enter image description here

If you prefer to plot proper lines instead of using scatter it's a little more tricky: take a look at How to plot one line in different colors

This assumes your dataset only includes monotonically increasing segments. If you also have decreasing ones you can use the sign of the first derivative (change color when sign changes). Something like:

c = np.pad((np.diff(np.sign(np.diff(y))) != 0).cumsum(), [1,1], mode='edge')

Just for the sake of completess let's try this with a sawtooth-ish signal:

y = np.linspace(0, 10000, 1000, endpoint=True)
for i in np.sort(np.random.randint(1000, size=5)):
    y = np.abs(y - y[i]) + y[i]

Let's use c from the relation above and do a scatter plot:

plt.scatter(np.arange(len(y)), y, marker='.', c=plt.cm.tab20(c))

enter image description here

Upvotes: 1

Related Questions