Bokeh is an interactive data visualization library for Python that targets web browsers for presentation. It can create versatile, data-driven graphics, and connect Python data-science stack to rich and interactive visualizations.
Here we'll demonstrate formats we can use with Bokeh, generate some basic plots, and begin customizations for selecting data.
from bokeh.io import output_file, show, output_notebook, reset_output
from bokeh.plotting import figure
reset_output()
output_notebook()
plot = figure(plot_width=400, plot_height = 400, tools='pan,box_zoom') # can also do plot_height vs. width
x = [1,2,3,4,5]
y = [8,6,5,2,3]
sizes = [10,20,30,40,50]
#plot both lines and circles together
plot.circle(x, y, size=sizes, fill_color = 'red')
plot.line(x, y, line_width=2)
show(plot)
In this example, we'll make a scatter plot of female literacy vs fertility using data from the European Environmental Agency.
This dataset highlights that countries with low female literacy have high birthrates.
The x-axis data will be fertility
and the y-axis data will be female_literacy
.
We will plot female_literacy
vs fertility
using the circle glyph.
Note that the right side options allow us to Pan, Box Zoom, and Wheel Zoom.
#import the data
import pandas as pd
fertility = pd.read_csv('data/fertility.csv').fertility
female_literacy = pd.read_csv('data/female_literacy.csv').female_literacy
# Import figure from bokeh.plotting
from bokeh.plotting import figure
# Import output_file and show from bokeh.io
from bokeh.io import show, output_notebook, reset_output #output_file
# Create the figure: p
p = figure(x_axis_label='fertility (children per woman)', y_axis_label='female_literacy (% population)')
# Add a circle glyph to the figure p
p.circle(fertility, female_literacy)
# Call the output_file() function and specify the name of the file
#output_file('fert_lit.html')
# Change from file to notebook inline:
output_notebook()
# Display the plot
show(p)
By calling multiple glyph functions on the same figure object, we can overlay multiple data sets in the same figure. We can customize the shape, color, size, and transparency (alpha) of the markers.
We make the same plot as before of female literacy vs fertility for two different regions, Africa and Latin America.
fertility_latin_america = pd.read_csv('data/fertility_latin_america.csv').fertility_latin_america
female_literacy_latin_america = pd.read_csv('data/female_literacy_latin_america.csv').female_literacy_latin_america
fertility_africa = pd.read_csv('data/fertility_africa.csv').fertility_africa
female_literacy_africa = pd.read_csv('data/female_literacy_africa.csv').female_literacy_africa
# Create the figure: p
p = figure(x_axis_label='fertility', y_axis_label='female_literacy (% population)')
# Add a circle glyph to the figure p
p.circle(fertility_latin_america, female_literacy_latin_america, color = 'blue', size = 10, alpha = 0.8)
# Add an x glyph to the figure p
p.x(fertility_africa, female_literacy_africa, color = 'red', size = 10, alpha = 0.8)
# Specify inline output
output_notebook()
# Display the plot
show(p)
#pd.DataFrame(female_literacy_africa, columns = ['female_literacy_africa']).to_csv('data/female_literacy_africa.csv',index = False)
import pandas as pd
from bokeh.palettes import Category10_4
from bokeh.plotting import figure, output_file, show
#import data
stocks = pd.read_csv('data/stocks.csv', parse_dates = [0], infer_datetime_format = True)
date = list(map(lambda x:x.to_pydatetime(), stocks.Date.tolist()))
#create figure
p = figure(plot_width=800, plot_height=250, x_axis_type="datetime")
p.title.text = 'Stock Data with interactive Legend'
#recursively plot each stock
for data, name, color in zip([stocks.AAPL, stocks.IBM, stocks.MSFT, stocks.CSCO], ["AAPL", "IBM", "CSCO", "MSFT"], Category10_4):
p.line(date, data, line_width=2, color=color, alpha=0.8, legend=name)
p.legend.location = "top_left"
p.legend.click_policy="hide"
show(p)
In Bokeh, extended geometrical shapes can be plotted by using the patches()
glyph function. The patches glyph takes as input a list-of-lists collection of numericvalues specifying the vertices in x and y directions of each distinct patch toplot.
In this exercise, we will plot the state borders of Arizona, Colorado, New Mexico and Utah.
from bokeh.sampledata.us_states import data as states
p = figure(x_axis_label='longitude (degrees)', y_axis_label='latitude (degrees)')
# Create a list of longitudes for Arizona, Colorado, New Mexico, and Utah.
x = [states['AZ']['lons'], states['CO']['lons'], states['NM']['lons'], states['UT']['lons']]
# Create a list of Latitudes for the same states
y = [states['AZ']['lats'], states['CO']['lats'], states['NM']['lats'], states['UT']['lats']]
# Add patches to figure p with line_color=white for x and y
p.patches(x, y, line_color = 'white')
# Specify the name of the output file if external output is needed and show the result
#output_file('four_corners.html')
show(p)
from bokeh.io import output_file, show
from bokeh.plotting import figure
x = [1,2,3,4,5]
y = [8,6,5,2,3]
plot = figure()
plot.line(x, y, line_width=3)
plot.circle(x, y, fill_color='white', size=10)
show(plot)
from bokeh.io import output_file, show
from bokeh.plotting import figure
import numpy as np
x = np.linspace(0, 10, 1000)
y = np.sin(x) + np.random.random(1000) * 0.2
plot = figure()
plot.line(x, y)
show(plot)
p = figure()
# Create array using np.linspace: x
x = np.linspace(0, 5, 100)
# Create array using np.cos: y
y = np.cos(x)
# Add circles at x and y
p.circle(x, y)
# Show the result
show(p)
You can create Bokeh plots from Pandas DataFrames by passing column selections to the glyph functions.
Bokeh can plot floating point numbers, integers, and datetime data types. In this example, we will read a CSV file containing information on 392 automobiles manufactured in the US, Europe and Asia from 1970 to 1982.
We will plot miles-per-gallon (mpg
) vs horsepower (hp
) by
passing Pandas column selections into the p.circle()
function. Additionally, each
glyph will be colored according to values in the color
column.
# Import pandas as pd
import pandas as pd
# Read in the CSV file: df
cars = pd.read_csv('data/auto.csv')
colors = pd.DataFrame({'origin':['US', 'Asia', 'Europe'], 'color':['blue', 'red','green']})
df = cars.merge(colors, on = 'origin')
# Import figure from bokeh.plotting
from bokeh.plotting import figure
# Create the figure: p
p = figure(x_axis_label='HP', y_axis_label='MPG')
# Plot mpg vs hp by color
p.circle(df['hp'], df['mpg'], color = df['color'], size = 10)
# Show the result
show(p)
The ColumnDataSource
is a table-like data object that maps string column names to sequences (columns) of data. It is the central and most common data structure in Bokeh. It is important that:
ColumnDataSource
must have the same length.ColumnDataSource
objects can be shared between different plots.ColumnDataSource
objects are not interchangeable with Pandas DataFrames. However, you can create a ColumnDataSource
object directly from a Pandas DataFrame by passing the DataFrame to the class initializer. from bokeh.models import ColumnDataSource
source = ColumnDataSource(data={
'x': [1,2,3,4,5],
'y': [8,6,5,2,3]})
source.data
We will read in a data set containing all Olympic medals awarded in the 100 meter sprint from 1896 to 2012.
A color
column has been added indicating the CSS colorname we wish to use in the plot for every data point.
We will create a new ColumnDataSource
object from the DataFrame df
, and plot circle glyphs with 'Year'
on the x-axis and
'Time'
on the y-axis. We will also color each glyph by the color
column.
# Read in the CSV file: df
df = pd.read_csv('data/sprint.csv')
# Create the figure: p
p = figure(x_axis_label='Year', y_axis_label='Time')
# Import the ColumnDataSource class from bokeh.plotting
from bokeh.plotting import ColumnDataSource
# Create a ColumnDataSource from df: source
source = ColumnDataSource(df)
# Add circle glyphs to the figure p
p.circle('Year', 'Time', source = source, color = 'color', size = 8)
# Show the result
show(p)
In this demonstration, you're going to add the box_select
tool to a figure
and change the selected and non-selected circle glyph properties so
that selected glyphs are red and non-selected glyphs are transparent blue.
Feel free to experiment with the Box Select tool
# Create a figure with the "box_select" tool: p
p = figure(x_axis_label = 'Year', y_axis_label = 'Time', tools = 'box_select')
# Add circle glyphs to the figure p with the selected and non-selected properties
p.circle('Year', 'Time', source = source, selection_color = 'red', nonselection_alpha = 0.1)
# Show the result
show(p)
In this demonstration, we'll plot blood glucose levels recorded every 5 minutes.
x
will be date and time while blood glucose levels in mg/dL will be the vertical dimension.
Here, instead of selecting, simply hovering near datapoints will cause the points to change to red.
#Data import
df = pd.read_csv('data/glucose.csv')
df.head()
x = df.index.values
y = df.glucose.values
# import the HoverTool
from bokeh.models import HoverTool
p=figure(x_axis_type='datetime', x_axis_label = 'Time of Day', y_axis_label = 'Blood Glucose (mg/dL)', title = 'Blood Glucose')
p.line(x, y, line_color = 'black', line_dash = 'dashed')
# Add circle glyphs to figure p
p.circle(x, y, size=10,
fill_color='grey', alpha=0.1, line_color=None,
hover_fill_color='firebrick', hover_alpha=0.5,
hover_line_color='white')
# Create a HoverTool: hover
hover = HoverTool(tooltips = None, mode = 'vline') #any point in a vertical line with the cursor
# Add the hover tool to the figure p
p.add_tools(hover)
# Show the result
show(p)
For our colormapping demo, we'll use the automobile dataset to plot miles-per-gallon vs weight and color each circle glyph by the region where the automobile was manufactured.
df = pd.read_csv('data/auto-mpg-color.csv')
#Import CategoricalColorMapper from bokeh.models
from bokeh.models import CategoricalColorMapper
from bokeh.models import HoverTool
hover = HoverTool(tooltips=[
('Name', '@name'),
('Year', '@yr'),
('Cylinder', '@cyl'),
])
p = figure(tools=[hover, 'pan', 'wheel_zoom'],
x_axis_type='datetime',
x_axis_label = 'weight (lbs)',
y_axis_label = 'miles-per-gallon',
title = 'Efficiency')
# Convert df to a ColumnDataSource: source
source = ColumnDataSource(df)
# Make a CategoricalColorMapper object: color_mapper
color_mapper = CategoricalColorMapper(factors=['Europe', 'Asia', 'US'],
palette=['red', 'green', 'blue'])
# Add a circle glyph to the figure p
p.circle(x = 'weight',
y = 'mpg',
source=source,
color=dict(field = 'origin',
transform = color_mapper),
legend='origin')
# Show the result
show(p)
Here we'll demonstrate how to combine mutiple Bokeh plots into different kinds of layouts on a page, how to easily link different plots together in various ways, and how to add annotations such as legends and hover tooltips.
Here we'll demonstrate rows of plots with the Literacy and Birth Rate data set to plot fertility vs female literacy and population vs female literacy.
By using the row()
method, you'll create a single layout of the two figures.
df = pd.read_csv('data/literacy_birth_rate.csv', nrows = 162)
df.columns = ['Country ', 'Continent', 'female_literacy', 'fertility', 'population']
source = ColumnDataSource(df)
# Import row from bokeh.layouts
from bokeh.layouts import row
# Create the first figure: p1
p1 = figure(x_axis_label='fertility (children per woman)', y_axis_label='female_literacy (% population)', plot_width = 400)
# Add a circle glyph to p1
p1.circle('fertility', 'female_literacy', source=source)
# Create the second figure: p2
p2 = figure(x_axis_label='population', y_axis_label='female_literacy (% population)', plot_width = 400)
# Add a circle glyph to p2
p2.circle('population', 'female_literacy', source = source)
# Put p1 and p2 into a horizontal row: layout
layout = row(p1, p2)
# Show the result
show(layout)
Here we'll demo how to use the column()
function to create a single column layout of the same two plots as above.
# Import column from the bokeh.layouts module
from bokeh.layouts import column
# Create a blank figure: p1
p1 = figure(x_axis_label='fertility (children per woman)', y_axis_label='female_literacy (% population)', plot_height = 400)
# Add circle scatter to the figure p1
p1.circle('fertility', 'female_literacy', source=source)
# Create a new blank figure: p2
p2 = figure(x_axis_label='population', y_axis_label='female_literacy (% population)', plot_height = 400)
# Add circle scatter to the figure p2
p2.circle('population', 'female_literacy', source=source)
# Put plots p1 and p2 in a column: layout
layout = column(p1, p2)
# Show the result
show(layout)
We can combine row and column layouts to create nested layouts of plots.
We can add in the sizing_mode
argument to scale the widths to fill the whole figure.
#import data
df = pd.read_csv('data/auto-mpg-color.csv')
avg_mpg_df = df.groupby('yr').mean()
avg_mpg_df['mpg']
#Import CategoricalColorMapper from bokeh.models
from bokeh.models import CategoricalColorMapper
from bokeh.models import HoverTool
source = ColumnDataSource(df)
hover = HoverTool(tooltips=[
('Name', '@name'),
('Year', '@yr'),
('Cylinder', '@cyl'),
])
mpg_hp = figure(tools=[hover, 'pan', 'wheel_zoom'], x_axis_label = 'miles-per-gallon', y_axis_label = 'hp')
mpg_weight = figure(tools=[hover, 'pan', 'wheel_zoom'], x_axis_label = 'miles-per-gallon', y_axis_label = 'weight')
avg_mpg = figure(tools=[hover, 'pan', 'wheel_zoom'], x_axis_label = 'yr', y_axis_label = 'avg-miles-per-gallon')
mpg_hp.circle('mpg', 'hp', source=source,
color=dict(field = 'origin', transform = color_mapper),
legend='origin')
mpg_weight.circle('mpg', 'weight', source=source,
color=dict(field = 'origin', transform = color_mapper),
legend='origin')
avg_mpg.line(avg_mpg_df.index,avg_mpg_df['mpg'])
# Import column and row from bokeh.layouts
from bokeh.layouts import column, row
# Make a column layout that will be used as the second row: row2
row2 = column([mpg_hp, mpg_weight], sizing_mode='scale_width')
# Make a row layout that includes the above column layout: layout
layout = row([avg_mpg, row2], sizing_mode='scale_width')
# Show the result
show(layout)
from bokeh.layouts import gridplot
layout = gridplot([[avg_mpg, None], [mpg_weight, mpg_hp]],
toolbar_location=None)
show(layout)
Regular grids of Bokeh plots can be generated with gridplot
.
In this example, you're going to display four plots of fertility vs female literacy for four regions: Latin America, Africa, Asia and Europe.
Your job is to create a list-of-lists for the four Bokeh plots that have been provided to you as p1
, p2
, p3
and p4
.
The list-of-lists defines the row and column placement of each plot.
Import gridplot
from the bokeh.layouts
module.
Create a list called row1
containing plots p1
and p2
.
Create a list called row2
containing plots p3
and p4
.
Create a gridplot using row1
and row2
. You will have to pass in row1
and row2
in the form of a list.
#import data
from bokeh.models import ColumnDataSource, CDSView, BooleanFilter
from bokeh.plotting import figure
df = pd.read_csv('data/literacy_birth_rate.csv', nrows = 162)
source = ColumnDataSource(df)
#create four filters
latin_view = CDSView(source=source, filters=[BooleanFilter(df.Continent == 'LAT')])
africa_view = CDSView(source=source, filters=[BooleanFilter(df.Continent == 'AF')])
asia_view = CDSView(source=source, filters=[BooleanFilter(df.Continent == 'ASI')])
europe_view = CDSView(source=source, filters=[BooleanFilter(df.Continent == 'EUR')])
#desired tools
tools = ["box_select", "hover", "reset", 'pan','wheel_zoom']
#create the four figures
p1 = figure(x_axis_label = 'fertility (children per woman)', y_axis_label = 'female literacy (% population)', tools=tools, title = 'Latin', plot_width=300, plot_height=300)
p1.circle(x='fertility', y='female literacy', size=10, hover_color="red", source=source, view=latin_view)
p2 = figure(x_axis_label = 'fertility (children per woman)', y_axis_label = 'female literacy (% population)', tools=tools, title = 'Africa', plot_width=300, plot_height=300)
p2.circle(x='fertility', y='female literacy', size=10, hover_color="red", source=source, view=africa_view)
p3 = figure(x_axis_label = 'fertility (children per woman)', y_axis_label = 'female literacy (% population)', tools=tools, title = 'Asia', plot_width=300, plot_height=300)
p3.circle(x='fertility', y='female literacy', size=10, hover_color="red", source=source, view=asia_view)
p4 = figure(x_axis_label = 'fertility (children per woman)', y_axis_label = 'female literacy (% population)', tools=tools, title = 'Europe', plot_width=300, plot_height=300)
p4.circle(x='fertility', y='female literacy', size=10, hover_color="red", source=source, view=europe_view)
# Import gridplot from bokeh.layouts
from bokeh.layouts import gridplot
# Create a list containing plots p1 and p2: row1
row1 = [p1, p2]
# Create a list containing plots p3 and p4: row2
row2 = [p3, p4]
# Create a gridplot using row1 and row2: layout
layout = gridplot([row1, row2])
# Show the result
show(layout)
Great work! In the next exercise, you will use these panels to build and display a tabbed layout.
Tabbed layouts can be created in Bokeh by placing plots or layouts in Panels. In essence, they are collections of Panel objects.
In this demo, we'll take the four fertility vs female literacy plots from the last demo and make a Panel()
for each.
Then we'll create a tabbed layout to change the region in the fertility vs female literacy plots.
Feel free to explore the tabs and "Pan", "Box Zoom" and "Wheel Zoom" tools are also all available as before.
# Import Panel from bokeh.models.widgets
from bokeh.models.widgets import Panel
# Create tab1 from plot p1: tab1
tab1 = Panel(child=p1, title='Latin America')
# Create tab2 from plot p2: tab2
tab2 = Panel(child=p2, title='Africa')
# Create tab3 from plot p3: tab3
tab3 = Panel(child=p3, title='Asia')
# Create tab4 from plot p4: tab4
tab4 = Panel(child=p4, title='Europe')
# Import Tabs from bokeh.models.widgets
from bokeh.models.widgets import Tabs
# Create a Tabs layout: layout
tab_layout = Tabs(tabs=[tab1, tab2, tab3, tab4])
# Specify the name of the output_file and show the result
#output_file('tabs.html')
show(tab_layout)
Linking axes between plots is achieved by sharing range
objects.
Here, we'll link four plots of female literacy vs fertility so that when one plot is zoomed or dragged, one or more of the other plots will respond.
# Link the x_range of p2 to p1: p2.x_range
p2.x_range = p1.x_range
# Link the y_range of p2 to p1: p2.y_range
p2.y_range = p1.y_range
# Link the x_range of p3 to p1: p3.x_range
p3.x_range = p1.x_range
# Link the y_range of p4 to p1: p4.y_range
p4.y_range = p1.y_range
# Specify the name of the output_file and show the result
#output_file('linked_range.html')
show(layout)
By sharing the same ColumnDataSource
object between multiple plots,
selection tools like BoxSelect and LassoSelect will highlight points
in both plots that share a row in the ColumnDataSource.
To demonstrate, we'll plot female literacy vs fertility and population vs fertility in two plots using the same ColumnDataSource.
Use the lasso or box select tool to see how selecting data in one plot also selects the same data in another.
from bokeh.layouts import row
# Create ColumnDataSource: source
source = ColumnDataSource(df)
# Create the first figure: p1
p1_linked = figure(x_axis_label='fertility (children per woman)', y_axis_label='female literacy (% population)',
tools=['box_select', 'lasso_select'], plot_width = 400, plot_height = 400)
# Add a circle glyph to p1
p1_linked.circle('fertility', 'female literacy', source = source)
# Create the second figure: p2
p2_linked = figure(x_axis_label='fertility (children per woman)', y_axis_label='population (millions)',
tools=['box_select', 'lasso_select'], plot_width = 400, plot_height = 400)
# Add a circle glyph to p2
p2_linked.circle('fertility', 'population', source = source)
# Create row layout of figures p1 and p2: layout
layout_linked = row(p1_linked, p2_linked)
# Show the result
show(layout_linked)
Legends can be added to any glyph by using the legend
keyword argument.
#basic setup
tools = ['box_zoom', 'wheel_zoom', 'pan', 'save', 'reset', 'help']
p = figure(x_axis_label = 'fertility (children per woman)', y_axis_label = 'female_literacy (%population)', tools = tools)
latin_america = ColumnDataSource(df.loc[df.Continent == 'LAT'])
africa = ColumnDataSource(df.loc[df.Continent == 'AF'])
# Add the first circle glyph to the figure p
p.circle('fertility', 'female literacy', source=latin_america, size=10, color='red', legend='Latin America')
# Add the second circle glyph to the figure p
p.circle('fertility', 'female literacy', source=africa, size=10, color='blue', legend='Africa')
# Show the result
show(p)
Properties of the legend can be changed by using the legend
member attribute of a Bokeh figure after the glyphs have been plotted.
Here we'll change the location and background color of the legend
# Assign the legend to the bottom left: p.legend.location
p.legend.location = 'bottom_left'
# Fill the legend background with the color 'lightgray': p.legend.background_fill_color
p.legend.background_fill_color = 'lightgray'
# Specify the name of the output_file and show the result
#output_file('fert_lit_groups.html')
show(p)
HoverTool
object and display the country for each circle glyph in the figure that we created in the last exercise. tooltips
keyword argument to a list-of-tuples specifying the label and the column of values from the ColumnDataSource using the @
operator. # Import HoverTool from bokeh.models
from bokeh.models import HoverTool
# Create a HoverTool object: hover
hover = HoverTool(tooltips = [('Country', '@Country')])
# Add the HoverTool object to figure p
p.add_tools(hover)
# Show the result
show(p)
Bokeh server applications let you connect all of the powerful Python libraries for analytics and data science, such as NumPy and Pandas, to rich interactive Bokeh visualizations.
We will also learn about Bokeh's built-in widgets, how to add them to Bokeh documents alongside plots, and how to connect everything to real python code using the Bokeh server.
Bokeh server will automatically keep every property of any Bokeh object in sync.
outline.py
from bokeh.io import curdoc
# Create plots and widgets
# Add callbacks
# Arrange plots and widgets in layouts
curdoc().add_root(layout)
Run single module apps at the shell or Windows command prompt:
bokeh serve --show myapp.py
“Directory” style apps run similarly:
bokeh serve --show myappdir/
Building an interactive Bokeh app typically begins with importing the curdoc
, or "current document", function from bokeh.io
. This current document will eventually hold all the plots, controls, and layouts we create.
# Perform necessary imports
from bokeh.io import curdoc
from bokeh.plotting import figure
# Create a new plot: plot
plot = figure()
# Add a line to the plot
plot.line(x = [1,2,3,4,5], y = [2,5,4,6,7])
# Add the plot to the current document
curdoc().add_root(plot)
show(plot)
# Perform the necessary imports
from bokeh.io import curdoc
from bokeh.layouts import widgetbox
from bokeh.models import Slider
# Create first slider: slider1
slider1 = Slider(title='slider1', start=0, end=10, step=0.1, value=2)
# Create second slider: slider2
slider2 = Slider(title='slider2', start=10, end=100, step=1, value=20)
# Add slider1 and slider2 to a widgetbox
layout = widgetbox(slider1, slider2)
# Add the layout to the current document
curdoc().add_root(layout)
show(layout)
A slider example
from bokeh.io import curdoc
from bokeh.layouts import column
from bokeh.models import ColumnDataSource, Slider
from bokeh.plotting import figure
from numpy.random import random
N = 300
source = ColumnDataSource(data={'x': random(N), 'y': random(N)})
# Create plots and widgets
plot = figure()
plot.circle(x= 'x', y='y', source=source)
slider = Slider(start=100, end=1000, value=N,
step=10, title='Number of points')
# Add callback to widgets
def callback(attr, old, new):
N = slider.value
source.data={'x': random(N), 'y': random(N)}
slider.on_change('value', callback)
# Arrange plots and widgets in layouts
layout = column(slider, plot)
curdoc().add_root(layout)
show(layout)
Callbacks are functions that a user can define, like def callback(attr, old, new)
, that can be called automatically when some property of a Bokeh object (e.g., the value
of a Slider
) changes.
A callback is added by calling myslider.on_change('value', callback).
A simple Bokeh application would have a slider, plot, and also updates the plot based on the slider.
#Imports
from bokeh.io import curdoc
from bokeh.layouts import column
from bokeh.models import ColumnDataSource, Slider
from bokeh.plotting import figure
from numpy.random import random
import numpy as np
from bokeh.layouts import widgetbox
x = np.asarray([0.3, 0.33244147, 0.36488294, 0.39732441, 0.42976589, 0.46220736, 0.49464883, 0.5270903 , 0.55953177, 0.59197324, 0.62441472, 0.65685619, 0.68929766, 0.72173913, 0.7541806 , 0.78662207, 0.81906355, 0.85150502, 0.88394649, 0.91638796, 0.94882943, 0.9812709 , 1.01371237, 1.04615385, 1.07859532, 1.11103679, 1.14347826, 1.17591973, 1.2083612 , 1.24080268, 1.27324415, 1.30568562, 1.33812709, 1.37056856, 1.40301003, 1.43545151, 1.46789298, 1.50033445, 1.53277592, 1.56521739, 1.59765886, 1.63010033, 1.66254181, 1.69498328, 1.72742475, 1.75986622, 1.79230769, 1.82474916, 1.85719064, 1.88963211, 1.92207358, 1.95451505, 1.98695652, 2.01939799, 2.05183946, 2.08428094, 2.11672241, 2.14916388, 2.18160535, 2.21404682, 2.24648829, 2.27892977, 2.31137124, 2.34381271, 2.37625418, 2.40869565, 2.44113712, 2.4735786 , 2.50602007, 2.53846154, 2.57090301, 2.60334448, 2.63578595, 2.66822742, 2.7006689 , 2.73311037, 2.76555184, 2.79799331, 2.83043478, 2.86287625, 2.89531773, 2.9277592 , 2.96020067, 2.99264214, 3.02508361, 3.05752508, 3.08996656, 3.12240803, 3.1548495 , 3.18729097, 3.21973244, 3.25217391, 3.28461538, 3.31705686, 3.34949833, 3.3819398 , 3.41438127, 3.44682274, 3.47926421, 3.51170569, 3.54414716, 3.57658863, 3.6090301 , 3.64147157, 3.67391304, 3.70635452, 3.73879599, 3.77123746, 3.80367893, 3.8361204 , 3.86856187, 3.90100334, 3.93344482, 3.96588629, 3.99832776, 4.03076923, 4.0632107 , 4.09565217, 4.12809365, 4.16053512, 4.19297659, 4.22541806, 4.25785953, 4.290301 , 4.32274247, 4.35518395, 4.38762542, 4.42006689, 4.45250836, 4.48494983, 4.5173913 , 4.54983278, 4.58227425, 4.61471572, 4.64715719, 4.67959866, 4.71204013, 4.74448161, 4.77692308, 4.80936455, 4.84180602, 4.87424749, 4.90668896, 4.93913043, 4.97157191, 5.00401338, 5.03645485, 5.06889632, 5.10133779, 5.13377926, 5.16622074, 5.19866221, 5.23110368, 5.26354515, 5.29598662, 5.32842809, 5.36086957, 5.39331104, 5.42575251, 5.45819398, 5.49063545, 5.52307692, 5.55551839, 5.58795987, 5.62040134, 5.65284281, 5.68528428, 5.71772575, 5.75016722, 5.7826087 , 5.81505017, 5.84749164, 5.87993311, 5.91237458, 5.94481605, 5.97725753, 6.009699 , 6.04214047, 6.07458194, 6.10702341, 6.13946488, 6.17190635, 6.20434783, 6.2367893 , 6.26923077, 6.30167224, 6.33411371, 6.36655518, 6.39899666, 6.43143813, 6.4638796 , 6.49632107, 6.52876254, 6.56120401, 6.59364548, 6.62608696, 6.65852843, 6.6909699 , 6.72341137, 6.75585284, 6.78829431, 6.82073579, 6.85317726, 6.88561873, 6.9180602 , 6.95050167, 6.98294314, 7.01538462, 7.04782609, 7.08026756, 7.11270903, 7.1451505 , 7.17759197, 7.21003344, 7.24247492, 7.27491639, 7.30735786, 7.33979933, 7.3722408 , 7.40468227, 7.43712375, 7.46956522, 7.50200669, 7.53444816, 7.56688963, 7.5993311 , 7.63177258, 7.66421405, 7.69665552, 7.72909699, 7.76153846, 7.79397993, 7.8264214 , 7.85886288, 7.89130435, 7.92374582, 7.95618729, 7.98862876, 8.02107023, 8.05351171, 8.08595318, 8.11839465, 8.15083612, 8.18327759, 8.21571906, 8.24816054, 8.28060201, 8.31304348, 8.34548495, 8.37792642, 8.41036789, 8.44280936, 8.47525084, 8.50769231, 8.54013378, 8.57257525, 8.60501672, 8.63745819, 8.66989967, 8.70234114, 8.73478261, 8.76722408, 8.79966555, 8.83210702, 8.86454849, 8.89698997, 8.92943144, 8.96187291, 8.99431438, 9.02675585, 9.05919732, 9.0916388 , 9.12408027, 9.15652174, 9.18896321, 9.22140468, 9.25384615, 9.28628763, 9.3187291 , 9.35117057, 9.38361204, 9.41605351, 9.44849498, 9.48093645, 9.51337793, 9.5458194 , 9.57826087, 9.61070234, 9.64314381, 9.67558528, 9.70802676, 9.74046823, 9.7729097 , 9.80535117, 9.83779264, 9.87023411, 9.90267559, 9.93511706, 9.96755853, 10.])
y = np.asarray([-0.19056796, 0.13314778, 0.39032789, 0.58490071, 0.72755027, 0.82941604, 0.90008145, 0.94719898, 0.97667411, 0.99299073, 0.99952869, 0.99882928, 0.99280334, 0.98288947, 0.97017273, 0.95547297, 0.93941048, 0.92245495, 0.90496191, 0.88720012, 0.86937208, 0.85162961, 0.83408561, 0.81682308, 0.79990193, 0.78336433, 0.76723876, 0.75154314, 0.7362873 , 0.72147487, 0.70710477, 0.69317237, 0.67967038, 0.66658956, 0.65391928, 0.64164796, 0.62976339, 0.61825301, 0.60710407, 0.59630386, 0.58583975, 0.57569933, 0.56587047, 0.55634135, 0.5471005 , 0.53813683, 0.52943965, 0.52099866, 0.51280394, 0.50484599, 0.49711569, 0.48960429, 0.48230342, 0.47520507, 0.46830157, 0.4615856 , 0.45505012, 0.44868845, 0.44249417, 0.43646114, 0.43058352, 0.42485569, 0.4192723 , 0.41382821, 0.40851854, 0.40333859, 0.39828387, 0.39335008, 0.38853312, 0.38382904, 0.37923407, 0.37474459, 0.37035715, 0.36606841, 0.3618752 , 0.35777446, 0.35376325, 0.34983877, 0.34599831, 0.34223928, 0.33855919, 0.33495564, 0.33142632, 0.32796903, 0.32458163, 0.32126208, 0.3180084 , 0.3148187 , 0.31169115, 0.30862399, 0.30561552, 0.30266411, 0.29976818, 0.29692621, 0.29413673, 0.29139834, 0.28870966, 0.28606938, 0.28347622, 0.28092895, 0.27842639, 0.27596739, 0.27355084, 0.27117567, 0.26884083, 0.26654532, 0.26428818, 0.26206846, 0.25988525, 0.25773767, 0.25562487, 0.25354602, 0.25150031, 0.24948698, 0.24750527, 0.24555444, 0.24363379, 0.24174264, 0.23988032, 0.23804617, 0.23623958, 0.234459