How can I set the width and height in a plotly theme? - python

Context
I use the .plot method of pandas dataframes throughout a JupyterLab notebook and have set the plotting backend to plotly and the default plotly theme to plotly
Every time I plot I do a .update_layout afterwards to set the width, height and margins. I do that because I plan on exporting the notebook to reveal.js slides, not setting those properties results in unpredictable output.
This is my example code, which creates a 200x200 plot without any margins.
import pandas as pd
import plotly.io as pio
pd.
options.plotting.backend = "plotly"
pio.templates.default = "plotly"
x = [1, 2, 3, 4]
y = [2, 4, 6, 8]
df = pd.DataFrame({"x": x, "y": y})
fig = df.plot(x=x, y=y)
fig.update_layout(width=200, height=200, margin=dict(l=0, r=0, t=0, b=0))
fig.show()
As I want this plot size and margins in all my plots, I wanted to make a theme which I can set at the beginning, such that I don't have to call .udpate_layout on every figure.
What I've tried
I tried this:
import pandas as pd
import plotly.io as pio
# Creat a custom theme and set it as default
pio.templates["custom"] = pio.templates["plotly"]
pio.templates["custom"].layout.margin = dict(l=0, r=0, t=0, b=0)
pio.templates["custom"].layout.width = 200
pio.templates["custom"].layout.height = 200
pio.templates.default = "custom"
x = [1, 2, 3, 4]
y = [2, 4, 6, 8]
df = pd.DataFrame({"x": x, "y": y})
fig = df.plot(x=x, y=y)
fig.show()
The resulting plot doesn't adhere to the size specifications unfortunately. The margin setting is respected though.
Question
How can I create a plotly theme to create plots of a specified size?

Turns out I was missing the autosize property in my template.
When I set it to False:
pio.templates["custom"].layout.autosize = False
a 200x200 plot comes out.

Related

How to add title to the plot of shap.plots.force with Matplotlib?

I want to add some modifications to my force plot (created by shap.plots.force) using Matplotlib, e.g. adding title, using tight layout etc. However, I tried to add title and the title doesn't show up. Any ideas why and how can I add the title using Matplotlib?
import numpy as np
import shap
import matplotlib.pyplot as plt
myBaseline=1.5
shap_values_0 = np.array([-1, -4, 3])
test_point_0 = np.array([11, 12, 13])
features_names = ['a1','a2','a3']
shap.plots.force(myBaseline,shap_values_0,test_point_0,features_names,matplotlib = 1)
plt.suptitle("This is my title") # It doesn't show up, why?
fig = plt.gcf()
fig.canvas.draw()
plt.close()
The last lines in force_plot are:
if show:
plt.show()
else:
return plt.gcf()
so, if you set show = False you can get prepared SHAP plot as figure object and customize it to your needs as usual:
import shap
myBaseline = 1.5
shap_values_0 = np.array([-1, -4, 3])
test_point_0 = np.array([11, 12, 13])
features_names = ["a1", "a2", "a3"]
shap.plots.force(
myBaseline, shap_values_0, test_point_0, features_names, matplotlib=True, show=False
)
plt.title("This is my title", y=1.75)
plt.show()
I had to add show=0 at shap.plots.force, i.e.
shap.plots.force(myBaseline,shap_values_0,test_point_0,features_names,matplotlib = 1, show=0)
I have no idea why it works, but it does.

Plotly: How to make all plots grayscale?

I am using Plotly to generate few line plots in Python. With a sample code like this:
from plotly import offline as plot, subplots as subplot, graph_objects as go
fig = subplot.make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.01)
trace1 = go.Scatter(x = [1, 2, 3], y = [1, 2, 3])
trace2 = go.Scatter(x = [1, 2, 3], y = [4, 5, 6])
fig.append_trace(trace1, 1, 1)
fig.append_trace(trace2, 2, 1)
config_test_plot = {'displaylogo': False, 'displayModeBar': False, 'scrollZoom': True}
test_plot_html = plot.plot(fig, output_type='div', include_plotlyjs=False, config= config_test_plot)
I am able to get the required plots. However, I want to be able to get all my plots in grayscale. I see that none of the Plotly default themes are of this type. Is there anyway I can do this?
You haven't specified whether to assign a grey color scheme for your entire plot, or only for your lines. But just to not make things easy for myself, I'm going to assume the former. In that case, I would:
use template = 'plotly_white' for the figure elements not directly connected to your dataset, and
assign a grey scale to all lines using n_colors(lowcolor, highcolor, n_colors, colortype='tuple').
Example plot:
But as #S3DEV mentions, using the greys color palette could be a way to go too, and this is accesible through:
# In:
px.colors.sequential.Greys
# Out:
# ['rgb(255,255,255)',
# 'rgb(240,240,240)',
# 'rgb(217,217,217)',
# 'rgb(189,189,189)',
# 'rgb(150,150,150)',
# 'rgb(115,115,115)',
# 'rgb(82,82,82)',
# 'rgb(37,37,37)',
# 'rgb(0,0,0)']
And this would work perfectly for your use case with a limited number of lines. In that case you could just use this setup:
from plotly import offline as plot, subplots as subplot, graph_objects as go
from itertools import cycle
fig = subplot.make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.01)
trace1 = go.Scatter(x = [1, 2, 3], y = [1, 2, 3])
trace2 = go.Scatter(x = [1, 2, 3], y = [4, 5, 6])
fig.append_trace(trace1, 1, 1)
fig.append_trace(trace2, 2, 1)
colors = cycle(list(set(px.colors.sequential.Greys)))
f = fig.full_figure_for_development(warn=False)
for d in fig.data:
d.line.color = next(colors)
fig.show()
And get:
And I assume that this is what you were looking for. But one considerable drawback here is that the number of colors in px.colors.sequential.Greys is limited, and I had to use a cycle to assign the line colors of your data. And n_colors(lowcolor, highcolor, n_colors, colortype='tuple') lets you define a starting color, an end color, and a number of colors scaled between them to form a complete scale for all your lines. This will also let you adjust the brightness of the colors to your liking. So you could get this:
...this:
or this:
Here's a complete setup for those figures if you would like to experiment with that as well:
import numpy as np
import pandas as pd
import plotly.graph_objects as go
import plotly.express as px
import datetime
from plotly.colors import n_colors
pd.set_option('display.max_rows', None)
pd.options.plotting.backend = "plotly"
# data sample
nperiods = 200
np.random.seed(123)
cols = 'abcdefghijkl'
df = pd.DataFrame(np.random.randint(-10, 12, size=(nperiods, len(cols))),
columns=list(cols))
datelist = pd.date_range(datetime.datetime(2020, 1, 1).strftime('%Y-%m-%d'),periods=nperiods).tolist()
df['dates'] = datelist
df = df.set_index(['dates'])
df.index = pd.to_datetime(df.index)
df.iloc[0] =1000
df = df.cumsum()#.reset_index()
greys_all = n_colors('rgb(0, 0, 0)', 'rgb(255, 255, 255)', len(cols)+1, colortype='rgb')
greys_dark = n_colors('rgb(0, 0, 0)', 'rgb(200, 200, 200)', len(cols)+1, colortype='rgb')
greys_light = n_colors('rgb(200, 200, 200)', 'rgb(255, 255, 255)', len(cols)+1, colortype='rgb')
greys = n_colors('rgb(100, 100, 100)', 'rgb(255, 255, 255)', len(cols)+1, colortype='rgb')
fig = df.plot(title = 'Greys_light', template='plotly_white', color_discrete_sequence=greys_light)
fig.update_layout(template='plotly_white')
fig.show()

How do I show only available values in the x-axis

I would like to plot a chart with plotly that shows only the existing values in the x-axis.
When I execute the code below, a chart that looks like in the following image appears:
The range on the x-axis as well as the range on the y-axis is evenly set from zero up to the maximal value.
import plotly.graph_objs as go
from plotly.offline import plot
xValues = [1, 2, 27, 50]
yValues = [7, 1, 2, 3]
trace = go.Scatter( x = xValues, y = yValues, mode='lines+markers', name='high limits' )
plottedData = [trace]
plot( plottedData )
Now, I would like to show only the existing values on the x axis. Related to my example, I want just the values [1, 2, 27, 50] to appear. And they should have the same space in between. Is this possible? If yes, how?
You can force the xaxis.type to be category like this:
plot( dict(data=plottedData, layout=go.Layout(xaxis = {"type": "category"} )))

Succint way to add line segments to plotly graph (with python/jupyter notebook)?

I want to create a lollipop plot with several horizontal line segments like this - https://python-graph-gallery.com/184-lollipop-plot-with-2-group. I'd like to use plotly since I prefer the graphics (and easy interactivity) but can't find a succint way.
There's both line graphs (https://plot.ly/python/line-charts/) and you can add lines in the layout (https://plot.ly/python/shapes/#vertical-and-horizontal-lines-positioned-relative-to-the-axes), but both of these solutions require each line segment to be added separately, with about 4-8 lines of code each. While I could just for-loop this, would appreciate if anyone can point me to anything with inbuilt vectorization, like the matplotlib solution (first link)!
Edit: Also tried the following code, to first make the plot ala matplotlib, then convert to plotly. The line segments disappear in the process. Starting to think it's just impossible.
mpl_fig = plt.figure()
# make matplotlib plot - WITH HLINES
plt.rcParams['figure.figsize'] = [5,5]
ax = mpl_fig.add_subplot(111)
ax.hlines(y=my_range, xmin=ordered_df['value1'], xmax=ordered_df['value2'],
color='grey', alpha=0.4)
ax.scatter(ordered_df['value1'], my_range, color='skyblue', alpha=1,
label='value1')
ax.scatter(ordered_df['value2'], my_range, color='green', alpha=0.4 ,
label='value2')
ax.legend()
# convert to plotly
plotly_fig = tls.mpl_to_plotly(mpl_fig)
plotly_fig['layout']['xaxis1']['showgrid'] = True
plotly_fig['layout']['xaxis1']['autorange'] = True
plotly_fig['layout']['yaxis1']['showgrid'] = True
plotly_fig['layout']['yaxis1']['autorange'] = True
# plot: hlines disappear :/
iplot(plotly_fig)
You can use None in the data like this:
import plotly.offline as pyo
import plotly.graph_objs as go
fig = go.Figure()
x = [1, 4, None, 2, 3, None, 3, 4]
y = [0, 0, None, 1, 1, None, 2, 2]
fig.add_trace(
go.Scatter(x=x, y=y))
pyo.plot(fig)
Plotly doesn't provide a built in vectorization for such chart, because it can be done easily by yourself, see my example based on your provided links:
import pandas as pd
import numpy as np
import plotly.offline as pyo
import plotly.graph_objs as go
# Create a dataframe
value1 = np.random.uniform(size = 20)
value2 = value1 + np.random.uniform(size = 20) / 4
df = pd.DataFrame({'group':list(map(chr, range(65, 85))), 'value1':value1 , 'value2':value2 })
my_range=range(1,len(df.index)+1)
# Add title and axis names
data1 = go.Scatter(
x=df['value1'],
y=np.array(my_range),
mode='markers',
marker=dict(color='blue')
)
data2 = go.Scatter(
x=df['value2'],
y=np.array(my_range),
mode='markers',
marker=dict(color='green')
)
# Horizontal line shape
shapes=[dict(
type='line',
x0 = df['value1'].loc[i],
y0 = i + 1,
x1 = df['value2'].loc[i],
y1 = i + 1,
line = dict(
color = 'grey',
width = 2
)
) for i in range(len(df['value1']))]
layout = go.Layout(
shapes = shapes,
title='Lollipop Chart'
)
# Plot the chart
fig = go.Figure([data1, data2], layout)
pyo.plot(fig)
With the result I got:

How can create Python iplot graph, colors changes with value?

Here you are part of my data.
I count my data
count_interests = interests.count()
then made a graph
count_interests.iplot(kind = 'bar', xTitle='Interests', yTitle='Number of Person', colors='Red')
I tried many times to find a function change columns color with values so bigger and smaller columns looks different colors.
I know there is colorscale and color functions and I tried many times I couldn't find. Does anyone know any function?
You could define a function which returns a color for each value and then pass the colors for each bar in a list.
import pandas as pd
import plotly
def color(val, median, std):
if val > median + std:
return 'darkgreen'
if val < median - std:
return 'darkred'
return 'orange'
df = pd.DataFrame({'cinema': [1, 2, 5, 3, 3, None],
'theatre': [3, 0, 8, 4, 0, 4],
'wine': [3, 2, 5, None, 1, None],
'beer': [4, 8, 2, None, None, None]})
med = df.count().median()
std = df.count().std()
colors = [color(i, med, std) for i in df.count()]
fig = plotly.graph_objs.Bar(x=df.columns,
y=df.count(),
marker=dict(color=colors))
plotly.offline.plot([fig])
The bars could be also colored either by pd.pivot_table() the rows to columns or by creating a separate list of traces for bars. Here, each column was aggregated by taking a sum() as an example. Code below:
# Import libraries
import datetime
from datetime import date
import pandas as pd
import numpy as np
from plotly import __version__
%matplotlib inline
import cufflinks as cf
from plotly.offline import download_plotlyjs, init_notebook_mode, plot, iplot
init_notebook_mode(connected=True)
init_notebook_mode(connected=True)
cf.go_offline()
import plotly.graph_objs as go
import plotly.offline as pyo
# Create dataframe
INT_M_PUB = [0,0,0,0,0,1,0,0,0,0]
INT_M_CINEMA = [1,1,1,0,0,0,0,0,0,1]
INT_M_THEATRE = [1,0,1,0,0,1,0,1,0,1]
INT_M_GYM = [0,0,0,0,0,1,0,0,0,1]
INT_M_ENTERTAIN = [0,0,1,1,0,1,0,1,0,1]
INT_M_EATOUT = [0,1,1,0,0,1,0,0,1,1]
INT_M_WINE = [0,0,0,0,0,1,0,0,0,1]
interests = pd.DataFrame({'INT_M_PUB':INT_M_PUB, 'INT_M_CINEMA':INT_M_CINEMA, 'INT_M_THEATRE':INT_M_THEATRE,
'INT_M_GYM':INT_M_GYM, 'INT_M_ENTERTAIN':INT_M_ENTERTAIN, 'INT_M_EATOUT':INT_M_EATOUT,
'INT_M_WINE':INT_M_WINE
})
interests.head(2)
dfm = interests.sum().reset_index().rename(columns={'index':'interests', 0:'value'})
dfm
# Re-creating the plot similar to that in question (note: y-axis scales are different)
df = dfm.copy()
col_list = df.columns
df.iplot(kind = 'bar', x='interests', y='value', xTitle='Interests', yTitle='Number of Person', title='These bars need to be colored', color='red')
# Color plots by creating traces
# Initialize empty list named data to collect traces for each bar
data = []
for col_name in col_list:
trace = go.Bar(
x=[col_name],
y=df[col_name],
name=col_name
)
data.append(trace)
data = data
layout = go.Layout(
barmode='group',
title='Interests',
xaxis=dict(title='Interests'),
yaxis=dict(title='Number of Person')
)
fig = go.Figure(data=data, layout=layout)
pyo.iplot(fig, filename='grouped-bar')
# Creating plot by pivoting the table
df = pd.pivot_table(dfm, values='value', columns='interests')
df.iplot(kind = 'bar',xTitle='Interests', yTitle='Number of Person')

Categories