I am looking for a way to port R code to Python. I wonder if anyone knows of some way to do a rough translation, to facilitate porting. Note that I am not interested in running R from Python, or Python from R - I want native Python from existing R code. I have written a few functions in R that translate a few language features automatically:
gsub.R.to.python <- function(x)
{
# very crude conversion of R code to python - facilitating manual port
out <- gsub.function.to.def(x)
out <- gsub(pattern = "<-","=",out)
out <- gsub.dots.to.underscore(out)
out <- gsub.python.remove.brackets(out)
out <- gsub.T.to.True(out)
out <- gsub.F.to.False(out)
cat(out)
invisible(out)
}
gsub.dots.to.underscore <- function(x)
{
# replace dots to underscores in variable names (i.e. between letters)
# gsub.dots.to.underscore(c("a.b","1.0")) --> "a_b" "1.0"
out <- gsub(pattern = "(?<=[a-zA-Z])\\.(?=[a-zA-Z])","_",x,perl = T)
return(out)
}
gsub.function.to.def <- function(x)
{
# change all function lines - use perl multiline searching
out <- gsub("(?m)^([a-zA-Z].+?) <- function\\((.+?)\\)","def \\1(\\2):",x,perl = T)
return(out)
}
gsub.python.remove.brackets<- function(x)
{
# get rid of brackets on their own lines
out <- gsub("(?m)^\\s*\\{\\s*$","",x,perl = T)
out <- gsub("(?m)^\\s*\\}\\s*$","",out,perl = T)
return(out)
}
gsub.T.to.True <- function(x) gsub("\\bT\\b|\\bTRUE\\b","True",x)
gsub.F.to.False <- function(x) gsub("\\bF\\b|\\bFALSE\\b","False",x)
Running gsub.R.to.python() on a block of R code produces a block of Python-ish gibberish that is a bit easier to translate into Python. I am looking for a better / more complete version of this idea, or some other tool to help in the porting process.
For example, running this code in R on the code itself, produces the following:
def gsub_R_to_python(x):
# very crude conversion of R code to python - facilitating manual port
out = gsub_function_to_def(x)
out = gsub(pattern = "=","=",out)
out = gsub_dots_to_underscore(out)
out = gsub_python_remove_brackets(out)
out = gsub_T_to_True(out)
out = gsub_F_to_False(out)
cat(out)
invisible(out)
def gsub_dots_to_underscore(x):
# replace dots to underscores in variable names (i_e. between letters)
# gsub_dots_to_underscore(c("a_b","1.0")) --> "a_b" "1.0"
out = gsub(pattern = "(?<=[a-zA-Z])\.(?=[a-zA-Z])","_",x,perl = True)
return(out)
def gsub_function_to_def(x):
# change all function lines - use perl multiline searching
out = gsub("(?m)^([a-zA-Z].+?) = function\((.+?)\)","def \1(\2):",x,perl = True)
return(out)
gsub_python_remove_brackets= function(x)
# get rid of brackets on their own lines
out = gsub("(?m)^\s*\{\s*$","",x,perl = True)
out = gsub("(?m)^\s*\}\s*$","",out,perl = True)
return(out)
def gsub_T_to_True(x): gsub("\bT\b|\bTRUE\b","True",x)
def gsub_F_to_False(x): gsub("\bF\b|\bFALSE\b","False",x)
Related
I'm trying to create an interactive filter in Bokeh for a graph representation
The source data come from a graph. The function SpotlightData(G) create the pandas dataframes with node and edge informations.
I need to pass the value from a range slider to the CDSView.
I try using js_on_change with a callback function (but I'm not a JS programmer).
Basically I want to create an array of bool values (evaluating a logical expression) and pass it to the view filter.
This is the code:
mytool = "pan , wheel_zoom , undo , reset"
plot = figure(title="Graph layout demonstration"
, tools=mytool
, width=800
, height=600
, background_fill_color='#efefef'
, toolbar_location = "below")
plot.axis.visible = False
plot.grid.grid_line_color = None
NodeDF , EdgeDF = SpotlightData(Spotlight)
Saldo_maxscale = math.ceil((NodeDF["Saldo"].abs().quantile(q=0.9))/1000)*1000
NodeSource = ColumnDataSource(NodeDF)
EdgeSource = ColumnDataSource(EdgeDF)
BF= BooleanFilter([True]*len(EdgeDF))
NodeView = CDSView(source=NodeSource ,filters=[])
EdgeView = CDSView(source=EdgeSource ,filters=[BF])
days_slider = RangeSlider(title="Dates",start=1,end=31,step=1,value=(1, 31))
callback = CustomJS(args=dict(source=EdgeDF["dt_ref_day"],boolout=BF.booleans), code="""
const data = source;
const s = cb_obj.value[0];
const e = cb_obj.value[1];
for (let i = 0; i < data.length; i++) {
boolout[i] = data[i]>=s && data[i]<=e
}
boolout.change.emit();
""")
days_slider.js_on_change('value', callback)
graph = GraphRenderer()
graph.layout_provider = StaticLayoutProvider(graph_layout=nx.spring_layout(Spotlight))
graph.node_renderer.data_source = NodeSource
graph.edge_renderer.data_source = EdgeSource
graph.node_renderer.view = NodeView
graph.edge_renderer.view = EdgeView
NodeColor = LinearColorMapper(palette = cc.CET_D3, low=(Saldo_maxscale*-1), high=Saldo_maxscale)
graph.node_renderer.glyph = Circle(size="LogSize",fill_color=transform('Saldo', NodeColor))
graph.edge_renderer.glyph = MultiLine(line_alpha=0.5, line_width="LogSize")
plot.renderers.append(graph)
show(column(days_slider, plot))
But nothing change in the visualization despite moving the slider.
Thank you all I'm quite a noob with Bokeh
PS: There is a constraint: In this case I can't run a bokeh server
My goal is to define scale bars programmatically, that have a fixed length, but may be changed afterwards by the graphic designer.
I have come sofar as to define a closed path within the document:
def addLine(doc):
def point(x, y):
result = Dispatch("Photoshop.PathPointInfo")
result.Kind = 2 # for PsPointKind --> 2 (psCornerPoint)
result.LeftDirection = result.rightDirection = result.Anchor = (x, y)
return result
points = [
point(100, 100),
point(200, 100),
point(200, 110),
point(100, 110)
]
lineSubPathArray = Dispatch("Photoshop.SubPathInfo")
lineSubPathArray.Operation = 1 #for PsShapeOperation --> 1 (psShapeAdd
lineSubPathArray.Closed = True
lineSubPathArray.EntireSubPath = points
myPathItem = doc.PathItems.Add("bar-path", [lineSubPathArray])
From here, I can load the saved document back into photoshop (CS6) and then create a shape layer manually: Layer | New fill layer | solid color ...
This results in a shape layer, similar to what I get by using the line tool, in which the line effectively is a rectangle whose height may be changed later.
First question: how to create the fill layer by using the API?
Second: I defined a rectangle 100pixels wide, but I get one 418 pixels wide. The doc has its doc.application.preferences.rulerUnits set to psPixels (1). Why is this?
Last: Isn't it possible to define a line as true line defined by two end points and set its stroke width instead of it's height?
This may be of use:
You can define a new colour with new SolidColor()
// define fillColor
var fillColor = new SolidColor();
var myColour = [57, 181,74];
fillColor.rgb.red = myColour[0];
fillColor.rgb.green = myColour[1];
fillColor.rgb.blue = myColour[2];
and then fill your path by adding myPathItem.fillPath(fillColor,ColorBlendMode.NORMAL,100,false,0,true,true);
// Switch off any dialog boxes
displayDialogs = DialogModes.NO; // OFF
var originalUnits = app.preferences.rulerUnits;
app.preferences.rulerUnits = Units.PIXELS;
// call the source document
var srcDoc = app.activeDocument;
create_path("bar-path");
function create_path(linename)
{
var points = [
[100, 100],
[200, 100],
[200, 110],
[100, 110]
];
// create the array of PathPointInfo objects
var lineArray = new Array();
for (var i = 0; i < points.length; i++)
{
lineArray[i] = new PathPointInfo;
lineArray[i].kind = PointKind.CORNERPOINT;
lineArray[i].anchor = points[i];
lineArray[i].leftDirection = lineArray[i].anchor;
lineArray[i].rightDirection = lineArray[i].anchor;
}
// create a SubPathInfo object, which holds the line array in its entireSubPath property.
var lineSubPathArray = new Array();
lineSubPathArray.push(new SubPathInfo());
lineSubPathArray[0].operation = ShapeOperation.SHAPEXOR;
lineSubPathArray[0].closed = true;
lineSubPathArray[0].entireSubPath = lineArray;
//create the path item, passing subpath to add method
var myPathItem = srcDoc.pathItems.add(linename, lineSubPathArray);
// define fillColor
var fillColor = new SolidColor();
var myColour = [57, 181,74];
fillColor.rgb.red = myColour[0];
fillColor.rgb.green = myColour[1];
fillColor.rgb.blue = myColour[2];
//fill the path so we can see something also
myPathItem.fillPath(fillColor,ColorBlendMode.NORMAL,100,false,0,true,true);
// deselect path
deselect_path();
}
// switch back to normal
app.preferences.rulerUnits = originalUnits;
// Set Display Dialogs back to normal
displayDialogs = DialogModes.ALL; // NORMAL
function deselect_path()
{
// =======================================================
var idslct = charIDToTypeID( "slct" );
var desc76 = new ActionDescriptor();
var idnull = charIDToTypeID( "null" );
var ref63 = new ActionReference();
var idPath = charIDToTypeID( "Path" );
var idOrdn = charIDToTypeID( "Ordn" );
var idTrgt = charIDToTypeID( "Trgt" );
ref63.putEnumerated( idPath, idOrdn, idTrgt );
desc76.putReference( idnull, ref63 );
var idselectionModifier = stringIDToTypeID( "selectionModifier" );
var idselectionModifierType = stringIDToTypeID( "selectionModifierType" );
var idremoveFromSelection = stringIDToTypeID( "removeFromSelection" );
desc76.putEnumerated( idselectionModifier, idselectionModifierType, idremoveFromSelection );
executeAction( idslct, desc76, DialogModes.NO );
}
As for rectangle being too large: What resolution and units do you have the psd file set to? The script below will switch to pixels, and set the resolution of your document to 72dpi
As for doing a stroke to replace the line. Well... you've got options.
You can do a brush stroke with a two point line:
var idStrk = charIDToTypeID( "Strk" );
var desc105 = new ActionDescriptor();
var idnull = charIDToTypeID( "null" );
var ref35 = new ActionReference();
var idPath = charIDToTypeID( "Path" );
var idOrdn = charIDToTypeID( "Ordn" );
var idTrgt = charIDToTypeID( "Trgt" );
ref35.putEnumerated( idPath, idOrdn, idTrgt );
desc105.putReference( idnull, ref35 );
var idUsng = charIDToTypeID( "Usng" );
var idPbTl = charIDToTypeID( "PbTl" );
desc105.putClass( idUsng, idPbTl );
executeAction( idStrk, desc105, DialogModes.NO );
However, I don't think that's what your after. You can turn your path into a shape and then have a stroke and fill colour assigned. However... you need a minimum of 3 points for that to work.
As a suggestion, instead of a path create a shape from start - which oddly will work with a minimum of two points. But I have no idea how to do that in code!
There are two lessons I have learned form translating the solution by Ghoul Fool into Python:
using COM in python, one can make a lot of mistakes that go unnoticed, otherwise than cryptic error messages far beside the point. Thies include syntactic errors such as missing/redundant parentheses and wrong capitalisation.
solutions that work with COM in python do not work the same with the Python-Photoshop-API and vice versa, even though, following the source code of the library, apparently exactly the same thing is happening. I have not always been able to divine solutions that work both ways.
Here is, for those who have come to this question later, what I have come to to get a shape layer with a bar that can be changed in height afterwards. The action that is called is nothing more than a recording of New Fill Layer from the layer-menu (CS6):
def makeBar(doc):
app = doc.parent
app.preferences.rulerUnits = Units.Pixels
def point(x, y):
result = Dispatch("Photoshop.PathPointInfo")
result.Kind = PointKind.CornerPoint
result.LeftDirection = result.rightDirection = result.Anchor = (x, y)
return result
points = [
point(100, 100),
point(200, 100),
point(200, 110),
point(100, 110)
]
lineSubPathArray = Dispatch("Photoshop.SubPathInfo")
lineSubPathArray.Operation = ShapeOperation.ShapeAdd
lineSubPathArray.Closed = True
lineSubPathArray.EntireSubPath = points
doc.PathItems.Add("bar-path", [lineSubPathArray])
app = Dispatch("Photoshop.Application")
doc = app.Open(self.fileName)
As for my second question: The unit of the path's cornerpoints are invariantly in Points, no matter what he rulers are set to.
thank you for your time reading this, this bugs me long time.I'm using Python3.8 and using ctypes to call a DLL. In most of cases ,ctypes works like charm.
One function from C DLL will output a list of names i.e
int get_date(char* names[]) // each name has length 10, with size of 5
in Python I'm successfully call it like
string_buffers = [ctypes.create_string_buffer(10) for i in range(5)]
char_array = (ctypes.c_char_p*5)(*map(ctypes.addressof, string_buffers))
handler.get_date(char_array)
But I am try to factor out a function to create such case :
def create_char_p_array(length:int, size:int):
string_buffers = [create_string_buffer(length) for i in range(size)]
ia = (c_char_p*size)(*map(addressof, string_buffers))
return ia
char_array = create_char_p_array(10,5)
handler.get_date(char_array) ### failed, or ,crashed, or random value ###
So to narrow down the issue, I move out the map(addressof) out, it magically works! :
def create_char_p_array(length:int, size:int):
string_buffers = [create_string_buffer(length) for i in range(size)]
return string_buffers
sbrs = create_char_p_array(10,5)
char_array = (c_char_p*5)(*map(addressof, sbrs ))
handler.get_date(char_array) # Success !
Is it because create_string_buffer created in function scope will be wipe out once the function create_char_p_array return ?
Thank you for your time ,much appreciated.
Here's what I came up with. addressof doesn't create a reference to the object it operates on. cast will.
test.c:
#include <string.h>
__declspec(dllexport) int get_date(char* names[])
{
strcpy_s(names[0],10,"One");
strcpy_s(names[1],10,"Two");
strcpy_s(names[2],10,"Three");
strcpy_s(names[3],10,"Four");
strcpy_s(names[4],10,"Five");
return 5;
}
test.py:
from ctypes import *
dll = CDLL('./x')
dll.get_date.argtypes = POINTER(c_char_p),
dll.get_date.restype = c_int
def create_char_p_array(length:int, size:int):
return (c_char_p*size)(*[cast(create_string_buffer(length),c_char_p) for _ in range(size)])
a = create_char_p_array(10,5)
dll.get_date(a)
for s in a:
print(s)
Output:
b'One'
b'Two'
b'Three'
b'Four'
b'Five'
I have the following function from which I would like to obtain its solution. I'm trying to use Scipy to get them but I observe that the solution being given by Scipy is quite different from that of Matlab, using same settings.
What am I doing wrong?
def WandG(GW,Zin):
G,W = GW # define real variables to be used for the function
R = (G**2)*(Zin.real - 0.1164) +(( 70e-12 * ( W/100 ) )**2)*( 2.9036155068866304e+16*(Zin.real - 0.1164) ) - G
I = (G**2)*( Zin.imag - 18.743998408378143 * (1-W/200) ) + (((W/100)*70e-12)**2)*( 2.9036155068866304e+16*(Zin.imag - 18.743998408378143 * (1-W/200)) ) + 170399985.53071037*(70e-12*(W/100) )
return R,I
sol = optimize.root(WandG, [0.136879496, 47.04],(12.652884410928804+14.632724423763523j), jac=False,tol=1e-14, method='lm',options={'maxiter':100*400,'epsfcn':1e-14,'xtol':1e-14})
The solution using Scipy would be
sol.x[0] = 0.0795283512113496 # G
sol.x[1] = 36.53727146377749 # W
Whereas with MATLAB for the same initial conditions, solver and Zin, it returns, which I trust more since it resembles better to the final result of W which is W=50
G = 0.0794
W = 44.5234
EDIT : added Matlab code
% configuration of fsolve
options = optimoptions('fsolve','Display','iter-detailed','PlotFcn',#optimplotfirstorderopt);
options.StepTolerance = 1e-14;
%options.OptimalityTolerance = 1e-14
options.FunctionTolerance = 1e-14;
options.MaxIterations = 100000;
options.MaxFunctionEvaluations = 400;
options.Algorithm = 'levenberg-marquardt';%'trust-region'%
fun= #WandG;
x0 = [0.136879496', 47.04'];
% Solve the function fun
gw =fsolve(fun,x0,options);
% Function to be solved by fsolve
function F = WandG(x)
F(:,1) = (x(:,1).^2).*(real(Zin) - 0.1164) +(( 70e-12 .* ( x(:,2)./100 ) ).^2).*( w.^2.*(real(Zin) - 0.1164) ) - x(:,1);
F(:,2) = (x(:,1).^2).*( img(Zin) - 18.743998408378143 * (1-x(:,2)./200) ) + (((x(:,2)./100).*70e-12).^2).*( 2.9036155068866304e+16*(img(Zin) - 18.743998408378143 .* (1-x(:,2)./200)) ) + 170399985.53071037.*(70e-12.*(x(:,2)./100) );
end
I'm using plotly for R, although I'm open to using the Python version, as well. When I hover over a datapoint, is there a way to make the popup contain another chart? Ideally the chart would be created from the data, although I can use a static image as a fallback.
I'm unsure where to start on this, and apologize in advance for not having an MWE.
Solution 1: Stick to R
Thanks to #MLavoie. The following example use pure R to create two plot, the "mainplot" and the "hover" which reacts to the hover event of the first one.
library(shiny)
library(plotly)
ui <- fluidPage(
plotlyOutput("mainplot"),
plotlyOutput("hover")
)
server <- function(input, output) {
output$mainplot <- renderPlotly({
# https://plot.ly/r/
d <- diamonds[sample(nrow(diamonds), 1000), ]
plot_ly(d, x = carat, y = price, text = paste("Clarity: ", clarity), mode = "markers", color = carat, size = carat, source="main")
})
output$hover <- renderPlotly({
eventdat <- event_data('plotly_hover', source="main") # get event data from source main
if(is.null(eventdat) == T) return(NULL) # If NULL dont do anything
point <- as.numeric(eventdat[['pointNumber']]) # Index of the data point being charted
# draw plot according to the point number on hover
plot_ly( x = c(1,2,3), y = c(point, point*2, point*3), mode = "scatter")
})
}
shinyApp(ui, server)
This example use the shiny binds for plotly. For every hover event, a POST request is sent to the server, then the server will update the popup-chart. It's very inefficient thus may not work well on slow connections.
The above code is just for demo, and not yet tested. See a working and much more complicated example here (with source).
Solution 2: Javascript
Yes, you can do it using the plotly Javascript API.
Short answer
Create your graph using R or Python or any other supported language.
Insert the graph into a new HTML page and add a callback function as shown in the example below. If you have good knowledge about DOM, you can also add the JS to the original HTML instead of creating a new one.
Draw the popup graph inside the callback function which accepts parameters containing the data of the datapoint on-hover.
Details
As #MLavoie mentioned, a good example is shown in plotly.hover-events
Let's dig into the code. In the JS file, there is a simple callback function attached to Plot:
Plot.onHover = function(message) {
var artist = message.points[0].x.toLowerCase().replace(/ /g, '-');
var imgSrc = blankImg;
if(artistToUrl[artist] !== undefined) imgSrc = artistToUrl[artist];
Plot.hoverImg.src = imgSrc;
};
Above, artistToUrl is a huge object filled with base64 string which I will not paste here to overflow the post. But you can see it under the JS tab of the example page. It has such structure:
var artistToUrl = { 'bob-dylan': 'data:image/jpeg;base64,/...',...}
Working example:
For demonstration, I prepare a simple example here (click to try):
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
</head>
<body>
<iframe id="plot" style="width: 900px; height: 600px;" src="https://plot.ly/~jackp/10816.embed" seamless></iframe>
<div id="myDiv"></div>
<script>
(function main() {
var Plot = { id: 'plot', domain: 'https://plot.ly' };
Plot.onHover = function(message) {
var y = message.points[0].y; /*** y value of the data point(bar) under hover ***/
var line1 = {
x: [0.25,0.5,1], /*** dummy x array in popup-chart ***/
y: [1/y, 2, y], /*** dummy y array in popup-chart ***/
mode: 'lines+markers'
};
var layout = {
title:'Popup graph on hover',
height: 400,
width: 480
};
Plotly.newPlot('myDiv', [ line1 ], layout); // this finally draws your popup-chart
};
Plot.init = function init() {
var pinger = setInterval(function() {
Plot.post({task: 'ping'});
}, 500);
function messageListener(e) {
var message = e.data;
if(message.pong) {
console.log('Initial pong, frame is ready to receive');
clearInterval(pinger);
Plot.post({
'task': 'listen',
'events': ['hover']
});
}
else if(message.type === 'hover') {
Plot.onHover(message);
}
}
window.removeEventListener('message', messageListener);
window.addEventListener('message', messageListener);
};
Plot.post = function post(o) {
document.getElementById(Plot.id).contentWindow.postMessage(o, Plot.domain);
};
Plot.init();
})();
</script>
</body>
</html>
This is modified from the poltly.hover-events example for python. Instead of poping up an image, I change the onhover callback to plot a curve based on the y value of the each bar.
The main chart is generated by python and inserted here as iframe. You can make your own by any language including R. In this page we add a <div id="myDiv"></div> and use the plotly.js to draw the popup-chart whithin it.
Export R data frame to JS enviornment
Shiny uses jsonlite to convert R objects to json and send them to the client. We can use the same mechanism to pack and send our data frame so that the JS callback can use the data to render the popup chart.
server.r
output$json <- reactive({
paste('<script>data =', RJSONIO::toJSON(your_data_frame, byrow=T, colNames=T),'</script>')
ui.r
fluidPage(..., htmlOutput("json"), ...)
In the JS callback function, you can use data as any other JS objects.
More detail goes here and here.
If you want to stick with R you could use Shiny to get almost the result you want. When you hover each point an image will be render under the main plot. For the example below, I used the first three rows of the mtcars datasets. To run the code, you only need 3 logos/images corresponding to the name of the first three rows (under mtcars$name, Mazda RX4, Mazda RX4 Wag, Datsun 710 in this example).
library(shiny)
library(plotly)
datatest <- diamonds %>% count(cut)
datatest$ImageNumber <- c(0, 1, 2, 3, 4)
datatest$name <- c("Image0", "Image1", "Image2", "Image3", "Image4")
ui <- fluidPage(
plotlyOutput("plot"),
# verbatimTextOutput("hover2"),
#imageOutput("hover"),
plotlyOutput("hover3")
)
server <- function(input, output, session) {
output$plot <- renderPlotly({
plot_ly(datatest, x = cut, y = n, type = "bar", marker = list(color = toRGB("black")))
})
selected_image <- reactive({
eventdat <- event_data('plotly_hover', source = 'A')
ImagePick <- as.numeric(eventdat[['pointNumber']])
sub <- datatest[datatest$ImageNumber %in% ImagePick, ]
return(sub)
})
# output$hover2 <- renderPrint({
#d <- event_data("plotly_hover")
#if (is.null(d)) "Hover events appear here (unhover to clear)" else d
#})
# output$hover <- renderImage({
# datag <- selected_image()
#filename <- normalizePath(file.path('/Users/drisk/Desktop/temp',
# paste(datag$name, '.png', sep='')))
# Return a list containing the filename and alt text
# list(src = filename,
# alt = paste("Image number", datag$name))
# }, deleteFile = FALSE)
output$hover3 <- renderPlotly({
datag <- selected_image()
# draw plot according to the point number on hover
plot_ly(data=datag, x = ImageNumber, y = n, mode = "scatter")
})
}
shinyApp(ui, server)
Seems the answers posted aren't working for you #Adam_G. I have been exploring similar libraries for my own work and determined that Plot.ly is not always the right path when you want advanced features. Have you seen bokeh? It is basically designed for this type of task and much easier to implement (also a D3.js library like Plot.ly). Here is a copy of an example they posted where you can move a slider to change a graph of data (similar to the example posted by #gdlmx for Plot.ly but you can use it without hosting it on a website). I added the flexx package so you can use this writing pure Python (no JavaScript - it can translate Python functions to JavaScript (CustomJS.from_py_func(callback)) https://github.com/zoofIO/flexx-notebooks/blob/master/flexx_tutorial_pyscript.ipynb):
from bokeh.io import vform
from bokeh.models import CustomJS, ColumnDataSource, Slider
from bokeh.plotting import figure, output_file, show
import flexx
output_file("callback.html")
x = [x*0.005 for x in range(0, 200)]
y = x
source = ColumnDataSource(data=dict(x=x, y=y))
plot = figure(plot_width=400, plot_height=400)
plot.line('x', 'y', source=source, line_width=3, line_alpha=0.6)
def callback(source=source):
data = source.get('data')
f = cb_obj.get('value') #this is the bokeh callback object, linked to the slider below
x, y = data['x'], data['y']
for i in range(len(x)):
y[i] = x[i]**f #the slider value passed to this function as f will alter chart as a function of x and y
source.trigger('change') #as the slider moves, the chart will change
slider = Slider(start=0.1, end=4, value=1, step=.1, title="power", callback=CustomJS.from_py_func(callback))
layout = vform(slider, plot)
show(layout)
See here for the actual example in action: http://docs.bokeh.org/en/0.10.0/docs/user_guide/interaction.html#customjs-for-widgets
To integrate with hover events see here ( from bokeh.models import HoverTool):
http://docs.bokeh.org/en/0.10.0/docs/user_guide/interaction.html#customjs-for-hover
Hover example:
from bokeh.plotting import figure, output_file, show, ColumnDataSource
from bokeh.models import HoverTool
output_file("toolbar.html")
source = ColumnDataSource(
data=dict(
x=[1, 2, 3, 4, 5],
y=[2, 5, 8, 2, 7],
desc=['A', 'b', 'C', 'd', 'E'],
)
)
hover = HoverTool(
tooltips=[
("index", "$index"),
("(x,y)", "($x, $y)"),
("desc", "#desc"),
]
)
p = figure(plot_width=400, plot_height=400, tools=[hover], title="Mouse over the dots")
p.circle('x', 'y', size=20, source=source)
show(p)
Looking at the 1st code you could put whatever formula you want under the def callback function - some playing around required. You can get the hover to alter a graph next to it (hform(leftchart, rightchart) or above / below it (vform(topchart, bottomchart)). This is passed as CustomJS which bokeh uses to allow extendability and flexx allows you to write it in Python.
The alternative is to put whatever you want customized on the hover tooltips using HTML (although this example is placing images in dictionaries instead of new plots from the underlying data): http://docs.bokeh.org/en/0.10.0/docs/user_guide/tools.html#custom-tooltip