1

I have the following bar graph generated using pandas. My problem is all the bars have the same pattern. I have tried many approaches but could not manage to get around this issue.

Moreover, only one entry(for the last subplot) is shown in the legend.

The data used is

enter image description here

The code is :

import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.ticker import ScalarFormatter
class ScalarFormatterForceFormat(ScalarFormatter):
    def _set_format(self):  # Override function that finds format to use.
        self.format = "%1.1f"  # Give format here
patterns = [ "\\" , "/" , "-","+" ,"x", "|", '.',  "O" ]
yfmt = ScalarFormatterForceFormat()
yfmt.set_powerlimits((0, 0))

bar_gap=0.005
bar_width=0.01

bar_pos = [0 for i in range(5)]
bar_pos[0]=bar_gap
for i in range(1,5):
    bar_pos[i]=bar_pos[i-1]+bar_gap+bar_width
colors = ['tab:blue', 'tab:green', 'tab:orange', 'tab:red','tab:olive']
patterns = [ "\\" , "/" , "+" , "-", ".", "*","x", "o", "O" ]

# file_locn = ''r'C:\Users\girum\Desktop\Throughput.csv'''
file_locn = ''r'my_file.csv'''
df = pd.read_csv(file_locn,index_col='Set')
df=df.T
fig, axes = plt.subplots(1,3,figsize=(8,5))#,sharey=True)
for i in range(3):
    axes[i].yaxis.set_major_formatter(yfmt)
df.Type_A.plot(ax=axes[0],kind='bar',color=colors)
df.Type_B.plot(ax=axes[1],kind='bar',color=colors)
df.Type_C.plot(ax=axes[2],kind='bar',color=colors)

handles, labels = axes[0].get_legend_handles_labels()
for ax in fig.axes:
    bars = ax.patches
    hatches = ''.join(h*len(df) for h in patterns)

    for bar, hatch in zip(bars, hatches):
        bar.set_hatch(2*hatch)

plt.xticks(rotation=360)
axes[0].set_ylabel('Speed')
for i in range(len(df)):
    axes[i].set_xlabel('')#Why is this line not working
    axes[i].tick_params(axis='x', rotation=360)

plt.legend(loc='center right', bbox_to_anchor=(.2,1.08), ncol=1)

plt.show()

enter image description here

hope
  • 85
  • 1
  • 7
  • @JohanC Your suggestion gave me the legends for all the bars but the legend entry shown in the picture above is still there as a fourth entry – hope Feb 01 '22 at 15:13
  • I tried it. It didn't work – hope Feb 01 '22 at 16:46
  • Please give the authors of the answers some credit for there effort using the voting arrows and mark one as correct (press the check mark below the voting arrows) to show to others that this question is solved. – mosc9575 Feb 01 '22 at 19:01

2 Answers2

2

The lines where you are joining the patterns generates a result, which you don't want.

patterns = [ "\\" , "/" , "+" , "-", ".", "*","x", "o", "O" ]
hatches = ''.join(h*3 for h in patterns)
>>> '\\\\\\///+++---...***xxxoooOOO'

# if you have the bars, this is the output
for bar, hatch in zip([0,1,3], hatches):
    print(2*hatch)
>>>
\\
\\
\\

Try to simplify this section using the patterns in your loop directly:

for bar, hatch in zip([0,1,3], patterns):
    print(2*hatch)`
>>>
\\
//
++

Output

I used your given code and data to create this output. more than one hatch

mosc9575
  • 5,618
  • 2
  • 9
  • 32
  • yes, thank you. this fixed the hatch issue, have you seen the legend issue and the xticks? Only the first bars legend is shown and it is also not correct since it says 'Type-C'. The xticks should also have been empty as per the line in the last loop. – hope Feb 01 '22 at 14:07
  • For you `x_axis` thing try `ax.set_xticks([])` or check out this [question](https://stackoverflow.com/questions/12998430/remove-xticks-in-a-matplotlib-plot). – mosc9575 Feb 01 '22 at 14:22
  • set_xticks([]) solved the label problem. thanks – hope Feb 01 '22 at 16:48
2

The code below has the following changes:

  • added some dummy test data to enable stand-alone test code
  • removed some unused variables
  • used the unaltered ScalarFormatter
  • only one loop through the axes and avoiding the plt interface
  • using ax.containers[0] to catch the bar container (ax.patches is a list of the rectangles, without the surrounding container)
  • change the label of the bar container to _no_legend, so it doesn't appear in the legend
  • used the patterns directly instead of concatenating them
  • removed h*len(df); note that multiplying a string such as '/' by e.g. 4, repeats the string (to '////'); repeated patterns are used in matplotlib to make the base pattern denser
  • used tick_params(axis='x', labelbottom=False, length=0) to remove the tick labels
  • added labels to the individual bars so they appear into the legend
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.ticker import ScalarFormatter

yfmt = ScalarFormatter()
yfmt.set_powerlimits((-9, 9))

colors = ['tab:blue', 'tab:green', 'tab:orange', 'tab:red', 'tab:olive']
patterns = ["\\", "/", "+", "-", ".", "*", "x", "o", "O"]

df = pd.DataFrame(np.random.randint(100000, 500000, (3, 3)),
                  columns=['A', 'B', 'C'],
                  index=['Type_A', 'Type_B', 'Type_C'])
df = df.T
fig, axes = plt.subplots(1, 3, figsize=(8, 5))
df.Type_A.plot(ax=axes[0], kind='bar', color=colors)
df.Type_B.plot(ax=axes[1], kind='bar', color=colors)
df.Type_C.plot(ax=axes[2], kind='bar', color=colors)

for ax in axes:
    bars = ax.containers[0]
    bars.set_label('_no_legend')
    hatches = [h * 2 for h in patterns]
    for bar, hatch, label in zip(bars, hatches, df.index):
        bar.set_hatch(2 * hatch)
        bar.set_label(label)
    ax.yaxis.set_major_formatter(yfmt)
    ax.tick_params(axis='x', labelbottom=False, length=0)

axes[0].set_ylabel('Speed')

axes[2].legend(loc='lower right', bbox_to_anchor=(1, 1.01), ncol=3)
plt.tight_layout()
plt.show()

bar plots with hatching

JohanC
  • 71,591
  • 8
  • 33
  • 66