How to draw bars on a categorical axis

Hi,

I’m currently trying to figure out how to basically draw a bar chart manually using glyphs. The problem is that, when create a categorical x-axis as described in the documentation here, I can’t find a way to create a quad that is centered on the ticks. I could us a rect to fix this specific problem (although calculating the vertical center seems a bit hacky), but the next step would be to group multiple bars around a single tick, like the bar chart does here. I’ve browsed the source code a bit, and it seems the BarBuilder uses a rect internally, but I don’t know how it achieves the correct position on a categorical axis. Can someone explain how this is done?

Cheers,

Björn

Hello Bjorn,

It is not exactly quads, but maybe it is useful:

import pandas as pd
import bokeh.plotting as bk

def main():
“”" Run the whole program “”"
cols= [‘Rock/Folk/Indie’,‘Country’ ,‘Singer-Songwriter’, ‘Jazz/Blues’]
d = {‘Rock/Folk/Indie’:[56,12] ,‘Country’:[11,7] ,‘Singer-Songwriter’:[7,10],
‘Jazz/Blues’:[7,6]}
df = pd.DataFrame(d, columns=cols, index=[‘male’, ‘female’])
print (df)
rows = len(df.index)
wdth = 0.3

categorical_coord = lambda name,row: "{}:{}".format(name, 0.35 + wdth*row)
x = [[categorical_coord(name,jdx) for name in cols]for jdx in range(rows)]
plot = bk.figure(x_range=cols, y_range= [0, 60], tools="", title="Genre")

n = 2 # number of bars
colour = ["#CD7F32", "silver"]
leg = ['male', 'female']
for idx in range(n):
    plot.rect(x=x[idx], y=df.iloc[idx]/n, width=wdth, height=df.iloc[idx], color=colour[idx], legend=leg[idx])
bk.show(plot)

if name == ‘main’:
main()

``

Best!

Hello,

The same example using quads. Please note I haven’t work much on simplifying the code (for example, you can combine the lambdas to get the right/left coords at once, and the put it into the for loop), but at least it can give you some start point.

import pandas as pd
import bokeh.plotting as bk

def main():
“”" Run the whole program “”"
bk.output_file(“bars.html”)
cols= [‘Rock/Folk/Indie’,‘Country’ ,‘Singer-Songwriter’, ‘Jazz/Blues’]
d = {‘Rock/Folk/Indie’:[56,12] ,‘Country’:[11,7] ,‘Singer-Songwriter’:[7,10],
‘Jazz/Blues’:[7,6]}
df = pd.DataFrame(d, columns=cols, index=[‘male’, ‘female’])
print (df)
rows = len(df.index)
wdth = 0.3

categorical_coord = lambda name,row: "{}:{}".format(name, 0.5 - wdth + wdth*row)
right = [[categorical_coord(name,jdx) for name in cols]for jdx in range(rows)]  
categorical_coord = lambda name,row: "{}:{}".format(name, 0.50 + wdth*row)
left = [[categorical_coord(name,jdx) for name in cols]for jdx in range(rows)]
plot = bk.figure(x_range=cols, y_range= [0, 60], tools="", title="Genre")

n = 2 # number of bars
colour = ["#CD7F32", "silver"]
leg = ['male', 'female']
for idx in range(n):
    plot.quad(bottom=0, top=df.iloc[idx], left=left[idx], right=right[idx], color=colour[idx], legend=leg[idx])
bk.show(plot)

if name == ‘main’:
main()

``

Let me know if you have questions.

Best !

As an FYI there is an open issue to add VBar/HBar glyphs, that would specifically be useful for simplifying this particular use case;

  Add VBar and HBar glyphs · Issue #3423 · bokeh/bokeh · GitHub

Thanks,

Bryan

···

On Jun 7, 2016, at 10:39 AM, Oscar Delgado <[email protected]> wrote:

Hello,

The same example using quads. Please note I haven't work much on simplifying the code (for example, you can combine the lambdas to get the right/left coords at once, and the put it into the for loop), but at least it can give you some start point.

import pandas as pd
import bokeh.plotting as bk

def main():
    """ Run the whole program """
    bk.output_file("bars.html")
    cols= ['Rock/Folk/Indie','Country' ,'Singer-Songwriter', 'Jazz/Blues']
    d = {'Rock/Folk/Indie':[56,12] ,'Country':[11,7] ,'Singer-Songwriter':[7,10],
            'Jazz/Blues':[7,6]}
    df = pd.DataFrame(d, columns=cols, index=['male', 'female'])
    print (df)
    rows = len(df.index)
    wdth = 0.3
    
    categorical_coord = lambda name,row: "{}:{}".format(name, 0.5 - wdth + wdth*row)
    right = [[categorical_coord(name,jdx) for name in cols]for jdx in range(rows)]
    categorical_coord = lambda name,row: "{}:{}".format(name, 0.50 + wdth*row)
    left = [[categorical_coord(name,jdx) for name in cols]for jdx in range(rows)]
    plot = bk.figure(x_range=cols, y_range= [0, 60], tools="", title="Genre")
    
    n = 2 # number of bars
    colour = ["#CD7F32", "silver"]
    leg = ['male', 'female']
    for idx in range(n):
        plot.quad(bottom=0, top=df.iloc[idx], left=left[idx], right=right[idx], color=colour[idx], legend=leg[idx])
    bk.show(plot)
    
if __name__ == '__main__':
    main()

Let me know if you have questions.

Best !

--
You received this message because you are subscribed to the Google Groups "Bokeh Discussion - Public" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit https://groups.google.com/a/continuum.io/d/msgid/bokeh/cb775dad-7250-4e4e-80db-fffa9ddf6851%40continuum.io\.
For more options, visit https://groups.google.com/a/continuum.io/d/optout\.

Hi Oscar,

thanks for providing these examples, they are very useful indeed. The key point there is that you specify coordinates as name:factor pairs. I wasn’t aware that this is possible, and I don’t understand what exactly goes on here. Can you explain this?

Cheers,

Björn
···

On 7 Jun 2016, at 16:39, Oscar Delgado [email protected] wrote:

Hello,

The same example using quads. Please note I haven’t work much on simplifying the code (for example, you can combine the lambdas to get the right/left coords at once, and the put it into the for loop), but at least it can give you some start point.

import pandas as pd
import bokeh.plotting as bk

def main():
“”" Run the whole program “”"
bk.output_file(“bars.html”)
cols= [‘Rock/Folk/Indie’,‘Country’ ,‘Singer-Songwriter’, ‘Jazz/Blues’]
d = {‘Rock/Folk/Indie’:[56,12] ,‘Country’:[11,7] ,‘Singer-Songwriter’:[7,10],
‘Jazz/Blues’:[7,6]}
df = pd.DataFrame(d, columns=cols, index=[‘male’, ‘female’])
print (df)
rows = len(df.index)
wdth = 0.3

categorical_coord = lambda name,row: "{}:{}".format(name, 0.5 - wdth + wdth*row)
right = [[categorical_coord(name,jdx) for name in cols]for jdx in range(rows)]  
categorical_coord = lambda name,row: "{}:{}".format(name, 0.50 + wdth*row)
left = [[categorical_coord(name,jdx) for name in cols]for jdx in range(rows)]
plot = bk.figure(x_range=cols, y_range= [0, 60], tools="", title="Genre")

n = 2 # number of bars
colour = ["#CD7F32", "silver"]
leg = ['male', 'female']
for idx in range(n):
    plot.quad(bottom=0, top=df.iloc[idx], left=left[idx], right=right[idx], color=colour[idx], legend=leg[idx])
bk.show(plot)

if name == ‘main’:
main()

``

Let me know if you have questions.

Best !

You received this message because you are subscribed to a topic in the Google Groups “Bokeh Discussion - Public” group.

To unsubscribe from this topic, visit https://groups.google.com/a/continuum.io/d/topic/bokeh/h22xpZmxId8/unsubscribe.

To unsubscribe from this group and all its topics, send an email to [email protected].

To post to this group, send email to [email protected].

To view this discussion on the web visit https://groups.google.com/a/continuum.io/d/msgid/bokeh/cb775dad-7250-4e4e-80db-fffa9ddf6851%40continuum.io.

For more options, visit https://groups.google.com/a/continuum.io/d/optout.

Hi Björn,

The factor allows you to specify a "sub-position" within the category, as a percentage. For example, if you have a categorical x-axis, and one of the categories is "foo", then basically:

* "foo:0.0" is a the left edge of the of category's area
* "foo:0.5" is in the center of category's area
* "foo" is the same as "foo:0.5"
* "foo:1.0" is at the right edge of the category's area

It might help to study this example:

  http://bokeh.pydata.org/en/latest/docs/gallery/periodic.html

There is not alot of documentation on this because, frankly it's not a great interface. It was added out of expediency, quite a long time ago (my fault). I hope to replace the inner workings of it entirely this year (while providing deprecated back-compat for a long time). I empathize that this is less than ideal for users, but realistically it's hard to justify spending time on documenting a thing you know you aren't going to keep, when it's already hard/impossible to keep up with other demands for things that *are* going to be around long term.

Bryan

···

On Jun 8, 2016, at 6:21 AM, Björn Pollex <[email protected]> wrote:

Hi Oscar,

thanks for providing these examples, they are very useful indeed. The key point there is that you specify coordinates as name:factor pairs. I wasn’t aware that this is possible, and I don’t understand what exactly goes on here. Can you explain this?

Cheers,

  Björn

On 7 Jun 2016, at 16:39, Oscar Delgado <[email protected]> wrote:

Hello,

The same example using quads. Please note I haven't work much on simplifying the code (for example, you can combine the lambdas to get the right/left coords at once, and the put it into the for loop), but at least it can give you some start point.

import pandas as pd
import bokeh.plotting as bk

def main():
    """ Run the whole program """
    bk.output_file("bars.html")
    cols= ['Rock/Folk/Indie','Country' ,'Singer-Songwriter', 'Jazz/Blues']
    d = {'Rock/Folk/Indie':[56,12] ,'Country':[11,7] ,'Singer-Songwriter':[7,10],
            'Jazz/Blues':[7,6]}
    df = pd.DataFrame(d, columns=cols, index=['male', 'female'])
    print (df)
    rows = len(df.index)
    wdth = 0.3
    
    categorical_coord = lambda name,row: "{}:{}".format(name, 0.5 - wdth + wdth*row)
    right = [[categorical_coord(name,jdx) for name in cols]for jdx in range(rows)]
    categorical_coord = lambda name,row: "{}:{}".format(name, 0.50 + wdth*row)
    left = [[categorical_coord(name,jdx) for name in cols]for jdx in range(rows)]
    plot = bk.figure(x_range=cols, y_range= [0, 60], tools="", title="Genre")
    
    n = 2 # number of bars
    colour = ["#CD7F32", "silver"]
    leg = ['male', 'female']
    for idx in range(n):
        plot.quad(bottom=0, top=df.iloc[idx], left=left[idx], right=right[idx], color=colour[idx], legend=leg[idx])
    bk.show(plot)
    
if __name__ == '__main__':
    main()

Let me know if you have questions.

Best !

--
You received this message because you are subscribed to a topic in the Google Groups "Bokeh Discussion - Public" group.
To unsubscribe from this topic, visit https://groups.google.com/a/continuum.io/d/topic/bokeh/h22xpZmxId8/unsubscribe\.
To unsubscribe from this group and all its topics, send an email to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit https://groups.google.com/a/continuum.io/d/msgid/bokeh/cb775dad-7250-4e4e-80db-fffa9ddf6851%40continuum.io\.
For more options, visit https://groups.google.com/a/continuum.io/d/optout\.

--
You received this message because you are subscribed to the Google Groups "Bokeh Discussion - Public" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit https://groups.google.com/a/continuum.io/d/msgid/bokeh/2C00C99E-B5EE-4726-B6DA-D923B3E6E850%40posteo.de\.
For more options, visit https://groups.google.com/a/continuum.io/d/optout\.

Hello Bjorn,

Those are called “Categorical Coordinates”. Bryan told me about them:

https://groups.google.com/a/continuum.io/forum/#!searchin/bokeh/categorical/bokeh/FzRGHuM8SrA/kvI8RfKJK5kJ

Note that I have something awkward in my example (the left-right are inverted) but the idea is that the first group of bars (males) should be moved to the left and the second group to the right. You should do a little of math based on the number of groups/width to determine the deviations but at the end, you can implement a loop when you have several groups (2+). NOTE: Check always the space between categories to avoid overlap when you have several groups.

Best!

···

Le mercredi 8 juin 2016 08:06:38 UTC-4, Björn Pollex a écrit :

Hi Oscar,

thanks for providing these examples, they are very useful indeed. The key point there is that you specify coordinates as name:factor pairs. I wasn’t aware that this is possible, and I don’t understand what exactly goes on here. Can you explain this?

Cheers,

Björn

On 7 Jun 2016, at 16:39, Oscar Delgado [email protected] wrote:

Hello,

The same example using quads. Please note I haven’t work much on simplifying the code (for example, you can combine the lambdas to get the right/left coords at once, and the put it into the for loop), but at least it can give you some start point.

import pandas as pd
import bokeh.plotting as bk

def main():
“”" Run the whole program “”"
bk.output_file(“bars.html”)
cols= [‘Rock/Folk/Indie’,‘Country’ ,‘Singer-Songwriter’, ‘Jazz/Blues’]
d = {‘Rock/Folk/Indie’:[56,12] ,‘Country’:[11,7] ,‘Singer-Songwriter’:[7,10],
‘Jazz/Blues’:[7,6]}
df = pd.DataFrame(d, columns=cols, index=[‘male’, ‘female’])
print (df)
rows = len(df.index)
wdth = 0.3

categorical_coord = lambda name,row: "{}:{}".format(name, 0.5 - wdth + wdth*row)
right = [[categorical_coord(name,jdx) for name in cols]for jdx in range(rows)]  
categorical_coord = lambda name,row: "{}:{}".format(name, 0.50 + wdth*row)
left = [[categorical_coord(name,jdx) for name in cols]for jdx in range(rows)]
plot = bk.figure(x_range=cols, y_range= [0, 60], tools="", title="Genre")

n = 2 # number of bars
colour = ["#CD7F32", "silver"]
leg = ['male', 'female']
for idx in range(n):
    plot.quad(bottom=0, top=df.iloc[idx], left=left[idx], right=right[idx], color=colour[idx], legend=leg[idx])
bk.show(plot)

if name == ‘main’:
main()

``

Let me know if you have questions.

Best !

You received this message because you are subscribed to a topic in the Google Groups “Bokeh Discussion - Public” group.

To unsubscribe from this topic, visit https://groups.google.com/a/continuum.io/d/topic/bokeh/h22xpZmxId8/unsubscribe.

To unsubscribe from this group and all its topics, send an email to [email protected].

To post to this group, send email to [email protected].

To view this discussion on the web visit https://groups.google.com/a/continuum.io/d/msgid/bokeh/cb775dad-7250-4e4e-80db-fffa9ddf6851%40continuum.io.

For more options, visit https://groups.google.com/a/continuum.io/d/optout.

Hi,

sorry to resurrect an old thread but is this supposed to work with patches (with missing points), too?

Kind regards,

Istvan

···

On Wednesday, June 8, 2016 at 5:31:01 PM UTC+2, Oscar Delgado wrote:

Hello Bjorn,

Those are called “Categorical Coordinates”. Bryan told me about them:

https://groups.google.com/a/continuum.io/forum/#!searchin/bokeh/categorical/bokeh/FzRGHuM8SrA/kvI8RfKJK5kJ

Note that I have something awkward in my example (the left-right are inverted) but the idea is that the first group of bars (males) should be moved to the left and the second group to the right. You should do a little of math based on the number of groups/width to determine the deviations but at the end, you can implement a loop when you have several groups (2+). NOTE: Check always the space between categories to avoid overlap when you have several groups.

Best!

Le mercredi 8 juin 2016 08:06:38 UTC-4, Björn Pollex a écrit :

Hi Oscar,

thanks for providing these examples, they are very useful indeed. The key point there is that you specify coordinates as name:factor pairs. I wasn’t aware that this is possible, and I don’t understand what exactly goes on here. Can you explain this?

Cheers,

Björn

On 7 Jun 2016, at 16:39, Oscar Delgado [email protected] wrote:

Hello,

The same example using quads. Please note I haven’t work much on simplifying the code (for example, you can combine the lambdas to get the right/left coords at once, and the put it into the for loop), but at least it can give you some start point.

import pandas as pd
import bokeh.plotting as bk

def main():
“”" Run the whole program “”"
bk.output_file(“bars.html”)
cols= [‘Rock/Folk/Indie’,‘Country’ ,‘Singer-Songwriter’, ‘Jazz/Blues’]
d = {‘Rock/Folk/Indie’:[56,12] ,‘Country’:[11,7] ,‘Singer-Songwriter’:[7,10],
‘Jazz/Blues’:[7,6]}
df = pd.DataFrame(d, columns=cols, index=[‘male’, ‘female’])
print (df)
rows = len(df.index)
wdth = 0.3

categorical_coord = lambda name,row: "{}:{}".format(name, 0.5 - wdth + wdth*row)
right = [[categorical_coord(name,jdx) for name in cols]for jdx in range(rows)]  
categorical_coord = lambda name,row: "{}:{}".format(name, 0.50 + wdth*row)
left = [[categorical_coord(name,jdx) for name in cols]for jdx in range(rows)]
plot = bk.figure(x_range=cols, y_range= [0, 60], tools="", title="Genre")

n = 2 # number of bars
colour = ["#CD7F32", "silver"]
leg = ['male', 'female']
for idx in range(n):
    plot.quad(bottom=0, top=df.iloc[idx], left=left[idx], right=right[idx], color=colour[idx], legend=leg[idx])
bk.show(plot)

if name == ‘main’:
main()

``

Let me know if you have questions.

Best !

You received this message because you are subscribed to a topic in the Google Groups “Bokeh Discussion - Public” group.

To unsubscribe from this topic, visit https://groups.google.com/a/continuum.io/d/topic/bokeh/h22xpZmxId8/unsubscribe.

To unsubscribe from this group and all its topics, send an email to [email protected].

To post to this group, send email to [email protected].

To view this discussion on the web visit https://groups.google.com/a/continuum.io/d/msgid/bokeh/cb775dad-7250-4e4e-80db-fffa9ddf6851%40continuum.io.

For more options, visit https://groups.google.com/a/continuum.io/d/optout.

It's actually a good time to mention that (terrible) categorical coordinates are going away in the next release. The entire way categoricals are handled has been re-worked for the better, supporting things link nest hierarchical axes:

  http://bokeh.pydata.org/en/dev/docs/user_guide/categorical.html

Offhand I would expect this to work with patches (and the previous syntax too), but it's certainly possible there are bugs. However, it's hard to say anything concrete without a complete test case that can be run and investigated.

Thanks,

Bryan

···

On Aug 10, 2017, at 05:04, [email protected] wrote:

Hi,

sorry to resurrect an old thread but is this supposed to work with patches (with missing points), too?

Kind regards,
Istvan

On Wednesday, June 8, 2016 at 5:31:01 PM UTC+2, Oscar Delgado wrote:
Hello Bjorn,

Those are called "Categorical Coordinates". Bryan told me about them:
Redirecting to Google Groups

Note that I have something awkward in my example (the left-right are inverted) but the idea is that the first group of bars (males) should be moved to the left and the second group to the right. You should do a little of math based on the number of groups/width to determine the deviations but at the end, you can implement a loop when you have several groups (2+). NOTE: Check always the space between categories to avoid overlap when you have several groups.

Best!

Le mercredi 8 juin 2016 08:06:38 UTC-4, Björn Pollex a écrit :
Hi Oscar,

thanks for providing these examples, they are very useful indeed. The key point there is that you specify coordinates as name:factor pairs. I wasn’t aware that this is possible, and I don’t understand what exactly goes on here. Can you explain this?

Cheers,

  Björn

On 7 Jun 2016, at 16:39, Oscar Delgado <[email protected]> wrote:

Hello,

The same example using quads. Please note I haven't work much on simplifying the code (for example, you can combine the lambdas to get the right/left coords at once, and the put it into the for loop), but at least it can give you some start point.

import pandas as pd
import bokeh.plotting as bk

def main():
    """ Run the whole program """
    bk.output_file("bars.html")
    cols= ['Rock/Folk/Indie','Country' ,'Singer-Songwriter', 'Jazz/Blues']
    d = {'Rock/Folk/Indie':[56,12] ,'Country':[11,7] ,'Singer-Songwriter':[7,10],
            'Jazz/Blues':[7,6]}
    df = pd.DataFrame(d, columns=cols, index=['male', 'female'])
    print (df)
    rows = len(df.index)
    wdth = 0.3
    
    categorical_coord = lambda name,row: "{}:{}".format(name, 0.5 - wdth + wdth*row)
    right = [[categorical_coord(name,jdx) for name in cols]for jdx in range(rows)]
    categorical_coord = lambda name,row: "{}:{}".format(name, 0.50 + wdth*row)
    left = [[categorical_coord(name,jdx) for name in cols]for jdx in range(rows)]
    plot = bk.figure(x_range=cols, y_range= [0, 60], tools="", title="Genre")
    
    n = 2 # number of bars
    colour = ["#CD7F32", "silver"]
    leg = ['male', 'female']
    for idx in range(n):
        plot.quad(bottom=0, top=df.iloc[idx], left=left[idx], right=right[idx], color=colour[idx],legend=leg[idx])
    bk.show(plot)
    
if __name__ == '__main__':
    main()

Let me know if you have questions.

Best !

--
You received this message because you are subscribed to a topic in the Google Groups "Bokeh Discussion - Public" group.
To unsubscribe from this topic, visit https://groups.google.com/a/continuum.io/d/topic/bokeh/h22xpZmxId8/unsubscribe\.
To unsubscribe from this group and all its topics, send an email to bokeh+un...@continuum.io.
To post to this group, send email to bo...@continuum.io.
To view this discussion on the web visit https://groups.google.com/a/continuum.io/d/msgid/bokeh/cb775dad-7250-4e4e-80db-fffa9ddf6851%40continuum.io\.
For more options, visit https://groups.google.com/a/continuum.io/d/optout\.

--
You received this message because you are subscribed to the Google Groups "Bokeh Discussion - Public" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit https://groups.google.com/a/continuum.io/d/msgid/bokeh/94f6d545-3fbd-4223-8a3a-7acc0c8061df%40continuum.io\.
For more options, visit https://groups.google.com/a/continuum.io/d/optout\.