Python plotly sankey export broken - python

I have a python sankey chart which works well when exporting the html but looks completely broken when exporting it to other file formats
import plotly.graph_objects as go
fig = go.Figure(data=[go.Sankey(
node = dict(label = data["label"]),
link = dict(source = data["source"],target = data["target"],value = data["value"])
)])
fig.write_image("sankey.svg")
fig.write_image("sankey.eps")
fig.write_image("sankey.png")
fig.write_html("sankey.html")
HTML Screenshot
PNG Export (SVG, EPS differ a bit but also look broken)
I'm using python 3.8.5 with the kaleido 0.0.3 engine.
Additionally, I've tried Orca 1.2.1 but got the same results.

The answer actually is very easy. Tough most of the charts can figure out the required size on their own, the sankey chart obviously can't. So basically you just have to set dimensions for all exports on sankey charts (yes even for vector graphics like eps and svg).
Also worth mentioning is that a minimum size is required. While my example now looks satisfying with 1920x1080, a size of 1280x720 looks broken even with vector-graphics.
fig = go.Figure(...)
fig.update_layout(width=1920, height=1080)
fig.write_image(...)

Related

How to save a Bokeh plot as PDF?

I'm working with Bokeh a lot and I'm looking for a way to create a PDF from the figure I created.
Is there an option to achieve this goal?
This is possible with a combination of the three python package bokeh, svglib and reportlab which works perfect for me.
This will include 3 steps:
creating a bokeh svg output
read in this svg
saving this svg as pdf
Minimal Example
To show how this could work please see the following example.
from bokeh.plotting import figure
from bokeh.io import export_svgs
import svglib.svglib as svglib
from reportlab.graphics import renderPDF
test_name = 'bokeh_to_pdf_test'
# Example plot p
p = figure(plot_width=400, plot_height=400, tools="")
p.circle(list(range(1,6)),[2, 5, 8, 2, 7], size=10)
# See comment 1
p.xaxis.axis_label_standoff = 12
p.xaxis.major_label_standoff = 12
# step 1: bokeh save as svg
p.output_backend = "svg"
export_svgs(p, filename = test_name + '.svg')
# see comment 2
svglib.register_font('helvetica', '/home/fonts/Helvetica.ttf')
# step 2: read in svg
svg = svglib.svg2rlg(test_name+".svg")
# step 3: save as pdf
renderPDF.drawToFile(svg, test_name+".pdf")
Comment 1
There is an extra information used for axis_label_standoff and major_label_standoff because the ticks of the x-axis are moving without this definition a bit up and this looks not so good.
Comment 2
If you get a long list of warnings like
Unable to find a suitable font for 'font-family:helvetica'
Unable to find a suitable font for 'font-family:helvetica'
....
Unable to find a suitable font for 'font-family:helvetica'
the ppdf is still created. This warning appears because the default font in bokeh is named helvetica, which is not known by svglib. svglib looks for this font at a defined place. If this font is not there, the message appears. This means bokeh will use its own default font instead.
To get rid of this message you can register a font in svglib like this
# name in svglib, path to font
svglib.register_font('helvetica' , f'/{PATH_TO_FONT}/Helvetica.ttf')
right before calling svglib.svg2rlg().
Output
This code will create the same figure twice, once with the suffix .svg and once with the suffix .pdf.
The figure looks like this:

Python plotly logo disapering from chart

I have to create a simple report that show one data table and have two logos in the top corners. The code below worked in a previous project but now that I’m reusing it on a new computer for a new project it wont show the logos.
I get no error message. Same version of plotly and python "plotly==4.6.0" "Python 3.6.1"
Please note that the only thing that changed is the data shown in the datatable.
import plotly.graph_objects as go
import pandas as pd
traces = go.Table(
header=dict(values=list(df.columns),
align='left'),
cells=dict(
values=df.T.values.tolist(),
align='left'))
layout = go.Layout(
title='Report <br> {}'.format( report_date),
title_x=0.5,
paper_bgcolor='#FFFFFF',
margin = {'t':100, 'b':40, 'r':40, 'l':40}
,images=[
dict(
source='assets\\MiniLogo.png',
xref='paper',yref='paper',
x=1,y=1.05,
sizex=0.2, sizey=0.2,
xanchor="right", yanchor="bottom"),
dict(
source='assets\\Titlelogo.png',
xref='paper',yref='paper',
x=0,y=1.05,
sizex=0.2, sizey=0.2,
xanchor="left", yanchor="bottom")
]
)
fig = go.Figure(
data=traces
,layout=layout)
fig.show()
I think the problem is within the source argument in your layout. I've used your code with this image URL instead of a relative path and it works perfectly and here is a screenshot knowing that I've used a simple table as my df:
In my opinion, you have two options to overcome that:
Upload these images to a cloud-service and use their URLs instead.
Or according to this Plolty community thread, you can use Pillow.Image class to read the image from your local machine. You can install it easily by running pip install pillow and modify your code to be like so:
from PIL import Image
layout= go.Layout(images= [dict(
source= Image.open('assets\\MiniLogo.png'),
...)])

Animated multiseries Line Graph using Plotly Express (Python)

I am trying to animate a multi series line graph using plotly. However after days of going through the documentation I still can't seem to find a solution.
Currently my code is as follows:
df = px.data.gapminder().query("continent=='Oceania' ")
fig = px.line(df, x="year" , y="lifeExp", color="country" , animation_frame="year", animation_group="lifeExp" , range_y=[68,84] , range_x=[1950,2010])
plot(fig)
This however generates and empty plot. Please help.
I am able to successfully generate a scatter plot and a bar graph using similar code.
For better understanding please view below link :
I have found an exact example of what I am looking for implemented in R.
https://plot.ly/r/cumulative-animations/#cumulative-lines-animation
For the empty plot, try changing the default renderer by adding this above your code:
import plotly.io as pio
pio.renderers.default = 'notebook'
There is some documentation on different renderers.

Multiple plotly plots on 1 page without subplot

I want to have multiple plotly plots on 1 html page without using the tools.make_subplots method. (I dont want to use that since I find that its not easy to read and I want a unique legend & layout in each of the subplot panels).
I want to define 2 figures with their own unique layouts, and arrange them arbitrarily on the page. I think I know how to do this with dash using the html.Div object, but I was wondering if there was an easy way to do this using only plotly?
I encountered the very same problem and followed the solution posted here:
Plotly: Plot multiple figures as subplots by Esostack
However, when I dumped the html of multiple figures into a single text file, I found the file size increasing by 5MB per figure that I add. 99.9% of this is caused by java script stuff that is added by plotly to make the plots interactive. Luckily, they also implemented a parameter to specify if you want to include the js or not. So you need to include it only for the first figure and skip it for the rest, like it is done in the following function. Hope that helps:
def figures_to_html(figs, filename):
'''Saves a list of plotly figures in an html file.
Parameters
----------
figs : list[plotly.graph_objects.Figure]
List of plotly figures to be saved.
filename : str
File name to save in.
'''
import plotly.offline as pyo
dashboard = open(filename, 'w')
dashboard.write("<html><head></head><body>" + "\n")
add_js = True
for fig in figs:
inner_html = pyo.plot(
fig, include_plotlyjs=add_js, output_type='div'
)
dashboard.write(inner_html)
add_js = False
dashboard.write("</body></html>" + "\n")
So, in conclusion, I have not found a way to do this purely with plotly. Below is my code for doing this with Dash, which has been working quite well:
Step 1: make a few plotly plots
import plotly.offline as pyo
import plotly.graph_objs as go
import plotly as py
fig1 = go.Scatter(y=[1,2,3])
fig2 = go.Scatter(y=[3,2,1])
plots = [fig1, fig2]
Step 2: Make dash Div objects:
app = dash.Dash()
layout = html.Div(
[html.Div(plots[i]) for i in range(len(plots))],
style = {'margin-right': '0px'}
)
Step 3: Run dash
app.layout = layout
app.run_server(port=8052)

How to prevent plotly from plotting automatically

I just discovered plotly and like it so far. I have this code provided by the main website
import plotly.plotly as py
from plotly.graph_objs import *
trace0 = Scatter(
x=[1,2,3,4],
y=[10,15,13,17]
)
trace1 = Scatter(
x=[1,2,3,4],
y=[16,5,11,9]
)
data = Data([trace0, trace1])
unique_url = py.plot(data, filename='basic-line')
I am curious about two things:
1) When I run this code, my browser automatically pops up and shows me the graph. All I want is the url so that I can later embed it in an html file. Is there a way to turn off the feature that opens my browser and shows me the graph?
2) Is there a way to get rid of the 'Play with this data' link?
I have combed through the documentation provided, but have come up empty-handed on these two issues.
To disable pop-ups you could use auto_open=FALSE and try the following
py.plot(data, filename='basic_line', auto_open=False)
py.plot(data, show_link=False) will take that link off (if you are referring to the link that says Export to plot.ly). At least it does using:
import plotly.offline as py. As for the link at the top (when you hover your mouse over the graph), I'm trying to get rid of the Save and edit plot in cloud but only find options for that under the java script version... and that hides the whole bar which has other useful items on it (javascript option is: {displayModeBar: false}). Obviously I am finding the reference to "play with this data" ambiguous. You can see the workaround I wrote here: Adding config modes to Plotly.Py offline - modebar
You can easily remove that Export to plot.ly link in the offline graph.
Open your saved html file in a text editor. and search for. {"showLink": true, "linkText": "Export to plot.ly"}
And change the true value to false.

Categories