I have a Holoviews code with the intent of saving the output as .html. The below works fine i.e. html is genereated and tags are renders but filters don't work. What am I doing wrong?
def load_data(country, lan_name, **kwargs):
df = subset
if country != 'ALL':
df = df[(df.country == country)]
if lan_name != 'ALL':
df = df[(df.lan_name == lan_name)]
table = format_chars(df['term'], df['hex'])
#hv.Table(df, ['country', 'lan_name'], [], label='Data Table')
layout = (table).opts(
opts.Layout(merge_tools=False),
opts.Div(width=700, height=400),
)
return layout
methods = ['ALL'] + sorted(list(subset['country'].unique()))
models = ['ALL'] + sorted(list(subset['lan_name'].unique()))
dmap = hv.DynamicMap(load_data, kdims=['country', 'lan_name']).redim.values(country=methods, lan_name=models)
hv.save(dmap, 'output.html', backend='bokeh')
By "filters" it sounds like you mean the widgets that select along the country and lan_name dimensions. Each time you select a new value of a widget, a DynamicMap calls the Python function that you provide it (load_data here) to calculate the display (which is what makes it "Dynamic"). There is no Python process available when you have a static HTML file, so the display will never get updated in that case.
To make some limited functionality available in a static HTML file, you can convert the DynamicMap to a HoloMap that contains all the displayed items for some specific combinations of widget values (http://holoviews.org/user_guide/Live_Data.html#Converting-from-DynamicMap-to-HoloMap). The resulting parameter space can quickly get quite large, so you will often need to select a feasible subset of values for this to be a practical option.
Related
I am trying to build an annotation interface using streamlit.
In my dataset, each data point may have multiple labels (i.e. labels in the code below). However, I could only select one label using st.multiselect() rather than the expected "multiple select". Specifically, every time I click the one of the choices, the page will be updated and the next data point pops up.
I am not sure what went wrong after getting stuck in this for hours. Could anyone provide any pointers for me?
import pandas as pd
import streamlit as st
df = pd.read_pickle("unlabeled.pkl")
records = df.to_dict("records")
if "annotations" not in st.session_state:
st.session_state.records = records
st.session_state.current_record = records[0]
annotated_data = list()
if st.session_state.records:
labels = st.session_state.current_record["labels"]
example = st.session_state.current_record["example"]
text = st.session_state.current_record["text"]
demo = "\n".join(["- {}".format(ee) for ee in example])
text = "- {}".format(text)
st.write(f"# Example\n{demo}\n# Output\n{text}")
labels = st.multiselect(
label="Select Labels",
options=labels
)
st.write('You Selected:', labels)
if st.button("Save"):
st.session_state.records.remove(st.session_state.current_record)
st.session_state.current_record = st.session_state.records[0]
annotated_data.append(
{
**st.session_state.current_record,
"label": labels
}
)
if len(annotated_data) % 50 == 0:
save_data(annotated_data)
save_data(annotated_data)
You can use a form to prevent the app from rerunning after interacting with the multiselect widget (instead, the app won't rerun until you hit "Submit").
The Problem
I wanted to create an interactive hbar plot, where you can switch between 3 different data sources, using a select widget, a python callback and a local bokeh serve. The plot with the default source renders fine, but when I switch to a different source, the y labels stay the same and the plot turns blank. Changing back to the original value on the select widget does not show the plot I started out with and stays blank.
When I hard-code the inital source to a different one in the code, it renders just fine until I switch it by using the widget again, so the data itself seems to work fine individually.
Am I missing something? I read through many threads, docs and tutorials but can't find anything wrong with my code.
Here is what I have done so far:
I read a .csv and create 3 seperate dataframes and then convert then to columndatasources. Every source has 10 data entries with the columns "species", "ci_lower" and "ci_upper".
Here is an example of one source (all three are built exactly the same way, with different taxon classes):
df = pd.read_csv(os.path.join(os.path.dirname(__file__), "AZA_MLE_Jul2018_utf8.csv",), encoding='utf-8')
m_df = df[df["taxon_class"]=="Mammalia"]
m_df = m_df.sort_values(by="mle", ascending=False)
m_df = m_df.reset_index(drop=True)
m_df = m_df.head(10)
m_df = m_df.sort_values(by="species", ascending=False)
m_df = m_df.reset_index(drop=True)
m_source = bp.ColumnDataSource(m_df)
I saved all 3 sources in a dict:
sources_dict={
"Mammalia": m_source,
"Aves": a_source,
"Reptilia": r_source
}
... and then created my variable called "source" that should change interactively with the "Mammalia" source as default:
source = sources_dict["Mammalia"]
Next I created a figure and added a hbar plot with the source variable as follows:
plot = bp.figure(x_range=(0, np.amax(source.data["ci_upper"])+5), y_range=source.data["species"])
plot.hbar(y="species", right="ci_lower", left="ci_upper", height=0.5, fill_color="#b3de69", source=source)
Then I added the select widget with a python callback:
def select_handler(attr, old, new):
source.data["species"]=sources_dict[new].data["species"]
source.data["ci_lower"]=sources_dict[new].data["ci_lower"]
source.data["ci_upper"]=sources_dict[new].data["ci_upper"]
select = Select(title="Taxonomic Class:", value="Mammalia", options=list(sources_dict.keys()))
select.on_change("value", select_handler)
curdoc().add_root(bk.layouts.row(plot, select))
I tried this:
My suspicion was that the error lies within the callback function, so I tried many different variants, all with the same bad result. I will list some of them here:
I tried using a python native dictionary:
new_data= {
'species': sources_dict[new].data["species"],
'ci_lower': sources_dict[new].data["ci_lower"],
'ci_upper': sources_dict[new].data["ci_upper"]
}
source.data=new_data
I tried assigning the whole data source, not just swapping the data
source=sources_dict[new]
I also tried using dict()
source.data = dict(species=sources_dict[new].data["species"], ci_lower=sources_dict[new].data["ci_lower"], ci_upper=sources_dict[new].data["ci_upper"])
Screenshots
Here is a screenshot of the initial plot, when I run the py file with bokeh serve --show file.py
And here one after changing the selected value:
Would greatly appreaciate any hints that could help me figure this out
Answering your question in the comment, changing data does not change the ranges because y_range=some_thing is just a convenience over creating a proper range class that's done behind the curtain.
Here's how you can do it manually. Notice that I don't touch x_range at all - by default it's DataRange1d that computes its start/end values automatically.
from bokeh.io import curdoc
from bokeh.layouts import column
from bokeh.models import Select, ColumnDataSource
from bokeh.plotting import figure
d1 = dict(x=[0, 1], y=['a', 'b'])
d2 = dict(x=[8, 9], y=['x', 'y'])
ds = ColumnDataSource(d1)
def get_factors(data):
return sorted(set(data['y']))
p = figure(y_range=get_factors(d1))
p.circle(x='x', y='y', source=ds)
s = Select(options=['1', '2'], value='1')
def update(attr, old, new):
if new == '1':
ds.data = d1
else:
ds.data = d2
p.y_range.factors = get_factors(ds.data)
s.on_change('value', update)
curdoc().add_root(column(p, s))
I'm writing a function for Jupyter notebooks, where a user will be able to obtain data as Pandas Dataframe (irrelevant for this question) and display it with filters he could create when needed.
My problem is that I can't "link" the interact filter to the data itself. I had no problems to define manually the filters in the code, but not from the user side. I researched a lot questions from Stackoverflow, Google, project Github and docs before posting here.
Here is the POC:
import pandas as pd
import ipywidgets as widgets
from ipywidgets import *
from IPython.display import display
import numpy as np
np.random.seed(0)
# Data example
my_columns = list('ABCD')
df = pd.DataFrame(np.random.randint(0,100,size=(100, 4)), columns=list('ABCD'))
# Encapsulate Table inside Output widget
table_out = widgets.Output()
with table_out:
display(df)
# Filter for ints
def filter_int(column, x):
return df.loc[df[column] > x]
# Our filter generator
def generate_filter(button):
# Check if exist before creating
if not select_definition.value in [ asd.children[0].description for asd in filters.children ]:
# Not exist. Create this filter
new_filter = interactive(
filter_int, # Our filter for ints
column=fixed(select_definition.value), # Which column as filter
x=widgets.IntSlider(min=0, max=100, step=1, value=10, description=select_definition.value) # Value from the user
)
# Append created filter
filters.children=tuple(list(filters.children) + [new_filter])
# Define button and event
button = widgets.Button(description="Add")
button.on_click(generate_filter)
# Define Dropdown
select_definition = widgets.Dropdown(options=my_columns, layout=Layout(width='10%'))
# Put Dropdown and button together
choose_filter = widgets.HBox([select_definition, button])
# Where we will put all our filters
filters = widgets.HBox()
display(choose_filter, filters, table_out)
Which will create this:
I'm able to create the filters for the columns dynamically, but I'm not sure how to make them update the table and link together (so the table will be updated based on multiple filters).
The expected result is to be able create filters for column A and B and update the table with values defined by them, as shown in the image below:
Any help is appreciated!
Note: The last image was generated with df.loc[(df['A'] > 22) & (df['B'] > 92)]
I have a dataframe that denotes events that occured in particular locations.
I am aware that folium does not allow dynamic display of the appearance of the events so I was thinking about basically iterate through the dates and save a png of each folium map created.
Unfortunately I am mentally stuck in a 2 part problem:
1) how to loop through a ranges of dates (for example one map for each month)
2) an appropriate way to save the generated images for each loop.
This is a dataframe sample for this example:
since = ['2019-07-05', '2019-07-17', '2014-06-12', '2016-03-11']
lats = [38.72572, 38.71362, 38.79263, 38.71931]
longs = [-9.13412, -9.14407, -9.40824, -9.13143]
since_map = {'since' : pd.Series(since), 'lats' : pd.Series(lats), 'longs' : pd.Series(longs)}
since_df = pd.DataFrame(since_map)
I was able to create the base map:
lat_l = 38.736946
long_l = -9.142685
base_l = folium.Map(location=[lat_l,long_l], zoom_start=12)
neigh = folium.map.FeatureGroup()
And add some of the markers to the folium map:
for lati, longi in zip(since_df.longs, since_df.lats):
neigh.add_child(folium.CircleMarker([longi, lati], radius = 2, color = 'blue', fill = True))
base_l.add_child(neigh)
I am struggling to visualize how to loop through ranges of the dates and save each file. From what I saw here:
https://github.com/python-visualization/folium/issues/35 I actually have to open the saved html and then save it as png for each image.
If you could point me to an example or documentation that could demonstrate how this can be accomplished I would be very appreciative.
If you think I am overcomplicating it or you have a better alternative to what I am thinking I have an open ear to suggestions.
Thank you for your help.
I have been playing around with Pygal, creating some line graphs for a project I am working on. I currently have my y axis set to be the value recorded and the x axis being the date / time the test was conducted. However I would also like to link the serial number to each data point. At the moment when you hover on a data point you get the y value in bold and underneith that you get the date it was recorded.
Does anyone know if it is possible to link information to data points without them being an axis label?
For reference I currently have the serial numbers being added to the list: 'sn_list'.
for row in line_graph_query:
if str(row.date_time) >= start_date and str(row.date_time) <= end_date :
min_values.append(float(row.minimum_value))
max_values.append(float(row.maximum_value))
recorded_values.append(float(row.recorded_value))
sn_list.append(row.product_serial_number)
date_list.append(row.date_time)
number_of_records = number_of_records + 1
print(min_values)
print(max_values)
print(recorded_values)
distance_x_axis = math.floor(number_of_records/6)
line_chart = pygal.Line(no_data_text='No result found', style=custom_style,x_labels_major_every=distance_x_axis, x_label_rotation=20, show_minor_x_labels=False )
line_chart.title = 'Detailed Results of '+test_name+' tests of '+board_pn
line_chart.x_labels = map(str,date_list)
line_chart.add('Minimum', min_values)
line_chart.add('Maximum', max_values)
line_chart.add('Recorded', recorded_values)
graph_render.append(line_chart.render_data_uri())
graphs_to_render[test_name] = graph_render[-1]
You can set the tooltip to any text you like by providing your data as dicts (see the documentation here). Each value should be represented by a dict that has at least a value attribute, this is the same value that you were providing to the chart directly. There are then a number of other attributes you can set, amongst them label.
You should be able to get the tooltips you want by changing the three lines that append data in your if structure:
min_values.append({"value": float(row.minimum_value),
"label": row.product_serial_number})
max_values.append({"value": float(row.maximum_value),
"label": row.product_serial_number})
recorded_values.append({"value": float(row.recorded_value),
"label": row.product_serial_number})
Unless you are using it somewhere else this also means that you have no need of the sn_list.