1
votes

Note from maintainers: This question concerns the obsolete bokeh.charts API, removed years ago. For information on creating all kinds of Bar charts with modern Bokeh, see:

https://docs.bokeh.org/en/latest/docs/user_guide/categorical.html



OBSOLETE:

I am trying to create a bar chart from a dataframe df in Python Bokeh library. The data I have simply looks like:

value datetime
5    01-01-2015
7    02-01-2015
6    03-01-2015
...  ... (for 3 years)

I would like to have a bar chart that shows 3 bars per month:

  • one bar for the MEAN of 'value' for the month
  • one bar for the MAX of 'value' for the month
  • one bar for the mean of 'value' for the month

I am able to create one bar chart any of MEAN/MAX/MIN with:

from bokeh.charts import Bar, output_file, show
p = Bar(df, 'datetime', values='value', title='mybargraph',
         agg='mean', legend=None)
output_file('test.html')
show(p)

How could I have the 3 bar (mean, max, min) on the same plot ? And if possible stacked above each other.

It looks like blend could help me (like in this example:

http://docs.bokeh.org/en/latest/docs/gallery/stacked_bar_chart.html

but I cannot find detailed explanations of how it works. The bokeh website is amazing but for this particular item it is not really detailed.

1

1 Answers

1
votes

Note from maintainers: This question concerns the obsolete bokeh.charts API, removed years ago. For information on creating all kinds of Bar charts with modern Bokeh, see:

https://docs.bokeh.org/en/latest/docs/user_guide/categorical.html



OBSOLETE:

That blend example put me on the right track.

import pandas as pd
from pandas import Series
from dateutil.parser import parse
from bokeh.plotting import figure
from bokeh.layouts import row
from bokeh.charts import Bar, output_file, show
from bokeh.charts.attributes import cat, color
from bokeh.charts.operations import blend

output_file("datestats.html")

Just some sample data, feel free to alter it as you see fit. First I had to wrangle the data into a proper format.

# Sample data
vals = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
dates = ["01-01-2015", "02-01-2015", "03-01-2015", "04-01-2015",
         "01-02-2015", "02-02-2015", "03-02-2015", "04-02-2015",
         "01-03-2015", "02-03-2015", "03-03-2015", "04-03-2015"
         ]

It looked like your date format was "day-month-year" - I used the dateutil.parser so pandas would recognize it properly.

# Format data as pandas datetime objects with day-first custom
days = []
days.append(parse(x, dayfirst=True) for x in dates)

You also needed it grouped by month - I used pandas resample to downsample the dates, get the appropriate values for each month, and merge into a dataframe.

# Put data into dataframe broken into min, mean, and max values each for month
ts = Series(vals, index=days[0])
firstmerge = pd.merge(ts.resample('M').min().to_frame(name="min"),
                      ts.resample('M').mean().to_frame(name="mean"),
                      left_index=True, right_index=True)
frame = pd.merge(firstmerge, ts.resample('M').max().to_frame(name="max"),
                 left_index=True, right_index=True)

Bokeh allows you to use the pandas dataframe's index as the chart's x values, as discussed here but it didn't like the datetime values so I added a new column for date labels. See timeseries comment below***.

# You can use DataFrame index for bokeh x values but it doesn't like timestamp
frame['Month'] = frame.index.strftime('%m-%Y')

Finally we get to the charting part. Just like the Olympic medal example, we pass some arguments to Bar. Play with these however you like, but note that I added the legend by building it outside of the chart altogether. If you have a lot of data points it gets very messy on the chart the way it's built here.

# Main object to render with stacking
bar = Bar(frame,
          values=blend('min', 'mean', 'max',
                       name='values', labels_name='stats'),
          label=cat(columns='Month', sort=False),
          stack=cat(columns='values', sort=False),
          color=color(columns='values',
                      palette=['SaddleBrown', 'Silver', 'Goldenrod'],
                      sort=True),
          legend=None,
          title="Statistical Values Grouped by Month",
          tooltips=[('Value', '@values')]
          )

# Legend info (displayed as separate chart using bokeh.layouts' row)
factors = ["min", "mean", "max"]
x = [0] * len(factors)
y = factors
pal = ['SaddleBrown', 'Silver', 'Goldenrod']
p = figure(width=100, toolbar_location=None, y_range=factors)
p.rect(x, y, color=pal, width=10, height=1)
p.xaxis.major_label_text_color = None
p.xaxis.major_tick_line_color = None
p.xaxis.minor_tick_line_color = None

# Display chart
show(row(bar, p))

Bokeh_output

If you copy/paste this code, this is what you will show.
If you render it yourself or if you serve it: hover over each block to see the tooltips (values).

I didn't abstract everything I could (colors come to mind).

This is the type of chart you wanted to build, but it seems like a different chart style would display the data more informatively since stacked totals (min + mean + max) don't provide meaningful information. But I don't know what your data really are.

***You might consider a timeseries chart. This could remove some of the data wrangling done before plotting.

You might also consider grouping your bars instead of stacking them. That way you could easily visualize each month's numbers.