facebook / prophet

Tool for producing high quality forecasts for time series data that has multiple seasonality with linear or non-linear growth.
https://facebook.github.io/prophet
MIT License
18.38k stars 4.52k forks source link

invalid format string error when calling fig2 = m.plot_components(forecast) but not fig1 = m.plot(forecast) #588

Closed dkrantz closed 6 years ago

dkrantz commented 6 years ago

I'm getting an invalid format string error when calling fig2 = m.plot_components(forecast)

I'm getting no such error when calling fig1 = m.plot(forecast)

My dates are in YYYY-MM-DD format: ds y cap floor 0 2016-01-01 2 160 25 1 2016-01-02 6 160 25 2 2016-01-03 1 160 25 3 2016-01-04 15 160 25 4 2016-01-05 27 160 25

Is there a parameter that I should call that could override this?

Here is the full error:

ValueError Traceback (most recent call last)

in () ----> 1 fig2 = m.plot_components(forecast) 2 3 #forecast[['ds', 'yhat', 'yhat_lower', 'yhat_upper']] C:\Users\dkrantz\AppData\Local\Continuum\anaconda2\lib\site-packages\fbprophet\forecaster.pyc in plot_components(self, fcst, uncertainty, plot_cap, weekly_start, yearly_start) 1324 name=plot, ax=ax, uncertainty=uncertainty) 1325 -> 1326 fig.tight_layout() 1327 return fig 1328 C:\Users\dkrantz\AppData\Local\Continuum\anaconda2\lib\site-packages\matplotlib\figure.pyc in tight_layout(self, renderer, pad, h_pad, w_pad, rect) 2005 kwargs = get_tight_layout_figure( 2006 self, self.axes, subplotspec_list, renderer, -> 2007 pad=pad, h_pad=h_pad, w_pad=w_pad, rect=rect) 2008 self.subplots_adjust(**kwargs) 2009 C:\Users\dkrantz\AppData\Local\Continuum\anaconda2\lib\site-packages\matplotlib\tight_layout.pyc in get_tight_layout_figure(fig, axes_list, subplotspec_list, renderer, pad, h_pad, w_pad, rect) 349 subplot_list=subplot_list, 350 ax_bbox_list=ax_bbox_list, --> 351 pad=pad, h_pad=h_pad, w_pad=w_pad) 352 353 if rect is not None: C:\Users\dkrantz\AppData\Local\Continuum\anaconda2\lib\site-packages\matplotlib\tight_layout.pyc in auto_adjust_subplotpars(fig, renderer, nrows_ncols, num1num2_list, subplot_list, ax_bbox_list, pad, h_pad, w_pad, rect) 128 129 tight_bbox_raw = union([ax.get_tightbbox(renderer) for ax in subplots --> 130 if ax.get_visible()]) 131 tight_bbox = TransformedBbox(tight_bbox_raw, 132 fig.transFigure.inverted()) C:\Users\dkrantz\AppData\Local\Continuum\anaconda2\lib\site-packages\matplotlib\axes\_base.pyc in get_tightbbox(self, renderer, call_axes_locator) 3958 bb.append(self._right_title.get_window_extent(renderer)) 3959 -> 3960 bb_xaxis = self.xaxis.get_tightbbox(renderer) 3961 if bb_xaxis: 3962 bb.append(bb_xaxis) C:\Users\dkrantz\AppData\Local\Continuum\anaconda2\lib\site-packages\matplotlib\axis.pyc in get_tightbbox(self, renderer) 1088 return 1089 -> 1090 ticks_to_draw = self._update_ticks(renderer) 1091 ticklabelBoxes, ticklabelBoxes2 = self._get_tick_bboxes(ticks_to_draw, 1092 renderer) C:\Users\dkrantz\AppData\Local\Continuum\anaconda2\lib\site-packages\matplotlib\axis.pyc in _update_ticks(self, renderer) 972 973 interval = self.get_view_interval() --> 974 tick_tups = list(self.iter_ticks()) 975 if self._smart_bounds and tick_tups: 976 # handle inverted limits C:\Users\dkrantz\AppData\Local\Continuum\anaconda2\lib\site-packages\matplotlib\axis.pyc in iter_ticks(self) 919 self.major.formatter.set_locs(majorLocs) 920 majorLabels = [self.major.formatter(val, i) --> 921 for i, val in enumerate(majorLocs)] 922 923 minorLocs = self.minor.locator() C:\Users\dkrantz\AppData\Local\Continuum\anaconda2\lib\site-packages\matplotlib\ticker.pyc in __call__(self, x, pos) 398 `x` and `pos` are passed through as-is. 399 """ --> 400 return self.func(x, pos) 401 402 C:\Users\dkrantz\AppData\Local\Continuum\anaconda2\lib\site-packages\fbprophet\forecaster.pyc in (x, pos) 1502 fmt_str = '{dt:%m}/{dt:%d}' 1503 ax.xaxis.set_major_formatter(FuncFormatter( -> 1504 lambda x, pos=None: fmt_str.format(dt=num2date(x)))) 1505 ax.set_xlabel('ds') 1506 ax.set_ylabel(name) ValueError: Invalid format string
bletham commented 6 years ago

I'm not able to reproduce this issue when I fit with the bit of dataframe that you pasted and logistic trend. Could you give the full code/data that you use when you run into this issue so I can reproduce?

dkrantz commented 6 years ago

Ben:

Thanks for getting back to me. Code snippet below. Basically, I'm trying to get a forecast by state, so I'll parse out data by state and set cap/floor depending on which state it is:

states = pd.Series(list(app.index.get_level_values('state'))).unique()

for state in states: df = app[(app.index.get_level_values('state') == state) & (app.index.get_level_values('entered_app') >= pd.Timestamp('2016-01-01'))].reset_index() df.drop('state',axis=1,inplace=True) df = df.rename(columns={'entered_app' : 'ds', 'app_count' : 'y'})

df['ds'] = df['ds'].dt.strftime('%B %#d')

if state == 'CA':
    m = Prophet(daily_seasonality=True,changepoints=['2018-04-01'])
    df['cap'] = 60
    df['floor'] = 5
    future_cap = 60
    future_floor = 5
else:
    m = Prophet(daily_seasonality=True)
    df['cap'] = 160
    df['floor'] = 25
    future_cap = 160
    future_floor = 25

m.fit(df)

future = m.make_future_dataframe(periods=120)

forecast = m.predict(future)

fig1 = m.plot(forecast)

fig2 = m.plot_components(forecast)
dkrantz commented 6 years ago

better formatting....



for state in states:
    df = app[(app.index.get_level_values('state') == state) & (app.index.get_level_values('entered_app') >= pd.Timestamp('2016-01-01'))].reset_index()
    df.drop('state',axis=1,inplace=True)
    df = df.rename(columns={'entered_app' : 'ds', 'app_count' : 'y'})
    #df['ds'] = df['ds'].dt.strftime('%B %#d')
    if state == 'CA':
        m = Prophet(daily_seasonality=True,changepoints=['2018-04-01'])
        df['cap'] = 60
        df['floor'] = 5
        future_cap = 60
        future_floor = 5
    else:
        m = Prophet(daily_seasonality=True)
        df['cap'] = 160
        df['floor'] = 25
        future_cap = 160
        future_floor = 25

    m.fit(df)

    future = m.make_future_dataframe(periods=120)

    forecast = m.predict(future)

    fig1 = m.plot(forecast)

    fig2 = m.plot_components(forecast)```
bletham commented 6 years ago

would it be possible to post a csv of just the (filtered) dataframe df that produces the error, so I can run the code on my machine?

dkrantz commented 6 years ago

sure, please see attached. note github won't let me upload csv, so I put it in excel format monthly_forecast_dump_20180625_1117.xlsx

bletham commented 6 years ago

After converting your xlsx to csv, I run the following code but do not get the error:

import pandas as pd
from fbprophet import Prophet

df = pd.read_csv('bad_plot_data.csv')
m = Prophet(daily_seasonality=True,changepoints=['2018-04-01'])
m.fit(df)
future = m.make_future_dataframe(periods=120)
forecast = m.predict(future)
fig1 = m.plot(forecast)
fig2 = m.plot_components(forecast)
fig2.show()

Can you verify that running the code I pasted produces the error for you? If so then it may be a matter of pandas/matplotlib versions. What version of matplotlib/pandas/numpy do you have?

import pandas
import matplotlib
import numpy

print(pandas.__version__)
print(matplotlib.__version__)
print(numpy.__version__)

I'd like to figure this out because we shouldn't raise exceptions like this, although in this particular case I suspect it is related to the daily_seasonality=True input argument. Daily seasonality means seasonality over a 24-hour period, like afternoons have higher values than mornings. There's no daily seasonality when you have only one observation per day as is the case here, so you should leave that turned off.

dkrantz commented 6 years ago

Ben:

Here are my versions:

pandas version is 0.20.3 matplotlib version is 2.1.0 numpy version is 1.13.3

You were correct, removing the daily seasonality parameter removed the error. I wasn't understanding daily seasonaility correctly ( i thought it meant to expect variance from monday to tuesday to friday etc).

bletham commented 6 years ago

Glad to hear that was the issue, weekly seasonality will cover that type of cycle.

I would still like to find the source of the error, though, but haven't run into it yet even with those package versions. Could you confirm that the code I posted above produces the error for you?

dkrantz commented 6 years ago

Yep still does. See below:


ValueError Traceback (most recent call last)

in () 14 forecast = m.predict(future) 15 fig1 = m.plot(forecast) ---> 16 fig2 = m.plot_components(forecast) 17 fig2.show() 18 # if state == 'CA': C:\Users\dkrantz\AppData\Local\Continuum\anaconda2\lib\site-packages\fbprophet\forecaster.pyc in plot_components(self, fcst, uncertainty, plot_cap, weekly_start, yearly_start) 1324 name=plot, ax=ax, uncertainty=uncertainty) 1325 -> 1326 fig.tight_layout() 1327 return fig 1328 C:\Users\dkrantz\AppData\Local\Continuum\anaconda2\lib\site-packages\matplotlib\figure.pyc in tight_layout(self, renderer, pad, h_pad, w_pad, rect) 2005 kwargs = get_tight_layout_figure( 2006 self, self.axes, subplotspec_list, renderer, -> 2007 pad=pad, h_pad=h_pad, w_pad=w_pad, rect=rect) 2008 self.subplots_adjust(**kwargs) 2009 C:\Users\dkrantz\AppData\Local\Continuum\anaconda2\lib\site-packages\matplotlib\tight_layout.pyc in get_tight_layout_figure(fig, axes_list, subplotspec_list, renderer, pad, h_pad, w_pad, rect) 349 subplot_list=subplot_list, 350 ax_bbox_list=ax_bbox_list, --> 351 pad=pad, h_pad=h_pad, w_pad=w_pad) 352 353 if rect is not None: C:\Users\dkrantz\AppData\Local\Continuum\anaconda2\lib\site-packages\matplotlib\tight_layout.pyc in auto_adjust_subplotpars(fig, renderer, nrows_ncols, num1num2_list, subplot_list, ax_bbox_list, pad, h_pad, w_pad, rect) 128 129 tight_bbox_raw = union([ax.get_tightbbox(renderer) for ax in subplots --> 130 if ax.get_visible()]) 131 tight_bbox = TransformedBbox(tight_bbox_raw, 132 fig.transFigure.inverted()) C:\Users\dkrantz\AppData\Local\Continuum\anaconda2\lib\site-packages\matplotlib\axes\_base.pyc in get_tightbbox(self, renderer, call_axes_locator) 3958 bb.append(self._right_title.get_window_extent(renderer)) 3959 -> 3960 bb_xaxis = self.xaxis.get_tightbbox(renderer) 3961 if bb_xaxis: 3962 bb.append(bb_xaxis) C:\Users\dkrantz\AppData\Local\Continuum\anaconda2\lib\site-packages\matplotlib\axis.pyc in get_tightbbox(self, renderer) 1088 return 1089 -> 1090 ticks_to_draw = self._update_ticks(renderer) 1091 ticklabelBoxes, ticklabelBoxes2 = self._get_tick_bboxes(ticks_to_draw, 1092 renderer) C:\Users\dkrantz\AppData\Local\Continuum\anaconda2\lib\site-packages\matplotlib\axis.pyc in _update_ticks(self, renderer) 972 973 interval = self.get_view_interval() --> 974 tick_tups = list(self.iter_ticks()) 975 if self._smart_bounds and tick_tups: 976 # handle inverted limits C:\Users\dkrantz\AppData\Local\Continuum\anaconda2\lib\site-packages\matplotlib\axis.pyc in iter_ticks(self) 919 self.major.formatter.set_locs(majorLocs) 920 majorLabels = [self.major.formatter(val, i) --> 921 for i, val in enumerate(majorLocs)] 922 923 minorLocs = self.minor.locator() C:\Users\dkrantz\AppData\Local\Continuum\anaconda2\lib\site-packages\matplotlib\ticker.pyc in __call__(self, x, pos) 398 `x` and `pos` are passed through as-is. 399 """ --> 400 return self.func(x, pos) 401 402 C:\Users\dkrantz\AppData\Local\Continuum\anaconda2\lib\site-packages\fbprophet\forecaster.pyc in (x, pos) 1502 fmt_str = '{dt:%m}/{dt:%d}' 1503 ax.xaxis.set_major_formatter(FuncFormatter( -> 1504 lambda x, pos=None: fmt_str.format(dt=num2date(x)))) 1505 ax.set_xlabel('ds') 1506 ax.set_ylabel(name) ValueError: Invalid format string
bletham commented 6 years ago

Odd, what version of fbprophet do you have?

dkrantz commented 6 years ago

pandas version is 0.20.3 matplotlib version is 2.1.0 numpy version is 1.13.3 fb prophet version is 0.2.1

bletham commented 6 years ago

I've tried this in Windows with Anaconda3 and don't produce the issue there, in fbprophet 0.3. I think this has been fixed with the new release.