AttributeError: 'Figure' object has no attribute savefig in Flask - python-3.x

I am trying to display plotly.express bar chart in Flask. But it is giving 'Figure' object has no attribute savefig error. The image gets displayed correctly while using fig.show().
import matplotlib.pyplot as plt
import plotly.express as px
figs = px.bar(
comp_df.head(10),
x = "Company",
y = "Staff",
title= "Top 10 departments",
color_discrete_sequence=["blue"],
height=500,
width=800
)
figs.savefig('static/images/staff_plot.png')
# fig.show()
return render_template('plot.html', name='new_plot', url='static/images/staff_plot.png')
In plot.html, the image is displayed as below:
<img src={{ url}} >

You have defined figs with the px.bar() method.
Per documentation px.bar() returns a plotly.graph_objects.Figure object.
Looking at this plotly.graph_objects.Figure class' documentation we can see all the methods available on this plotly.graph_objects.Figure class.
show() appears to be a valid method for this type of object. However there is no savefig() method for this class.
This is why fig.show() works and fig.savefig() doesn't work.
It looks like there is a savefig() method on the matplotlib.pyplot class as documented here, however your figs object is an instance of plotly.graph_objects.Figure not matplotlib.pyplot.
If your goal is to write your figs object to a file, it looks like the documentation specifies 3 methods that provide this functionality:
plotly.graph_objects.Figure
write_html
write_image
write_json
Try replacing:
figs.savefig('static/images/staff_plot.png')
with
figs.write_image(file='static/images/staff_plot.png', format='.png')

Instead of using figs.savefig, try to use plt.savefig
import matplotlib.pyplot as plt
plt.savefig('static/images/staff_plot.png')

Related

Bokeh plot title 'str' object is not callable

In Jupyter Notebooks I read in a dataframe and create several plots with Pandas / Bokeh.
While creating one of the latter I get an error.
Search for similar problems said, that there might be somewhere above in the script something like
plt.title = "Title"
which overwrites the method. But this is not the case for me. I have nothing similar in the code above -exept in the figure parameters. Here the Bokeh documentation describes to set a figure title like I used it.
Using the part of the code that leads the the error in the complete notebook in a stand-alone script only does NOT lead to the error. So, also in my case the problem might have something to do with my code above. But maybe some of you has an idea when seeing this..(?)
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from bokeh.plotting import figure, show, output_notebook, ColumnDataSource
from bokeh.io import output_notebook
from bokeh.layouts import column, gridplot
from bokeh.models import Label, Title
from bokeh.models import Div
data = df
output_notebook()
# Title of the overall plot
abovetitle = ("This should be the overall title of all graphs")
# GRAPH 1
s1 = figure(width = 250, plot_height = 250, title="Graph 1", x_axis_label = "axis title 1", y_axis_label = 'µs')
s1.line(x, y, width=1, color="black", alpha=1, source = data)
# s1.title.text = "Title With Options" # this is a instead-off 'title=' test, but does not solve the problem
# GRAPH 2
s2 = figure(width = 250, plot_height = 250, title="Graph 2", x_axis_label = "axis title 2, y_axis_label = 'µs')
s2.line(x, y, width=1, color="blue", alpha=1, source = data)
#s2.title.text = "Title With Options" # this is a instead-off 'title=' test, but does not solve the problem
# plot graphs:
p = gridplot([[s1, s2]])
show(column(Div(text=abovetitle), p))
leads to the type error
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-24-33e4828b986d> in <module>
31 # plot graphs:
32 p = gridplot([[s1, s2]])
---> 33 show(column(Div(text=title), p))
TypeError: 'str' object is not callable
Recalling
import matplotlib.pyplot as plt
does not solve the problem. Hence, recalling
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from bokeh.plotting import figure, show, output_notebook, ColumnDataSource
from bokeh.io import output_notebook
from bokeh.layouts import column, gridplot
from bokeh.models import Label, Title
from bokeh.models import Div
solves the problem. Any further idea what might cause this error?
In the mean time I got a very useful hint: In one of the prior cells I accidentially used a Bokeh API function name as variable name and overwrote the function. If someone faces a comparable problem have a look at your variable naming. Maybe there happend the same accident... ;-)
#############################
# Define column names of XData binary part
header = ["Col1","Col2","Col3"]
# Split XData in single, space separated columns
x_df = selected_df.XData.str.split(' ', expand=True)
x_df.drop(0, inplace=True, axis=1)
x_df.columns = header
#print(x_df)
# Binary XData to integer
for column in x_df: # DONT DO THAT!!!!! THIS OVERWRITES BOKEH API FUNCTION. EG. USE `col` INSTEAD OF `column`
x_df[column] = x_df[column].apply(int, base=16) # DONT DO THAT!!!!! THIS OVERWRITES BOKEH API FUNCTION. EG. USE `col` INSTEAD OF `column`

AttributeError: 'NoneType' object has no attribute 'coords'

I have a problem compiling this in python3, the code is
import numpy as np
import matplotlib.pyplot as plt
import astropy.units as u
from astropy.wcs import WCS
from astropy.io import fits
from astropy.utils.data import get_pkg_data_filename
from astropy.coordinates import SkyCoord
from astropy.coordinates import ICRS, Galactic, FK4, FK5
from astropy.coordinates import Angle, Latitude, Longitude
import astropy.units as u
filename = get_pkg_data_filename('jopi.fits')
hdu = fits.open(filename)[0]
wcs = WCS(hdu.header).celestial
wcs.wcs.crval = [0,0]
wcs.wcs.ctype = [ 'XOFFSET' , 'YOFFSET' ]
fig = plt.figure(figsize=(8,6))
ax = fig.add_subplot(projection=wcs)
plt.imshow(hdu.data[0][0], origin='lower')
lon = ax.coords[0]
lat = ax.coords[1]
lon.set_major_formatter('x')
lat.set_major_formatter('x')
lon.set_format_unit(u.milliarcsecond)
lat.set_format_unit(u.milliarcsecond)
ax.set_xlim(200,800)
ax.set_ylim(200,800)
ax.set_xlabel('Relative R.A ()')
ax.set_ylabel('Relative Dec ()')
I always get
lon = ax.coords[0] AttributeError: 'NoneType' object has no attribute
'coords'
Is it something missing?
This means that the ax variable has the value of None. It is not an Axes object as you are expecting. You could confirm this with some basic debugging e.g. putting a print statement after:
ax = fig.add_subplot(projection=wcs)
though I would also recommend looking at the code for fig.add_subplot. In IPython/Jupyter you can type fig.add_subplot?? to see this, and the first lines of the code for the function (after the docstring) are:
if not len(args):
return
(which IMO is a confusing misfeature).
You need to pass some additional arguments to fig.add_subplot as documented here.
For example:
ax = fig.add_subplot(111, projection=wcs)
to give the layout and position of the subplot you want to work on.
You are likely using matplotlib < 3.1.0, since this was changed in matplotlib 3.1.0 so that Figure.add_subplot has a default value of 111 for the position arguments, allowing code like you wrote to work: https://github.com/matplotlib/matplotlib/pull/13127

Add annotations to heatmap with python3

I created a heatmap using matplotlib and seaborn, It looks ok.
But my question is how to add values on heatmap. My current heatmap contains only different colors.
import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns
data = pd.DataFrame(data={'x':index, 'y':colonnes, 'z':score})
data = data.pivot(index='x', columns='y', values='z')
sns.heatmap(data)
plt.show()
Any idea please?
Thanks
sns.heatmap(data, annot=True)
From documentation:
annot : bool or rectangular dataset, optional. If True, write the data value in each cell. If an array-like with the same shape as data, then use this to annotate the heatmap instead of the raw data.
Also, play around with fmt and annot_kws paramaters.

Arranging widgets in ipywidgets interactive

I have this interactive graph code using ipywidgets; but not sure how to arragne the each variable inside the interactive function in widgets. the default layout is vertical. But I want to arrange them in horizontal way.
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
plt.style.use('seaborn')
%config InlineBackend.figure_format = 'svg'
from ipywidgets import interactive,interact
#function to plot the different curves
def plot_function(u=1,v=2,w=3,x=4,y=5,z=6):
time=np.arange(0,1,0.01)
df=pd.DataFrame({"Y1":np.sin(time*u*2*np.pi),"y2":np.sin(time*v*2*np.pi),"y3":np.sin(time*w*2*np.pi),
"y4":np.sin(time*x*2*np.pi),"y5":np.sin(time*y*2*np.pi),"y6":np.sin(time*z*2*np.pi)})
df.plot()
widget=interactive(plot_function,u=1,v=2,w=3,x=4,y=5,z=6)
widget
interactive is restricted to fairly simple widget layouts. Have a look at the Flexbox options if you want to customize them some more.
One simple get around is to use the interactive call to generate and link your widgets and functions, then restructure the widgets inside a HBox. Then add a layout that tells the box to wrap at line ends. I added a couple more imports and three lines at the end to achieve this.
1) controls - an HBox of your input widgets.
2) The Output widget generated by the interactive call.
3) A VBox that wraps the two together.
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
plt.style.use('seaborn')
%config InlineBackend.figure_format = 'svg'
#importing the necessary items from the Ipywidgets library
from ipywidgets import interactive,interact, HBox, Layout,VBox
#function to plot the different curves
def plot_function(u=1,v=2,w=3,x=4,y=5,z=6):
time=np.arange(0,1,0.01)
df=pd.DataFrame({"Y1":np.sin(time*u*2*np.pi),"y2":np.sin(time*v*2*np.pi),"y3":np.sin(time*w*2*np.pi),
"y4":np.sin(time*x*2*np.pi),"y5":np.sin(time*y*2*np.pi),"y6":np.sin(time*z*2*np.pi)})
df.plot()
widget=interactive(plot_function,u=1,v=2,w=3,x=4,y=5,z=6)
controls = HBox(widget.children[:-1], layout = Layout(flex_flow='row wrap'))
output = widget.children[-1]
display(VBox([controls, output]))
Hi this is the decorator which I am using instead of #interact:
def interact_delayed(_InteractFactory__interact_f=None, **kwargs):
def patch(obj):
if hasattr(obj.widget, 'layout'):
obj.widget.layout = Layout(flex_flow='row wrap')
for child in obj.widget.children:
if hasattr(child, 'continuous_update'):
child.continuous_update = False
return obj
if _InteractFactory__interact_f is None:
def decorator(f):
obj = interact(f, **kwargs)
return patch(obj)
return decorator
else:
obj = interact(_InteractFactory__interact_f, **kwargs)
return patch(obj)
The patch function modifies default attributes of ipywidget object: applies the Layout suggested in the previous answer and also sets continuous_update to false which I found useful in my cases.
The if-else branches takes care about decorator versus function use-case scenarios.
There is no way to arrange widgets by adding a parameter in "interact" or interactive.

Remove Bokeh Logo in HoloViews

Is it possible to remove the Bokeh logo from plots generated with HoloViews? Nothing against it... it's just that it may not make sense to display it in certain reports. :)
I know that in Bokeh I can simply do:
p = bkp.figure(...)
...
p.toolbar.logo = None
UPDATE
Here's my import section:
import sys
import os
import numpy as np
np.random.seed(0)
import random
random.seed(0)
import pandas as pd
from bokeh.models import HoverTool
import holoviews as hv
hv.extension("bokeh", logo=False)
Currently (as of holoviews 1.9.1) the option to disable the bokeh logo in the toolbar is not directly exposed, but you can supply a so called finalize_hook which lets you modify the plot directly. You can add such a hook directly on the ElementPlot to set it globally:
def disable_logo(plot, element):
plot.state.toolbar.logo = None
hv.plotting.bokeh.ElementPlot.finalize_hooks.append(disable_logo)
or set it as a plot option:
hv.Curve(range(10)).opts(plot=dict(finalize_hooks=[disable_logo])
To remove the Bokeh logo for more complicated layouts, I think you need to render it to a Bokeh figure, and then use Bokeh's native method to remove it.
layout = C + D
plot = renderer.get_plot(layout)
p = plot.state
p.children[0].toolbar.logo = None
show(p)
hv.extension("bokeh",logo=False)
1) This is almost the same as philippjfr answer, but slightly shorter using hooks:
def remove_bokeh_logo(plot, element):
plot.state.toolbar.logo = None
hv.Scatter(df).opts(hooks=[remove_bokeh_logo])
2) And there's Andrew's answer, rendering the plot as bokeh and then removing the logo:
from bokeh.plotting import show
hv_plot = hv.Scatter(df)
bokeh_plot = hv.render(hv_plot, backend='bokeh')
bokeh_plot.toolbar.logo = None
show(bokeh_plot)

Resources