Transition not showing for animated polarbar plot with plotly (offline) - python

I am trying to make a polar barplot rotate smoothly using plotly in offline mode. Following the examples available in the docs, I do this by creating a button with as method "animate" and setting the transition time to a value >0 ms.
The same problem occurred when using a scatterpolar type plot instead of a barplot, however the animation did work for a non-polar type scatter plot.
import plotly.graph_objs as go
import plotly.offline as offline
import pandas as pd
import numpy as np
offline.init_notebook_mode()
#some data to plot:
df = pd.DataFrame({'artist':['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'],
'birth': pd.to_datetime(pd.Series(['1990-04-01T00:00:00.000000000', '1945-12-01T00:00:00.000000000',
'1955-01-01T00:00:00.000000000', '1956-01-01T00:00:00.000000000',
'1976-12-01T00:00:00.000000000', '1930-05-01T00:00:00.000000000',
'1942-01-01T00:00:00.000000000', '1936-11-01T00:00:00.000000000',
'1971-12-01T00:00:00.000000000', '1952-12-01T00:00:00.000000000'])),
'death': pd.to_datetime(pd.Series(['2012-04-01T00:00:00.000000000', '2015-12-01T00:00:00.000000000',
'2010-01-01T00:00:00.000000000', '2017-01-01T00:00:00.000000000',
'2016-12-01T00:00:00.000000000', '2017-05-01T00:00:00.000000000',
'2010-01-01T00:00:00.000000000', '2015-11-01T00:00:00.000000000',
'2014-12-01T00:00:00.000000000', '2013-12-01T00:00:00.000000000']))} )
#creating the barplot:
shift = df['birth'] - pd.datetime(1970, 1 ,1)
trace = {
'name': "to",
'r': (df['death']- shift).dt.date,
'theta': np.linspace(0,360,11),
'base':df['birth'].dt.date,
'type': 'barpolar'
}
data = [trace]
nsteps = 20
tracedicts = []
start_thetas = np.linspace(0,360,nsteps)
for i in start_thetas:
tracedicts.append(trace.copy())
tracedicts[-1]['theta'] = np.linspace(i,360+i,11)
frames = [{'data': [tracei]} for tracei in tracedicts]
layout = {
'polar':{
'angularaxis':{
'visible': False,
},
'radialaxis':{
'showgrid': True,
'type': 'date',
'hoverformat': '%m-%Y',
}
},
'updatemenus': [{
'type': 'buttons',
'x': 0.1,
'y': 0,
'buttons':[{'label':'Play', 'method':'animate',
'args':[None, {'frame':{'duration':600, 'redraw':True},
'transition':{'duration':400},
'fromcurrent':True,
'easing': 'linear'}]}]
}],
}
fig = go.Figure(data=data, layout=layout, frames = frames)
offline.iplot(fig,auto_play=False)
The animation works as far as showing the different frames, but the transition does not work.
Is this a feature that simply does not exist for all polar and/or bar plots?
(You'll also notice that I set 'redraw':True - this is because otherwise the animation only worked when setting auto_play=True at the end.)

Related

Can Plotly timeline be used / reproduced in Jupyter Notebook Widget?

The plotly plotly.express.timeline is marvelous, but creates it's own figure. It seems like I need to embed this visual in a FigureWidget to get it to play nice with the layout in a Jupyter Notebook. So I am trying to re-create the plot using the plotly.graph_objects.Bar() that px.timeline() is built upon.
Unfortunately, I can't figure out how to accomplish this. It appears that the values for the bars are added to the 'base' vector (as a relative value) not used as absolute positions. Plotly does not appear to understand datetime.timedelta() objects. Printing the timeline() figure version shows the values as
an array of floating point values which it isn't clear how they are computed. I've tried simply copying them, but this ends up with plotly thinking the x axis isn't a datetime axis.
Any clue would be most welcome. Either how to use the Box() to draw the appropriate figure, or how to embed/animate/layout the px.timeline() figure in a notebook.
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
from datetime import datetime
# the data:
df = pd.DataFrame([
dict(Task="one", Start=datetime(2009,1,1), Finish=datetime(2009,4,28)),
dict(Task="two", Start=datetime(2009,5,5), Finish=datetime(2009,7,15)),
dict(Task="three", Start=datetime(2009,7,20), Finish=datetime(2009,9,30))
])
# working plotly express figure:
pxfig = px.timeline(df, x_start="Start", x_end="Finish", y="Task")
pxfig.show() # looks great
# Broken bar figure:
plainfig = go.Figure()
plainfig.add_bar(base=df['Start'],
# x=pxfig.data[0].x, # this breaks the axis as they are not of type datetime.
# x=df['Finish']-df['Start'], # this doesn't produce the right plot
x=df['Finish'], # these appear to be relative to base, not absolute
y=df['Task'], orientation='h')
plainfig.show()
# looking at the two shows interesting differences in the way the x data is stored
print(pxfig)
print(plainfig)
Figure({
'data': [{'alignmentgroup': 'True',
'base': array([datetime.datetime(2009, 1, 1, 0, 0),
datetime.datetime(2009, 5, 5, 0, 0),
datetime.datetime(2009, 7, 20, 0, 0)], dtype=object),
'x': array([1.01088e+10, 6.13440e+09, 6.22080e+09]),
'xaxis': 'x',
'y': array(['one', 'two', 'three'], dtype=object),
'yaxis': 'y'}],
'layout': {'barmode': 'overlay',
'legend': {'tracegroupgap': 0},
'margin': {'t': 60},
'template': '...',
'xaxis': {'anchor': 'y', 'domain': [0.0, 1.0], 'type': 'date'},
'yaxis': {'anchor': 'x', 'domain': [0.0, 1.0], 'title': {'text': 'Task'}}}
})
Figure({
'data': [{'base': array([datetime.datetime(2009, 1, 1, 0, 0),
datetime.datetime(2009, 5, 5, 0, 0),
datetime.datetime(2009, 7, 20, 0, 0)], dtype=object),
'orientation': 'h',
'type': 'bar',
'x': array([datetime.datetime(2009, 4, 28, 0, 0),
datetime.datetime(2009, 7, 15, 0, 0),
datetime.datetime(2009, 9, 30, 0, 0)], dtype=object),
'y': array(['one', 'two', 'three'], dtype=object)}],
'layout': {'template': '...'}
})
I can't answer how to embed the timeline in a FigureWidget, but I think I have the answer to your original problem of getting the timeline to play nicely with the jupyter notebook layout. I'm guessing you want to be able to update the timeline interactively?
I have gotten around this problem by embedding the figure produced by px.timeline in an output widget. Then whenever I need the figure to be updated (from a button callback, for example) I just clear the output in the output widget, create a new timeline figure and display that new figure. It's not the most elegant way of doing things but it gets the job done.
import ipywidgets as widgets
from IPython.display import display, clear_output
import pandas as pd
import plotly.express as px
from datetime import datetime
output = widgets.Output()
df = pd.DataFrame([
dict(Task="one", Start=datetime(2009,1,1), Finish=datetime(2009,4,28)),
dict(Task="two", Start=datetime(2009,5,5), Finish=datetime(2009,7,15)),
dict(Task="three", Start=datetime(2009,7,20), Finish=datetime(2009,9,30))
])
updated_df = pd.DataFrame([
dict(Task="one", Start=datetime(2009,1,1), Finish=datetime(2009,4,28)),
dict(Task="two", Start=datetime(2009,5,5), Finish=datetime(2009,7,15)),
dict(Task="three", Start=datetime(2009,7,20), Finish=datetime(2009,9,30)),
dict(Task="four", Start=datetime(2009,10,5), Finish=datetime(2009,10,10))
])
# display the original timeline figure
pxfig = px.timeline(df, x_start="Start", x_end="Finish", y="Task")
with output:
display(pxfig)
# create a button which when pressed will update the timeline figure
button = widgets.Button(description='update figure')
def on_click(button):
with output:
clear_output()
new_pxfig = px.timeline(updated_df, x_start="Start", x_end="Finish", y="Task")
display(new_pxfig)
button.on_click(on_click)
display(button)

Plotly: How to add text labels to a histogram?

Is there a way how to display the counted value of the histogram aggregate in the Plotly.Express histogram?
px.histogram(pd.DataFrame({"A":[1,1,1,2,2,3,3,3,4,4,4,5]}),x="A")
If I would use regular histogram, I can specify text parameter which direct to the column which contain the value to display.
px.bar(pd.DataFrame({"val":[1,2,3,4,5], "height": [3,2,3,3,1]}), x="val", y="height", text="height")
But with histograms, this value is calculated and it's not even part of the fig.to_dict(). Is there a way to add the text labels into histogram?
Using the answers below, I've summarized the finding to an article - https://towardsdatascience.com/histograms-with-plotly-express-complete-guide-d483656c5ad7
The text_auto parameter set to True will do what you want.
Taking your example code, this is what i get :
fig = px.histogram(pd.DataFrame({"A":[1,1,1,2,2,3,3,3,4,4,4,5]}),x="A",
text_auto=True)
fig.show()
Being a new member i cannot embed the screenshot yet, but here is a link.
Histogram
A bit late but hope this will help.
As far as I know, plotly histograms do not have a text attribute. It also turns out that it's complicated if at all possible to retrieve the applied x and y values and just throw them into appropriate annotations. Your best option seems to be to take care of the binning using numpy.histogram and the set up your figure using go.Bar. The code snippet below will produce the following plot:
Complete code:
import numpy as np
import plotly.express as px
import plotly.graph_objects as go
# sample data
df = px.data.tips()
# create bins
bins = [0, 10, 20, 30, 40, 50]
counts, bins = np.histogram(df.total_bill, bins=bins)
#bins2 = 0.5 * (bins1[:-1] + bins2[1:])
fig = go.Figure(go.Bar(x=bins, y=counts))
fig.data[0].text = counts
fig.update_traces(textposition='inside', textfont_size=8)
fig.update_layout(bargap=0)
fig.update_traces(marker_color='blue', marker_line_color='blue',
marker_line_width=1, opacity=0.4)
fig.show()
I had his same problem this morning while trying to plot a histogram of TDD percentages. Using plotly, I wanted to normalize (histnorm: 'percent') so I could see percentages of my monthly TDD values instead of the counts. I found this solution by simply doing a print(tdd_hist)
First, I printed the histogram to the console and saw this output...
Figure({
'data': [{'alignmentgroup': 'True',
'bingroup': 'x',
'histnorm': 'percent',
'hovertemplate': 'Total Demand Distortion TDD %=%{x}<br>count=%{y}<extra></extra>',
'legendgroup': '',
'marker': {'color': '#636efa'},
'name': '',
'offsetgroup': '',
'orientation': 'v',
'showlegend': False,
'type': 'histogram',
'x': array([0.67, 0.68, 0.68, ..., 2.41, 2.48, 2.01]),
'xaxis': 'x',
'yaxis': 'y'}],
'layout': {'barmode': 'relative',
'legend': {'tracegroupgap': 0},
'template': '...',
'title': {'text': 'Percent Histogram of TDD%'},
'xaxis': {'anchor': 'y', 'domain': [0.0, 1.0], 'title': {'text': 'Total Demand Distortion TDD %'}},
'yaxis': {'anchor': 'x', 'domain': [0.0, 1.0], 'title': {'text': 'count'}, 'type': 'log'}}
Now I can clearly see that to change this, I do a
tdd_hist.layout.yaxis.title.text = 'Percent'
And it works!

Plotly offline, and dropdown widgets

I have to create a graph with various curves to plot. My goal is to create a dropdown widget where I can select the curve I want to plot, i.e. choose the data my iplot is going to plot.
I am using Jupyter Notebook. I imported iplot and initialized notebook mode.
from plotly.offline import iplot, init_notebook_mode
My x axis is always the same: X = [1,2,3,4,5,6,7,8,9,10,11,12].
My y data is what I want to be able to change through the widget: I have a list of lists, and I want to be able to choose the index of the list I want to plot.
So far, I have a graph where I am ploting all the data and a dropdown widget that does nothing:
k=len(Y)
updatemenus = [
{
'buttons': [
{
'method': 'restyle',
'label': str(i),
'args': [
{},
]
} for i in range(k)
],
'direction': 'down',
'showactive': True,
}
]
iplot({
'data':
[{'x': [1,2,3,4,5,6,7,8,9,10,11,12],
'y': Y[i],
'name': str(i)
} for i in range(k)],
'layout': {'title': 'Title',
'updatemenus': updatemenus}
})
I have the sense that I have to write something in the 'args' of the updatemenus, but I can't find what it would be. I've tried things like 'y': Y[i], but it just plots a straight line as if there was no 'y' axis indicated in the iplot.
So I have found a solution, although it is probably not ideal: in the args option of updatemenus, I've now written {'visible': [False]*i+[True]+[False]*(k-i-1)}.
And for the graph to only plot the first curve when I execute the program, I've modified the iplot command like this :
iplot({
'data':
[{'x': [1,2,3,4,5,6,7,8,9,10,11,12],
'y': Y[i],
'name': str(i),
'visible': i==0
} for i in range(k)],
'layout': {'title': 'Title',
'updatemenus': updatemenus}
})

Python: How to get data from linked brushes in mlpd3, Bokeh, Plotly?

Using the code below I can get a 2x2 graph with 4 plots. With brushes, I can select some data points. The question I have is how do get the selected data points as a JSON array or cvs. This code uses mlpd3, but bokeh can do similar selections with brushes.. But there is no example of selecting the data points. I am trying to get selected data as object to continue processing with python. It would be nice to see the data in a cell.
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mat
import mpld3
mpld3.enable_notebook()
from mpld3 import plugins
fig, ax = plt.subplots(2, 2, figsize=(10, 8))
fig.subplots_adjust(hspace=0.1, wspace=0.1)
ax = ax[::-1]
X = np.random.normal(size=(2, 100))
for i in range(2):
for j in range(2):
ax[i, j].xaxis.set_major_formatter(plt.NullFormatter())
ax[i, j].yaxis.set_major_formatter(plt.NullFormatter())
points = ax[i, j].scatter(X[j], X[i])
plugins.connect(fig, plugins.LinkedBrush(points))
Bokeh has similar behavior in CustomJS for Selections
http://docs.bokeh.org/en/latest/docs/user_guide/interaction/callbacks.html#userguide-interaction-jscallbacks-customjs-interactions
Whichever one is easier to extract the selected item -- would work.. If there is a Plotly solution, that would also work.
You can get the selected data from a Plotly chart by using Plotly's new Dash framework.
There is an example in the docs here under "Graph Crossfiltering" https://plot.ly/dash/getting-started-part-2
I've pasted the full example below just for preservation of history.
In each of the callbacks below, you have access to the either the selected points, the points that you just hovered over, or the points that you just clicked on. This app simply displays the values of the points in the app, but you could do anything with the points (e.g. compute something else).
import dash
from dash.dependencies import Input, Output
import dash_core_components as dcc
import dash_html_components as html
import json
app = dash.Dash(__name__)
app.layout = html.Div([
dcc.Graph(
id='basic-interactions',
figure={
'data': [
{
'x': [1, 2, 3, 4],
'y': [4, 1, 3, 5],
'text': ['a', 'b', 'c', 'd'],
'customdata': ['c.a', 'c.b', 'c.c', 'c.d'],
'name': 'Trace 1',
'mode': 'markers',
'marker': {'size': 12}
},
{
'x': [1, 2, 3, 4],
'y': [9, 4, 1, 4],
'text': ['w', 'x', 'y', 'z'],
'customdata': ['c.w', 'c.x', 'c.y', 'c.z'],
'name': 'Trace 2',
'mode': 'markers',
'marker': {'size': 12}
}
]
}
),
html.Div([
dcc.Markdown("""
**Hover Data**
Mouse over values in the graph.
""".replace(' ', '')),
html.Pre(id='hover-data')
], style=styles['column']),
html.Div([
dcc.Markdown("""
**Click Data**
Click on points in the graph.
""".replace(' ', '')),
html.Pre(id='click-data'),
], style=styles['column']),
html.Div([
dcc.Markdown("""
**Selection Data**
Choose the lasso or rectangle tool in the graph's menu
bar and then select points in the graph.
""".replace(' ', '')),
html.Pre(id='selected-data'),
])
])
#app.callback(
Output('hover-data', 'children'),
[Input('basic-interactions', 'hoverData')])
def display_hover_data(hoverData):
#
# This is where you can access the hover data
# This function will get called automatically when you hover over points
# hoverData will be equal to an object with that data
# You can compute something off of this data, and return it to the front-end UI
#
return json.dumps(hoverData, indent=2)
#app.callback(
Output('click-data', 'children'),
[Input('basic-interactions', 'clickData')])
def display_click_data(clickData):
# Similarly for data when you click on a point
return json.dumps(clickData, indent=2)
#app.callback(
Output('selected-data', 'children'),
[Input('basic-interactions', 'selectedData')])
def display_selected_data(selectedData):
# Similarly for data when you select a region
return json.dumps(selectedData, indent=2)
if __name__ == '__main__':
app.run_server(debug=True)
This is outside of ipython but you can run flask or django in conjunction with d3.js and jquery to get the data back into python.

Color points in scatter plot of Bokeh

I have the following simple pandas.DataFrame:
df = pd.DataFrame(
{
"journey": ['ch1', 'ch2', 'ch2', 'ch1'],
"cat": ['a', 'b', 'a', 'c'],
"kpi1": [1,2,3,4],
"kpi2": [4,3,2,1]
}
)
Which I plot as follows:
import bokeh.plotting as bpl
import bokeh.models as bmo
bpl.output_notebook()
source = bpl.ColumnDataSource.from_df(df)
hover = bmo.HoverTool(
tooltips=[
("index", "#index"),
('journey', '#journey'),
("Cat", '#cat')
]
)
p = bpl.figure(tools=[hover])
p.scatter(
'kpi1',
'kpi2', source=source)
bpl.show(p) # open a browser
I am failing to color code the dots according to the cat. Ultimately, I want to have the first and third point in the same color, and the second and fourth in two more different colors.
How can I achieve this using Bokeh?
Here's a way that avoids manual mapping to some extent. I recently stumbled on bokeh.palettes at this github issue, as well as CategoricalColorMapper in this issue. This approach combines them. See the full list of available palettes here and the CategoricalColorMapper details here.
I had issues getting this to work directly on a pd.DataFrame, and also found it didn't work using your from_df() call. The docs show passing a DataFrame directly, and that worked for me.
import pandas as pd
import bokeh.plotting as bpl
import bokeh.models as bmo
from bokeh.palettes import d3
bpl.output_notebook()
df = pd.DataFrame(
{
"journey": ['ch1', 'ch2', 'ch2', 'ch1'],
"cat": ['a', 'b', 'a', 'c'],
"kpi1": [1,2,3,4],
"kpi2": [4,3,2,1]
}
)
source = bpl.ColumnDataSource(df)
# use whatever palette you want...
palette = d3['Category10'][len(df['cat'].unique())]
color_map = bmo.CategoricalColorMapper(factors=df['cat'].unique(),
palette=palette)
# create figure and plot
p = bpl.figure()
p.scatter(x='kpi1', y='kpi2',
color={'field': 'cat', 'transform': color_map},
legend='cat', source=source)
bpl.show(p)
For the sake of completeness, here is the adapted code using low-level chart:
import pandas as pd
import bokeh.plotting as bpl
import bokeh.models as bmo
bpl.output_notebook()
df = pd.DataFrame(
{
"journey": ['ch1', 'ch2', 'ch2', 'ch1'],
"cat": ['a', 'b', 'a', 'c'],
"kpi1": [1,2,3,4],
"kpi2": [4,3,2,1],
"color": ['blue', 'red', 'blue', 'green']
}
)
df
source = bpl.ColumnDataSource.from_df(df)
hover = bmo.HoverTool(
tooltips=[
('journey', '#journey'),
("Cat", '#cat')
]
)
p = bpl.figure(tools=[hover])
p.scatter(
'kpi1',
'kpi2', source=source, color='color')
bpl.show(p)
Note that the colors are "hard-coded" into the data.
Here is the alternative using high-level chart:
import pandas as pd
import bokeh.plotting as bpl
import bokeh.charts as bch
bpl.output_notebook()
df = pd.DataFrame(
{
"journey": ['ch1', 'ch2', 'ch2', 'ch1'],
"cat": ['a', 'b', 'a', 'c'],
"kpi1": [1,2,3,4],
"kpi2": [4,3,2,1]
}
)
tooltips=[
('journey', '#journey'),
("Cat", '#cat')
]
scatter = bch.Scatter(df, x='kpi1', y='kpi2',
color='cat',
legend="top_right",
tooltips=tooltips
)
bch.show(scatter)
you could use the higher level Scatter like here
or provide a color column to the ColumnDataSource and reference it in your p.scatter(..., color='color_column_label')

Categories