skip to Main Content

I have used bokeh to generate 400 graphs and saved them into 400 html-files (file_1.htmlfile_400.html) on my local drive of my Mac.

An example of the codes that I used to generate a graph and save it is below

import numpy as np

from bokeh.plotting import figure, output_file, save

p = figure(plot_width=400, plot_height=400)

x = np.arange(1, 1000)  # all 400 graphs have the same x
y1 = np.arange(1, 1000)*2  # different file can have different y
p.line(x, y1, line_width=2)

output_file('file_1.html')
save(p)

I need to view the 400 html-files one by one, and I am interested only in a zoomed-in view of each graph, meaning the last 100 points of each graph. Note that the curve in each graph has to be viewed by me (due to my expertise), so I cannot use things like artificial intelligence to view the graphs for me.

What I can do now, is:

  1. open the folder containing these 400 html-files
  2. double click one file then it will be opened with safari web-browser
  3. click the zoom-in button defined by bokeh
  4. find the area of the last 100 points and drag a rectangle by mouse to zoom-in
  5. close this file
  6. repeat the above 5 steps for another 399 times.

This approach is very time-consuming and boring.

Do you have better ways to go through all these files?

One preferred feature is that I can open them all in a window, they are automatically zoomed-in, and I just need to hit the button of left-arrow and right-arrow on my keyboard to navigate through the graphs.

Looking forward to your help and thanks!

2

Answers


  1. Alright, let’s see how we can do this. My first thought is, this could be accomplished through selenium. I’m going to assume that you haven’t used it before. In short, it’s a way to programmatically do things with a browser.

    Let’s get started with that! Install the python library

    pip install selenium
    

    You’ll also need to install geckodriver (we’ll use firefox in this example). If you’re on osx you can install that with brew.

    brew install geckodriver
    

    Then we can start writing our script to open 400 tabs! It’ll open all the figures that you have locally. I’ll leave it up to you how to figure out how to zoom. The documentation for selenium can be found here ->

    (the script uses python 3, and pathlib only exists in python 3)

    from pathlib import Path
    from selenium import webdriver
    from selenium.webdriver.common.keys import Keys
    from selenium.webdriver.common.action_chains import ActionChains
    
    html_path = Path.cwd()
    browser = webdriver.Firefox()
    
    for no in range(1, 400):
        (ActionChains(browser).key_down(Keys.CONTROL)
                              .send_keys('t')
                              .key_up(Keys.CONTROL)
                              .perform())
        file_path = html_path / 'file_1.html'
        browser.get('file://' + str(file_path))
    
    Login or Signup to reply.
  2. This actually seems like a perfect use case for a little Bokeh server application you can run locally. You can put the code in a file app.py then run bokeh serve --show app.py at the command line.

    import numpy as np
    from bokeh.io import curdoc
    from bokeh.models import Button, ColumnDataSource, TextInput
    from bokeh.layouts import widgetbox, row
    from bokeh.plotting import figure
    
    current = 0
    
    x = np.linspace(0, 20, 500)
    y = np.sin(x)    
    source = ColumnDataSource(data=dict(x=x, y=y))
    
    plot = figure(x_range=(10,20), title="Plot 0")
    plot.line('x', 'y', source=source)
    
    def update_data(i):
        global current
        current = i
    
        # compute new data or load from file, etc
        source.data = dict(x=x, y = np.sin(x*(i+1)))
        plot.title.text = "Plot %d" % i
    
    def update_range(attr, old, new):
        plot.x_range.start = float(start.value)
        plot.x_range.end = float(end.value)
    
    start = TextInput(title="start", value="10")
    start.on_change('value', update_range)
    end = TextInput(title="start", value="20")
    end.on_change('value', update_range)
    
    next = Button(label="next")
    next.on_click(lambda: update_data(current+1))
    prev = Button(label="prev")
    prev.on_click(lambda: update_data(current-1))
    
    curdoc().add_root(row(widgetbox(start, end, next, prev), plot))
    

    This could be improved with some error handling and maybe some additional bells and whistles, but is hopefully demonstrative. It yields the interactive app below:


    enter image description here

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search