Combine Bar and line plot in plotly - python

I have this kind of data:
qq=df = pd.DataFrame(
{
"Predicted": np.sort(np.random.uniform(3, 15, 4)),
"real": np.sort(np.random.uniform(3, 15, 4)),
"Category":['A','B','C','D'],
"new_val":np.random.uniform(3,15,4)
}
)
I am plotting Bar plot:
I want to add this plot line plot of 'real' variable.
I am using the following command:
px.bar(qq, x=qq['Category'], y=['Predicted', 'real', 'new_val'], title="Long-Form Input").add_trace(px.line(x=qq['Category'], y=qq['real']))
But this gives me the error:
Where am I wrong?

you want to add the traces from px.line() not the figure. Hence .data
have also updated the traces from px.line() so it will show in the legend
import pandas as pd
import plotly.express as px
qq = pd.DataFrame(
{
"Predicted": np.sort(np.random.uniform(3, 15, 4)),
"real": np.sort(np.random.uniform(3, 15, 4)),
"Category": ["A", "B", "C", "D"],
"new_val": np.random.uniform(3, 15, 4),
}
)
px.bar(
qq, x="Category", y=["Predicted", "real", "new_val"], title="Long-Form Input"
).add_traces(
px.line(qq, x="Category", y="real").update_traces(showlegend=True, name="real").data
)
second yaxis
Update per comments
import pandas as pd
import plotly.express as px
qq = pd.DataFrame(
{
"Predicted": np.sort(np.random.uniform(3, 15, 4)),
"real": np.sort(np.random.uniform(3, 15, 4)),
"Category": ["A", "B", "C", "D"],
"new_val": np.random.uniform(3, 15, 4),
}
)
px.bar(
qq, x="Category", y=["Predicted", "real", "new_val"], title="Long-Form Input"
).add_traces(
px.line(qq, x="Category", y="real").update_traces(showlegend=True, name="real", yaxis="y2").data
).update_layout(yaxis2={"side":"right", "overlaying":"y"})

Related

Is there a way to control which vertices connect in a plotly.express.line_geo map?

I'm trying to make a connection map that has the option to use an animation_frame to show different months/years. Plotly.express has this option, but the plotly.express.line_geo maps seem to just attach the vertices of the network at random. I was looking at these examples from https://plotly.com/python/lines-on-maps/.
import plotly.express as px
df = px.data.gapminder().query("year == 2007")
fig = px.line_geo(df, locations="iso_alpha",
color="continent", # "continent" is one of the columns of gapminder
projection="orthographic")
fig.show()
Plotly.graph_objects allows you to map actual connections between vertices, but doesn't seem to have an animation option.
import plotly.graph_objects as go
import pandas as pd
df_airports = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/2011_february_us_airport_traffic.csv')
df_airports.head()
df_flight_paths = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/2011_february_aa_flight_paths.csv')
df_flight_paths.head()
fig = go.Figure()
flight_paths = []
for i in range(len(df_flight_paths)):
fig.add_trace(
go.Scattergeo(
locationmode = 'USA-states',
lon = [df_flight_paths['start_lon'][i], df_flight_paths['end_lon'][i]],
lat = [df_flight_paths['start_lat'][i], df_flight_paths['end_lat'][i]],
mode = 'lines',
line = dict(width = 1,color = 'red'),
opacity = float(df_flight_paths['cnt'][i]) / float(df_flight_paths['cnt'].max()),
)
)
fig.show()
Does anyone know of a way that i could make a map like the flight path map, but allow an animation option to look at the flight maps for different months/years?
you can animate any trace type using frames
taking sample flight path data used in question, have split it into groups based on first letter of start airport
there is no need to create a trace per flight, instead create pairs of start end locations in arrays separated by None
with this it is simple to create a frame with a trace for each group
then just create the figure from the frames, plus a default trace
add play button and slider
import plotly.graph_objects as go
import plotly.express as px
import pandas as pd
df_flight_paths = pd.read_csv(
"https://raw.githubusercontent.com/plotly/datasets/master/2011_february_aa_flight_paths.csv"
)
frames = []
# lets split data based on first letter of start airport
# create a frame for each grouping
bins = 6
for color, df in df_flight_paths.groupby(
pd.qcut(
df_flight_paths["airport1"].str[0].apply(ord),
q=bins,
labels=px.colors.qualitative.Plotly[:bins],
)
):
name = f'{df["airport1"].str[0].min()}-{df["airport1"].str[0].max()}'
frames.append(
go.Frame(
name=name,
data=go.Scattergeo(
lon=df.assign(nan=None)[["start_lon", "end_lon", "nan"]].values.ravel(),
lat=df.assign(nan=None)[["start_lat", "end_lat", "nan"]].values.ravel(),
mode="lines",
line=dict(width=1, color=color),
),
)
)
# now create figure and add play button and slider
go.Figure(
data=frames[0].data,
frames=frames,
layout={
"updatemenus": [
{
"type": "buttons",
"buttons": [{"label": "Play", "method": "animate", "args": [None]}],
}
],
"sliders": [
{
"active": 0,
"steps": [
{
"label": f.name,
"method": "animate",
"args": [[f.name]],
}
for f in frames
],
}
],
},
).update_geos(
scope="north america",
)

Align header in Dash DataTable

I want to align the header to the left in a Dash DataTable. I use the example code from the Dash DataTable documentation. Then I have implemented the style_header dicitionary as suggested in the DataTable reference.
from dash import Dash, dash_table
import pandas as pd
from collections import OrderedDict
data = OrderedDict(
[
("Date", ["2015-01-01", "2015-10-24", "2016-05-10", "2017-01-10", "2018-05-10", "2018-08-15"]),
("Region", ["Montreal", "Toronto", "New York City", "Miami", "San Francisco", "London"]),
("Temperature", [1, -20, 3.512, 4, 10423, -441.2]),
("Humidity", [10, 20, 30, 40, 50, 60]),
("Pressure", [2, 10924, 3912, -10, 3591.2, 15]),
]
)
df = pd.DataFrame(data)
app = Dash(__name__)
app.layout = dash_table.DataTable(
data=df.to_dict('records'),
columns=[{'id': c, 'name': c} for c in df.columns],
style_header={'textAlign': 'left'},
style_cell={'textAlign': 'left'}
)
if __name__ == '__main__':
app.run_server(debug=True)
I would expect the same behaviour as in style_cell, but it has no effect, the columns are left-aligned and the header is right-aligned. I'm not sure, but it occurs to me since I have updated to Dash 2.1.0. How can I align the header to the left?
The behavior seems to be caused by these styles
.dash-table-container .dash-spreadsheet-container .dash-spreadsheet-inner .column-header-name {
margin-left: auto;
}
As a workaround you could add your own styles to overwrite the above
div.dash-table-container .dash-spreadsheet-container .dash-spreadsheet-inner .column-header-name {
margin-left: unset;
}

plotly graph_objects (go) selecting two lines from two dropdowns menu to compare in the same figure

i am trying to compare two lines in the same fig in plotly go, the goal is to let the user to select two entities (i.e. countries) from two dropdown menu in the chart
this is the chart with all entities active
when i select different country in the two dropdown:
i.e. dropdown1 = Italy, dropdown2= France
the chart shows just one line (the last selected from the dropdown, so it update)but it doesn't plot the two lines in the same figure
Plot with just one line
this is the dataframe:
dataframe head
this is the code i am working on:
fig1 = go.Figure()
for column in df.columns.to_list():
fig1.add_trace(
go.Scatter(
x = df.index,
y = df[column],
name = column
)
)
button_all = dict(label = 'All',
method = 'restyle',
args = [{'visible': df.columns.isin(df.columns),
'title': 'All',
'showlegend':True}])
button_none = dict(label = 'None',
method = 'update',
args = [{'visible': df.columns.isin(df.columns),
'title': 'All',
'showlegend':True}])
def create_layout_button(column):
return dict(label = column,
method = 'restyle',
args = [{'visible': df.columns.isin([column]),
'title': column,
'showlegend': True}])
def create_layout_buttonE(column):
return dict(label = column,
method = 'update',
args = [{'visible': df.columns.isin([column]),
'title': column,
'showlegend': True}])
addAll = True
# Update plot sizing
fig1.update_layout(
width=700,
height=500,
autosize=False,
margin=dict(t=120, b=0, l=0, r=0),
)
# Add dropdowns
button_layer_1_height = 1.08
fig1.update_layout(
updatemenus=[
dict(
buttons=([button_all] * addAll) + list(df.columns.map(lambda column: create_layout_button(column))),
direction="down",
pad={"r": 10, "t": 0},
showactive=True,
x=0.1,
xanchor="left",
y=button_layer_1_height,
yanchor="top"
),
dict(
buttons=([button_none] * addAll) + list(df.columns.map(lambda column: create_layout_buttonE(column))),
direction="down",
pad={"r": 10, "t": 0},
showactive=True,
x=0.37,
xanchor="left",
y=button_layer_1_height,
yanchor="top"
),
]
)
fig1.show()
synthesized dataframe in same structure as you defined
taken a different approach. Rather than creating all traces and controlling visibility in updatemenus. Create two traces and control name and contents of y in updatemenus
import pandas as pd
import numpy as np
import plotly.graph_objects as go
# get some countries
countries = (
pd.read_csv(
"https://raw.githubusercontent.com/owid/covid-19-data/master/public/data/vaccinations/locations.csv"
)
.loc[lambda d: d["iso_code"].str.len() == 3, "location"]
.sample(20)
.sort_values()
.values
)
# build data frame of same struct
df = pd.DataFrame(
np.random.randint(200, 1500, [22, len(countries)]),
columns=countries,
index=range(2000, 2022),
)
# create a figure with two place holder traces
fig = go.Figure(
[
go.Scatter(x=df.index, y=np.full(len(df), np.nan), meta=i, name=i)
for i in range(2)
]
)
# but data for y and name in when country is selected
fig.update_layout(
updatemenus=[
{
"x": b / 3,
"y": 1.4,
"active": None,
"buttons": [
{
"label": c,
"method": "restyle",
"args": [{"y": [df[c]], "name": c}, [b]],
}
for c in df.columns
],
}
for b in range(2)
]
)

Choropleth Plotly Graph not appearing on Dash

I have a csv file with various states, and their scores for some particular dates. It is being plotted correctly in the VSCode Notebook but when I try to display it on Dash, the default map is appearing but there's no colour coding. I have tried changing the dataset and even the display options but it's still the same. Anyone else facing this?
I am attaching my entire Dash code below for reference
#importing libraries
import dash
from dash import Dash, dcc, html, Input, Output
import plotly.express as px
import pandas as pd
app = Dash(__name__)
#-- Import and clean data (importing csv into pandas)
df = pd.read_csv("C:\Data Science\Jupyter_Workspace\Twitter_Sentiment\Data\JSONLs\\final_df.csv")
#print(df[:5]) #printing out a sample to verify if it's correct or not
# Importing the GeoJSON File
import geojson
with open("C:\Data Science\Jupyter_Workspace\Twitter_Sentiment\Dash Deployment\states_india.geojson") as f:
india_states = geojson.load(f)
# ------------------------------------------------------------------------------
# App layout
app.layout = html.Div([
html.H1("Sentiment Timeline for Covid-19 in India 2021-22", style={'text-align': 'center'}),
dcc.Dropdown(id="selected_date",
options=[
{"label": "March 20, 2020", "value": 20200320},
{"label": "March 25, 2020", "value": 20200325},
{"label": "March 27, 2020", "value": 20200327},
{"label": "March 30, 2020", "value": 20200330}],
multi=False,
value=20200320,
style={'width': "40%"}
),
html.Div(id='output_container', children=[]),
html.Br(),
dcc.Graph(id='sentiment_map', figure={})
])
# ------------------------------------------------------------------------------
# Connect the Plotly graphs with Dash Components
#app.callback(
[Output(component_id='output_container', component_property='children'),
Output(component_id='sentiment_map', component_property='figure')],
[Input(component_id='selected_date', component_property='value')]
)
def update_graph(date_selected):
print(date_selected)
print(type(date_selected))
container = "The date chosen by user was: {}".format(date_selected)
dff = df.copy()
dff = dff[dff["date"] == date_selected]
# Plotly Express
fig = px.choropleth_mapbox(
data_frame = dff,
locations = 'state',
geojson = india_states,
range_color=(-1, 1),
color = 'vader_score',
mapbox_style = "carto-positron",
color_continuous_scale = px.colors.diverging.RdBu,
color_continuous_midpoint = 0,
center = {'lat': 24, 'lon': 78},
zoom = 2.85,
labels = {'vader_score': 'Sentiment Score'},
title = "Sentiment Map"
)
return container, fig
# --------------------------------
if __name__ == '__main__':
app.run_server(debug=True)
data sourcing
using publicly available geojson for geometry
generating a data frame of sentiment data for dates in dash app
NB state column corresponds to ID in geojson
import requests
import pandas as pd
import numpy as np
import plotly.express as px
# fmt off
india_states = requests.get("https://raw.githubusercontent.com/Subhash9325/GeoJson-Data-of-Indian-States/master/Indian_States").json()
df = pd.DataFrame({"name":['Andaman and Nicobar', 'Andhra Pradesh', 'Arunachal Pradesh',
'Assam', 'Bihar', 'Chandigarh', 'Chhattisgarh',
'Dadra and Nagar Haveli', 'Daman and Diu', 'Delhi', 'Goa',
'Gujarat', 'Haryana', 'Himachal Pradesh', 'Jammu and Kashmir',
'Jharkhand', 'Karnataka', 'Kerala', 'Lakshadweep',
'Madhya Pradesh', 'Maharashtra', 'Manipur', 'Meghalaya', 'Mizoram',
'Nagaland', 'Orissa', 'Puducherry', 'Punjab', 'Rajasthan',
'Sikkim', 'Tamil Nadu', 'Tripura', 'Uttar Pradesh', 'Uttaranchal',
'West Bengal'],
"state":[ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34,
35]})
df = pd.concat([df.assign(date=d, vader_score=np.random.uniform(-1,1,len(df))) for d in [20200320, 20200325, 20200327, 20200330]])
# fmt on
dash app
the issue as such was not with dash but creation of Plotly Express figure
the data frame state column needs to link to an id in geonjson
featureidkey="properties.ID_1" https://plotly.com/python/mapbox-county-choropleth/#indexing-by-geojson-properties
# importing libraries
import dash
from dash import Dash, dcc, html, Input, Output
import plotly.express as px
import pandas as pd
from jupyter_dash import JupyterDash
# app = Dash(__name__)
app = JupyterDash(__name__)
# ------------------------------------------------------------------------------
# App layout
app.layout = html.Div(
[
html.H1(
"Sentiment Timeline for Covid-19 in India 2021-22",
style={"text-align": "center"},
),
dcc.Dropdown(
id="selected_date",
options=[
{"label": "March 20, 2020", "value": 20200320},
{"label": "March 25, 2020", "value": 20200325},
{"label": "March 27, 2020", "value": 20200327},
{"label": "March 30, 2020", "value": 20200330},
],
multi=False,
value=20200320,
style={"width": "40%"},
),
html.Div(id="output_container", children=[]),
html.Br(),
dcc.Graph(id="sentiment_map", figure={}),
]
)
# ------------------------------------------------------------------------------
# Connect the Plotly graphs with Dash Components
#app.callback(
[
Output(component_id="output_container", component_property="children"),
Output(component_id="sentiment_map", component_property="figure"),
],
[Input(component_id="selected_date", component_property="value")],
)
def update_graph(date_selected):
print(date_selected)
print(type(date_selected))
container = "The date chosen by user was: {}".format(date_selected)
dff = df.copy()
dff = dff[dff["date"] == date_selected]
# Plotly Express
fig = px.choropleth_mapbox(
data_frame=dff,
locations="state",
geojson=india_states,
featureidkey="properties.ID_1",
range_color=(-1, 1),
color="vader_score",
mapbox_style="carto-positron",
color_continuous_scale=px.colors.diverging.RdBu,
color_continuous_midpoint=0,
center={"lat": 24, "lon": 78},
zoom=2.85,
labels={"vader_score": "Sentiment Score"},
title="Sentiment Map",
)
return container, fig
# --------------------------------
if __name__ == "__main__":
# app.run_server(debug=True)
app.run_server(mode="inline")

Use a slider to shift only one scatter trace in time in plotly

I have a scatter (line) plot with several traces. I would like to be able to shift one trace forward and backward in time, while the others remain fixed.
I can achieve this by creating multiple traces with preset offsets but this is results is a huge file (18mb vs 4mb w/o all the steps). Is it possible to update the data of the trace with the slider?
plot w/o slider:
import pandas as pd
import plotly.graph_objects as go
from plotly.subplots import make_subplots
data = "https://gist.githubusercontent.com/simonmcconnell/42654e09f17805f63b2afa2cdc7166c1/raw/5dbed5a14bc6db6912420d5a1ae4e82e173f6a34/sampledata.csv"
col_names = [
"DATE",
"TIME",
"A",
"B",
"C",
]
dtypes = {
"A": "float64",
"B": "float64",
"C": "float64",
}
df = pd.read_csv(
data,
header=0,
names=col_names,
dtype=dtypes,
parse_dates=[["DATE", "TIME"]],
infer_datetime_format=True,
dayfirst=True,
cache_dates=True,
skipinitialspace=True,
)
fig = make_subplots(specs=[[{"secondary_y": True}]])
fig.add_trace(
go.Scatter(
x=df["DATE_TIME"],
y=df["B"],
mode="lines",
name="B",
),
secondary_y=False,
)
fig.add_trace(
go.Scatter(
x=df["DATE_TIME"],
y=df["C"],
mode="lines",
),
secondary_y=False,
)
fig.add_trace(
go.Scatter(
x=df["DATE_TIME"],
y=df["A"],
mode="lines",
),
secondary_y=True,
)
fig.write_html("no_offset.html", auto_open=True)
plot with slider:
from datetime import timedelta
import pandas as pd
import numpy as np
import humanize
import plotly.graph_objects as go
from plotly.subplots import make_subplots
data = "https://gist.githubusercontent.com/simonmcconnell/42654e09f17805f63b2afa2cdc7166c1/raw/5dbed5a14bc6db6912420d5a1ae4e82e173f6a34/sampledata.csv"
col_names = [
"DATE",
"TIME",
"A",
"B",
"C",
]
dtypes = {
"A": "float64",
"B": "float64",
"C": "float64",
}
df = pd.read_csv(
data,
header=0,
names=col_names,
dtype=dtypes,
parse_dates=[["DATE", "TIME"]],
infer_datetime_format=True,
dayfirst=True,
cache_dates=True,
skipinitialspace=True,
)
fig = make_subplots(specs=[[{"secondary_y": True}]])
sample_period = (df["DATE_TIME"][1] - df["DATE_TIME"][0]).total_seconds()
step_size = 30
sample_count = len(df)
slider_positions = 60
for step in np.arange(0, slider_positions, 1):
fig.add_trace(
go.Scatter(
visible=False,
x=df["DATE_TIME"].truncate(
after=sample_count - step_size * step / sample_period - 1
),
y=df["B"].truncate(before=step_size * step / sample_period - 1),
mode="lines",
name="Offset Trace",
),
secondary_y=False,
)
fig.data[0].visible = True
fig.add_trace(
go.Scatter(
x=df["DATE_TIME"],
y=df["C"],
mode="lines",
),
secondary_y=False,
)
fig.add_trace(
go.Scatter(
x=df["DATE_TIME"],
y=df["A"],
mode="lines",
),
secondary_y=True,
)
steps = []
for i in range(1, slider_positions):
step = dict(
method="restyle", args=["visible", [False] * slider_positions + [True] * 3],
)
step["args"][1][i] = True
step["label"] = humanize.naturaldelta(timedelta(seconds=i * 30))
steps.append(step)
sliders = [
dict(
active=slider_positions,
currentvalue={"prefix": "Offset: "},
pad={"t": 250},
steps=steps,
)
]
fig.update_layout(sliders=sliders)
fig.write_html("time_offset.html", auto_open=True)

Categories

Resources