Plotly subplot does not like looped bar plots? - python

I'm trying to create a simple pdf of a subplot consisting of one bar chart and one data table:
The Bar and table function works but when i try and create a subplot plotly gives the following error:
ValueError:
Invalid element(s) received for the 'data' property of
Invalid elements include
I wrote the following: Does it not like that i looped the bar traces?
I first create functions to build the bar chart and data table:
import plotly
import plotly.graph_objects as go
import plotly.figure_factory as ff
import os
import numpy as np
from plotly.subplots import make_subplots
import pandas as pd
def build_barchart(df,columns):
x = df.City.unique() # City column should always be the x axis
fig = go.Figure()
fig.update_layout(barmode='stack')
for column in columns:
Y=df[column].tolist()
fig.add_trace(go.Bar(name=column,x=x,y=Y))
return fig
def build_table(df):
table = ff.create_table(df)
return table
def create_pdf(df,columns):
fig = make_subplots(rows=1, cols=2)
fig.add_trace(
build_barchart(df,columns),
row=1, col=1
)
fig.add_trace(
build_table(df),
row=1, col=2
)
if not os.path.exists("images"):
os.mkdir("images")
fig.write_image("images/fig1.pdf")
return
After creating the build functions i try and use them...
df = pd.read_csv('DATA.csv', delimiter=';')
columns=['%Something','%Dogs','%Cats','%Cars']
table = build_table(df)
bars = build_barchart(df,columns)
fig = make_subplots(rows=1, cols=2)
fig.add_trace(
bars,
row=1, col=1
)
fig.add_trace(
table,
row=1, col=2
)
fig.show()
test data
City;%Something;%Dogs;%Cats;%Cars;Nr Alarms;Nr Block
USA;60;10;5;25;1;1
CANADA;20;10;5;65;2;2
MEXICO;90;5;5;0;3;3
SWEDEN;10;10;5;75;4;4

Please don't take it as an answer. Given that your code has some pitfalls I'm adding a polished version. In particular in case you have duplicated countries x = df.City.astype('str').unique() is not going to work well with Y and you should arrange/check your data before to plot.
import pandas as pd
import plotly.graph_objects as go
from plotly.subplots import make_subplots
df = pd.DataFrame({
'City': {0: 'USA', 1: 'CANADA', 2: 'MEXICO', 3: 'SWEDEN'},
'%Something': {0: 60, 1: 20, 2: 90, 3: 10},
'%Dogs': {0: 10, 1: 10, 2: 5, 3: 10},
'%Cats': {0: 5, 1: 5, 2: 5, 3: 5},
'%Cars': {0: 25, 1: 65, 2: 0, 3: 75},
'Nr Alarms': {0: 1, 1: 2, 2: 3, 3: 4},
'Nr Block': {0: 1, 1: 2, 2: 3, 3: 4}})
def build_subplot(df, columns=None):
if columns is None:
columns = df.columns
# only columns starting with %
columns = [col for col in columns if col.startswith("%")]
fig = make_subplots(rows=1, cols=2,
specs=[
[{"type": "bar"},{"type": "table"}]
]
)
fig.update_layout(barmode='stack')
for column in columns:
fig.append_trace(
go.Bar(x=df["City"],
y=df[column],
name=column),
row=1,col=1)
fig.add_trace(
go.Table(
header=dict(
values=df.columns,
font=dict(size=10),
align="left"
),
cells=dict(
values=df.T.values.tolist(),
align = "left")
),
row=1, col=2
)
return fig
build_subplot(df, ['%Something', '%Dogs', 'Nr Block'])
Addendum: I think it will look better if you can have your subplot with 1 column and 2 rows as some details from the table could be hard to read.

Figured out what i did wrong!
Instead of using plotly.figure_factory's create_table() i used plotly.graph_objects Table()
I also had to define the type of figure used in the creation of the subplot.
The final solution for creating the subplot looks like this:
def build_subplot(df,columns):
x = df.City.astype('str').unique()
fig = make_subplots(rows=1, cols=2,
specs=[
[{"type": "bar"},{"type": "table"}]
]
)
fig.update_layout(barmode='stack')
for column in columns:
Y=df[column].tolist()
fig.append_trace(go.Bar(name=column,x=x,y=Y),row=1,col=1)
fig.add_trace(
go.Table(
header=dict(
values=df.columns,
font=dict(size=10),
align="left"
),
cells=dict(
values=[df[k].tolist() for k in df.columns],
align = "left")
),
row=1, col=2
)
return fig
I hope this helps someone else :)

Related

How to add a Pie chart and grouped Bar chart on plotly express subplots

Below is the syntax used to get the pie chart and grouped bar chart using plotly express subplots
import plotly.express as px
import plotly.graph_objects as go
from plotly.subplots import make_subplots
specs = [[{'type':'pie'}, {"type": "bar"}]]
fig = make_subplots(rows=1, cols=2, specs=specs, shared_yaxes = True,subplot_titles=['Pie Chart',
'Grouped Bar Chart'])
fig.add_trace(go.Pie(
labels = df_pie['index'],
values = df_pie['count'],
hole = 0.6,
marker_colors = ['#353837','#646665', '#8e9492', '#c9d1ce'],
), 1, 1)
fig.add_trace(go.Bar(
x = df_bar['Satisfaction'],
y = df_bar['count'],
base =df_bar['Gender'],
),1,2)
fig.update_layout(showlegend=False,
title=dict(text="Visualization",
font=dict(
family="Arial",
size=20,
color='#283747')
))
fig.show()
and below is the results I get based on the above code,
How can I get the pie chart look like this
and the bar chart look like this
by plotly express subplots.
You can achieve this by using textinfo for pie-chart. For bar graph, you will need to create the text you want to show on each bar and then use text to display it. Also, as you are looking for grouped bar plots, you will need to use create two traces and then combine then to the subplot - 1,2. Note that the textposition=auto will select the right way to display the text. In the case of bars, due to length, it has moved the text to be displayed vertically.
As the data was not provided, I created some basic data. Hope this is what you are looking for.
import plotly.express as px
import plotly.graph_objects as go
from plotly.subplots import make_subplots
specs = [[{'type':'pie'}, {"type": "bar"}]]
fig = make_subplots(rows=1, cols=2, specs=specs, shared_yaxes = True, subplot_titles=['Pie Chart', 'Grouped Bar Chart'])
##My data creation##
df_pie=pd.DataFrame({'index':[1,2,3,4], 'count':[442,459,289,280]})
df_bar=pd.DataFrame({'Satisfaction': ['Excellent', 'Excellent', 'Good', 'Good', 'Poor', 'Poor', 'Neutral', 'Neutral'], 'count': [442, 459, 289, 280, 442, 459, 289, 280], 'Gender': ['Male', 'Female', 'Male', 'Female', 'Male', 'Female', 'Male', 'Female']})
fig.add_trace(go.Pie(
labels = df_pie['index'],
values = df_pie['count'],
hole = 0.6,
marker_colors = ['#353837','#646665', '#8e9492', '#c9d1ce'],
textinfo='percent+value', ## ADD - display both
), 1, 1)
## New column to get percentage of row across all Male/Females
df_bar['percentage'] = df_bar.groupby(['Gender'])['count'].transform(lambda z: round(z / z.sum() * 100))
## New column - text of count and percentage - how you need the annotation shown
df_bar['Text']=df_bar['count'].astype(str) + ',' + df_bar['percentage'].astype(str)+'%'
## Create individual traces for Male and Female
trace1 = go.Bar(
x=df_bar[df_bar.Gender == 'Male']['Satisfaction'],
y=df_bar[df_bar.Gender == 'Male']['count'],
name='Male',
text=df_bar[df_bar.Gender == 'Male']['Text'], ## Display text
textposition='auto',
)
trace2 = go.Bar(
x=df_bar[df_bar.Gender == 'Female']['Satisfaction'],
y=df_bar[df_bar.Gender == 'Female']['count'],
name='Female',
text=df_bar[df_bar.Gender == 'Male']['Text'], ## Display text
textposition='auto'
)
fig.append_trace(trace1, 1,2) ## Add as first set of bars in second subplot
fig.append_trace(trace2,1,2) ## Add as second set of bars in second subplot
##Removed your original code
#fig.add_trace(go.Bar(
# x = df_bar['Satisfaction'],
# y = df_bar['count'],
# base =df_bar['Gender'],
# ),1,2)
fig.update_layout(showlegend=False,
title=dict(text="Visualization",
font=dict(
family="Arial",
size=20,
color='#283747')
))
fig.show()

Sorting two plotly bar charts by column value

Let's suppose this is my dataset:
my_data = pd.DataFrame.from_dict({
'Countries':['Australia', 'Canada', 'China', 'Netherlands'],
'A':[1.89,1.45,1.22,0.94],
'B':[0.8,1,1.45,1.6]
})
I have this two bar plots:
I want to order the countries by the value of 'A' in both plots, but I tried a lot of things and just can't. I will be very grateful for any help.
This is my code:
from plotly.subplots import make_subplots
import plotly.graph_objects as go
import pandas as pd
fig = make_subplots(rows=1, cols=2)
fig.add_trace(
go.Bar(
x=my_data['A'],
y=my_data['Countries'],
orientation='h',
text = my_data['A'],
name = 'A'),
row=1, col=1
)
fig.add_trace(
go.Bar(
x=my_data['B'],
y=my_data['Countries'],
orientation='h',
text = my_data['B'],
name = 'B'),
row=1, col=2
)
fig.update_layout(barmode='stack', yaxis={'categoryorder':'total ascending'})
fig.update_layout(height=600, width=1000, title_text="Side By Side Subplots")
fig.show()
There may be a more plotly-specific way to do this, but you can use fig.update_yaxes(categoryorder='array', categoryarray=desired_order) to update all the subplot y axes, where desired_order is a list to use for ordering. So if you want to order by column A, desired_order = my_data.sort_values('A')['Countries'].to_list(). All together:
from plotly.subplots import make_subplots
import plotly.graph_objects as go
import pandas as pd
my_data = pd.DataFrame.from_dict({
'Countries':['Australia', 'Canada', 'China', 'Netherlands'],
'A':[1.89,1.45,1.22,0.94],
'B':[0.8,1,1.45,1.6]
})
desired_order = my_data.sort_values('A')['Countries'].to_list()
print(desired_order)
fig = make_subplots(rows=1, cols=2)
fig.add_trace(
go.Bar(
x=my_data['A'],
y=my_data['Countries'],
orientation='h',
text = my_data['A'],
name = 'A'),
row=1, col=1
)
fig.add_trace(
go.Bar(
x=my_data['B'],
y=my_data['Countries'],
orientation='h',
text = my_data['B'],
name = 'B'),
row=1, col=2
)
fig.update_yaxes(categoryorder='array', categoryarray=desired_order)
fig.update_layout(barmode='stack')
fig.update_layout(height=600, width=1000, title_text="Side By Side Subplots")
fig.show()
produces
I couldn't get the same behavior using just update_layout, not sure if that's a bug or a usage issue (I don't know plotly super well).
Edit: I figured out how to use update_layout. As noted in this thread: https://community.plotly.com/t/fig-update-layout-only-affecting-first-subplot/29648 , yaxis is only the first subplot, you can specify yaxis2 to reference the second subplot. So if you want to use update_layout (and not update_yaxes), you could do the following:
fig.update_layout(barmode='stack', yaxis={'categoryorder':'array', 'categoryarray':desired_order},
yaxis2={'categoryorder':'array', 'categoryarray':desired_order})

How to get rid of scribbled lines in plotly line plot?

I am trying to plot a subplot using plotly where I have some line plots and all the plots in the subplot needs to share the same x-axis as shown.
fig = make_subplots(
rows=5,
cols=1,
vertical_spacing=0.05,
subplot_titles=['Count / Anzahl', 'min_nValue', 'max_nValue', 'avg_nValue', 'sum_nValue'],
shared_xaxes=True,
)
fig.append_trace(go.Scatter(
x=df_dict_nValueAgg['Erste_15_Minuten']['KurzName'],
y=df_dict_nValueAgg['Erste_15_Minuten']['min_nValueNorm'],
name = "min_nValue_" + "Erste_15_Minuten",
mode='lines+markers',
#legendgroup = 2
), row=2, col=1)
fig.append_trace(go.Scatter(
x=df_dict_nValueAgg['Erste_15_Minuten']['KurzName'],
y=df_dict_nValueAgg['Erste_15_Minuten']['max_nValueNorm'],
name = "max_nValue_" + "Erste_15_Minuten",
mode='lines+markers',
#legendgroup = 2
), row=2, col=1)
.
.
.
# couple of plots more
.
.
fig.update_layout(
legend_orientation="v",
height=1000,
width=2000,
title_text=currentEventTitle+pastEventTitle+nAttributes,
)
fig.update_xaxes(tickangle=45)
fig.write_image('fig1.png')
fig.show()
which gives me this figure
So I filter the data for each
The last three plots produces scribbled lines. Now I understand that since I am filtering the data based on four values of a column i.e. Erste_15_Minuten, Zweite_15_Minuten, Dritte_15_Minuten and Letzte_15_Minuten the number of xticks for the last three plots are unequal or maybe in different order. Is there a way where I can avoid this problem? Switching to Bar Plot would avoid this problem but I need to use only line plot. Thank you in advance.
from looking at your code. There are multiple data frames of same format in a dict
there is no guarantee that these dataframes are in same KurzName order
have simulated data to match above understanding
then have provided a way to re-order data frames to be consistent with third for generating line plots
merge to first dataframe on KurzName
use index of first dataframe to define sort order
looking at image
bar chart - ok, not order dependent
first line chart is scribbled, second is not
hence forcing order of data frames has resolved the issue
import numpy as np
import pandas as pd
import plotly.graph_objects as go
from plotly.subplots import make_subplots
# fmt: off
words = ['adipisci', 'aliquam', 'amet', 'consectetur', 'dolor', 'dolore', 'dolorem', 'eius', 'est', 'etincidunt', 'ipsum', 'labore', 'magnam', 'modi', 'neque', 'non', 'numquam', 'porro', 'quaerat', 'quiquia', 'quisquam', 'sed', 'sit', 'tempora', 'ut', 'velit', 'voluptatem']
# fmt: on
r = np.random.choice(words, [2, 30])
r = np.char.add(r[0], np.char.add("_", r[1]))
# Erste_15_Minuten, Zweite_15_Minuten, Dritte_15_Minuten and Letzte_15_Minuten the number
df_dict_nValueAgg = {}
for k in [
"Erste_15_Minuten",
"Zweite_15_Minuten",
"Dritte_15_Minuten",
"Letzte_15_Minuten",
]:
np.random.shuffle(r)
df_dict_nValueAgg[k] = pd.DataFrame(
{
"KurzName": r,
"Count": np.random.randint(100, 300, len(r)),
"min_nValueNorm": np.random.uniform(0, 0.5, len(r)),
"max_nValueNorm": np.random.uniform(0.5, 1, len(r)),
}
)
fig = make_subplots(
rows=5,
cols=1,
vertical_spacing=0.05,
subplot_titles=[
"Count / Anzahl",
"min_nValue",
"max_nValue",
"avg_nValue",
"sum_nValue",
],
shared_xaxes=True,
)
for k in df_dict_nValueAgg.keys():
fig.add_trace(
go.Bar(
x=df_dict_nValueAgg[k]["KurzName"], y=df_dict_nValueAgg[k]["Count"], name=k
),
row=1,
col=1,
)
# this will be scibbled as each dataframe is in a different order
for k in df_dict_nValueAgg.keys():
fig.add_trace(
go.Scatter(
x=df_dict_nValueAgg[k]["KurzName"],
y=df_dict_nValueAgg[k]["max_nValueNorm"],
name=k + " scribble max",
),
row=4,
col=1,
)
# force order of dataframes to be same as first
for i, k in enumerate(df_dict_nValueAgg.keys()):
df = df_dict_nValueAgg[k]
if i > 0:
df = df.merge(
df_dict_nValueAgg[list(df_dict_nValueAgg.keys())[0]]
.loc[:, "KurzName"]
.reset_index(),
on="KurzName",
).sort_values("index")
fig.add_trace(
go.Scatter(
x=df["KurzName"],
y=df["max_nValueNorm"],
name=k + " max",
),
row=5,
col=1,
)
fig

Plotly: How to add volume to a candlestick chart

code:
from plotly.offline import init_notebook_mode, iplot, iplot_mpl
def plot_train_test(train, test, date_split):
data = [Candlestick(x=train.index, open=train['open'], high=train['high'], low=train['low'], close=train['close'],name='train'),
Candlestick(x=test.index, open=test['open'], high=test['high'], low=test['low'], close=test['close'],name='test')
]
layout = {
'shapes': [
{'x0': date_split, 'x1': date_split, 'y0': 0, 'y1': 1, 'xref': 'x', 'yref': 'paper',
'line': {'color': 'rgb(0,0,0)', 'width': 1}}],
'annotations': [{'x': date_split, 'y': 1.0, 'xref': 'x', 'yref': 'paper', 'showarrow': False, 'xanchor': 'left','text': ' test data'},
{'x': date_split, 'y': 1.0, 'xref': 'x', 'yref': 'paper', 'showarrow': False, 'xanchor': 'right', 'text': 'train data '}] }
figure = Figure(data=data, layout=layout)
iplot(figure)
The above code is ok.But now I want to 'volume' in this candlestick chart
code:
from plotly.offline import init_notebook_mode, iplot, iplot_mpl
def plot_train_test(train, test, date_split):
data = [Candlestick(x=train.index, open=train['open'], high=train['high'], low=train['low'], close=train['close'],volume=train['volume'],name='train'),
Candlestick(x=test.index, open=test['open'], high=test['high'], low=test['low'],close=test['close'],volume=test['volume'],name='test')]
layout = {
'shapes': [
{'x0': date_split, 'x1': date_split, 'y0': 0, 'y1': 1, 'xref': 'x', 'yref': 'paper',
'line': {'color': 'rgb(0,0,0)', 'width': 1}}
],
'annotations': [
{'x': date_split, 'y': 1.0, 'xref': 'x', 'yref': 'paper', 'showarrow': False, 'xanchor': 'left',
'text': ' test data'},
{'x': date_split, 'y': 1.0, 'xref': 'x', 'yref': 'paper', 'showarrow': False, 'xanchor': 'right',
'text': 'train data '}
]
}
figure = Figure(data=data, layout=layout)
iplot(figure)
error:
ValueError: Invalid property specified for object of type
plotly.graph_objs.Candlestick: 'volume'
If you looking add smaller subplot of volume just below OHLC chart, you can use:
rows and cols to specify the grid for subplots.
shared_xaxes=True for same zoom and filtering
row_width=[0.2, 0.7] to change height ratio of charts. ie. smaller volume chart than OHLC
Plot:
import pandas as pd
import plotly.graph_objects as go
from plotly.subplots import make_subplots
# data
df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/finance-charts-apple.csv')
# Create subplots and mention plot grid size
fig = make_subplots(rows=2, cols=1, shared_xaxes=True,
vertical_spacing=0.03, subplot_titles=('OHLC', 'Volume'),
row_width=[0.2, 0.7])
# Plot OHLC on 1st row
fig.add_trace(go.Candlestick(x=df["Date"], open=df["AAPL.Open"], high=df["AAPL.High"],
low=df["AAPL.Low"], close=df["AAPL.Close"], name="OHLC"),
row=1, col=1
)
# Bar trace for volumes on 2nd row without legend
fig.add_trace(go.Bar(x=df['Date'], y=df['AAPL.Volume'], showlegend=False), row=2, col=1)
# Do not show OHLC's rangeslider plot
fig.update(layout_xaxis_rangeslider_visible=False)
fig.show()
You haven't provided a complete code snippet with a data sample, so I'm going to have to suggest a solution that builds on an example here.
In any case, you're getting that error message simply because go.Candlestick does not have a Volume attribute. And it might not seem so at first, but you can easily set up go.Candlestick as an individual trace, and then include an individual go.Bar() trace for Volumes using:
fig = make_subplots(specs=[[{"secondary_y": True}]])
fig.add_traces(go.Candlestick(...), secondary_y=True)
fig.add_traces(go.Bar(...), secondary_y=False)
Plot:
Complete code:
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import pandas as pd
# data
df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/finance-charts-apple.csv')
# Create figure with secondary y-axis
fig = make_subplots(specs=[[{"secondary_y": True}]])
# include candlestick with rangeselector
fig.add_trace(go.Candlestick(x=df['Date'],
open=df['AAPL.Open'], high=df['AAPL.High'],
low=df['AAPL.Low'], close=df['AAPL.Close']),
secondary_y=True)
# include a go.Bar trace for volumes
fig.add_trace(go.Bar(x=df['Date'], y=df['AAPL.Volume']),
secondary_y=False)
fig.layout.yaxis2.showgrid=False
fig.show()
Here is my improvement implementation based on the previous answer by Vestland, with some labelling and colouring improvements.
import plotly.graph_objects as go
from plotly.subplots import make_subplots
candlesticks = go.Candlestick(
x=candles.index,
open=candles['open'],
high=candles['high'],
low=candles['low'],
close=candles['close'],
showlegend=False
)
volume_bars = go.Bar(
x=candles.index,
y=candles['volume'],
showlegend=False,
marker={
"color": "rgba(128,128,128,0.5)",
}
)
fig = go.Figure(candlesticks)
fig = make_subplots(specs=[[{"secondary_y": True}]])
fig.add_trace(candlesticks, secondary_y=True)
fig.add_trace(volume_bars, secondary_y=False)
fig.update_layout(title="ETH/USDC pool after Uniswap v3 deployment", height=800)
fig.update_yaxes(title="Price $", secondary_y=True, showgrid=True)
fig.update_yaxes(title="Volume $", secondary_y=False, showgrid=False)
fig.show()
You can find the full source code in this open-source notebook.
If you want to add different colors for buy/sell isay 'green'/'red', you can use some libs (e.g. mplfinance) which do these automatically however the plots are non-interactive. To get interactive plot with plotly with separate colors for buy/sell colors, one needs to add trace for each data point. Here is code:
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import pandas as pd
# Create subplots and mention plot grid size
title=df.symbol.unique()[0]
fig = make_subplots(rows=2, cols=1, shared_xaxes=True,
vertical_spacing=0.02,
row_width=[0.25, 0.75])
# Plot OHLC on 1st row
fig.add_trace(go.Candlestick(x=df.index,
open=df['open'], high=df['high'],
low=df['low'], close=df['close'],showlegend=False),row=1, col=1,)
# Bar trace for volumes on 2nd row without legend
# fig.add_trace(go.Bar(x=df.index, y=df['volume'], showlegend=False), row=2, col=1)
df['color']=''
df['color']=['red' if (x>y) else t for x,y,t in zip(df['open'],df['close'],df['color'])]
df['color']=['green' if (x<y) else t for x,y,t in zip(df['open'],df['close'],df['color'])]
colors=df.color.tolist()
df['prev_color']=[colors[0]]+colors[:(len(colors)-1)]
df.loc[((df.open==df.close) & (df.color=='')),'color']=[z for x,y,z,t in zip(df['open'],df['close'],df['prev_color'],df['color']) if (x==y and t=='')]
colors=df.color.tolist()
df['prev_color']=[colors[0]]+colors[:(len(colors)-1)]
df.loc[((df.open==df.close) & (df.color=='')),'color']=[z for x,y,z,t in zip(df['open'],df['close'],df['prev_color'],df['color']) if (x==y and t=='')]
markers=['green','red']
for t in markers:
df_tmp=df.loc[~(df.color==t)] ## somehow the color it takes is opposite so take negation to
fig.add_trace(go.Bar(x=df_tmp.index, y=df_tmp['volume'], showlegend=False), row=2, col=1)
# Do not show OHLC's rangeslider plot
fig.update(layout_xaxis_rangeslider_visible=False)
fig.layout.yaxis2.showgrid=False
fig.update_layout(title_text=title,title_x=0.45)
fig.show()
My two cents on Plotting Volume in a different subplot with colors, it is just making #user6397960 response shorter without hacks to get the right color, just use marker_color. Think about it, what makes a candle green? The fact of having Close price above the Open price, and what about red candle? well, having a close price below the open price, so with this basics:
import plotly.graph_objects as go
from plotly.subplots import make_subplots
# Create a Figure with 2 subplots, one will contain the candles
# the other will contain the Volume bars
figure = make_subplots(rows=2, cols=1, shared_xaxes=True, row_heights=[0.7, 0.3])
# Plot the candles in the first subplot
figure.add_trace(go.Candlestick(x=df.index, open=df.open, high=df.high, low=df.low, close=df.close, name='price',
increasing_line_color='#26a69a', decreasing_line_color='#ef5350'),
row=1, col=1)
# From our Dataframe take only the rows where the Close > Open
# save it in different Dataframe, these should be green
green_volume_df = df[df['close'] > df['open']]
# Same for Close < Open, these are red candles/bars
red_volume_df = df[df['close'] < df['open']]
# Plot the red bars and green bars in the second subplot
figure.add_trace(go.Bar(x=red_volume_df.index, y=red_volume_df.volume, showlegend=False, marker_color='#ef5350'), row=2,
col=1)
figure.add_trace(go.Bar(x=green_volume_df.index, y=green_volume_df.volume, showlegend=False, marker_color='#26a69a'),
row=2, col=1)
# Hide the Range Slider
figure.update(layout_xaxis_rangeslider_visible=False)
figure.update_layout(title=f'BTC/USDT', yaxis_title=f'Price')
figure.update_yaxes(title_text=f'Volume', row=2, col=1)
figure.update_xaxes(title_text='Date', row=2)
References
https://plotly.com/python/subplots/
https://plotly.com/python/candlestick-charts/

How to use dropdown for data selection in Python and Plotly

I have grouped data with more than 2 groups. I then plot it with a grouped barchart using plotly for a specific set of 2 groups. How can I create 2 dropdown menus that select which group to be plotted as trace1 and which group as trace2?
The example below uses hardcoded groups 1 for trace1 and group 2 for trace2. I would like to control these with the dropdown menus.
import pandas as pd
import plotly as py
import plotly.graph_objs as go
d = {'x': ['a','b','c','a','b','c','a','b','c'], 'y': [1,2,3,10,20,30,100,200,300], 'group': [1,1,1,2,2,2,3,3,3]}
df = pd.DataFrame(data=d)
trace1 = go.Bar(
x=df['x'],
y=df[df['group']==1].y,
name='trace1'
)
trace2 = go.Bar(
x=df['x'],
y=df[df['group']==2].y,
name='trace2'
)
data = [trace1, trace2]
layout = go.Layout(
barmode='group'
)
fig = go.Figure(data=data, layout=layout)
py.offline.plot(fig, filename='grouped-bar')
Plot:
The following suggestion should let you do exactly what you're looking for.
Just select the source of the traces using the two dropdown menus:
Plot 1 - Selection is group 1 vs group 1:
Plot 2 - Selection is group 2 vs group 3:
Code:
# Imports
import plotly.graph_objs as go
import pandas as pd
import numpy as np
# data
d = {'x': ['a','b','c','a','b','c','a','b','c'], 'y': [1,2,3,10,20,30,100,200,300], 'group': [1,1,1,2,2,2,3,3,3]}
df = pd.DataFrame(data=d)
# split df by groups and organize them in a dict
groups = df['group'].unique().tolist()
dfs={}
for g in groups:
dfs[str(g)]=df[df['group']==g]
# get column names from first dataframe in the dict
#colNames = list(dfs[list(dfs.keys())[0]].columns)
#colNames=colNames[:2]
# one trace for each column per dataframe
fig=go.Figure()
# set up the first trace
fig.add_trace(go.Bar(x=dfs['1']['x'],
y=dfs['1']['y'],
visible=True)
)
# set up the second trace
fig.add_trace(go.Bar(x=dfs['1']['x'],
y=dfs['1']['y'],)
)
#f=fig.to_dict()
# plotly start
# buttons for menu 1, names
updatemenu=[]
buttons=[]
# button with one option for each dataframe
for df in dfs.keys():
#print(b, df)
buttons.append(dict(method='restyle',
label=df,
visible=True,
args=[{'y':[dfs[str(df)]['y'].values],
'type':'bar'}, [0]],
)
)
# another button with one option for each dataframe
buttons2=[]
for df in dfs.keys():
buttons2.append(dict(method='restyle',
label=df,
visible=True,
args=[{'y':[dfs[str(df)]['y'].values],
'type':'bar'}, [1]],
)
)
# some adjustments to the updatemenus
updatemenu=[]
your_menu=dict()
updatemenu.append(your_menu)
your_menu2=dict()
updatemenu.append(your_menu2)
#updatemenu[1]
updatemenu[0]['buttons']=buttons
updatemenu[0]['direction']='down'
updatemenu[0]['showactive']=True
updatemenu[1]['buttons']=buttons2
updatemenu[1]['y']=0.5
# add dropdown menus to the figure
fig.update_layout(showlegend=False, updatemenus=updatemenu)
# add notations to the dropdown menus
fig.update_layout(
annotations=[
go.layout.Annotation(text="<b>group/<br>trace:</b>",
x=-0.15, xref="paper",
y=1.15, yref="paper",
align="left", showarrow=False),
go.layout.Annotation(text="<b>group/<br>trace:</b>",
x=-0.15, xref="paper", y=0.6,
yref="paper", showarrow=False),
]
)
fig.show()

Categories