2

I have a number of plots that show transcribed text from a speech to text engine in which I want to show the bars where the S2T engine transcribed correctly. I have labeled the subplots according to their expected values and now want to color the bars where the engine transcribed correctly in a different number than the other bars.

That means I need to access the color of the bars depending on their x-tick label. How do I do that?

Basically:

for xlabel in fig.xlabels:
   if(xlabel.text == fig.title):
      position = xlabel.position
      fig.colorbar(position, 'red')

Code that is used to generate the plots:

def count_id(id_val, ax=None):
    title = df.loc[df['ID'] == id_val, 'EXPECTED_TEXT'].iloc[0]
    fig = df[df['ID']==id_val]['TRANSCRIPTION_STRING'].value_counts().plot(kind='bar', ax=ax, figsize=(20,6), title=title)
    fig.set_xticklabels(fig.get_xticklabels(), rotation=40, ha ='right')    
    fig.yaxis.set_major_locator(MaxNLocator(integer=True))

fig, axs = plt.subplots(2, 4)
fig.suptitle('Classic subplot')
fig.subplots_adjust(hspace=1.4)

count_id('byte', axs[0,0])
count_id('clefting', axs[0,1])
count_id('left_hander', axs[0,2])
count_id('leftmost', axs[0,3])
count_id('right_hander', axs[1,0])
count_id('rightmost', axs[1,1])
count_id('wright', axs[1,2])
count_id('write', axs[1,3])

If anyone has an idea how to iterate over axs so I don't have to call count_id() 8 times, that'd be super helpful too. And yea I tried:

misses = ['byte', 'cleftig', 'left_hander', 'leftmost', 'right_hander', 'rightmost', 'wright', 'write']

for ax, miss in zip(axs.flat, misses):
   count_id(ax, miss) # <- computer says no

enter image description here

blkpingu
  • 1,556
  • 1
  • 18
  • 41
  • When setting the color of the bars you can either pass just a color (all bars will be the same) or an array containing the color for each individual label. Example: https://stackoverflow.com/a/59578106/9142735 – Juan Estevez Oct 03 '20 at 01:53
  • I mean, yea, but is there a way to access the color property after initializing plt.plot()? – blkpingu Oct 03 '20 at 02:36
  • Oh I'm not sure you can. Instead of using `DataFrame.plot` to create your figures you could use matplotlib's `plt.bar` directly and pass it your color array when creating each plot (like in the example above). – Juan Estevez Oct 03 '20 at 02:48

1 Answers1

3

You can set the color of each bar according to the label both before and after plotting the bars.

I will use the sample data below for demonstration.

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
data = pd.DataFrame({'word': list('abcdefg'), 'number': np.arange(1, 8)})

1. Before plotting: This is the most common way of plotting a colored barplot. Your can pass a list of colors to plt.plot().

def plot_color_label_before(data, target):
    colors = ['red' if word == target else 'blue' for word in data.word]
    bars = plt.bar(x=data.word, height=data.number, color=colors, alpha=0.5)

The data passed to the function contains two columns, where the first lists all the words on your xtick, and the second lists the corrsponding numbers. The target is your expected word. The code determines the colors for each word according to whether it is consistent with your target. For example,

plot_color_label_before(data, 'c')

enter image description here

2. After plotting: If you want to access the color after calling plt.plot, use set_color to change the color of a specific bar.

def plot_color_label_after(data, target):
    bars = plt.bar(x=data.word, height=data.number, color='blue', alpha=0.5)
    for idx, word in enumerate(data.word):
        if word == target:
            bars[idx].set_color(c='yellow')

plt.bar returns a BarContainer, the i-th element of which is a patch (rectangle). Iterate over all the labels and change the color if the word hits the target. For example,

plot_color_label_after(data, 'c')

enter image description here

Finally, as to iterating over axs, just ravel it can solve the problem.

fig, axs = plt.subplots(2, 4)
for ax in axs.ravel():
    ax.plot(...)
Cheng
  • 301
  • 2
  • 9