1

I want to create a barplot with these specifities;

  • no space between bar
  • Show only unique value of the common values of bars

This is not similar question! This question solves only one of my problems.

import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_axes([0,0,1,1])
langs = [1,2,3,4,5,6,7,8,9,10]
students = [10,10,20,20,20,20,30,30,15,15]
ax.bar(langs,students)
plt.savefig('barplot.pdf')

the output of code

ax.bar(langs,students,width = 1.0)

"width = 1.0" is NOT EXACTLY working. There is little gaps, too.

I want to create the exact output below.

enter image description here

How can I do this?

tdy
  • 36,675
  • 19
  • 86
  • 83
babeyh
  • 659
  • 2
  • 7
  • 19
  • 1
    `width=1` should work but maybe try `ec=None` as well. https://i.stack.imgur.com/oF8qV.png. The issue of bar labels is more complex. – BigBen Mar 02 '22 at 20:01
  • Did you try the solution from the duplicate of your question? `xs.bar(bar_lefts, bar_heights, width=1.0, facecolor='black', edgecolor='black')` seems to be working just fine. Probably you are still using wrong color for the borders – pptaszni Mar 09 '22 at 15:49

2 Answers2

1

Seamless bars

Use width=1 and set both color and ec to the same color:

fig, ax = plt.subplots()

width = 1
color = 'blue'
ax.bar(langs, students, width=width, color=color, ec=color)
ax.set_ylim(0, 35)

Merged labels

Use pandas with groupby.mean. Find the transition points of each block and then compute the mean lang (x) per block (y):

import pandas as pd

df = pd.DataFrame(dict(langs=langs, students=students))
blocks = df.students.ne(df.students.shift()).cumsum()
labels = df.groupby(blocks, as_index=False)[['students', 'langs']].mean()
#    students  langs
# 0        10    1.5
# 1        15    9.5
# 2        20    4.5
# 3        30    7.5

for x, y in zip(labels['langs'], labels['students']):
    ax.text(x, y + 1, f'{y:.0f}', ha='center')

Note that in your sample data, it's not necessary to find the transition points because none of the blocks are repeated. If the blocks are guaranteed to never repeat, we can just group by students without determining the blocks:

# only if blocks are guaranteed to never repeat
labels = df.groupby('students', as_index=False)['langs'].mean()

However, in practice there might be repeated blocks, like the following example which has an extra block of 20s on the far right.


Complete example

import matplotlib.pyplot as plt
import pandas as pd

fig, ax = plt.subplots()

langs = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
students = [10, 10, 20, 20, 20, 20, 30, 30, 15, 15, 20, 20, 20]
#                                                   ^ another block of 20s

width = 1
color = 'blue'
ax.bar(langs, students, width=width, color=color, ec=color)
ax.set_ylim(0, 35)

df = pd.DataFrame(dict(langs=langs, students=students))
blocks = df.students.ne(df.students.shift()).cumsum()
labels = df.groupby(blocks, as_index=False)[['langs', 'students']].mean()
#    langs  students
# 0    1.5      10.0
# 1    4.5      20.0
# 2    7.5      30.0
# 3    9.5      15.0
# 4   12.0      20.0 <- repeated 20, so groupby('students') would not have worked

for x, y in zip(labels['langs'], labels['students']):
    ax.text(x, y + 1, f'{y:.0f}', ha='center')
tdy
  • 36,675
  • 19
  • 86
  • 83
-1

You can add width=1.0 in bar().

Like this : ax.bar(langs,students, width=1.0).

aloisS
  • 1