I am experimenting with Bokeh and mixing pieces of code. I created the graph below from a Pandas DataFrame, which displays the graph correctly with all the tool elements I want. However, the tooltip is partially displaying the data.
Here is the graph:
Here is my code:
from bokeh.plotting import figure, show
from bokeh.io import output_notebook
from bokeh.models import HoverTool
from collections import OrderedDict
x = yearly_DF.index
y0 = yearly_DF.weight.values
y1 = yearly_DF.muscle_weight.values
y2 = yearly_DF.bodyfat_p.values
#output_notebook()
p = figure(plot_width=1000, plot_height=600,
tools="pan,box_zoom,reset,resize,save,crosshair,hover",
title="Annual Weight Change",
x_axis_label='Year',
y_axis_label='Weight',
toolbar_location="left"
)
hover = p.select(dict(type=HoverTool))
hover.tooltips = OrderedDict([('Year', '#x'),('Total Weight', '#y0'), ('Muscle Mass', '$y1'), ('BodyFat','$y2')])
output_notebook()
p.line(x, y0, legend="Weight")
p.line(x, y1, legend="Muscle Mass", line_color="red")
show(p)
I have tested with Firefox 39.0, Chrome 43.0.2357.130 (64-bit) and Safari Version 8.0.7. I have cleared the cache and I get the same error in all browsers. Also I did pip install bokeh --upgrade to make sure I have the latest version running.
Try using ColumnDataSource.
Hover tool needs to have access to the data source so that it can display info.
#x, #y are the x-y values in data unit. (# prefix is special, can only followed by a limited set of variable, #y2 is not one of them)., Normally I would use $+ column_name to display the value of my interest, such as $weight. See here for more info.
Besides, I am surprised that the hover would appear at all. As I thought hoverTool doesn't work with line glyph, as noted here
Try the following : (I haven't tested, might have typos).
df = yearly_DF.reset_index() # move index to column.
source = ColumnDataSource(ColumnDataSource.from_df(df)
hover.tooltips = OrderedDict([('x', '#x'),('y', '#y'), ('year', '$index'), ('weight','$weight'), ('muscle_weight','$muscle_weight'), ('body_fat','$bodyfat_p')])
p.line(x='index', y='weight', source=source, legend="Weight")
p.line(x='index', y='muscle_weight', source=source, legend="Muscle Mass", line_color="red")
Are you using Firefox? This was a reported issue with some older versions of FF:
https://github.com/bokeh/bokeh/issues/1981
https://github.com/bokeh/bokeh/issues/2122
Upgrading FF resolved the issue.
Related
I tried the following example 3D Mesh example with AlphaNull to test alphahull but my jupyter notebook display just something blank.
When I set alphahull=5, the display is blank:
But when i set alphahull = 0, it works:
and when i set alphahull = -1, it works :
Why is this happening and how can I fix it?
Thank you in advance for your help.
Unfortunately I think rendering for alphahull values larger than 0 may be broken as of the latest plotly update. I noticed that in the documentation page, their code example with alphahull=5 also doesn't render. I tried with other positive values and none of these render either (the same alpha shape algorithm is used for any alphanull > 0)
However, I tried downgrading to plotly==4.14.0 and the same example with alphahull=5 does render.
import plotly.graph_objects as go
import numpy as np
pts = np.loadtxt(np.DataSource().open('https://raw.githubusercontent.com/plotly/datasets/master/mesh_dataset.txt'))
x, y, z = pts.T
fig = go.Figure(data=[go.Mesh3d(x=x, y=y, z=z,
alphahull=5,
opacity=0.4,
color='cyan')])
fig.show()
So in your jupyter notebook, you can run the line !pip install plotly==4.14.0 in a separate cell and see if that allows you to render positive alphahull values.
I found this post on Geopandas and bokeh extract xs and ys from data
What I need is basically the same thing but for the map of the whole world (extract XS and ys from GeoPandas and convert into bokeh readable format). I am struggling with the fact the world data has both polygons and multi polygons.
If anyone can help, that would be much appreciated.
Thanks!
This is how you can run a GeoJSON using pandas GeoJSONDataSource like I mentioned in my comment.
from bokeh.models import GeoJSONDataSource
from bokeh.plotting import figure, show, output_notebook
import geopandas as gp
output_notebook()
world = gp.read_file(gp.datasets.get_path('naturalearth_lowres'))
geo_source = GeoJSONDataSource(geojson=world.to_json())
p = figure(title='World', tooltips=[('Country', '#name')],
x_range=(-180, 180), y_range=(-90, 90),
x_axis_location=None, y_axis_location=None,
plot_width=1000, plot_height=500
)
p.patches('xs', 'ys', fill_alpha=0.4, fill_color='grey',
line_color='black', line_width=0.5, source=geo_source
)
show(p)
Output
Ok, so I solved it but it needs polishing. If you have any ideas how to improve, let me know. I am also running into a runtime error and that is either solved by uncommenting certain parts of code (marked in the code) or by downgrading bokeh. I have not tried downgrading bokeh, I just saw that answer somewhere.
(Also, I have never posted on SO before so I am not really sure how this whole thing works so be gentle).
https://github.com/nikosarcevic/GeoMapping
I have a Bokeh plot created with the multi_line glyph. I can select a single line with the tap tool or multiple lines with the TapTool+Shift. Is there a way to select several lines at ones with the BoxSelectTool (like on the pic)? The density of the points along each line is quite high, so it's okay if selection works only when 1+ points of a line are inside the box selection area.
I'm looking for a stand-alone solution without a Python server. Writing of some CustomJS code is fine.
from bokeh.models import ColumnDataSource
from bokeh.layouts import column
import numpy as np
output_file('tst.html', mode="inline")
n = 8
t = np.linspace(0., 10., 80)
data = ColumnDataSource(dict(xx=[t for cnt in range(n)],
yy=[(10 + cnt/2*(-1)**cnt)*np.sin(t + cnt/3) for cnt in range(n)],
zz=[(10 - cnt/2*(-1)**cnt)*np.cos(t) for cnt in range(n)]))
f1 = figure(plot_width=800, plot_height=300, tools='tap,box_select,reset')
f2 = figure(plot_width=800, plot_height=300)
f1.multi_line(xs='xx', ys='yy', source=data)
f2.multi_line(xs='xx', ys='zz', source=data)
save(column(f1, f2))
Plot sample with the box selection
Thanks.
This functionality is not built-in, at least not in Bokeh 2.0.2. The MultiLine glyph defines only _hit_point (used by the tap, hover, and edit tools) and _hit_span (used by the hover tool) methods. For the box select tool to work, there needs to be a _hit_rect method defined. It's not that hard to do it yourself, but you will have to create a custom Bokeh model and write some TypeScript for it.
With that being said, please feel free to create a feature request on Bokeh's GitHub!
I use matplotlib to generate data analysis plots, which I then show to people at conferences, in publications etc. I normally store all the data and scripts that generate every interesting/useful plot, just in case I need to update the charts at some point after creating them. Here's an example of my box-standard piece of code that generates the below plot:
# In[Imports]:
import pandas, matplotlib, matplotlib.pyplot
# In[Plot formatting]:
ticksFontSize = 18
labelsFontSize = 30
legendFontSize = 16
cm=matplotlib.pyplot.cm.get_cmap('viridis')
matplotlib.rc('xtick', labelsize=ticksFontSize)
matplotlib.rc('ytick', labelsize=ticksFontSize)
# In[Read the data]:
# CDF of TLE update frequenies
dfGood=pandas.read_csv('blue.csv',sep=',',header=None)
epochsGood=dfGood[0].values
cdfsGood=dfGood[1].values
# In[Plot the data]:
fig,ax=matplotlib.pyplot.subplots(1,1,sharex=True,figsize=(14,8))
ax.scatter(epochsGood,cdfsGood,c='indigo',marker='o',lw=0,s=30)
ax.set_xlabel(r"$TLE\ update\ frequency\ (orbital\ periods)$",
size=labelsFontSize)
ax.set_ylabel(r"$Cumulative\ Distribution\ Function\ (-)$",
fontsize=labelsFontSize)
ax.grid(linewidth=1)
ax.tick_params(axis='both',reset=False,which='both',
length=5,width=1.5)
ax.tick_params(axis='x', which='major', pad=15)
ax.set_xlim(0,5)
ax.set_ylim(0,1.1)
matplotlib.pyplot.subplots_adjust(left=0.1,right=0.95,
bottom=0.15,top=0.9)
fig.show()
Recently (within the last few months, but it could have been a long overdue update...), I updated matplotlib and the above script started generating a differently formatted plot:
The clipped Y-axis is no biggie, I can just live with that and adjust the plot/axis. What I am a bit perplexed about is the changed font. It appears that sometime during the matplotlib update the matplotlibrc file has changed. I can live with that and explicitly set matplotlibrc properties in my scripts, or set label text properties on a per-label basis as shown in this answer. But I have no idea how to go back to the previous formatting, i.e. what text properties to set. Any ideas?
Some useful info
Current Python version = 3.5.4
Python version I used to generate the "old plot" = 3.5.something
Current matplotlib version = 2.2.2
matplotlib version I used to generate the "old plot" = I wish I knew...
The default math font has changed in version 2.x from "Computer Modern" to "DejaVu Sans". You can change it in your script as mentioned in the documentation back to the previous version:
from matplotlib import pyplot as plt
plt.rcParams['mathtext.fontset'] = 'cm'
plt.rcParams['mathtext.rm'] = 'serif'
Sample output (without your data because your question does not contain an MCVE):
I would like to plot a whole pandas DataFrame with Bokeh. I.e., I am looking for a Bokeh equivalent of the third line:
import pandas as pd
income_df = pd.read_csv("income_2013_dollars.csv", sep='\t', thousands=',')
income_df.plot(x="year")
Is there currently a way to do that, or do I have to pass each y-value separately?
Note from Bokeh project maintainers: This answer refers to an obsolete and deprecated API was long since removed from Bokeh. For information about creating bar charts with modern and fully supported Bokeh APIs, see other Questions/Answers.
You may find the charts examples useful:
https://github.com/bokeh/bokeh/tree/master/examples/charts
If you wanted a bar chart it would be:
from bokeh.charts import Bar
Bar(income_df, notebook=True).show() # assuming the index is corretly set on your df
You may want a Line or TimeSeries which work similarly - just checkout the examples for more details and more configuration - like adding titles, labels etc.
Note that you can use other output methods - notebook, file, or server. See the documentation here:
http://docs.bokeh.org/en/latest/docs/user_guide/charts.html#generic-arguments
Update: (sorry for the confusion on how to display the output). An alternative way of specifying the display type of the chart is to use the methods output_notebook(), output_file("file.html"), output_server() and then use the show method. For example
from bokeh.charts import Bar
from bokeh.plotting import output_notebook, show
output_notebook()
bar = Bar(income_df)
show(bar)
However, you cannot do the following
from bokeh.charts import Bar
from bokeh.plotting import output_notebook
output_notebook()
Bar(income_df).show() # WILL GIVE YOU AN ERROR
The two show methods are different.
See this User's Guide Section for modern information on creating Bar charts with Pandas:
https://docs.bokeh.org/en/latest/docs/user_guide/categorical.html#pandas
For example:
from bokeh.models import ColumnDataSource
from bokeh.plotting import figure, show
from bokeh.sampledata.autompg import autompg as df
from bokeh.transform import factor_cmap
df.cyl = df.cyl.astype(str)
group = df.groupby('cyl')
source = ColumnDataSource(group)
cyl_cmap = factor_cmap('cyl', palette="Spectral5", factors=sorted(df.cyl.unique()))
p = figure(x_range=group, title="MPG by # Cylinders",
toolbar_location=None, tools="")
p.vbar(x='cyl', top='mpg_mean', width=1, source=source,
line_color=cyl_cmap, fill_color=cyl_cmap)
p.y_range.start = 0
p.xgrid.grid_line_color = None
p.xaxis.axis_label = "some stuff"
p.xaxis.major_label_orientation = 1.2
p.outline_line_color = None
show(p)