I'm trying to do some really basic scatterplots. I'm following instructions from here: https://plot.ly/julia/subplots/
using Plotly
trace1 = [
"x" => [1, 2, 3],
"y" => [4, 5, 6],
"type" => "scatter"
]
trace2 = [
"x" => [20, 30, 40],
"y" => [50, 60, 70],
"xaxis" => "x2",
"yaxis" => "y2",
"type" => "scatter"
]
data = [trace1, trace2]
layout = [
"xaxis" => ["domain" => [0, 0.45]],
"yaxis2" => ["anchor" => "x2"],
"xaxis2" => ["domain" => [0.55, 1]]
]
response = Plotly.plot(data, ["layout" => layout, "filename" => "simple-subplot", "fileopt" => "overwrite"])
plot_url = response["url"]
I get:
ERROR: MethodError: no method matching PlotlyJS.Plot(::Array{Array{Pair{String,Any},1},1}, ::Array{Pair{String,Any},1})
Closest candidates are:
PlotlyJS.Plot(::AbstractArray{T<:Union{AbstractString, Date, Number, Symbol},1}, ::AbstractArray{T,1} where T) where T<:Union{AbstractString, Date, Number, Symbol} at /Users/username/.julia/v0.6/PlotlyJS/src/convenience_api.jl:56
PlotlyJS.Plot(::AbstractArray{T<:Union{AbstractString, Date, Number, Symbol},1}, ::AbstractArray{T,1} where T, ::PlotlyJS.Layout; kind, style, kwargs...) where T<:Union{AbstractString, Date, Number, Symbol} at /Users/username/.julia/v0.6/PlotlyJS/src/convenience_api.jl:56
PlotlyJS.Plot(::AbstractArray{T<:(AbstractArray{T,1} where T),1}, ::AbstractArray{T,2} where T) where T<:(AbstractArray{T,1} where T) at /Users/username/.julia/v0.6/PlotlyJS/src/convenience_api.jl:68
...
Stacktrace:
[1] #plot#36(::Array{Any,1}, ::Function, ::Array{Array{Pair{String,Any},1},1}, ::Vararg{Any,N} where N) at /Users/username/.julia/v0.6/PlotlyJS/src/display.jl:76
[2] plot(::Array{Array{Pair{String,Any},1},1}, ::Vararg{Any,N} where N) at /Users/username/.julia/v0.6/PlotlyJS/src/display.jl:76
I see the errors invoke PlotlyJS vs regular Plotly. I copy/pasted the example code into a fresh Julia shell. I've done Pkg.add("PlotlyJS"), Pkg.add("Plotly"), and Pkg.update(). Still no luck...
My reading of the documentation suggests that this is the kind of approach to try:
using PlotlyJS
t1 = scatter(;x=[1, 2, 3, 4, 5],
y=[1, 6, 3, 6, 1],
mode="lines",
name="Team A",
text=["A-1", "A-2", "A-3", "A-4", "A-5"])
t2 = scatter(;x=[1, 2, 3, 4, 5],
y=[10, 60, 30, 60, 10],
mode="lines",
name="Team B",
text=["B-1", "B-2", "B-3", "B-4", "B-5"])
layout = Layout(;title="Data Labels Hover",
xaxis_range=[0, 10],
yaxis_range=[0, 100])
data = [t1, t2]
PlotlyJS.plot(data, layout)
I think I'd recommend using Plots.jl for local plotting, though (which can use PlotlyJS.jl). Presumably Plotly.jl is for plotting to the cloud?
Related
I am using the following taken from the examples https://juliastats.org/TimeSeries.jl/latest/plotting/
using Plots
using TimeSeries
gr()
ta = [[1,5,0,2]]
plot(ta, seriestype = :candlestick)
But I'm getting the error
ERROR: LoadError: The backend must not support the series type Val{:candlestick}, and there isn't a series recipe defined.
It has to be a TimeArray
data = (datetime = [DateTime(2018, 11, 21, 12, 0), DateTime(2018, 11, 21, 13, 0)],
open = [1, 11.2],
high = [5, 21.2],
low = [0, 31.2],
close = [2, 31.2])
ta = TimeArray(data; timestamp = :datetime, meta = "Example")
import hvplot.pandas
from bokeh.sampledata.autompg import autompg_clean
autompg_clean['origin']=autompg_clean.origin.map({'North America': 'North America '*5,
'Asia': 'Asia '*5,
'Europe': 'Europe '*5,
})
Here is the corresponding annotated output. I have tried using p=hv.render() to get the Bokeh figure object back, but doing something like p.yaxis.major_label_text_align = 'left' does not seem to do anything even if I inject newline \n characters into the long string label.
Multiline labels are available with the newline charactert \n for categorical factors.
I was not able to reproduce your example, but I think the solution is to set you y-axis to a FactorRange and set the factors with a list of your wanted strings, which can include \n.
See the example below, which is adapted from here.
from bokeh.io import output_file, show
from bokeh.models import ColumnDataSource
from bokeh.palettes import GnBu3, OrRd3
from bokeh.plotting import figure
output_file("stacked_split.html")
fruits = [f'{item}\n{item}' for item in ['Apples', 'Pears', 'Nectarines', 'Plums', 'Grapes', 'Strawberries']]
years = ["2015", "2016", "2017"]
exports = {'fruits' : fruits,
'2015' : [2, 1, 4, 3, 2, 4],
'2016' : [5, 3, 4, 2, 4, 6],
'2017' : [3, 2, 4, 4, 5, 3]}
imports = {'fruits' : fruits,
'2015' : [-1, 0, -1, -3, -2, -1],
'2016' : [-2, -1, -3, -1, -2, -2],
'2017' : [-1, -2, -1, 0, -2, -2]}
p = figure(y_range=fruits, height=250, x_range=(-16, 16), title="Fruit import/export, by year",
toolbar_location=None)
p.hbar_stack(years, y='fruits', height=0.9, color=GnBu3, source=ColumnDataSource(exports),
legend_label=["%s exports" % x for x in years])
p.hbar_stack(years, y='fruits', height=0.9, color=OrRd3, source=ColumnDataSource(imports),
legend_label=["%s imports" % x for x in years])
p.y_range.range_padding = 0.1
p.ygrid.grid_line_color = None
p.legend.location = "top_left"
p.axis.minor_tick_line_color = None
p.outline_line_color = None
show(p)
Output
I am building a chart using Bokeh. I need to create an empty Figure and when the page is loaded, if I click on a button, data is loaded and displayed on the chart.
I wrote this code but it doesn't work.
def _op_produttivitaRidge1day(db, custom_attrs, start_date, end_date, postazione, stazione: str, mongo_coll, hours: list, hours2: list, stazione2: str, stazione3: str, cod_avaria: str):
def crosscorr(datax, datay, lag=0, wrap=False):
""" Lag-N cross correlation.
Shifted data filled with NaNs
Parameters
----------
lag : int, default 0
datax, datay : pandas.Series objects of equal length
Returns
----------
crosscorr : float
"""
if wrap:
shiftedy = datay.shift(lag)
shiftedy.iloc[:lag] = datay.iloc[-lag:].values
return datax.corr(shiftedy)
else:
return datax.corr(datay.shift(lag))
# custom data
custom_attrs = {
"OP30": ["ST7_LETTURA_INI_TRASD", "ST7_LETTURA_TRASD", "ST3_CONT_SCARTI_CONS", "ST4_CONT_SCARTI_CONS",
"ST7_CONT_SCARTI_CONS"],
"OP40": ["ST2_VAL_CRIMPATURA", "ST2_VAL_INI_CRIMPATURA", "ST5_CNT_SCARTI_CONS_CHECKER", "ST1_CONT_SCARTI_CONS",
"ST2_CONT_SCARTI_CONS", "ST5_CONT_SCARTI_CONS"],
"OP50": ["ST3_CNT_VETRINO_SALD", "ST1_VAL_PRESSIONE", "ST1_VAL_PERDITA", "ST2_CONT_SCARTI_CONS",
"ST3_CONT_SCARTI_CONS"],
"OP60": ["ST2_LETTURA_INI_TRASD", "ST3_COUNT_CONTROLLO_VETRINO", "ST4_CONT_FOTO1_COGNEX",
"ST4_CONT_FOTO2_COGNEX", "ST4_RIPROVE_SCARTO", "ST1_CONT_SCARTI_CONS", "ST2_CONT_SCARTI_CONS",
"ST3_CONT_SCARTI_CONS", "ST4_CONT_SCARTI_CONS"],
"OP70": ["ST1_CONT_SCARTI_CONS"],
"OP80": ["ST1_COUNT_CONTROLLO_VETRINO", "ST1_CONT_SCARTI_CONS", "ST2_CONT_SCARTI_CONS"],
"OP90": ["ST1_VAL_TRASD_DAMPER", "ST1_VAL_TRASD_INI_DAMPER", "ST1_VAL_TRASD_CUP_INI_DAMPER",
"ST1_VAL_TRASD_CUP_DAMPER", "ST1_CONT_SCARTI_CONS"],
"OP100": [],
"OP110": [],
"OP120": ["ST1_VAL_MISURA_PISTON", "ST1_VAL_TRASD_INI", "ST1_CONT_SCARTI_CONS"]
}
# Produttività Ridge Graph
# day, hour, number of items produced
attr_name = stazione + "_PZ_IN"
attr_value = "$" + attr_name
c5 = db[mongo_coll].aggregate([
{"$match": {"$and": [{"Timestamp": {"$gte": start_date}}, {"Timestamp": {"$lte": end_date}}]}},
# {"$match": {{"$substr": ["$Timestamp" , 0, 10]} : {"$in": days_list}}},
{"$project": {"DAY": {"$substr": ["$Timestamp", 0, 10]}, "HOUR": {"$substr": ["$Timestamp", 11, 4]},
attr_name: 1}},
{"$group": {"_id": {"DAY": "$DAY", "HOUR": "$HOUR"}, "ITEM_COUNT": {"$sum": attr_value}}},
{"$project": {"_id": 0, "DAY": "$_id.DAY", "HOUR": "$_id.HOUR", "ITEM_COUNT": 1}},
{"$sort": {"DAY": 1, "HOUR": 1}}
])
c5_df = pd.DataFrame(list(c5))
days = sorted(c5_df["DAY"].unique())
PR = figure(title="Produzione " + postazione + " " + stazione, y_range=days, plot_width=900, x_range=(-2, 208),
toolbar_location=None)
bt = Button(
label="Show data",
button_type="success",
width=50
)
def createPR(FIG, rm_num_values):
c5_df['ITEM_COUNT_RM'] = c5_df.iloc[:, 0].rolling(window=rm_num_values).mean().fillna(0)
for d in days:
y = []
for h in hours:
try:
# hf = str(int(h)).zfill(2)
hf = h
num_items = (c5_df.loc[c5_df["HOUR"] == hf].loc[c5_df["DAY"] == d].iloc[0]["ITEM_COUNT_RM"]) / 80
# print(hf,d,num_items)
if num_items > 1:
num_items = 1
except:
num_items = 0
y.append([d, num_items])
# print(y)
s5 = ColumnDataSource(data=dict(x=range(146)))
s5.add(y, d)
FIG.patch('x', d, source=s5)
FIG.outline_line_color = None
FIG.background_fill_color = "#efefef"
FIG.y_range.range_padding = 0.12
FIG.xaxis.ticker = FixedTicker(
ticks=[0, 6, 12, 18, 24, 30, 36, 42, 48, 54, 60, 66, 72, 78, 84, 90, 96, 102, 108, 114, 120, 126, 132, 138])
FIG.xaxis.major_label_overrides = {0: '0', 6: '1', 12: '2', 18: '3', 24: '4', 30: '5', 36: '6', 42: '7', \
48: '8', 54: '9', 60: '10', 66: '11', 72: '12', 78: '13', 84: '14', 90: '15', \
96: '16', 102: '17', 108: '18', 114: '19', 120: '20', 126: '21', 132: '22',
138: '23'}
FIG.outline_line_color = background
FIG.border_fill_color = background
FIG.background_fill_color = background
return FIG
#PR = createPR(PR, 3)
bt.on_click(createPR(PR, 3))
return column(bt, PR)
This code was written for Bokeh v2.1.1. Run it with bokeh serve -show main.py.
Replace data = dict(x=[1, 2, 3, 2], y=[6, 7, 2, 2]) with your data acquisition function: data = _op_produttivitaRidge1day(db, custom_attrs, start_date, end_date, ...) Everything should work if your function returns a dictionary with x and y vectors of equal length.
The approach is to first pass an empty data_source to the figure and then after a button click, download the data from your MongoDB and assign it as dictionary to the patch.data_source.data.
main.py:
from bokeh.plotting import curdoc, figure
from bokeh.models import Column, Button
plot = figure()
patch = plot.patch(x=[], y=[])
def callback():
data = dict(x=[1, 2, 3, 2], y=[6, 7, 2, 2]) # <-- your data acquisition function comes here
patch.data_source.data = data
button = Button()
button.on_click(callback)
curdoc().add_root(Column(plot, button))
I am trying to plot several scatter glyphs to one figure, using a loop. The goal is to use an own glyph for each brand and to update the values if another value is chosen by the Select widgets.
But the figure shows an empty graph. I assume that the problem is the ColumnDataSource and the update in the function "update()".
This is an executable example. Do you have any idea?
from bokeh.layouts import column, row
from bokeh.models import ColumnDataSource, Select, HoverTool
from bokeh.plotting import figure, show
import pandas as pd
brands = ['a', 'b', 'c', 'a', 'b', 'b', 'c']
product = ['v1', 'v2', 'v3', 'v4', 'v5', 'v6', 'v7']
price = [2, 3, 54, 48, 9, 2, 4]
size = [10, 11, 12, 13, 14, 15, 16]
value = [5, 4, 3, 8, 1, 0, 1]
id = [1, 2, 3, 4, 5, 6, 7]
col = ['ID', 'brand', 'product', 'price', 'size', 'value']
label = ['price', 'size', 'value']
df = pd.DataFrame(zip(brands, product, price, size, value, id), columns=col)
# Widgets:
select_x_axis = Select(title="x-Axis:", value=label[0], options=label)
select_y_axis = Select(title="y-Axis:", value=label[1], options=label)
# Set up figure
hover = HoverTool(tooltips=[
("index", "#id"),
('Brand', '#brand'),
('Product', '#product'),
(select_x_axis.value, '#x'),
(select_y_axis.value, '#y')
])
# Set up plots:
fig = figure(plot_height=400, plot_width=800, title="xyz",
# tooltips=TOOLTIPS,
tools=[hover, 'reset'],
x_axis_label=select_x_axis.value,
y_axis_label=select_y_axis.value)
source = {}
plots = {}
for brand in brands:
# Create Column Data Source that will be used by the plot
source[brand] = ColumnDataSource(data=dict(x=[], y=[], id=[], product=[], brand=[]))
plots[brand] = fig.scatter(x='x', y='y', size=5, source=source[brand])
def update():
x_name = select_x_axis.value
y_name = select_y_axis.value
fig.xaxis.axis_label = x_name
fig.yaxis.axis_label = y_name
for brand in brands:
df1 = df.loc[df['brand'] == brand]
source[brand].data = dict(
x=df1[x_name],
y=df1[y_name],
id=df1['ID'],
product=df1['product'],
brand=df1['brand']
)
# Set up layouts and add to document
controls = [select_x_axis, select_y_axis]
for control in controls:
control.on_change('value', lambda attr, old, new: update())
inputs = column(select_x_axis, select_y_axis)
update() # initial load of the data
show(row(inputs, fig, width=1000))
#curdoc().add_root(row(inputs, fig, width=1000))
#curdoc().title = "xyz"
resultig plot
Something like that ?
from bokeh.layouts import column, row
from bokeh.models import ColumnDataSource, Select, HoverTool, CustomJS
from bokeh.plotting import figure, show
import pandas as pd
def make_data() :
brands = ['a', 'b', 'c', 'a', 'b', 'b', 'c']
product = ['v1', 'v2', 'v3', 'v4', 'v5', 'v6', 'v7']
price = [2, 3, 54, 48, 9, 2, 4]
size = [10, 11, 12, 13, 14, 15, 16]
value = [5, 4, 3, 8, 1, 0, 1]
ident = [1, 2, 3, 4, 5, 6, 7]
col = ['ID', 'brand', 'product', 'price', 'size', 'value']
label = ['price', 'size', 'value']
return pd.DataFrame(zip(brands, product, price, size, value, ident), columns=col)
if __name__ == "__main__" :
df = make_data()
default_xcol = "price"
default_ycol = "size"
df["xvalues"] = df[default_xcol]
df["yvalues"] = df[default_ycol]
# Scatter plot
hover = HoverTool(tooltips=[(name, "#" + name) for name in df.columns])
figure = figure(tools=[hover, 'reset'])
# Source
source = ColumnDataSource(df)
figure.scatter("xvalues", "yvalues", source=source)
# Selects
options = ["product", "price", "size", "value"]
select_x_axis = Select(title="x-Axis:", value=default_xcol, options=options)
select_y_axis = Select(title="y-Axis:", value=default_ycol, options=options)
# callback
callback = CustomJS(args={"source":source, "axis":figure.xaxis[0]}, code="""
source.data['xvalues'] = source.data[cb_obj.value];
source.change.emit();
axis.axis_label = cb_obj.value;
""")
select_x_axis.js_on_change("value", callback)
callback = CustomJS(args={"source":source, "axis":figure.yaxis[0]}, code="""
source.data['yvalues'] = source.data[cb_obj.value];
source.change.emit();
axis.axis_label = cb_obj.value;
""")
select_y_axis.js_on_change("value", callback)
show(row(
column(select_x_axis, select_y_axis),
figure, width=1000))
#jsgounot Thanks for your help. I figured something out that works quiet well at the moment:
from bokeh.layouts import column, row
from bokeh.models import ColumnDataSource, Select, HoverTool, CustomJS
from bokeh.plotting import figure, show
from bokeh.palettes import d3
import pandas as pd
brands = ['a', 'b', 'c', 'a', 'b', 'b', 'c']
product = ['v1', 'v2', 'v3', 'v4', 'v5', 'v6', 'v7']
price = [2, 3, 54, 48, 9, 2, 4]
size = [10, 11, 12, 13, 14, 15, 16]
value = [5, 4, 3, 8, 1, 0, 1]
id = [1, 2, 3, 4, 5, 6, 7]
col = ['ID', 'brand', 'product', 'price', 'size', 'value']
label = ['price', 'size', 'value']
colors = d3["Category20c"][len(brands)]
markers = ['circle', 'square', 'triangle', 'asterisk', 'circle_x', 'square_x', 'inverted_triangle', 'x', 'circle_cross', 'square_cross', 'diamond', 'cross']
df = pd.DataFrame(zip(id, brands, product, price, size, value), columns=col)
default_xcol = "price"
default_ycol = "size"
df["xvalues"] = df[default_xcol]
df["yvalues"] = df[default_ycol]
# Widgets:
select_x_axis = Select(title="x-Axis:", value=label[0], options=label)
select_y_axis = Select(title="y-Axis:", value=label[1], options=label)
# Set up figure
hover = HoverTool(tooltips=[
("index", "#ID"),
('Brand', '#brand'),
('Product', '#product'),
(select_x_axis.value, '#xvalues'),
(select_y_axis.value, '#yvalues')
])
# Set up plots:
fig = figure(plot_height=400, plot_width=800, title="xyz",
# tooltips=TOOLTIPS,
tools=[hover, 'reset'],
x_axis_label=select_x_axis.value,
y_axis_label=select_y_axis.value)
df_brand = []
sources = []
plots = {}
for index, brand in enumerate(df.brand.unique()):
df_brand.append(df.loc[df['brand'] == brand])
sources.append(ColumnDataSource(df_brand[index]))
plots[brand] = fig.scatter(x='xvalues', y='yvalues', source=sources[index],
legend_label=brand,
marker=markers[index],
color=colors[index])
fig.legend.click_policy="hide"
callback_x = CustomJS(args={'sources': sources, 'axis': fig.xaxis[0], 'brands': df.brand.unique()}, code="""
for (var i = 0; i <= 2; i++){
var source = sources[i]
source.data['xvalues'] = source.data[cb_obj.value];
source.change.emit();
}
axis.axis_label = cb_obj.value;
""")
callback_y = CustomJS(args={'sources': sources, 'axis': fig.yaxis[0], 'hov': fig.hover, 'brands': df.brand.unique()}, code="""
for (var i = 0; i <= 2; i++){
var source = sources[i]
source.data['yvalues'] = source.data[cb_obj.value];
source.change.emit();
}
axis.axis_label = cb_obj.value;
""")
select_x_axis.js_on_change('value', callback_x)
select_y_axis.js_on_change('value', callback_y)
show(row(
column(select_x_axis, select_y_axis),
fig, width=1000))
I am trying to make a stacked bar chart, where de values that are being stacked have a specific order.
I understand that you can do this by ordering the series as they come in. But I am adding all series at once, so I'm not sure how this would work.
I have tried ordering the dataframe itself, but this does not appear to do anything.
I've also read about indexing, but I haven't been able to get that to work.
An example:
# create data frame
cat1 <- c('cat','dog','cat','bunny','dog','bunny')
cat2 <- c('brown','grey','grey','grey','brown','brown')
value <- c(15,80,85,36,20,64)
# Join the variables to create a data frame
df <- data.frame(cat1,cat2,value)
df
#create barchart
hchart(df, type = 'bar', hcaes( y = value, group=cat2, x = cat1)) %>%
hc_plotOptions(series = list(stacking = "normal"))
This creates a barchart with grey first and brown second. But I would like to set the order myself. Is this possible?
Off course, I can split my dataframe into separate datasets, but I also have charts where I have many categories that need to be stacked. I would rather not add all of these separately.
You can change levels before plotting -
> df <- within(df, cat2 <- factor(cat2, levels=c("grey","brown")))
> hchart(df, type = 'bar', hcaes( y = value, group=cat2, x = cat1)) %>%
hc_plotOptions(series = list(stacking = "normal"))
You can define index property in your series. Here you have JS example of this:
series: [{
name: 'First',
data: [5, 6, 4],
index: 0
}, {
name: 'Second',
data: [1, 3, 2],
index: 4
}, {
name: 'Third',
data: [6, 7, 2],
index: 1
}, {
name: 'Fourth',
data: [11, 13, 12],
index: 3
}, {
name: 'Fifth',
data: [9, 9, 2],
index: 2
}]
jsFiddle: https://jsfiddle.net/BlackLabel/uj7qmp3d
API: https://api.highcharts.com/highcharts/series.bar.index