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
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.
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,
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
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.
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,