3

I want to produce a graph showing the error of a numerical function for various convergence methods/values on the same graph.

I am using a while loop to increment through different values of convergence condition and plot the error on a graph, once for each convergence method (labelled with the suffix A & B).

The problem is that each time I call plot in matplotlib the line displays as a different colour. I want the same colour for each convergence value (ie to plot the lines as the same colour if they share a convergence value) to make it easier for the reader to see which lines are connected.

An example of what my code currently produces.

Whereas I would like the coloured and black dotted line in each pair to be the same colour. I haven't included the definitions of the functions, but the code I'm using to produce the graph is

conv = 1e-0
h=0.1

while conv>1e-4:

    errorlist_A, errorlist_B = [],[]
    gridsize = []
    grid_x = 40


    while grid_x <= 100:

        analytical = (np.ones((grid_x,grid_x)))*edge_val
        P = np.zeros((grid_x,grid_x))

        gridsize.append(grid_x)


        solA = solvelap_avg_rand(grid_x,grid_x,edge_val,P,h,conv)
        solB = solvelap_max_rand(grid_x,grid_x,edge_val,P,h,conv)

        errorlist_A.append(errorfunc(grid_x,grid_y,analytical,solA,edge_val))
        errorlist_B.append(errorfunc(grid_x,grid_y,analytical,solB,edge_val))

        grid_x +=5
        print(grid_x)

    plt.plot(gridsize,errorlist_A, label = "{0:.1g}".format(conv))
    plt.plot(gridsize,errorlist_B, label = "{0:.1g}".format(conv) , linestyle = "--", color = "k")

    conv *=0.1


plt.title("Fig:6 Error per Node Vs. Grid Width")
plt.xlabel("Grid Width")
plt.ylabel("Average Error Per Node (Log10)")
plt.yscale("log")
plt.legend()
plt.show() 

Note: I'm aware you can set the colours of the lines manually as an argument, but I'm not sure how to do this in a loop.

In addition, if there's anyway to prevent the legend from blocking the plots that would also be very helpful.

Eli Rees
  • 178
  • 2
  • 14

1 Answers1

4

I would use a colormap to define automatically the color. However, since you don't know in advance how many curves there will be, we can plot all the curves and color them afterwards.

Modify the following lines in your code:

# Remove the `color=` parameter here, it will be overwritten anyway.
plt.plot(gridsize, errorlist_A, label="{0:.1g}".format(conv))
plt.plot(gridsize, errorlist_B, label="{0:.1g}".format(conv), linestyle = "--")

Then add those right before plt.show():

# After you plotted them, apply a random color to each curve.
import matplotlib.cm as cm
colormap = cm.get_cmap("plasma") # The various colormaps can be found here: https://matplotlib.org/3.1.0/tutorials/colors/colormaps.html
ax = plt.gca()
lines = ax.lines
N = len(lines)
for n in range(0, N, 2): # For each two-lines made via `plt.plot(...)`:
    random_color = colormap(n/N) # This function takes a number between 0 and 1 and returns a color.
    lines[n].set_color(random_color)
    lines[n+1].set_color(random_color)

enter image description here

Guimoute
  • 4,407
  • 3
  • 12
  • 28
  • Hi there, I tried to implement your solution but it threw a SyntaxError: can't assign to function call error on the "for n, enumerate(line) in ax.lines:" line. Also, I will know how many lines will be plotted, since I know what the convergence is initially and how much it changes by after each loop I know there will be ten lines in all, five pairs. Though I may reduce this to four pairs. – Eli Rees Feb 12 '20 at 13:15
  • It's a typo, my bad. I meant `for n, line in enumerate(ax.lines):`. – Guimoute Feb 12 '20 at 14:23
  • @EliRees Actually forget it, I changed the approach to something simpler. It should be much nicer to understand now! :) – Guimoute Feb 12 '20 at 14:41
  • 1
    My pleasure! Feel free to try other colormaps and fiddle with their color value. You can restrain it to a certain portion of the map by using modulos or offsets, for example `colormap(-n/N % 1)`, `colormap(n/N%0.5 + 0.5)` or `colormap(n/N/2)`, etc. – Guimoute Feb 14 '20 at 10:14