Plotly Waterfall Chart Stacked in Python - python-3.x

All,
I can create a nice little waterfall chart using the code below. However, I would like to be able to split the data to show contributions to that specific metric.
For example I would like to split the waterfall bars by Local and International. ie. the Sales would have both Local and International adding up to the total. Can this be done.
import plotly.graph_objects as go
fig = go.Figure(go.Waterfall(
name = "20", orientation = "v",
measure = ["relative", "relative", "total", "relative", "relative", "total"],
x = ["Sales", "Consulting", "Net revenue", "Purchases", "Other expenses", "Profit before tax"],
textposition = "outside",
text = ["+60", "+80", "", "-40", "-20", "Total"],
y = [60, 80, 0, -40, -20, 0],
connector = {"line":{"color":"rgb(63, 63, 63)"}},
))
fig.update_layout(
title = "Profit and loss statement 2018",
showlegend = True
)
fig.show()

Related

Diamond overlays x-axis in forest plot

I cannot seem to be able to shift down the x-axis so it does not overlay the overall effect. Any suggestions please?
I have several subgroups in my figure and this happens to the sub-group overall effects too (they overlay slightly the individual effects). It is quite a large forest plot.
setEPS()
postscript("myFig.eps", width = 20, height = 20)
forest.meta.myv (Fig_subgroup, xlim=c(0, 14), digits = 1, smlab=" ", xlab= "Title",
rightcols = c("event","time", "effect.ci"), rightlabs = c("cases", "time", "Rate"), col.by = 2,
leftcols = c("studlab", "AA", "BB", "CC"),
leftlabs = c("Reference", "AA", "BB", "CC"),
colgap.forest.left = "15mm", colgap.forest.right = "5mm", colgap.forest = "100mm",
bylab = "", byseparator = ": ",
digits.tau2=1,
digits.I2=1,
digits.pval.Q=3,
col.inside = "black",
text.random.w = "Subgroup",
just.addcols.right = "right",
just.addcols.left = "left",
squaresize= 5,
col.diamond="white",
col.diamond.lines="black",
col.square = "darkgrey",
)
dev.off()
enter image description here

Capture which items are displayed on Python Plotly Express chart based on legend selection

DISTRIB_DESCRIPTION="Ubuntu 20.04.5 LTS"
Streamlit, version 1.12.2
plotly==5.10.0
I have a Plotly Express px.scatter chart being generated in a Streamlit page. The different data points available to be shown are set by the color= parameter in ...
fig = px.scatter(x=df[x_column_name],
y=df[y_column_name],
color=df[color_column_name])
Which data (color) points are actually shown on the chart can be selected in the legend (see images.)
Is there a way to detect in the code (via the fig or something else) which data points (colors) have actually been selected in the legend to appear on the chart? I.e. In the example pictures, for the Streamlit (Python) code to know that only DMP, OTP, and BP are currently being seen on the plotly chart?
All selected
None selected
DMP, OTP, BP selected
FULL CODE
def control_chart_by_compound(df,
x_column_name,
y_column_name,
color_column_name,
chart_width = 800,
marker_line_width = 1,
standard_deviation = False,
stddev_colors = ["#CCFF00","#FFCC99","#FF9966"],
average = False,
average_color = "green",
custom_marker_lines = [],
custom_marker_lines_colors = []
):
if custom_marker_lines_colors == []:
custom_marker_lines_colors = CSS_blues()
fig = px.scatter(x=df[x_column_name],
y=df[y_column_name],
color=df[color_column_name],
width=chart_width,
labels={
"x": x_column_name,
"y": y_column_name,
color_column_name: "Compounds"
},
)
# Adds buttons select or deselect all amongst the legend (default the compounds as different colors)
fig.update_layout(dict(updatemenus=[
dict(
type = "buttons",
direction = "left",
buttons=list([
dict(
args=["visible", "legendonly"],
label="Deselect All compounds",
method="restyle"
),
dict(
args=["visible", True],
label="Select All compounds",
method="restyle"
)
]),
pad={"r": 10, "t": 10},
showactive=False,
x=1,
xanchor="right",
y=1.1,
yanchor="top"
),
]
))
if average != False:
fig.add_hline(y=np.average(df[y_column_name]),
line_color=average_color,
line_width=marker_line_width,
line_dash="dash")
# Add zero hline
fig.add_hline(y=0, line_color="gainsboro")
### Standard deviations
if standard_deviation != False:
stddev = df[y_column_name].std()
for idx, color in enumerate(stddev_colors):
fig.add_hline(y=stddev * (idx+1), line_color=color, line_width=marker_line_width,)
fig.add_hline(y=-stddev * (idx+1), line_color=color, line_width=marker_line_width,)
for idx, line in enumerate(custom_marker_lines):
fig.add_hline(y=line, line_color=custom_marker_lines_colors[idx], line_width=marker_line_width,)
fig.add_hline(y=-line, line_color=custom_marker_lines_colors[idx], line_width=marker_line_width,)
# Background to clear
fig.update_layout({
'plot_bgcolor': 'rgba(0, 0, 0, 0)',
'paper_bgcolor': 'rgba(0, 0, 0, 0)',
})
fig.update_layout(xaxis=dict(showgrid=False),
yaxis=dict(showgrid=False))
return fig

Specify colors in a Gadfly (Julia) boxplot

I am trying to reproduce this Seaborn plot using Gadfly.
The code I have so far is:
using CSV, DataFrames, Gadfly
download("https://raw.githubusercontent.com/mwaskom/seaborn-data/master/tips.csv", "tips.csv")
tips = DataFrame(CSV.File("tips.csv"));
plot(
tips,
x = :day,
y = :total_bill,
color = :smoker,
Geom.boxplot,
Scale.x_discrete(levels = ["Thur", "Fri", "Sat", "Sun"]),
Theme(
key_position = :top,
boxplot_spacing = 20px
),
)
I would like to specify the colors "green" and "purple" to match the Seaborn plot.
Any suggestions how to do this in Gadfly?
Additional:
How to set the smoker order from yes to no?
You need to add a line with Scale.color_discrete_manual:
using CSV, DataFrames, Gadfly
download(
"https://raw.githubusercontent.com/mwaskom/seaborn-data/master/tips.csv",
"tips.csv",
)
tips = DataFrame(CSV.File("tips.csv"));
plot(
tips,
x = :day,
y = :total_bill,
color = :smoker,
Geom.boxplot,
Scale.x_discrete(levels = ["Thur", "Fri", "Sat", "Sun"]),
Scale.color_discrete_manual("purple", "green", order=[2, 1]),
Theme(key_position = :top, boxplot_spacing = 20px),
)

Adding labels to bokeh pie chart wedge

I am new to bokeh, and want to render a pie chart using bokeh figure.
I used the reference from https://docs.bokeh.org/en/latest/docs/gallery/pie_chart.html in order to create my pie chart figure.
Now, I need to add on each part of the pie chart a label which represent the percentage of this part, and the label position should be align to the center.
I could not find a simple way to do it via the documentation, and try to find ways to do it manually, like this example: Adding labels in pie chart wedge in bokeh
I tried to create a label set and add the layout to the plot but i could not figure out if there is a way to control the label position, size, and font. text_align (right, left, center) does not do the job for me.
Here is my code - this function create and return an html of the pie chart
The chart argument contains the relevant data for the chart. in this case its a tuple (size 1), and series[0] contains the name of the series (series.title), list of x values (series.x), and list of y values (series.y)
def render_piechart(self, chart):
"""
Renders PieChart object using Bokeh
:param chart: Pie chart
:return:
"""
series = chart.series[0]
data_dict = dict(zip(series.x, series.y))
data = pd.Series(data_dict).reset_index(name='value').rename(columns={'index': 'Category'})
data['angle'] = data['value'] / data['value'].sum() * 2 * pi
data['color'] = palette[:len(series.x)]
data['percentage'] = data['value'] / data['value'].sum() * 100
data['percentage'] = data['percentage'].apply(lambda x: str(round(x, 2)) + '%')
TOOLTIPS = [('Category', '#Category'), ('Value', '#value'), ('Percentage', '#percentage')]
fig = figure(title=series.title,
plot_width=400 if chart.sizehint == 'medium' else 600,
plot_height=350 if chart.sizehint == 'medium' else 450,
tools='hover', tooltips=TOOLTIPS, x_range=(-0.5, 1.0))
fig.wedge(x=0, y=1, radius=0.45, start_angle=cumsum('angle', include_zero=True),
end_angle=cumsum('angle'), line_color='white', fill_color='color',
legend='Category', source=data)
fig.title.text_font_size = '20pt'
source = ColumnDataSource(data)
labels = LabelSet(x=0, y=1, text='percentage', level='glyph', angle=cumsum('angle', include_zero=True),
source=source, render_mode='canvas')
fig.add_layout(labels)
fig.axis.axis_label = None
fig.axis.visible = False
fig.grid.grid_line_color = None
return bokeh.embed.file_html(fig, bokeh.resources.CDN)
And this is the results:
pie chart consist of 3 parts
pie chart consist of 10 parts
in the 2 examples - the series title is 'kuku'
x and y values for the first example:
x=["A", "B", "C"]
y=[10, 20, 30]
and for the second example:
x=["A", "B", "C", "D", "E", "F", "G", "H", "I"]
y=[10, 20, 30, 100, 90, 80, 70, 60, 30 , 40 ,50]
I know that in the past i could do it easily with Donut but it is deprecated.
I want to be able to get something like this one:
example1
or this: example2
The problem, as you understand, is here:
labels = LabelSet(x=0, y=1, text='percentage', level='glyph', angle=cumsum('angle', include_zero=True), source=source, render_mode='canvas')
It's a bit confusing to create labels in Bokeh, but still:
you should add columns like 'text_pos_x' and 'text_pos_y' for every row you draw and fill it in with coordinates where you would like to place the text. And then apply it in LabelSet function, giving x='text_pos_x' and y='text_pos_y' so that every single part of plot have its own coordinates where to place a label:
labels = LabelSet(x='text_pos_x', y='text_pos_y', text='percentage', level='glyph', angle=0, source=source, render_mode='canvas')
and yes, it's necessary to set angle = 0 to avoid text being rotated.
To complete #Higem 's answer I would suggest you some formula to centre your labels correctly on your pie chart. I modified your code as follows:
def render_piechart(self, chart):
"""
Renders PieChart object using Bokeh
:param chart: Pie chart
:return:
"""
radius = 0.45 # Radius of your pie chart
series = chart.series[0]
data_dict = dict(zip(series.x, series.y))
data = pd.Series(data_dict).reset_index(name='value').rename(columns={'index': 'Category'})
data['angle'] = data['value'] / data['value'].sum() * 2 * pi
data['color'] = palette[:len(series.x)]
data['percentage'] = data['value'] / data['value'].sum() * 100
data['percentage'] = data['percentage'].apply(lambda x: str(round(x, 2)) + '%')
# Projection on X and Y axis for label positioning
data['label_x_pos'] = np.cos(data['angle'].cumsum()-data['angle'].div(2))*3*radius/4
data['label_y_pos'] = np.sin(data['angle'].cumsum()-data['angle'].div(2))*3*radius/4
TOOLTIPS = [('Category', '#Category'), ('Value', '#value'), ('Percentage', '#percentage')]
fig = figure(title=series.title,
plot_width=400 if chart.sizehint == 'medium' else 600,
plot_height=350 if chart.sizehint == 'medium' else 450,
tools='hover', tooltips=TOOLTIPS, x_range=(-0.5, 1.0))
fig.wedge(x=0, y=0, radius=radius, start_angle=cumsum('angle', include_zero=True),
end_angle=cumsum('angle'), line_color='white', fill_color='color',
legend='Category', source=data) # Change center of the pie chart to (0, 0)
fig.title.text_font_size = '20pt'
source = ColumnDataSource(data)
labels = LabelSet(x='label_x_pos', y='label_y_pos', text='percentage', level='glyph', text_align='center', source=source, render_mode='canvas')
fig.add_layout(labels)
fig.axis.axis_label = None
fig.axis.visible = False
fig.grid.grid_line_color = None
return bokeh.embed.file_html(fig, bokeh.resources.CDN)
The result is the following:
I used the basic formula to convert polar coordinates to cartesian coordinates, see Wikipedia.

How to do a threshold line on a bar chart using plotly

Currently written this code that produces a bar chart but would like to add a threshold line. Could anyone help me please?
def make_bar_chart(data):
"""Takes a list of dicts with a time and price"""
# Times
chart_x = []
# Prices
chart_y = []
# Create the relevant arrays
for item in data:
chart_x.append(item["time"])
chart_y.append(item["price"])
# Make the chart
the_graph = Bar(x = chart_x, y = chart_y , name = "Stocks")
graph_data = Data([the_graph])
the_layout = Layout(title = "Stocks", xaxis = dict(title = "Time"), yaxis = dict(title = "Price"))
the_figure = Figure(data = graph_data, layout = the_layout)
plotly.offline.plot(the_figure, filename = "stocks.html")
Try something like this. In plotly it seems that lines are provided via shapes.
the_layout = Layout(title = "Stocks",
xaxis = dict(title = "Time"),
yaxis = dict(title = "Price"),
shapes=[
{
'type': 'line',
'xref': 'paper',
'x0': 0,
'y0': 100, # use absolute value or variable here
'x1': 1,
'y1': 100, # ditto
'line': {
'color': 'rgb(50, 171, 96)',
'width': 1,
'dash': 'dash',
},
},
],
)
I haven't tested this as you haven't provided sample data. Well done for supplying code on your first question, but on Stack Overflow it's best to provide a completely self-contained example that people can copy and run 'as is.'

Resources