trace not showing on plotly when second y axis added - python-3.x

I'm plotting temperature and humidity readings from raspberry pi to plotly.
On the same y axis they appear fine.
When I add a second y axis via layout, the temperature trace (trace1) doesn't show.
On plotly itself it shows the data for both trace1 and trace2 but it's not plotting trace1 for some reason.
Any ideas?!
import plotly.plotly as py
import json
import time
import datetime as dt
import plotly.graph_objs as go
import numpy as np
import sqlite3
import pandas as pd
con = sqlite3.connect('/home/pi/environment.db')
#c = conn.cursor()
df = pd.read_sql_query("SELECT date_time, temp, humid FROM readings",
con)
df['temp_MA'] = df.temp.rolling(10).mean()
df['humid_MA'] = df.humid.rolling(10).mean()
trace1 = go.Scatter(
name = 'Temperature degC',
x=df.date_time,
y=df.temp_MA,
yaxis = 'y1'
)
trace2 = go.Scatter(
name = 'Rel Humidity %',
x=df.date_time,
y=df.humid_MA,
yaxis = 'y2'
)
data = [trace1, trace2]
layout = go.Layout(
yaxis=dict(
title='Temperature deg C',
side='left'
),
yaxis2=dict(
title='Humidity Rel %',
side='right'
)
)
fig = go.Figure(data=data, layout=layout)
py.plot(fig)
con.close()
The plotly graph is here:
https://plot.ly/~uilfut/58/temperature-degc-vs-rel-humidity/

Just in case anyone else discovers this thread from googling - I figured out the missing line of code...
Under layout, yaxis2, you have to specify overlaying='y'.
It works now :)
layout = go.Layout(
yaxis=dict(
title='Temperature deg C',
side='left'
),
yaxis2=dict(
title='Humidity Rel %',
side='right',
overlaying='y'
)
)

Related

How to translate hexagon matplotlib plot to an interactive bokeh plot?

I have been working with the excellent minisom package and want to plot interactively the hexagonal map that reflects the results of the self-organising maps training process. There's already a code example that does this statically using matplotlib but to do so interactively, I would like to use bokeh. This is where I am struggling.
This is the code to generate a simplified matplotlib example of what's already on the package page:
from minisom import MiniSom
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import RegularPolygon
from matplotlib import cm
from bokeh.plotting import figure
from bokeh.io import save, show, output_file, output_notebook
output_notebook()
data = pd.read_csv('https://archive.ics.uci.edu/ml/machine-learning-databases/00236/seeds_dataset.txt',
names=['area', 'perimeter', 'compactness', 'length_kernel', 'width_kernel',
'asymmetry_coefficient', 'length_kernel_groove', 'target'], sep='\t+')
t = data['target'].values
data = data[data.columns[:-1]]
# data normalisation
data = (data - np.mean(data, axis=0)) / np.std(data, axis=0)
data = data.values
# initialisation and training
som = MiniSom(15, 15, data.shape[1], sigma=1.5, learning_rate=.7, activation_distance='euclidean',
topology='hexagonal', neighborhood_function='gaussian', random_seed=10)
som.train(data, 1000, verbose=True)
# plot hexagonal topology
f = plt.figure(figsize=(10,10))
ax = f.add_subplot(111)
ax.set_aspect('equal')
xx, yy = som.get_euclidean_coordinates()
umatrix = som.distance_map()
weights = som.get_weights()
for i in range(weights.shape[0]):
for j in range(weights.shape[1]):
wy = yy[(i, j)]*2/np.sqrt(3)*3/4
hex = RegularPolygon((xx[(i, j)], wy), numVertices=6, radius=.95/np.sqrt(3),
facecolor=cm.Blues(umatrix[i, j]), alpha=.4, edgecolor='gray')
ax.add_patch(hex)
for x in data:
w = som.winner(x)
# place a marker on the winning position for the sample xx
wx, wy = som.convert_map_to_euclidean(w)
wy = wy * 2 / np.sqrt(3) * 3 / 4
plt.plot(wx, wy, markerfacecolor='None',
markeredgecolor='black', markersize=12, markeredgewidth=2)
plt.show()
matplotlib hexagonal topology plot
I've tried to translate the code into bokeh but the resulting hex plot (to me, primitively) looks like it needs to be flipped vertically onto the points and for the skew to be straightened out.
tile_centres_column = []
tile_centres_row = []
colours = []
for i in range(weights.shape[0]):
for j in range(weights.shape[1]):
wy = yy[(i, j)] * 2 / np.sqrt(3) * 3 / 4
tile_centres_column.append(xx[(i, j)])
tile_centres_row.append(wy)
colours.append(cm.Blues(umatrix[i, j]))
weight_x = []
weight_y = []
for x in data:
w = som.winner(x)
wx, wy = som.convert_map_to_euclidean(xy=w)
wy = wy * 2 / np.sqrt(3) * 3/4
weight_x.append(wx)
weight_y.append(wy)
# plot hexagonal topology
plot = figure(plot_width=800, plot_height=800,
match_aspect=True)
plot.hex_tile(q=tile_centres_column, r=tile_centres_row,
size=.95 / np.sqrt(3),
color=colours,
fill_alpha=.4,
line_color='black')
plot.dot(x=weight_x, y=weight_y,
fill_color='black',
size=12)
show(plot)
bokeh hexagonal topology plot
How can I translate this into a bokeh plot?
Found out how to do it after reaching out to the minisom package author for help. Complete code available here.
from bokeh.colors import RGB
from bokeh.io import curdoc, show, output_notebook
from bokeh.transform import factor_mark, factor_cmap
from bokeh.models import ColumnDataSource, HoverTool
from bokeh.plotting import figure, output_file
hex_centre_col, hex_centre_row = [], []
hex_colour = []
label = []
# define labels
SPECIES = ['Kama', 'Rosa', 'Canadian']
for i in range(weights.shape[0]):
for j in range(weights.shape[1]):
wy = yy[(i, j)] * 2 / np.sqrt(3) * 3 / 4
hex_centre_col.append(xx[(i, j)])
hex_centre_row.append(wy)
hex_colour.append(cm.Blues(umatrix[i, j]))
weight_x, weight_y = [], []
for cnt, i in enumerate(data):
w = som.winner(i)
wx, wy = som.convert_map_to_euclidean(xy=w)
wy = wy * 2 / np.sqrt(3) * 3 / 4
weight_x.append(wx)
weight_y.append(wy)
label.append(SPECIES[t[cnt]-1])
# convert matplotlib colour palette to bokeh colour palette
hex_plt = [(255 * np.array(i)).astype(int) for i in hex_colour]
hex_bokeh = [RGB(*tuple(rgb)).to_hex() for rgb in hex_plt]
output_file("resulting_images/som_seed_hex.html")
# initialise figure/plot
fig = figure(title="SOM: Hexagonal Topology",
plot_height=800, plot_width=800,
match_aspect=True,
tools="wheel_zoom,save,reset")
# create data stream for plotting
source_hex = ColumnDataSource(
data = dict(
x=hex_centre_col,
y=hex_centre_row,
c=hex_bokeh
)
)
source_pages = ColumnDataSource(
data=dict(
wx=weight_x,
wy=weight_y,
species=label
)
)
# define markers
MARKERS = ['diamond', 'cross', 'x']
# add shapes to plot
fig.hex(x='y', y='x', source=source_hex,
size=100 * (.95 / np.sqrt(3)),
alpha=.4,
line_color='gray',
fill_color='c')
fig.scatter(x='wy', y='wx', source=source_pages,
legend_field='species',
size=20,
marker=factor_mark(field_name='species', markers=MARKERS, factors=SPECIES),
color=factor_cmap(field_name='species', palette='Category10_3', factors=SPECIES))
# add hover-over tooltip
fig.add_tools(HoverTool(
tooltips=[
("label", '#species'),
("(x,y)", '($x, $y)')],
mode="mouse",
point_policy="follow_mouse"
))
show(fig)

How do I create a Bokeh Select menu for a line plot for an indeterminate number of options?

I've been working on getting a select menu and Bokeh plot up and running on a dataset I'm working with. The dataset can be found here. I have no experience with JavaScript, but I believe my select menu isn't connected/-ing to my plot. Therefore, I have a plot outline, but no data displayed. As I run the script from the console with bokeh serve --show test.py, I get the first 7 notifications in my JS console. The last three (those in the red bracket in the screenshot) occur when I try and change to a different item in my select menu.
Goal: Display the plot of data for rows those id number ('ndc' in this example) is selected in the Select menu.
Here's my code (modified from this post) that I used to get started. This one was also used, as were a handful of others, and the Bokeh documentation itself.
import pandas as pd
from bokeh.io import curdoc, output_notebook, output_file
from bokeh.layouts import row, column
from bokeh.models import Select, DataRange1d, ColumnDataSource
from bokeh.plotting import figure
# output_notebook()
output_file('test.html')
def get_dataset(src, drug_id):
src.drop('Unnamed: 0', axis = 1, inplace = True)
df = src[src.ndc == drug_id].copy()
df['date'] = pd.to_datetime(df['date'])
df = df.set_index(['date'])
df.sort_index(inplace=True)
source = ColumnDataSource(data=df)
return source
def make_plot(source, title):
plot = figure(plot_width=800, plot_height = 800, tools="", x_axis_type = 'datetime', toolbar_location=None)
plot.xaxis.axis_label = 'Time'
plot.yaxis.axis_label = 'Price ($)'
plot.axis.axis_label_text_font_style = 'bold'
plot.x_range = DataRange1d(range_padding = 0.0)
plot.grid.grid_line_alpha = 0.3
plot.title.text = title
plot.line(x= 'date', y='nadac_per_unit', source=source)
return plot
def update_plot(attrname, old, new):
ver = vselect.value
plot.title.text = "Drug Prices"
src = get_dataset(df, ver)
source.date.update(src.date)
df = pd.read_csv('data/plotting_data.csv')
ver = '54034808' #Initial id number
cc = df['ndc'].astype(str).unique() #select-menu options
vselect = Select(value=ver, title='Drug ID', options=sorted((cc)))
source = get_dataset(df, ver)
plot = make_plot(source, "Drug Prices")
vselect.on_change('value', update_plot)
controls = row(vselect)
curdoc().add_root(row(plot, controls))
There were some problems in your code:
You want to drop the Unnamed: 0 column. This can only be done once and when you try this again it will throw an error since this column does not exist anymore.
The way you tried to filter the dataframe didn't work and would result in an empty dataframe. You can select rows based on a column value like this: df.loc[df['column_name'] == some_value]
Updating the ColumnDataSource object can be done by replacing source.data with the new data.
import pandas as pd
from bokeh.io import curdoc, output_notebook, output_file
from bokeh.layouts import row, column
from bokeh.models import Select, DataRange1d, ColumnDataSource
from bokeh.plotting import figure
output_notebook()
output_file('test.html')
def get_dataset(src, drug_id):
src.drop('Unnamed: 0', axis = 1, inplace = True)
df = src.loc[src['ndc'] == int(drug_id)]
df['date'] = pd.to_datetime(df['date'])
df = df.set_index(['date'])
df.sort_index(inplace=True)
source = ColumnDataSource(data=df)
return source
def make_plot(source, title):
plot = figure(plot_width=800, plot_height = 800, tools="", x_axis_type = 'datetime', toolbar_location=None)
plot.xaxis.axis_label = 'Time'
plot.yaxis.axis_label = 'Price ($)'
plot.axis.axis_label_text_font_style = 'bold'
plot.x_range = DataRange1d(range_padding = 0.0)
plot.grid.grid_line_alpha = 0.3
plot.title.text = title
plot.line(x= 'date', y='nadac_per_unit', source=source)
return plot
def update_plot(attrname, old, new):
ver = vselect.value
df1 = df.loc[df['ndc'] == int(new)]
df1['date'] = pd.to_datetime(df1['date'])
df1 = df1.set_index(['date'])
df1.sort_index(inplace=True)
newSource = ColumnDataSource(df1)
source.data = newSource.data
df = pd.read_csv('data/plotting_data.csv')
ver = '54034808' #Initial id number
cc = df['ndc'].astype(str).unique() #select-menu options
vselect = Select(value=ver, title='Drug ID', options=sorted((cc)))
source = get_dataset(df, ver)
plot = make_plot(source, "Drug Prices")
vselect.on_change('value', update_plot)
controls = row(vselect)
curdoc().add_root(row(plot, controls))

python bokeh interactively plot n curves between min and max

I am trying to generate the plot of a function of two parameters, where one is used as x_axis and for the other I plot n curves, varying the parameter between a min and max value.
The following code works:
import numpy as np
import bokeh
from bokeh.plotting import figure
from bokeh.io import push_notebook, show, output_notebook
output_notebook()
x = np.linspace(0,10,100)
f = figure()
fmin=1
fmax=3
nfreq=4
freq=np.linspace(fmin,fmax,nfreq)
for i in freq:
y = np.sin(i*x)
f.line(x,y)
show(f)
Now I would like to have 3 sliders to interactively vary fmin, fmax and nfreq. I could not figure out how to do it...
Thanks for any help
This example works for Bokeh v1.0.4. Run as: bokeh serve --show app.py
The content of app.py:
import numpy as np
from bokeh.models import Slider, Row, Column
from bokeh.plotting import figure, show, curdoc
from bokeh.models.sources import ColumnDataSource
plot = figure()
layout = Column(plot)
sources, lines = {}, {}
def get_x(n): return [np.linspace(0, 10, 100) for i in range(n)]
def get_y(n): return [np.sin(i * np.linspace(0, 10, 100)) for i in n]
def update(attr, old, new):
update_sources(layout.children[-3].value, layout.children[-2].value, layout.children[-1].value)
def update_sources(fmin, fmax, nfreq):
freq = np.linspace(fmin, fmax, nfreq)
for f, x, y in zip(freq, get_x(len(freq)), get_y(freq)):
data = {'x': x, 'y': y}
if f not in sources:
sources[f] = ColumnDataSource(data)
line = plot.line('x', 'y', source = sources[f])
lines[f] = line
else:
sources[f].data = data
for line in lines:
lines[line].visible = (False if line not in freq else True)
for txt, max in zip(['fmin', 'fmax', 'nfreq'], [3, 4, 5]):
slider = Slider(start = 1, end = max, value = 1, title = txt)
slider.on_change('value', update)
layout.children.append(slider)
update_sources(layout.children[-3].value, layout.children[-2].value, layout.children[-1].value)
[plot.line('x', 'y', source = sources[idx]) for idx in sources]
curdoc().add_root(layout)

Plotly iplot() doesnt run within a function

I am trying to use iplot() within a function within Jupyter so that i can use a filter on the graph and have it change dynamically. The code works in a cell on its own like this
# Code for put by ticker
data = []
opPriceDic = priceToArray(getPuts(getOptionPricesByTicker('ABBV')))
for key, values in opPriceDic.items():
trace = go.Scatter(
x = numberOfDays,
y = values,
name = 'option',
line = dict(
width = 4)
)
data.append(trace)
# Edit the layout
layout = dict(title = 'Call prices for ' ,
xaxis = dict(title = 'Days to Expiration'),
yaxis = dict(title = 'Price '),
)
fig = dict(data=data, layout=layout)
py.iplot(fig, filename='calls For ')
But once this is placed within a function the graph fails to load
def graph(ticker):
# Code for put by ticker
data = []
opPriceDic = priceToArray(getPuts(getOptionPricesByTicker(ticker)))
for key, values in opPriceDic.items():
trace = go.Scatter(
x = numberOfDays,
y = values,
name = 'option',
line = dict(
width = 4)
)
data.append(trace)
# Edit the layout
layout = dict(title = 'Call prices for ' ,
xaxis = dict(title = 'Days to Expiration'),
yaxis = dict(title = 'Price '),
)
fig = dict(data=data, layout=layout)
py.iplot(fig, filename='calls For ')
But if I change the iplot() to plot() it calls the plotly API and opens a new tab with the graph displaying.
I am just wondering if anyone has noticed this before and may have come across a solution?
(if I am in the wrong area I will remove the post)
I have tried to use pandas data.reader calls to pull ticker data between a start and end date. The data.reader seems to work from within the function. In the question code, if the opPriceDic dictionary could be converted to a dataframe, then iplot() could plot it without use of layout and fig as below:
# Import libraries
import datetime
from datetime import date
import pandas as pd
import numpy as np
from plotly import __version__
%matplotlib inline
import cufflinks as cf
from plotly.offline import download_plotlyjs, init_notebook_mode, plot, iplot
init_notebook_mode(connected=True)
init_notebook_mode(connected=True)
cf.go_offline()
# Create function that uses data.reader and iplot()
def graph(ticker):
# create sample data set
start = datetime.datetime(2006, 1, 1)
end = datetime.datetime(2016, 1, 1)
df = data.DataReader(ticker, 'morningstar', start, end)
df = df.reset_index()
df['numberOfDays'] = df.apply(lambda x: abs((datetime.datetime.now() - x['Date']).days), axis=1)
# call iplot within the function graph()
df.iplot(kind='line', x='numberOfDays', y='Close', xTitle='Days', yTitle='Value', title='Prices', width=4)

Bokeh return empty map

I am trying to create a map with bokeh to show the population in US_cities, but as soon as I run the code, it returns empty map, frame is there but map is not. I am trying to do something like this but for all US Cities.
Here is my code using "us_cities.json" file in bokeh data:
import pandas as pd
from bokeh.io import show
from bokeh.models import (
ColumnDataSource,
HoverTool,
LogColorMapper
)
from bokeh.palettes import Viridis6 as palette
from bokeh.plotting import figure
palette.reverse()
new_data = pd.read_json("/home/alvin/.bokeh/data/us_cities.json")
#Creating random data that I want to show on map
new_data['pop'] = ((new_data['lat'] * 100) - new_data["lon"])/ 800
#Converting pd series to array
xs = new_data['lat'].tolist()
ys = new_data['lon'].tolist()
pops = new_data['pop'].tolist()
#creating ColumnDataSource
source = ColumnDataSource(data=dict(
x=xs,
y=ys,
pop = pops,
))
TOOLS = "pan,wheel_zoom,reset,hover,save"
p = figure(
title="Just a US Map", tools=TOOLS,
x_axis_location=None, y_axis_location=None
)
color_mapper = LogColorMapper(palette=palette)
p.grid.grid_line_color = None
p.patches('x', 'y', source=source,
fill_color={'field': 'pop', 'transform': color_mapper},
fill_alpha=0.7, line_color="white", line_width=0.5)
hover = p.select_one(HoverTool)
hover.point_policy = "follow_mouse"
hover.tooltips = [
("population)", "#pop%"),
("(Long, Lat)", "($x, $y)"),
]
show(p)
What could be the problem here?
I am running python3 and bokeh 0.12.6
If I check my data it looks like this:
enter image description here
The US cities data does not contain glyphs, like the counties data. You can show the counties data, and overlay the cities data as a scatter plot on top:
import pandas as pd
from bokeh.io import show
from bokeh.models import (
ColumnDataSource,
HoverTool,
LogColorMapper
)
from bokeh.palettes import Viridis6 as palette
from bokeh.plotting import figure
from bokeh.sampledata.us_counties import data as counties
palette.reverse()
counties = {
code: county for code, county in counties.items() if county["state"] == "tx"
}
county_xs = [county["lons"] for county in counties.values()]
county_ys = [county["lats"] for county in counties.values()]
csource = ColumnDataSource(data=dict(
x=county_xs,
y=county_ys,
))
new_data = pd.read_json("/home/tc427/.bokeh/data/us_cities.json")[::100]
#Creating random data that I want to show on map
new_data['pop'] = ((new_data['lat'] * 100) - new_data["lon"])/ 800
#Converting pd series to array
xs = new_data['lon'].tolist()
ys = new_data['lat'].tolist()
pops = new_data['pop'].tolist()
#creating ColumnDataSource
source = ColumnDataSource(data=dict(
x=xs,
y=ys,
pop = pops,
))
TOOLS = "pan,wheel_zoom,reset,hover,save"
p = figure(
title="Just a US Map", tools=TOOLS,
)
color_mapper = LogColorMapper(palette=palette, low=0, high=10)
p.patches('x', 'y', source=csource)
#p.patches('x', 'y', source=csource,
# fill_color={'field': 'pop', 'transform': color_mapper},
# fill_alpha=0.7, color="red", line_width=0.5)
p.scatter('x', 'y', source=source, color={'field': 'pop', 'transform': color_mapper})
hover = p.select_one(HoverTool)
hover.point_policy = "follow_mouse"
hover.tooltips = [
("population)", "#pop%"),
("(Long, Lat)", "($x, $y)"),
]
show(p)
I assume you are using a jupyter notebook...
As i have faced the same problem recently, I would suggest you try following steps;
open your browser's JavaScript console and check for errors.
read the discussion in link It seems to be a cronical problem.
It might be caused by many reasons like internet connetction issues, API problems etc. You can find and solve the problem by following above steps.
Once you detect and solve your problem, restarting the kernel will not help since the bokehjs is still loaded in the page. You will need to reload the page.

Resources