Dropdown filter above the Chart in altair - altair

As default, the Dropdown filter is on the left bottom of the Chart. How do I get the Dropdown filter to be above the Chart?
Example Code:
import altair as alt
import pandas as pd
lst = [['tom', 25], ['krish', 30],
['nick', 26], ['juli', 22]]
df = pd.DataFrame(lst, columns =['Name', 'Age'])
df
name = ['tom', 'krish', 'nick', 'juli']
dropdown = alt.binding_select(options= name)
select = alt.selection_single(fields=['Name'], bind=dropdown, name = 'Selector', init={'Name': name[0]})
chart = alt.Chart(df).mark_bar().encode(
x = alt.X('Name:N'),
y = alt.Y('Age:Q'),
).add_selection(
select
).transform_filter(
select
).properties(
width=200,
height=250
)
chart
Hier, I named the Dropdown filter 'Selector_Name'. Now I want that to be not under the Chart but on the top/above the Chart. How do I do this?
Thanks.

As per the answer in here (altair: change the position of a slider)
lst = [['tom', 25], ['krish', 30],
['nick', 26], ['juli', 22]]
df = pd.DataFrame(lst, columns =['Name', 'Age'])
df
name = ['tom', 'krish', 'nick', 'juli']
dropdown = alt.binding_select(options= name)
select = alt.selection_single(fields=['Name'], bind=dropdown, name = 'Selector', init={'Name': name[0]})
chart = alt.Chart(df).mark_bar().encode(
x = alt.X('Name:N'),
y = alt.Y('Age:Q'),
).add_selection(
select
).transform_filter(
select
).properties(
width=400,
height=450
)
display(HTML("""
<style>
form.vega-bindings {
position: absolute;
left: 65px;
top: -4px;
}
</style>
"""))
chart

Related

How to display data across, by row, in pie chart in plotly/streamlit?

I have pandas df that looks like this that I want to display as a dashboard:
fname col1 col2 col3 sum
A 2 3 3 10
B 1 2 3 12
C 6 6 3 13
If a fname is selected by row, I want to display the pie slices as the column values by row.
What is the best way to display the data by fname grouped across by row in a pie chart?
I am not sure what to display when all the column values for fname are selected.
I tried creating a sunburst chart like so, but the chart is extremely convoluted:
px.sunburst(df, values='sum', path=[
'col3',
'col2',
'col1',
'fname'],
title='pie')
Here is a basic example.
import plotly.express as px
import pandas as pd
import streamlit as st
data = {
'ctry': ['USA', 'PHI', 'CHN'],
'gold': [12, 1, 20,],
'silver': [4,4, 12],
'bronze': [8, 2, 30],
'sum': [24, 7, 62]
}
df = pd.DataFrame(data)
st.dataframe(df)
cols = st.columns([1, 1])
with cols[0]:
medal_type = st.selectbox('Medal Type', ['gold', 'silver', 'bronze'])
fig = px.pie(df, values=medal_type, names='ctry',
title=f'number of {medal_type} medals',
height=300, width=200)
fig.update_layout(margin=dict(l=20, r=20, t=30, b=0),)
st.plotly_chart(fig, use_container_width=True)
with cols[1]:
st.text_input('sunburst', label_visibility='hidden', disabled=True)
fig = px.sunburst(df, path=['ctry', 'gold', 'silver', 'bronze'],
values='sum', height=300, width=200)
fig.update_layout(margin=dict(l=20, r=20, t=30, b=0),)
st.plotly_chart(fig, use_container_width=True)
Output

Can not get any Bokeh graphs show up when the check boxes are ticked

I have tried the below code to generate bokeh graphs for each of the element when the their respective check boxes are ticked.
data is a df with columns like 'id', 'data_point', 'max', 'min'
I am expecting a graph where if the data_point is empty or nonetype then the graph should not appear otherwise when the check boxes are ticked beside the id the graphs should appear for whichever id is being selected by the check box
This should happen for every unique id.
for one unique id the graph should look like below
data = df
if len(data) == 0:
logging.debug(" No data")
else:
req_hover = HoverTool(
tooltips=[
('data_point: ', '#data_point'),
('id: ', '#id')])
req_figure = figure(title='Graph', x_axis_label="Index", y_axis_label="id/data_point [ms]",
plot_width=800, plot_height=400, output_backend="webgl")
req_figure.add_tools(handover_hover)
id_values = data['id'].drop_duplicates()
column_view_data = data[
['id', 'tag', 'sw', 'name']].drop_duplicates().dropna()
column_view_data_source = ColumnDataSource(column_view_data)
data_table = DataTable(selectable='checkbox', source=column_view_data_source, columns=columns, width=1450,
height=400, css_classes=['blueTable'])
name_dict_req = {'name': [], 'legend': [], 'label': []}
logging.info('START OF DRAWINGS')
for ind, element in enumerate(elements):
d = []
for i, item in data.iterrows():
it_color = Turbo256[random.randint(0, 255)]
if element == item['id']:
if item['data_point'] is None or str(item['data_point']) == '[]':
item['data_point'] = '0'
else:
item['data_point'] = [float(x) for x in item['data_point'].strip('[').strip(']').split(',')]
raw_data_element_count = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
20] # this is my x axis which will be 1 to 20
d.append({'id': id,
'number of raw data points': data_point_element_count,
'data_point': item['data_point']})
d_new = pd.DataFrame(d)
name_glyph_req = req_figure.line(x='number of raw data points',
y='data_point',
line_width=2,
legend_label=str(item['id']),
source=d_new,
color=it_color)
name_dict_req['name'].append(name_glyph_req)
name_dict_req['label'].append(str(item['id']))
logging.info('AFTER DRAWINGS LOOP')
for label in range(len(data.id.unique())):
name_dict_req['legend'].append(req_figure.legend.items[label])
initial_value = []
options = list(data.id.unique())
for i, name in enumerate(options):
options[i] = str(name)
for i in range(len(options)):
if name_dict_req['label'][i] in initial_value:
name_dict_req['name'][i].visible = True
name_dict_req['legend'][i].visible = True
else:
name_dict_req['name'][i].visible = False
name_dict_req['legend'][i].visible = False
#########################
callback_datatable = CustomJS(args=dict(source=column_view_data_source), code=callback_file_content_mq)
###################
column_view_data_source.selected.js_on_change('indices', callback_datatable)
req_figure.legend.location = "top_left"
req_figure.legend.click_policy = "hide"
logging.info('END DRAWINGS END SETUP')
I think I did not provide a name dict in the customJS call in my code. So the issue was there that even if I click the checkboxes the graphs were not showing

Altair: how do I get the values from a dropdown menu

Here is the code for generating the image:
input_dropdown = alt.binding_select(options=['Brand','Function','Category'])
selection = alt.selection_single(name='Color By', fields=['categories'], bind=input_dropdown)
alt.Chart(df_PCs).mark_circle().encode(x="PC1:Q", y="PC2:Q", color="Function:N", tooltip=['Name']).add_selection(selection)
What I want to do is to color the dots either by Brand, Function or Category whatever the value that comes from the dropdown menu. Is there a way to get the value of the dropdown menu? Such as selection.value()?
The best approach to this is similar to the Vega-Lite answer in Dynamically Change Y-Axis Field in Encoding Based on Selection Vega-Lite
Selections cannot filter on column titles, only on column values. Fortunately, you can use the fold transform to stack multiple columns and turn those column names into column values.
Here is an example of a Fold Transform in conjunction with a selection box to choose which column to color by:
import altair as alt
import pandas as pd
import numpy as np
df = pd.DataFrame({
'x': np.random.randn(100),
'y': np.random.randn(100),
'c1': np.random.randint(0, 3, 100),
'c2': np.random.randint(0, 3, 100),
'c3': np.random.randint(0, 3, 100),
})
selector = alt.selection_single(
name='Color by',
fields=['column'],
bind=alt.binding_select(options=['c1', 'c2', 'c3']),
init={'column': 'c1'}
)
alt.Chart(df).transform_fold(
['c1', 'c2', 'c3'], as_=['column', 'value']
).transform_filter(
selector
).mark_point().encode(
x='x:Q',
y='y:Q',
color='value:Q',
column='column:N'
).add_selection(
selector
)
For your data, it might look like this (though I've been unable to test it because the data is not included in the question)
selection = alt.selection_single(
fields=['column'],
bind=alt.binding_select(
name="Color by: ",
options=['Brand','Function','Category']
),
init={'column':'Function'}
)
alt.Chart(df_PCs).transform_fold(
["Brand", "Function", "Category"],
as_=['column', 'value']
).transform_filter(
selection
).mark_point().encode(
x="PC1:Q",
y="PC2:Q",
color="value:N",
column="column:N",
tooltip=['Name:N']
).add_selection(selection)

How can I annotate a Grouped Broken Barh Chart Python Matplotlib

I have searched to exhaustion trying to annotate my grouped broken barH chart. I would like to have the "Event" from my dataframe annotated in each broken bar section. The examples I have found online manually enter the events x,y positions, AND, are not grouped broken bar examples.
the end goal is to have these events display on-hover, but I believe I wont have an issue if I can just get the events to display.
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import datetime
import matplotlib.ticker as ticker
import io
pd.plotting.register_matplotlib_converters()
inp = u""" T29,11/4/2019 0:00,11/4/2019 0:00,off,none
T29,11/4/2019 0:00,11/5/2019 0:00,off,eventa
T29,11/5/2019 0:00,11/6/2019 0:00,on,none
T35,11/4/2019 0:00,11/5/2019 0:00,off,eventb
T35,11/5/2019 0:00,11/6/2019 0:00,paused,eventa
T43,11/4/2019 0:00,11/4/2019 4:01,on,none
T43,11/4/2019 4:01,11/4/2019 12:06,off,none
T43,11/4/2019 12:06,11/5/2019 8:07,on,eventc
T43,11/5/2019 8:07,11/5/2019 10:12,paused,eventd
T43,11/5/2019 10:12,11/5/2019 16:15,on,none
T43,11/5/2019 18:12,11/5/2019 20:15,off,none
"""
mydateparser = lambda x: pd.datetime.strptime(x, "%m/%d/%Y %H:%M")
df = pd.read_csv(io.StringIO(inp), header=0, encoding = "ISO-8859-1", parse_dates=['StartTime', 'FinishTime'], date_parser=mydateparser, names=["Name", "StartTime", "FinishTime", "Status", "Event"])
color = {"on": "g", "paused": "yellow", "off": "black"}
df["Diff"] = df.FinishTime - df.StartTime
minDate = (datetime.datetime.toordinal(min(df.StartTime)))
maxDate = (datetime.datetime.toordinal(max(df.FinishTime)))
days = mdates.DayLocator()
Mcount = 0
fig, ax = plt.subplots(figsize=(6, 3), edgecolor="black", linewidth=1)
labels = []
for i, task in enumerate(df.groupby("Name")):
Mcount += 1
labels.append(task[0])
for r in task[1].groupby("Status"):
data = r[1][["StartTime", "Diff"]]
ax.broken_barh(data.values, (i - 0.4, 0.8), edgecolor="black", alpha=1, linewidth=1,
color=color[r[0]])
ax.set_ylim(bottom=-0.8, top=Mcount)
ax.set_yticks(range(len(labels)))
ax.set_yticklabels(labels)
ax.set_ylabel("Names", rotation=90, fontdict={'family': 'DejaVu Sans', 'color': 'black', 'weight': 'bold', 'size': 14})
ax.set_xlim(left=minDate, right=maxDate)
ax.set_xlabel("Date", fontdict={'family': 'DejaVu Sans', 'color': 'black', 'weight': 'bold', 'size': 14})
ax.xaxis.set_major_formatter(mdates.DateFormatter('%m-%d-%Y'))
ax.tick_params(which='major', axis='x', rotation=0, length=11, color='black')
ax.xaxis.set_major_locator(days)
ax.xaxis.set_minor_formatter(mdates.DateFormatter('%H:%M'))
ax.tick_params(which='minor', rotation=0, labelsize=8, length=4, color='red', size=2)
ax.xaxis.set_minor_locator(ticker.MultipleLocator(.50))
plt.show()
Hello and welcome to StackOverflow. IIUC, you can append a for loop to your enumerate statement to add text to the axes.
for i, task in enumerate(df.groupby("Name")):
Mcount += 1
labels.append(task[0])
for r in task[1].groupby("Status"):
data = r[1][["StartTime", "Diff"]]
ax.broken_barh(data.values,
(i - 0.4, 0.8),
edgecolor="black",
alpha=1,
linewidth=1,
color=color[r[0]]
)
for x1, x2 in data.values:
ax.text(x=x1 + x2/2,
y=i,
s=r[1]["Event"].values[0],
ha='center',
va='center',
color='white',
)
Modified from the docs.
Output:
You can, of course, modify the text formatting.
The text requires an x location, a y location, and a string. The hacky indexing was the quickest way I could pull the event info out of your dataframe.

How to add annotation based on the value in bokeh

I want to be able to display "NO DATA" when there is a value '0' in counts. For example for Strawberries, "NO DATA" should be displayed in the graph.
from bokeh.io import show, output_file
from bokeh.plotting import figure
output_file("bar_basic.html")
fruits = ['Apples', 'Pears', 'Nectarines', 'Plums', 'Grapes', 'Strawberries']
counts = [5, 3, 4, 2, 4, 0]
p = figure(x_range=fruits, plot_height=350, title="Fruit Counts")
p.vbar(x=fruits, top=counts, width=0.9)
p.y_range.start = 0
show(p)
For example, for above data the graph should look like this:example vbar with NO DATA
You can select the data with the count value '0' with Pandas. This new dataframe can be used to create another ColumnDataSource to use for the LabelSet to show the text 'NO DATA' in the figure.
from bokeh.io import show, output_file
from bokeh.plotting import figure
from bokeh.models import ColumnDataSource, LabelSet
import pandas as pd
output_file("bar_basic.html")
fruits = ['Apples', 'Pears', 'Nectarines', 'Plums', 'Grapes', 'Strawberries']
counts = [5, 3, 4, 2, 4, 0]
df = pd.DataFrame.from_dict({'fruits': fruits, 'counts': counts})
source = ColumnDataSource(df)
p = figure(x_range=fruits, plot_height=350, title="Fruit Counts")
p.vbar(x='fruits', top='counts', source=source, width=0.9)
df_nodata = df.loc[df['counts'] == 0]
pd.options.mode.chained_assignment = None
df_nodata.loc[:, 'text'] = 'NO DATA'
source_nodata = ColumnDataSource(df_nodata)
labels = LabelSet(x='fruits', y=1, text='text', text_align='center', source=source_nodata)
p.add_layout(labels)
p.y_range.start = 0
show(p)

Resources