Black Lives Matter. Please consider donating to Black Girls Code today.

Static Image Export Fails with Choropleth Chart

With the below sample-supplied code, I can’t export a chart (which renders correctly in Jupyter) to a static file
py.image.save_as(fig, 'MyChart.png', scale=3)

Error Dump

---------------------------------------------------------------------------
JSONDecodeError                           Traceback (most recent call last)
//anaconda/envs/OSMNX/lib/python3.6/site-packages/plotly/api/v2/utils.py in validate_response(response)
     65     try:
---> 66         parsed_content = response.json()
     67     except ValueError:

//anaconda/envs/OSMNX/lib/python3.6/site-packages/requests/models.py in json(self, **kwargs)
    891                     pass
--> 892         return complexjson.loads(self.text, **kwargs)
    893 

//anaconda/envs/OSMNX/lib/python3.6/json/__init__.py in loads(s, encoding, cls, object_hook, parse_float, parse_int, parse_constant, object_pairs_hook, **kw)
    353             parse_constant is None and object_pairs_hook is None and not kw):
--> 354         return _default_decoder.decode(s)
    355     if cls is None:

//anaconda/envs/OSMNX/lib/python3.6/json/decoder.py in decode(self, s, _w)
    338         """
--> 339         obj, end = self.raw_decode(s, idx=_w(s, 0).end())
    340         end = _w(s, end).end()

//anaconda/envs/OSMNX/lib/python3.6/json/decoder.py in raw_decode(self, s, idx)
    356         except StopIteration as err:
--> 357             raise JSONDecodeError("Expecting value", s, err.value) from None
    358         return obj, end

JSONDecodeError: Expecting value: line 2 column 1 (char 1)

During handling of the above exception, another exception occurred:

PlotlyRequestError                        Traceback (most recent call last)
<ipython-input-195-f01521e13baf> in <module>()
    112 # profit_df= pd.read_pickle(url)
    113 # profit_df.columns
--> 114 f(af_df, start_year=2013, end_year=2016, client_count=30, mytitle='Sales over Median Income')

<ipython-input-195-f01521e13baf> in f(df, start_year, end_year, client_count, mytitle)
    104 
    105     #py.image.save_as(fig,'./charts/{}.png'.format(mytitle.title().replace(" ","")))
--> 106     py.image.save_as(fig, 'my_image.png', scale=3)
    107 
    108     return py.iplot(fig, filename='Affinity Sales')

//anaconda/envs/OSMNX/lib/python3.6/site-packages/plotly/plotly/plotly.py in save_as(cls, figure_or_data, filename, format, width, height, scale)
    830             filename += '.' + format
    831 
--> 832         img = cls.get(figure_or_data, format, width, height, scale)
    833 
    834         f = open(filename, 'wb')

//anaconda/envs/OSMNX/lib/python3.6/site-packages/plotly/plotly/plotly.py in get(figure_or_data, format, width, height, scale)
    743             payload['scale'] = scale
    744 
--> 745         response = v2.images.create(payload)
    746 
    747         headers = response.headers

//anaconda/envs/OSMNX/lib/python3.6/site-packages/plotly/api/v2/images.py in create(body)
     16     """
     17     url = build_url(RESOURCE)
---> 18     return request('post', url, json=body)

//anaconda/envs/OSMNX/lib/python3.6/site-packages/plotly/api/v2/utils.py in request(method, url, **kwargs)
    151         content = response.content if response else 'No content'
    152         raise exceptions.PlotlyRequestError(message, status_code, content)
--> 153     validate_response(response)
    154     return response

//anaconda/envs/OSMNX/lib/python3.6/site-packages/plotly/api/v2/utils.py in validate_response(response)
     67     except ValueError:
     68         message = content if content else 'No Content'
---> 69         raise exceptions.PlotlyRequestError(message, status_code, content)
     70 
     71     message = ''

PlotlyRequestError: 
<html><head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<title>502 Server Error</title>
</head>
<body text=#000000 bgcolor=#ffffff>
<h1>Error: Server Error</h1>
<h2>The server encountered a temporary error and could not complete your request.<p>Please try again in 30 seconds.</h2>
<h2></h2>
</body></html>

My Code (which renders correctly within Jupyter)

import plotly.plotly as py
from plotly.graph_objs import *
import pandas as pd

def popup_text(row):
    val = '''{0}<br>\
    Total Profit: ${1:,.0f}<br>\
    {2}, {3}<br>\
    ShipToZip: {4}<br>\
    CityPop: {5:,.0f}<br>\
    CityGrowth: {6:.0%}<br>\
    Density: {7:,.0f}<br>\
    County Density: {8:,.0f}<br>\
    Households: {9:,.0f}<br>\
    AvgIncome: ${10:,.0f}<br>\
    CountyAvgIncome: ${11:,.0f}<br>\
    White: {12:.0%}<br>\
    FEMA Disaster Count: {13:,.0f}<br>\
    Bldg Valuation Growth: {14:.0%}'''.format(
        row['Company'],
        pd.to_numeric(row['Profit_sum'],errors='coerce'), 
        row['City'], row['State'], row['ShipToLocZip'],
        row['CityPop2016'], row['CityPopGrowth'], row['Density'],
        row['County_PopDensity'], row['Households'], row['AvgIncome'],
        row['County_MedianHouseoldIncome'], row['PopulationRaceWhite'],
        row['DisasterCount'], row['ValuationGrowth'])
    
    return val.replace('\\','').strip()


def f(df, start_year, end_year, client_count, mytitle):

    # Set range
    df = df[df.OrderDate.dt.year.between(start_year, end_year)]

    # Add Profit Total for Every Client
    df['Profit_sum'] = df.groupby('Company')['Profit'].transform(sum)
    df = df.sort_values(['Profit_sum', 'Company'], ascending=[False, False])

    # Entire Sales History
    plotly_df = df[[
        'Company', 'ShipToLocZip', 'City', 'State', 'Population', 'Density',
        'CityPop2016', 'CityPopGrowth', 'AvgIncome',
        'County_MedianHouseoldIncome', 'County_PopDensity', 'DisasterCount',
        'ValuationGrowth', 'PopulationRaceWhite', 'Households', 'Latitude',
        'Longitude', 'Profit_sum'
    ]]

    # plotly_df.head(1)
    plotly_df['Text'] = plotly_df.apply(lambda row: popup_text(row), axis=1)

    # 20 best clients
    bestclient_df = (plotly_df.groupby('Company').agg({
        'Profit_sum':
        'min',
        'Latitude':
        lambda x: x.value_counts().index[0],
        'Longitude':
        lambda x: x.value_counts().index[0],
        'Text':
        'min'
    }).reset_index().sort_values('Profit_sum',
                                 ascending=False)[0:client_count])
    # bestclient_df.head(1)

    data =  graph_objs.Data([
        Scattermapbox(
            lat=list(plotly_df.Latitude),
            lon=list(plotly_df.Longitude),
            mode='markers',
            marker=Marker(
                size=4,
                #color=colors,
                opacity=0.7),
            text=list(plotly_df.Text),
            hoverinfo='text',
            showlegend=False),
        
        Scattermapbox(
            lat=list(bestclient_df.Latitude),
            lon=list(bestclient_df.Longitude),
            mode='markers',
            marker=Marker(size=12, color='rgb(50, 255, 100)', opacity=0.8),
            text=list(bestclient_df.Text),
            hoverinfo='text',
            showlegend=False,
        )
    ])

    layout =  graph_objs.Layout(
        title= '{0} and {1} Best Clients ({2}-{3})'.format(
            mytitle, client_count, start_year, end_year),
        autosize=True,
        hovermode='closest',
        mapbox=dict(
        layers=[
            dict(sourcetype = 'geojson',
                     source =sources[k],
                     below="water",
                     type = 'fill',
                     #color = sources[k],#the list of colors for each shape/layer in choropleth
                     color = sources[k]['features'][0]['properties']['Color'],
                     opacity=0.5,
                    ) for k in range(len(sources))
           ],
        accesstoken=MAPBOX_API_KEY,
        bearing=0,
        center=dict(lat=38, lon=-94),
        pitch=0,
        zoom=3,
        style='light'
        )
    )

    fig = dict(data=data, layout=layout)

    #py.image.save_as(fig,'./charts/{}.png'.format(mytitle.title().replace(" ","")),scale=3)
    py.image.save_as(fig, 'MyChart.png', scale=3)
    
    return py.iplot(fig, filename='Affinity Sales')

f(af_df, start_year=2013, end_year=2016, client_count=30, mytitle='Sales over Median Income')

Much appreciation in advance as the chart (which renders in Jupyter) is too small for presentation purposes.

This worked.

plotly.offline.plot(fig, output_type='file', image=image_type,
                    image_width= 1400, image_height=850, 
                    image_filename='{0}.{1}'.format(mytitle.title().replace(" ",""), image_type))