How to order axis' on Plotly graph - python

I am revisiting an old Plotly-Dash app and a bubble chart is not properly ordering the axis'. I tried 'categoryorder':'total ascending' under go.Layout but this didn't help.
The application uses an old version of Dash and Plotly. Using the most recent version breaks the app so I'd like to make it work with the below versions:
def initial_bubble_chart(data):
bubble_tickers, energy_value_change, energy_pct_change, industry, market_cap = [], [], [], [], []
for t, _ in data.items():
try:
if bool(data[t][resource_consumption_q][energy_consumption_bucket_q][energy_consumption_q][value_change_q]) == True:
energy_value_change.append(data[t][resource_consumption_q][energy_consumption_bucket_q][energy_consumption_q][value_change_q])
energy_pct_change.append(data[t][resource_consumption_q][energy_consumption_bucket_q][energy_consumption_q][pct_change_q])
industry.append(data[t][financials_topic_q][descriptive_bucket_q][industry_q][value_2017_q])
market_cap.append(data[t][financials_topic_q][company_valuation_bucket_q][market_cap_q][value_2017_q])
bubble_tickers.append(t)
else:
continue
except:
pass
x = list(zip(bubble_tickers, energy_value_change, energy_pct_change, market_cap, industry))
y_test = pd.DataFrame(x)
y_test.columns = ['Tickers', 'Energy Value Change', 'Energy percent change', 'market cap', 'industry']
traces = []
for industry_name in y_test['industry'].unique():
df_by_industry = y_test[y_test['industry']==industry_name].sort_values(by=['Energy Value Change'])
traces.append(go.Scatter(
x = df_by_industry['Energy Value Change'].apply(pd.to_numeric),
y = df_by_industry['Energy percent change'].apply(pd.to_numeric),
#makes this a scatter plot
mode ='markers',
opacity = 0.7,
marker = dict(size = y_test['market cap'].astype(str).astype(float)/1000000000),
#doesn't show proper ticker
hovertext=bubble_tickers,
name = industry_name,
#template= {'template':'seaborn'}
))
return {'data':traces,
#remember that the graph dictionary takes two parameters:
#1: 'data': traces
#2: 'layout':go.Layout(), where the xaxis takes a dict
'layout':go.Layout(
title='Energy Consumption (MwH) 2012-2017 vs. 2017 Market Cap',
xaxis={'title':'Energy Consumption Value Change', 'categoryorder':'total ascending'},
yaxis={'title': 'Energy Consumption Percent Change', 'categoryorder':'total ascending'},
hovermode = 'closest',
template = 'plotly_white',
)}
Calling the function:
html.Div([
dcc.Graph(id='bubble-graph',
figure = go.Figure(initial_bubble_chart(data)))
],style={'display':'inline-block',
'width':'80%',
'border':{'width':'2px', 'color':'black'},
'marginLeft':350,
'marginTop':10,
'marginBottom':50},
className = 'six columns'
),
I would like to have the x and y axis' ordered and reformat the axis'. Graph:
Bubble Chart

Related

How to display Resampling of candles / time series data in plotly?

How can I merge the two functions given below to achieve something like the histogram example. Any button or drop down would do fine.
If you run the function, you get a nice Candlesticks chart with the functionality of removing non trading day gaps.
def plot_candlesticks(df, names = ('DATE','OPEN','CLOSE','LOW','HIGH'), mv:list = [200], slider:bool = False, fig_size:bool = (1400,700), plot:bool = True):
'''
Plot a candlestick on a given dataframe
args:
df: DataFrame
names: Tuple of column names showing ('DATE','OPEN','CLOSE','LOW','HIGH')
mv: Moving Averages
slider: Whether to have below zoom slider or not
fig_size: Size of Figure as (Width, Height)
plotting: Whether to plot the figure or just return the figure for firther modifications
'''
freq = 5 # 5 min candle
candle_text = f"{str(freq)} Min"
stocks = df.copy()
stocks.sort_index(ascending=False, inplace = True) # Without reverse, recent rolling mean will be either NaN or equal to the exact value
Date, Open, Close, Low, High = names
mv = [] if not mv else mv # just in case you don't want to have any moving averages
colors = sample(['black','magenta','teal','brown','violet'],len(mv))
# To remove, non-trading days, grab first and last observations from df.date and make a continuous date range from that
start = stocks['DATE'].iloc[0] - timedelta(days=1)
end = stocks['DATE'].iloc[-1] + timedelta(days=1)
dt_all = pd.date_range(start=start,end=end, freq = f'{str(freq)}min')
# check which dates from your source that also accur in the continuous date range
dt_obs = [d.strftime("%Y-%m-%d %H:%M:%S") for d in stocks['DATE']]
# isolate missing timestamps
dt_breaks = [d for d in dt_all.strftime("%Y-%m-%d %H:%M:%S").tolist() if not d in dt_obs]
rangebreaks=[dict(dvalue = freq*60*1000, values=dt_breaks)]
range_selector = dict(buttons = list([dict(step = 'all', label = 'All')]))
candle = go.Figure(data = [go.Candlestick(opacity = 0.9, x = stocks[Date], name = 'X',
open = stocks[Open], high = stocks[High], low = stocks[Low], close = stocks[Close]),])
for i in range(len(mv)):
stocks[f'{str(mv[i])}-SMA'] = stocks[Close].rolling(mv[i], min_periods = 1).mean()
candle.add_trace(go.Scatter(name=f'{str(mv[i])} MA',x=stocks[Date], y=stocks[f'{str(mv[i])}-SMA'],
line=dict(color=colors[i], width=1.7)))
candle.update_xaxes(title_text = 'Date', rangeslider_visible = slider, rangeselector = range_selector, rangebreaks=rangebreaks)
candle.update_layout(autosize = False, width = fig_size[0], height = fig_size[1],
title = {'text': f"{stocks['SYMBOL'][0]} : {str(candle_text)} Candles",'y':0.97,'x':0.5,
'xanchor': 'center','yanchor': 'top'},
margin=dict(l=30,r=30,b=30,t=30,pad=2),
paper_bgcolor="lightsteelblue")
candle.update_yaxes(title_text = 'Price in Rupees', tickprefix = u"\u20B9" ) # Rupee symbol
if plot:
candle.show()
return candle
and running the below code resamples your data.
def resample_data(self,to:str = '15min', names:tuple = ('OPEN','CLOSE','LOW','HIGH','DATE')):
'''
Resample the data from 5 Minutes to 15 or 75 Minutes
args:
data: Dataframe of Daily data
to: One of [15M, 75M]
'''
Open, Close, Low, High, Date = names
data = data.resample(to,on=Date).agg({Open:'first', High:'max', Low: 'min', Close:'last'})
return data.sort_index(ascending = False).reset_index()
Is there a functionality when I click 15M / 75M button in my chart, it shows me exactly the same data but resampled? Just like there is functionality in online trading softwares.
no sample data so I have used https://plotly.com/python/candlestick-charts/ sample
at core use https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.resample.html and change trace contents with resampled data
plus using https://ipywidgets.readthedocs.io/en/latest/examples/Widget%20Events.html for events from widgets
import pandas as pd
import numpy as np
import plotly.graph_objects as go
import ipywidgets as widgets
df = pd.read_csv(
"https://raw.githubusercontent.com/plotly/datasets/master/finance-charts-apple.csv",
parse_dates=["Date"],
)
fig = go.FigureWidget(
data=[
go.Candlestick(
x=df["Date"],
open=df["AAPL.Open"],
high=df["AAPL.High"],
low=df["AAPL.Low"],
close=df["AAPL.Close"],
)
]
).update_layout(margin={"t": 30, "b": 0, "l": 0, "r": 0})
out = widgets.Output(layout={"border": "1px solid black"})
out.append_stdout("Output appended with append_stdout\n")
reset = widgets.Button(description="Reset")
slider = widgets.IntSlider(
value=1,
min=1,
max=10,
step=1,
description='Days:',
disabled=False,
continuous_update=False,
orientation='horizontal',
readout=True,
readout_format='d'
)
#out.capture()
def on_slider_change(v):
print(f"slider: {v['new']}")
dfr = df.resample(f"{v['new']}B", on="Date").mean().reset_index()
t = fig.data[0]
t.update(
x=dfr["Date"],
open=dfr["AAPL.Open"],
high=dfr["AAPL.High"],
low=dfr["AAPL.Low"],
close=dfr["AAPL.Close"],
)
#out.capture()
def on_reset_clicked(b):
print("reset")
t = fig.data[0]
t.update(
x=df["Date"],
open=df["AAPL.Open"],
high=df["AAPL.High"],
low=df["AAPL.Low"],
close=df["AAPL.Close"],
)
out.clear_output()
reset.on_click(on_reset_clicked)
slider.observe(on_slider_change, names='value')
widgets.VBox([widgets.HBox([reset, slider]), widgets.VBox([fig, out])])

Hover callback problem - doesn't display dataframe

I am new to Python and have limited coding experience, so any input and advice is deeply appreciated.
I have created a dynamic choropleth map which includes a scatter_geo plot that overlays the relevant areas.
I am trying create a hover callback so that when I hover over one of these points, a dataframe appears that is indexed according to the point id (the first column in the defined dataframe). Essentially, it is a choropleth map equivalent of this example: https://plotly.com/python/v3/cars-exploration/ but without using FigureWidget.
I keep getting stuck on the hover callback function; no dataframe displays when I hover. Below is the code I have so far.
license_df1 = pd.read_excel(lic, "Primary Holdings by License", dtype = "str").fillna('')
license_df2 = pd.read_excel(lic, "Secondary Holdings by License", dtype = "str").fillna('')
### CREATE PLOTTING FEATURES ###
app = dash.Dash(__name__, suppress_callback_exceptions = True)
app.css.config.serve_locally = True
app.scripts.config.serve_locally = True
app.layout = html.Div([
html.P("Spectrum Band:"), # Create Toggle Items between spectrum bands
dcc.RadioItems(id = "Band", options=[{'value': x, 'label':x} for x in df1_band], value = df1_band[0]),
dcc.Graph(id = "choropleth"),
dash_table.DataTable(id = "table")])
#app.callback(
Output("choropleth", "figure"),
[Input("Band", "value")])
def build_graph(value):
if value == '600 MHz':
df1_600 = df1[(df1["Band"] == "600 MHz")]
fig1 = px.choropleth(df1_600, geojson = PEAs, featureidkey = "properties.PEA_Num",
locations = 'PEA # ', hover_data = {'PEA # ': False}, scope = "usa")
# Overlay Geographic Scatter Plot for interactive functionality
fig1b = px.scatter_geo(df1_600, geojson = PEAs, featureidkey = "properties.PEA_Num",
locations = 'PEA # ', hover_name = 'Market', scope = "usa")
fig1.add_trace(fig1b.data[0])
fig1.update_traces(showlegend = False)
fig1.update_layout(margin={"r":0,"t":0,"l":0,"b":0})
return fig1
elif value == '3.7 GHz':
df1_3700 = df1[(df1["Band"] == "3.7 GHz")]
fig2 = px.choropleth(df1_3700, geojson = PEAs, featureidkey = "properties.PEA_Num",
locations = 'PEA # ', hover_data = {'PEA # ': False}, scope = "usa")
# Overlay Geographic Scatter Plot for interactive functionality
fig2b = px.scatter_geo(df1_3700, geojson = PEAs, featureidkey = "properties.PEA_Num",
locations = 'PEA # ', hover_name = 'Market', scope = "usa")
fig2.add_trace(fig2b.data[0])
fig2.update_traces(showlegend = False)
fig2.update_layout(margin={"r":0,"t":0,"l":0,"b":0})
return fig2
#app.callback(
Output("table", "data"),
[Input("fig1", "hover_data")]) # WHERE I AM HAVING TROUBLE
def disp_license1(hover_data):
table_vals = license_df1.iloc[points.point_inds[0]].to_frame().to_html()
return table_vals
app.run_server(debug = True)

Plotting data from two different dataframes to a single Dash graph

I'm trying to generate a Dash app which displays historical and forecasted housing prices. I've got the forecasted data stored in a different dataframe from the historical prices, and I'd like to plot them both on the same graph in Dash, and have the graph get updated via callback when the user picks a different city from a dropdown menu. I would like both traces of the graph to update when a value is selected in the dropdown. I've tried various things but can only get one trace from one dataframe plotted for the graph in my callback:
# --- import libraries ---
import dash
import dash_table
import dash_core_components as dcc
import dash_html_components as html
import pandas as pd
import numpy as np
import plotly.express as px
import plotly.graph_objects as go
from dash.dependencies import Output, Input
# --- load data ---
df_h = pd.read_csv('df_h.csv')
df_arima = pd.read_csv('df_arima.csv')
options = [] #each column in the df_h dataframe is an option for the dropdown menu
for column in df_h.columns:
options.append({'label': '{}'.format(column, column), 'value': column})
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
# --- initialize the app ---
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
# --- layout the dashboard ---
app.layout = html.Div(
children = [
html.Div([
html.Label('Select a feature from drop-down to plot'),
dcc.Dropdown(
id = 'city-dropdown',
options = options,
value = 'Denver, CO',
multi = False,
clearable = True,
searchable = True,
placeholder = 'Choose a City...'),
html.Div(id = 'forecast-container',
style = {'padding': '50px'}),
]),
],
)
# --- dropdown callback ---
#app.callback(
Output('forecast-container', 'children'),
Input('city-dropdown', 'value'))
def forecast_graph(value):
dff = df_h[['Date', value]] #'value' is identical between the two dataframes. references
dfa = df_arima[df_arima['City'] == value] # a col in dff and row values in dfa
return [
dcc.Graph(
id = 'forecast-graph',
figure = px.line(
data_frame = dff,
x = 'Date',
y = value).update_layout(
showlegend = False,
template = 'xgridoff',
yaxis = {'title': 'Median Home Price ($USD)'},
xaxis = {'title': 'Year'},
title = {'text': 'Median Home Price vs. Year for {}'.format(value),
'font': {'size': 24}, 'x': 0.5, 'xanchor': 'center'}
),
)
]
I was able to accomplish this in Plotly but can't figure out how to do it in Dash. This is what I want in Dash:
Plotly graph I am trying to reproduce in callback, that is linked to a dropdown menu
This is all I can get to work in Dash:
Only one dataframe plots in Dash
This is the code that works in plotly graph objects:
from statsmodels.tsa.arima_model import ARIMA
df_ml = pd.read_csv('df_ml.csv')
# --- data cleaning ---
df_pred = df_ml[df_ml['RegionName'] == city]
df_pred = df_pred.transpose().reset_index().drop([0])
df_pred.columns = ['Date', 'MedianHomePrice_USD']
df_pred['MedianHomePrice_USD'] = df_pred['MedianHomePrice_USD'].astype('int')
df_pred['Date'] = pd.to_datetime(df_pred['Date'])
df_pred['Date'] = df_pred['Date'].dt.strftime('%Y-%m')
df_model = df_pred.set_index('Date')
model_data = df_model['MedianHomePrice_USD']
def house_price_forecast(model_data, forecast_steps, city):
#--- ARIMA Model (autoregressive integrated moving average) ---
model = ARIMA(model_data, order = (2,1,2), freq = 'MS')
res = model.fit()
forecast = res.forecast(forecast_steps)
forecast_mean = forecast[0]
forecast_se = forecast[1]
forecast_ci = forecast[2]
df_forecast = pd.DataFrame()
df_forecast['Mean'] = forecast_mean.T
df_forecast['Lower_ci'], df_forecast['Upper_ci'] = forecast_ci.T
df_forecast['Date'] = pd.date_range(start = '2021-02', periods = forecast_steps, freq = 'MS')
df_forecast['Date'] = df_forecast['Date'].dt.strftime('%Y-%m')
df_forecast.index = df_forecast['Date']
fig = go.Figure()
fig.add_trace(go.Scatter(x = df_pred['Date'], y = df_pred['MedianHomePrice_USD'],
mode = 'lines', name = 'Median Home Price ($USD)',
line_color = 'rgba(49, 131, 189, 0.75)', line_width = 2))
fig.add_trace(go.Scatter(x = df_forecast.index, y = df_forecast['Mean'],
mode = 'lines', line_color = '#e6550d',
name = 'Forecast mean'))
fig.add_trace(go.Scatter(x = df_forecast.index, y = df_forecast['Upper_ci'],
mode = 'lines', line_color = '#e0e0e0', fill = 'tonexty',
fillcolor = 'rgba(225,225,225, 0.3)',
name = 'Upper 95% confidence interval'))
fig.add_trace(go.Scatter(x = df_forecast.index, y = df_forecast['Lower_ci'],
mode = 'lines', line_color = '#e0e0e0', fill = 'tonexty',
fillcolor = 'rgba(225,225,225, 0.3)',
name = 'Lower 95% confidence interval'))
fig.update_layout(title = 'Median Home Price in {}, {} - {} (Predicted)'.format(
city, model_data.idxmin()[:-3], df_forecast_mean.idxmax()[:-3]),
xaxis_title = 'Year', yaxis_title = 'Median Home Price ($USD)',
template = 'xgridoff')
fig.show()
house_price_forecast(model_data, 24, 'Denver, CO') #24 month prediction
Perhaps a more succinct way of asking this question: How do I add a trace to an existing Dash graph, with data from a different dataframe, and both traces get updated when the user selects a value from a single dropdown?
Figured it out...
Don't use the syntax I used above in your callback. Put the px.line call inside a variable(fig, in this case), and then use fig.add_scatter to add data from a different dataframe to the graph. Both parts of the graph will update from the callback.
Also, fig.add_scatter doesn't have a dataframe argument, so use df.column or df[column] (ex. 'dfa.Date' below)
# --- import libraries ---
import dash
import dash_core_components as dcc
import dash_html_components as html
import pandas as pd
import plotly.express as px
from dash.dependencies import Output, Input
# --- load data ---
df_h = pd.read_csv('df_h.csv')
df_h['Date'] = pd.to_datetime(df_h['Date'])
df_arima = pd.read_csv('df_arima.csv')
df_arima['Date'] = pd.to_datetime(df_arima['Date'])
df_arima['Date'] = df_arima['Date'].dt.strftime('%Y-%m')
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
# --- initialize the app ---
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
app.layout = html.Div([
dcc.Graph(id = 'forecast-container')
]
)
# --- dropdown callback ---
#app.callback(
Output('forecast-container', 'figure'),
Input('city-dropdown', 'value'))
def update_figure(selected_city):
dff = df_h[['Date', selected_city]]
# dff[selected_city] = dff[selected_city].round(0)
dfa = df_arima[df_arima['City'] == selected_city]
fig = px.line(dff, x = 'Date', y = selected_city,
hover_data = {selected_city: ':$,f'})
fig.add_scatter(x = dfa.Date, y = dfa.Mean,
line_color = 'orange', name = 'Forecast Mean')
fig.add_scatter(x = dfa.Date, y = dfa.Lower_ci,
fill = 'tonexty', fillcolor = 'rgba(225,225,225, 0.3)',
marker = {'color': 'rgba(225,225,225, 0.9)'},
name = 'Lower 95% Confidence Interval')
fig.add_scatter(x = dfa.Date, y = dfa.Upper_ci,
fill = 'tonexty', fillcolor = 'rgba(225,225,225, 0.3)',
marker = {'color': 'rgba(225,225,225, 0.9)'},
name = 'Upper 95% Confidence Interval')
fig.update_layout(template = 'xgridoff',
yaxis = {'title': 'Median Home Price ($USD)'},
xaxis = {'title': 'Year'},
title = {'text': 'Median Home Price vs. Year for {}'.format(selected_city),
'font': {'size': 24}, 'x': 0.5, 'xanchor': 'center'})
return fig
if __name__ == '__main__':
app.run_server(debug = True)

Choropleth map not displaying colors

I have a dataset
data2 = dict(type = 'choropleth',
locations = df3['Code'],
z = df3['Power Consumption KWH'])
layout2 = dict(title = '2014 Global Power Consumption',
geo = dict(showframe = False, projection = {'type': 'natural earth'}))
choromap2 = go.Figure(data = [data2], layout=layout2)
iplot(choromap2)
After that, I created a choropleth, but my problem is the map didn't show any color, it only showed the earth form.
Thanks in advance!
I tried to replicate your data and found you are missing one important parameter i.e locationmode = "country names". Adding this should work
data2 = dict(type = 'choropleth',
locations = df['Code'],
locationmode = "country names", # add this
z = df['Power Consumption KWH'])
layout2 = dict(title = '2014 Global Power Consumption',
geo = dict(showframe = False, projection = {'type': 'natural earth'}))
choromap2 = go.Figure(data = [data2], layout=layout2)
iplot(choromap2)

Choropleth world map not showing all countries

I wanted to make a choropleth world map, which shows the hits(number of searches) of a word, on a World map.
Following is the code:
import plotly
import plotly.offline
import pandas as pd
df = pd.read_excel('F:\\Intern\\csir\\1yr\\news\\region_2016_2017.xlsx')
df = df.query('keyword==["addiction"]')
scl = [[0.0, 'rgb(242,240,247)'],[0.2, 'rgb(218,218,235)'],[0.4, 'rgb(188,189,220)'],\
[0.6, 'rgb(158,154,200)'],[0.8, 'rgb(117,107,177)'],[1.0, 'rgb(84,39,143)']]
data = [dict(
type='choropleth',
colorscale=scl,
locations = df['location'],
z = df['hits'].astype(int),
locationmode = "country names",
autocolorscale = False,
reversescale = False,
marker = dict(
line = dict (
color = 'rgb(180,180,180)',
width = 0.5)),
colorbar = dict(
autotick = False,
title = 'Hits'),)]
layout = dict(
title = 'Addiction keyword 1yr analysis',
geo = dict(
showframe = False,
showcoastlines = False,
projection = dict(
type = 'Mercator'
)
)
)
fig = dict(data = data,layout = layout)
plotly.offline.plot(fig,validate=False,filename = 'd3-world-map.html')
And the plotted map is:
As one can see clearly, many countries are missing. This may be due to the fact that many countries didn't have entries which explicitly stated that they have zero hits.
I don't want to explicitly do that with my data. Is there any other way out of this? So that we can see all of the countries.
Data set can be found here.
Note that the dataset that I've linked is an .csv file whereas the file used in the program is an .xlsx version of the file.
You need to turn on country outlines under layout...
"geo":{
"countriescolor": "#444444",
"showcountries": true
},

Categories