1

I have a dictionary containing keys as labels and values as colors for a plot. Using an example from:Change color of specific ticks at plot with matplotlib . How do I change the label colors based on the keys in the dictionary. Note, the labels can be repeated multiple times on the x-axis and they can be strings e.g labels = 'A', 'B', 'C', 'D'.

Here is the little modification of the code:

import matplotlib.pyplot as plt

fig, ax = plt.subplots(figsize=(5,4))
ax.plot([1,2,3])
colors = {0.00 :'grey',0.25:'yellow',0.50:'brown',2.00:'red'}
for k, v in colors.items():
    ax.get_xticklabels()[k].set_color(v)

plt.show()

I expected each label on the plot to be associated with a color but I am getting a plain plot with this error: TypeError: list indices must be integers or slices, not float. I am trying to adapt it to string labels specifically.

Tomerikoo
  • 18,379
  • 16
  • 47
  • 61
Starter
  • 417
  • 4
  • 12

2 Answers2

1

The error is due to the fact that ax.get_xticklabels() returns a list. Elements of a list are get through their position (index, starting from 0), not their value.
What you have in your dictionary is the value of the tick, not its position.

You need to extract its position before, and use that to set the color. I adjusted you code to do this:

import numpy as np
import matplotlib.pyplot as plt

def colorticks(event=None):
    locs, labels = plt.xticks()
    colors = {0.00 :'grey',0.25:'yellow',0.50:'brown',2.00:'red'}
    for k, v in colors.items():
        idxs = np.where(locs == k)[0]
        for pos in idxs:
            labels[pos].set_color(v)

fig, ax = plt.subplots(figsize=(5,4))
ax.plot([1,2,3])
colorticks()

#following line is needed to keep the correct colors when resizing
cid = fig.canvas.mpl_connect('resize_event', colorticks)

plt.show()

enter image description here

The key is locs, labels = plt.xticks(): xticks returns a list with the values (locs) and their Text objects (labels).
Then numpy.where is used to find the indexes of each given value in the loop, and that indexes are used to set the color of the Text objects.

EDIT

Following @ImportanceOfBeingErnest's comment, the code above has been updated to make it works when the figure is resized.
The relevant lines have been wrapped in a function, used as a callback for the 'resize_event'.

Community
  • 1
  • 1
Valentino
  • 7,291
  • 6
  • 18
  • 34
1

One solution is to check for the existence of the tick labels in the keys of your dictionary. To compare the exact values, I have converted the keys from float to strings. This should not be a problem for you I believe.

P.S.: Based on @ImportanceOfBeingEarnest's comment, I would state that this answer assumes that the figure stays static and is not resized.

import matplotlib.pyplot as plt

fig, ax = plt.subplots(figsize=(5,4))
ax.plot([1,2,3])
colors = {'0.00' :'grey','0.25':'yellow','0.50':'brown','2.00':'red'}

fig.canvas.draw()

for xtic in ax.get_xticklabels():
    if xtic.get_text() in colors.keys(): # Change color if exist else not
        xtic.set_color(colors[xtic.get_text()])

plt.show()

enter image description here

Sheldore
  • 37,862
  • 7
  • 57
  • 71
  • The danger here is that the result will become wrong once the figure is resized. – ImportanceOfBeingErnest Jun 17 '19 at 18:44
  • @ImportanceOfBeingErnest : Isn't that taken care by the `if` statement? **Only** if the tick labels are existing on the canvas, will they get colored. Also, I didn't think of this scenario, since the OP stated no such condition. But yea, you are right – Sheldore Jun 17 '19 at 18:46
  • Well, the if statement gets executed once before the plot is shown. You could connect to an event such that it is reevaluated when the tick positions change. (And yes, no such condition is stated, but one can be sure that someone will at some point refer to such problem if it isn't made clear already in which scope this answer works.) – ImportanceOfBeingErnest Jun 17 '19 at 18:51
  • @ImportanceOfBeingErnest: Thanks. I have included that disclaimer in the post. But could you plz clarify what you mean by wrong? Because if one zooms out let's say, then the small values will go away and the higher tick values will stay black, except 2 which will be red, unless it zooms out too. – Sheldore Jun 17 '19 at 19:00
  • If you zoom a bit you might get a figure like [this](https://i.stack.imgur.com/v8vl5.png), which doesn't even have 0.25 and 0.5 as labels, or like [this](https://i.stack.imgur.com/FShLd.png) where 0 ends up yellow. – ImportanceOfBeingErnest Jun 17 '19 at 19:08
  • I tried all the answers. They all seem to work even though I did not zoom and the likes. Can I accept all answers or just one? Thanks guys – Starter Jun 17 '19 at 23:46