0

I would like to plot one line graph. And the line has to change color based on the y-value. so for example:

red = 0 - 0.5
blue = 0.5 - 1
green = 1 - 1.5
black = 1.5 - 2

But I have also tried to make a extra column in my data frame which looks like this:

                    gradient result
date                                
2022-04-15 09:43:20  0.206947      E
2022-04-15 10:25:00  0.102620      E
2022-04-15 11:06:40  0.019450      C
2022-04-15 11:48:20  0.025945      D
2022-04-15 12:30:00  0.022455      D
                      ...    ...
2022-05-02 14:13:20  0.003770      A
2022-05-02 14:55:00  0.084120      E
2022-05-02 15:36:40  0.134970      E
2022-05-02 16:18:20  0.261385      E
2022-05-02 17:00:00  0.955833    NaN

So it can be also possible to make:

red = A
blue = C
green = D
black = E

I have found a script on the internet, but this does not work for me. This is the original script:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
from matplotlib.colors import ListedColormap, BoundaryNorm

x = np.linspace(0, 3 * np.pi, 500)
y = np.sin(x)
dydx = np.cos(0.5 * (x[:-1] + x[1:]))  # first derivative

# Create a set of line segments so that we can color them individually
# This creates the points as a N x 1 x 2 array so that we can stack points
# together easily to get the segments. The segments array for line collection
# needs to be (numlines) x (points per line) x 2 (for x and y)
points = np.array([x, y]).T.reshape(-1, 1, 2)
segments = np.concatenate([points[:-1], points[1:]], axis=1)

fig, axs = plt.subplots(2, 1, sharex=True, sharey=True)

# Create a continuous norm to map from data points to colors
norm = plt.Normalize(dydx.min(), dydx.max())
lc = LineCollection(segments, cmap='viridis', norm=norm)
# Set the values used for colormapping
lc.set_array(dydx)
lc.set_linewidth(2)
line = axs[0].add_collection(lc)
fig.colorbar(line, ax=axs[0])

# Use a boundary norm instead
cmap = ListedColormap(['r', 'g', 'b'])
norm = BoundaryNorm([-1, -0.5, 0.5, 1], cmap.N)
lc = LineCollection(segments, cmap=cmap, norm=norm)
lc.set_array(dydx)
lc.set_linewidth(2)
line = axs[1].add_collection(lc)
fig.colorbar(line, ax=axs[1])

axs[0].set_xlim(x.min(), x.max())
axs[0].set_ylim(-1.1, 1.1)
plt.show()

It should show a graph that looks like this:

original graph

And I have tried to adapt the script to my own code:

x = data_646_mean.index
y = data_646_mean.gradient
dydx = y  # first derivative

# Create a set of line segments so that we can color them individually
# This creates the points as a N x 1 x 2 array so that we can stack points
# together easily to get the segments. The segments array for line collection
# needs to be (numlines) x (points per line) x 2 (for x and y)
points = np.array([x, y]).T.reshape(-1, 1, 2)
segments = np.concatenate([points[:-1], points[1:]], axis=1)

fig, axs = plt.subplots(2, 1, sharex=True, sharey=True)



# Use a boundary norm instead
cmap = ListedColormap(['r', 'g', 'b'])
norm = BoundaryNorm([-1, -0.5, 0.5, 1], cmap.N)
lc = LineCollection(segments, cmap=cmap, norm=norm)
lc.set_array(dydx)
lc.set_linewidth(2)
line = axs[1].add_collection(lc)
fig.colorbar(line, ax=axs[1])

axs[0].set_xlim(x.min(), x.max())
axs[0].set_ylim(-1.1, 1.1)
plt.show()

Which results in only good x-values:

failed graph

It is doing something with a derivative which I do not understand. Does anyone know how to use this script or maybe another way. It does not matter if the color is defined by a extra column in the data frame or by a given interval.

Kind regards,

Simon

This is a example of the data, as the StackOverflow user 'a_guest' requested:

WeTransfer link to csv example

SimonDL
  • 186
  • 10
  • For your (desired) example plots, the color scale doesn't seem to match the y-values? – a_guest May 16 '22 at 08:46
  • @a_guest Well most values are below 1, so my y-values do match the color scale. It would at least show a line with 1 color, if it worked. – SimonDL May 16 '22 at 08:50
  • You can use the example code from the [matplotlib page](https://matplotlib.org/stable/gallery/lines_bars_and_markers/multicolored_line.html) and simply change `lc.set_array(dydx)` to `lc.set_array(y)`. – a_guest May 16 '22 at 08:51
  • You probably need to adjust the `ax[0].set_xlim` and `ax[0].set_ylim` as well, to match your data. – a_guest May 16 '22 at 08:58
  • @a_guest I have tried that but it gives the following error: TypeError: cannot add DatetimeArray and DatetimeArray. So I have also changed the 'dydx = np.cos(0.5 * (x[:-1] + x[1:]))' to 'dydx = y'. But this results in no error but a emty plot – SimonDL May 16 '22 at 08:58
  • @a_guest. No I dont have to change the xlim and ylim because my y data is between -1 and 1 and the x data autofits – SimonDL May 16 '22 at 09:00
  • Can you please provide your data (or a subset thereof) as well so that I can reproduce the problem? An empty plot suggests that it's limited to the wrong range, but let's see. – a_guest May 16 '22 at 09:20
  • @a_guest Thanks for helping me. I have put a WeTransfer link for the dataframe in a CSV file. I can also copy pase a part of it in my post if you want. – SimonDL May 16 '22 at 09:33
  • 1
    It seems that `LineCollection` doesn't handle datetime objects correctly. Please check if [this answer](https://stackoverflow.com/a/44649053/3767239) solves your problem. – a_guest May 16 '22 at 13:55
  • Yes this approach works for a big part. I had already found it, but I do not know how to make the colors change with at least 5 given value. So for example. 0.5 - 1 = red, 1 - 1.5 = blue etc... – SimonDL May 16 '22 at 14:16
  • It seems that you have to change the values given to `norm = BoundaryNorm([-1, -0.5, 0.5, 1], cmap.N)`, e.g. something like `norm = BoundaryNorm(np.linspace(v_min, v_max, n_steps), cmap.N)`. – a_guest May 16 '22 at 14:53

0 Answers0