Colors problem in plotting figure with matplotlib in python

Question:

I am trying to plot a bipartite graph to highlight the differences between two rankings. I am doing so by connecting the city in the left list to the same city on the right list with a colored arrow. The color should be proportional to the difference in rankings.

Here is a MWE:

import matplotlib.pyplot as plt

# Sample data
cities = ['City A', 'City B', 'City C', 'City D', 'City E',
          'City F', 'City G', 'City H', 'City I', 'City J']
genepy_rank = [3, 1, 4, 2, 5, 8, 7, 10, 9, 6]
fitness_rank = [7, 9, 2, 5, 4, 6, 3, 1, 8, 10]

# Calculate the difference in ranking
diff_rank = [genepy - fitness for genepy, fitness in zip(genepy_rank, fitness_rank)]

# Plot the graph
fig, ax = plt.subplots()
for i, city in enumerate(cities):
    x = [genepy_rank[i], fitness_rank[i]]
    y = [i, i]
    color = diff_rank[i]
    ax.plot(x, y, color=color, marker='o')
    ax.annotate(city, (x[0], y[0]), xytext=(-20, 20),
                textcoords='offset points', ha='right', va='bottom',
                bbox=dict(boxstyle='round,pad=0.5', fc='yellow', alpha=0.5),
                arrowprops=dict(arrowstyle = '->', connectionstyle='arc3,rad=0'))
ax.set_xlim(0, 11)
ax.set_ylim(-1, 11)
ax.set_yticks([i for i in range(len(cities))])
ax.set_yticklabels(cities)
ax.spines['left'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)
ax.yaxis.tick_right()
ax.yaxis.set_label_position("right")
plt.show()

The problem is that this exits with an error: ValueError: -4 is not a valid value for color which I understand. Is there a way to determine a color grid and assign a color to the arrows based on diff_rank?

Thank you

Asked By: Lusian

||

Answers:

Instead of specifying a color, why don’t you use a colormap?

If you want to show also a colorbar (as I did) remember that plt.plot doesn’t provide the ScalarMappable that’s necessary to instantiate a Colorbar, so you have to provide one.

enter image description here

import matplotlib.pyplot as plt
from matplotlib.cm import ScalarMappable
from matplotlib.colors import BoundaryNorm

# Sample data
cities = ['City A', 'City B', 'City C', 'City D', 'City E',
          'City F', 'City G', 'City H', 'City I', 'City J']
genepy_rank = [3, 1, 4, 2, 5, 8, 7, 10, 9, 6]
fitness_rank = [7, 9, 2, 5, 4, 6, 3, 1, 8, 10]

# Calculate the difference in ranking
diff_rank = [genepy - fitness for genepy, fitness in zip(genepy_rank, fitness_rank)]
dmin, dmax = min(diff_rank), max(diff_rank)

boundaries = [dmin-0.5]+[d+0.5 for d in range(dmin, dmax+1)]
cm = plt.get_cmap('Spectral')
norm = BoundaryNorm(boundaries, 256)

print(type(norm))
# Plot the graph
fig, ax = plt.subplots(layout='constrained')
for i, city in enumerate(cities):
    x = [genepy_rank[i], fitness_rank[i]]
    y = [i, i]
    color = cm(norm(diff_rank[i]))
    ax.plot(x, y,  color=color, marker='o')
    ax.annotate(city, (x[0], y[0]), xytext=(-20, 20),
                textcoords='offset points', ha='right', va='bottom',
                bbox=dict(boxstyle='round,pad=0.5', fc='yellow', alpha=0.5),
                arrowprops=dict(arrowstyle = '->', connectionstyle='arc3,rad=0'))
ax.set_xlim(0, 11)
ax.set_ylim(-1, 11)
cb = plt.colorbar(ScalarMappable(norm, cm), ax=ax)
cb.set_ticks(range(dmin, dmax+1))
cb.set_ticklabels(['%+2d'%t for t in range(dmin, dmax+1)])
cb.setlabel('Ranking Difference (genepy -fitness)')
plt.show()

I have to say that I do not think this is a good way to show your data (even if it could be the best one, I don’t know…).


Maybe this is better,

enter image description here

Answered By: gboffi
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.