Search code examples
python-3.xpandasmatplotlibcolorsaxis-labels

Matplotlib Color y-tick Labels via Loop


Given the following data frame:

import pandas as pd
import numpy as np
df=pd.DataFrame({'A':['A','B','C','D','E','F','G','H','I','J','K','L','M','N'],
                'B':[20,25,39,43,32,17,40, 40, 34, 56, 76, 23, 54, 34]})

I'd like to create a bubble chart where each y-tick label is the same color as its respective dot. The code below works great if I only had say 4 rows of data and 4 colors in my color list. However, for some reason, when I have more than 9 or so rows of data (and colors in my color list), it only takes the first 9 elements of colors in the l.set_color(i) line. Any thoughts as to why this occurs? Is it a limitation of zip when iterating? Related to the data frame?

import matplotlib.pyplot as plt
import matplotlib.ticker as mtick
labels=df.A[::-1]
vals=df.B[::-1]
ind=np.arange(len(labels))
colors1=['r','g','b','c','y','y','y','g','b','c','y','y','y','g']
fig, ax = plt.subplots(1, 1, figsize = (6,4))
for i in ind:
    plt.plot(vals[i],i,marker='o',markeredgecolor='none', markersize=17, alpha=.5, linestyle='none', color=colors1[i])
ax.tick_params(axis='x',which='both',bottom='on',top='off',color='grey',labelcolor='grey')
ax.tick_params(axis='y',which='both',left='off',right='off',color='grey',labelcolor='grey')
ax.spines['top'].set_visible(False);ax.spines['right'].set_visible(False);
ax.spines['bottom'].set_visible(False);ax.spines['left'].set_visible(False)

ax.set_xlim([0,50])
ax.set_ylim([min(ind)-1,max(ind)+1])
fontcols=colors1[::-1]
for l,i in zip(ax.yaxis.get_ticklabels(),fontcols):
    l.set_color(i)
    l.set_fontsize(11)
    print(l,i) #This shows that only 9 members are being colored for some reason
plt.yticks(ind,labels,fontsize=14)

plt.show()

enter image description here

Thanks in advance!


Solution

  • You just need to set the yticks before you try and set the colours. As it is, matplotlib creates 9 ticks by default, you set their colours, then you tell it you want 14 ticks after. With just a little reordering, it all works:

    import matplotlib.pyplot as plt
    import matplotlib.ticker as mtick
    import pandas as pd
    import numpy as np
    df=pd.DataFrame({'A':['A','B','C','D','E','F','G','H','I','J','K','L','M','N'],
                    'B':[20,25,39,43,32,17,40, 40, 34, 56, 76, 23, 54, 34]})
    
    labels=df.A[::-1]
    vals=df.B[::-1]
    ind=np.arange(len(labels))
    colors1=['r','g','b','c','y','y','y','g','b','c','y','y','y','g']
    fig, ax = plt.subplots(1, 1, figsize = (6,4))
    for i in ind:
        plt.plot(vals[i],i,marker='o',markeredgecolor='none', markersize=17, alpha=.5, linestyle='none', color=colors1[i])
    ax.tick_params(axis='x',which='both',bottom='on',top='off',color='grey',labelcolor='grey')
    ax.tick_params(axis='y',which='both',left='off',right='off',color='grey',labelcolor='grey')
    ax.spines['top'].set_visible(False);ax.spines['right'].set_visible(False);
    ax.spines['bottom'].set_visible(False);ax.spines['left'].set_visible(False)
    
    ax.set_xlim([0,80])  # I increased this to fit all your data in
    ax.set_ylim([min(ind)-1,max(ind)+1])
    fontcols=colors1     # NOTE: you don't need to reverse this
    plt.yticks(ind,labels,fontsize=14)
    for l,i in zip(ax.yaxis.get_ticklabels(),fontcols):
        l.set_color(i)
        l.set_fontsize(11)
        print(l,i) 
    
    plt.show()
    

    enter image description here

    Also note, you don't need to reverse the colour list before setting the tick colours