commit db5d46760a6b81da073ffb334606644d1674311a Author: ilgazca Date: Wed Jul 30 16:12:03 2025 +0300 first commit diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 00000000..13566b81 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/TimeSeriesAnalysis.iml b/.idea/TimeSeriesAnalysis.iml new file mode 100644 index 00000000..e0d56747 --- /dev/null +++ b/.idea/TimeSeriesAnalysis.iml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 00000000..105ce2da --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 00000000..019f0211 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 00000000..8f155b37 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 00000000..e69de29b diff --git a/Uploads/airline_passengers.csv b/Uploads/airline_passengers.csv new file mode 100644 index 00000000..e7eecfea --- /dev/null +++ b/Uploads/airline_passengers.csv @@ -0,0 +1,145 @@ +"Month","Thousands of Passengers" +"1949-01",112 +"1949-02",118 +"1949-03",132 +"1949-04",129 +"1949-05",121 +"1949-06",135 +"1949-07",148 +"1949-08",148 +"1949-09",136 +"1949-10",119 +"1949-11",104 +"1949-12",118 +"1950-01",115 +"1950-02",126 +"1950-03",141 +"1950-04",135 +"1950-05",125 +"1950-06",149 +"1950-07",170 +"1950-08",170 +"1950-09",158 +"1950-10",133 +"1950-11",114 +"1950-12",140 +"1951-01",145 +"1951-02",150 +"1951-03",178 +"1951-04",163 +"1951-05",172 +"1951-06",178 +"1951-07",199 +"1951-08",199 +"1951-09",184 +"1951-10",162 +"1951-11",146 +"1951-12",166 +"1952-01",171 +"1952-02",180 +"1952-03",193 +"1952-04",181 +"1952-05",183 +"1952-06",218 +"1952-07",230 +"1952-08",242 +"1952-09",209 +"1952-10",191 +"1952-11",172 +"1952-12",194 +"1953-01",196 +"1953-02",196 +"1953-03",236 +"1953-04",235 +"1953-05",229 +"1953-06",243 +"1953-07",264 +"1953-08",272 +"1953-09",237 +"1953-10",211 +"1953-11",180 +"1953-12",201 +"1954-01",204 +"1954-02",188 +"1954-03",235 +"1954-04",227 +"1954-05",234 +"1954-06",264 +"1954-07",302 +"1954-08",293 +"1954-09",259 +"1954-10",229 +"1954-11",203 +"1954-12",229 +"1955-01",242 +"1955-02",233 +"1955-03",267 +"1955-04",269 +"1955-05",270 +"1955-06",315 +"1955-07",364 +"1955-08",347 +"1955-09",312 +"1955-10",274 +"1955-11",237 +"1955-12",278 +"1956-01",284 +"1956-02",277 +"1956-03",317 +"1956-04",313 +"1956-05",318 +"1956-06",374 +"1956-07",413 +"1956-08",405 +"1956-09",355 +"1956-10",306 +"1956-11",271 +"1956-12",306 +"1957-01",315 +"1957-02",301 +"1957-03",356 +"1957-04",348 +"1957-05",355 +"1957-06",422 +"1957-07",465 +"1957-08",467 +"1957-09",404 +"1957-10",347 +"1957-11",305 +"1957-12",336 +"1958-01",340 +"1958-02",318 +"1958-03",362 +"1958-04",348 +"1958-05",363 +"1958-06",435 +"1958-07",491 +"1958-08",505 +"1958-09",404 +"1958-10",359 +"1958-11",310 +"1958-12",337 +"1959-01",360 +"1959-02",342 +"1959-03",406 +"1959-04",396 +"1959-05",420 +"1959-06",472 +"1959-07",548 +"1959-08",559 +"1959-09",463 +"1959-10",407 +"1959-11",362 +"1959-12",405 +"1960-01",417 +"1960-02",391 +"1960-03",419 +"1960-04",461 +"1960-05",472 +"1960-06",535 +"1960-07",622 +"1960-08",606 +"1960-09",508 +"1960-10",461 +"1960-11",390 +"1960-12",432 \ No newline at end of file diff --git a/Uploads/processed_airline_passengers.csv b/Uploads/processed_airline_passengers.csv new file mode 100644 index 00000000..7a05e6c7 --- /dev/null +++ b/Uploads/processed_airline_passengers.csv @@ -0,0 +1,145 @@ +Month,Thousands of Passengers,Trend,Seasonality,Residuals +1949-01-01,112,,-24.74873737373736, +1949-02-01,118,,-36.18813131313131, +1949-03-01,132,,-2.2411616161616195, +1949-04-01,129,,-8.036616161616166, +1949-05-01,121,,-4.5063131313131395, +1949-06-01,135,,35.40277777777778, +1949-07-01,148,126.79166666666666,63.83080808080808,-42.62247474747474 +1949-08-01,148,127.25,62.82323232323231,-42.07323232323231 +1949-09-01,136,127.95833333333331,16.52020202020202,-8.478535353535335 +1949-10-01,119,128.58333333333331,-20.642676767676765,11.05934343434345 +1949-11-01,104,129.0,-53.593434343434346,28.593434343434346 +1949-12-01,118,129.75,-28.619949494949505,16.869949494949505 +1950-01-01,115,131.25,-24.74873737373736,8.49873737373736 +1950-02-01,126,133.08333333333331,-36.18813131313131,29.104797979797993 +1950-03-01,141,134.91666666666666,-2.2411616161616195,8.324494949494962 +1950-04-01,135,136.41666666666666,-8.036616161616166,6.619949494949509 +1950-05-01,125,137.41666666666666,-4.5063131313131395,-7.910353535353518 +1950-06-01,149,138.75,35.40277777777778,-25.15277777777778 +1950-07-01,170,140.91666666666666,63.83080808080808,-34.74747474747474 +1950-08-01,170,143.16666666666666,62.82323232323231,-35.98989898989897 +1950-09-01,158,145.70833333333331,16.52020202020202,-4.228535353535335 +1950-10-01,133,148.41666666666666,-20.642676767676765,5.226010101010107 +1950-11-01,114,151.54166666666666,-53.593434343434346,16.05176767676769 +1950-12-01,140,154.70833333333331,-28.619949494949505,13.911616161616191 +1951-01-01,145,157.125,-24.74873737373736,12.62373737373736 +1951-02-01,150,159.54166666666666,-36.18813131313131,26.64646464646465 +1951-03-01,178,161.83333333333331,-2.2411616161616195,18.407828282828305 +1951-04-01,163,164.125,-8.036616161616166,6.911616161616166 +1951-05-01,172,166.66666666666666,-4.5063131313131395,9.839646464646481 +1951-06-01,178,169.08333333333331,35.40277777777778,-26.486111111111093 +1951-07-01,199,171.25,63.83080808080808,-36.08080808080808 +1951-08-01,199,173.58333333333331,62.82323232323231,-37.406565656565625 +1951-09-01,184,175.45833333333331,16.52020202020202,-7.978535353535335 +1951-10-01,162,176.83333333333331,-20.642676767676765,5.80934343434345 +1951-11-01,146,178.04166666666666,-53.593434343434346,21.55176767676769 +1951-12-01,166,180.16666666666663,-28.619949494949505,14.453282828282877 +1952-01-01,171,183.12499999999997,-24.74873737373736,12.623737373737388 +1952-02-01,180,186.20833333333331,-36.18813131313131,29.979797979797993 +1952-03-01,193,189.04166666666663,-2.2411616161616195,6.199494949494991 +1952-04-01,181,191.29166666666666,-8.036616161616166,-2.255050505050491 +1952-05-01,183,193.58333333333331,-4.5063131313131395,-6.077020202020175 +1952-06-01,218,195.83333333333331,35.40277777777778,-13.236111111111093 +1952-07-01,230,198.04166666666663,63.83080808080808,-31.872474747474712 +1952-08-01,242,199.75,62.82323232323231,-20.57323232323231 +1952-09-01,209,202.20833333333331,16.52020202020202,-9.728535353535335 +1952-10-01,191,206.25,-20.642676767676765,5.392676767676765 +1952-11-01,172,210.41666666666666,-53.593434343434346,15.176767676767689 +1952-12-01,194,213.375,-28.619949494949505,9.244949494949505 +1953-01-01,196,215.83333333333331,-24.74873737373736,4.915404040404045 +1953-02-01,196,218.5,-36.18813131313131,13.688131313131308 +1953-03-01,236,220.91666666666666,-2.2411616161616195,17.324494949494962 +1953-04-01,235,222.91666666666666,-8.036616161616166,20.11994949494951 +1953-05-01,229,224.08333333333331,-4.5063131313131395,9.422979797979824 +1953-06-01,243,224.70833333333331,35.40277777777778,-17.111111111111093 +1953-07-01,264,225.33333333333331,63.83080808080808,-25.164141414141397 +1953-08-01,272,225.33333333333331,62.82323232323231,-16.156565656565625 +1953-09-01,237,224.95833333333331,16.52020202020202,-4.478535353535335 +1953-10-01,211,224.58333333333331,-20.642676767676765,7.05934343434345 +1953-11-01,180,224.45833333333331,-53.593434343434346,9.135101010101032 +1953-12-01,201,225.54166666666666,-28.619949494949505,4.078282828282848 +1954-01-01,204,227.99999999999997,-24.74873737373736,0.7487373737373879 +1954-02-01,188,230.45833333333331,-36.18813131313131,-6.270202020202007 +1954-03-01,235,232.25,-2.2411616161616195,4.9911616161616195 +1954-04-01,227,233.91666666666666,-8.036616161616166,1.119949494949509 +1954-05-01,234,235.62499999999997,-4.5063131313131395,2.881313131313168 +1954-06-01,264,237.75,35.40277777777778,-9.152777777777779 +1954-07-01,302,240.49999999999997,63.83080808080808,-2.3308080808080547 +1954-08-01,293,243.95833333333331,62.82323232323231,-13.781565656565625 +1954-09-01,259,247.16666666666666,16.52020202020202,-4.686868686868678 +1954-10-01,229,250.25,-20.642676767676765,-0.6073232323232354 +1954-11-01,203,253.5,-53.593434343434346,3.093434343434346 +1954-12-01,229,257.125,-28.619949494949505,0.49494949494950546 +1955-01-01,242,261.83333333333326,-24.74873737373736,4.915404040404102 +1955-02-01,233,266.66666666666663,-36.18813131313131,2.521464646464679 +1955-03-01,267,271.125,-2.2411616161616195,-1.8838383838383805 +1955-04-01,269,275.2083333333333,-8.036616161616166,1.8282828282828518 +1955-05-01,270,278.5,-4.5063131313131395,-3.9936868686868605 +1955-06-01,315,281.9583333333333,35.40277777777778,-2.361111111111093 +1955-07-01,364,285.75,63.83080808080808,14.419191919191917 +1955-08-01,347,289.3333333333333,62.82323232323231,-5.1565656565656255 +1955-09-01,312,293.25,16.52020202020202,2.2297979797979792 +1955-10-01,274,297.16666666666663,-20.642676767676765,-2.523989898989864 +1955-11-01,237,301.0,-53.593434343434346,-10.406565656565654 +1955-12-01,278,305.4583333333333,-28.619949494949505,1.161616161616191 +1956-01-01,284,309.9583333333333,-24.74873737373736,-1.209595959595955 +1956-02-01,277,314.41666666666663,-36.18813131313131,-1.228535353535321 +1956-03-01,317,318.625,-2.2411616161616195,0.6161616161616195 +1956-04-01,313,321.75,-8.036616161616166,-0.7133838383838338 +1956-05-01,318,324.5,-4.5063131313131395,-1.9936868686868605 +1956-06-01,374,327.0833333333333,35.40277777777778,11.513888888888907 +1956-07-01,413,329.54166666666663,63.83080808080808,19.627525252525288 +1956-08-01,405,331.8333333333333,62.82323232323231,10.343434343434375 +1956-09-01,355,334.4583333333333,16.52020202020202,4.021464646464665 +1956-10-01,306,337.54166666666663,-20.642676767676765,-10.898989898989864 +1956-11-01,271,340.54166666666663,-53.593434343434346,-15.948232323232283 +1956-12-01,306,344.0833333333333,-28.619949494949505,-9.463383838383809 +1957-01-01,315,348.25,-24.74873737373736,-8.50126262626264 +1957-02-01,301,353.0,-36.18813131313131,-15.811868686868692 +1957-03-01,356,357.62499999999994,-2.2411616161616195,0.6161616161616763 +1957-04-01,348,361.375,-8.036616161616166,-5.338383838383834 +1957-05-01,355,364.5,-4.5063131313131395,-4.9936868686868605 +1957-06-01,422,367.16666666666663,35.40277777777778,19.430555555555593 +1957-07-01,465,369.4583333333333,63.83080808080808,31.710858585858603 +1957-08-01,467,371.2083333333333,62.82323232323231,32.968434343434375 +1957-09-01,404,372.16666666666663,16.52020202020202,15.31313131313135 +1957-10-01,347,372.41666666666663,-20.642676767676765,-4.773989898989864 +1957-11-01,305,372.75,-53.593434343434346,-14.156565656565654 +1957-12-01,336,373.625,-28.619949494949505,-9.005050505050495 +1958-01-01,340,375.25,-24.74873737373736,-10.50126262626264 +1958-02-01,318,377.91666666666663,-36.18813131313131,-23.72853535353532 +1958-03-01,362,379.5,-2.2411616161616195,-15.25883838383838 +1958-04-01,348,380.0,-8.036616161616166,-23.963383838383834 +1958-05-01,363,380.70833333333337,-4.5063131313131395,-13.202020202020233 +1958-06-01,435,380.9583333333333,35.40277777777778,18.638888888888907 +1958-07-01,491,381.8333333333333,63.83080808080808,45.3358585858586 +1958-08-01,505,383.66666666666663,62.82323232323231,58.51010101010106 +1958-09-01,404,386.5,16.52020202020202,0.9797979797979792 +1958-10-01,359,390.3333333333333,-20.642676767676765,-10.69065656565655 +1958-11-01,310,394.7083333333333,-53.593434343434346,-31.11489898989897 +1958-12-01,337,398.625,-28.619949494949505,-33.00505050505049 +1959-01-01,360,402.5416666666666,-24.74873737373736,-17.792929292929212 +1959-02-01,342,407.16666666666663,-36.18813131313131,-28.97853535353532 +1959-03-01,406,411.875,-2.2411616161616195,-3.6338383838383805 +1959-04-01,396,416.33333333333326,-8.036616161616166,-12.296717171717091 +1959-05-01,420,420.49999999999994,-4.5063131313131395,4.006313131313196 +1959-06-01,472,425.5,35.40277777777778,11.097222222222221 +1959-07-01,548,430.70833333333326,63.83080808080808,53.46085858585866 +1959-08-01,559,435.125,62.82323232323231,61.05176767676769 +1959-09-01,463,437.7083333333333,16.52020202020202,8.771464646464665 +1959-10-01,407,440.9583333333333,-20.642676767676765,-13.31565656565655 +1959-11-01,362,445.8333333333333,-53.593434343434346,-30.23989898989897 +1959-12-01,405,450.625,-28.619949494949505,-17.005050505050495 +1960-01-01,417,456.33333333333326,-24.74873737373736,-14.584595959595898 +1960-02-01,391,461.37499999999994,-36.18813131313131,-34.186868686868635 +1960-03-01,419,465.2083333333333,-2.2411616161616195,-43.967171717171695 +1960-04-01,461,469.3333333333333,-8.036616161616166,-0.2967171717171482 +1960-05-01,472,472.75,-4.5063131313131395,3.7563131313131395 +1960-06-01,535,475.04166666666663,35.40277777777778,24.555555555555593 +1960-07-01,622,,63.83080808080808, +1960-08-01,606,,62.82323232323231, +1960-09-01,508,,16.52020202020202, +1960-10-01,461,,-20.642676767676765, +1960-11-01,390,,-53.593434343434346, +1960-12-01,432,,-28.619949494949505, diff --git a/app.py b/app.py new file mode 100644 index 00000000..3dcd85cb --- /dev/null +++ b/app.py @@ -0,0 +1,285 @@ +from flask import Flask, request, render_template, send_file, session +import pandas as pd +import io +import os +from statsmodels.tsa.seasonal import seasonal_decompose +from statsmodels.graphics.tsaplots import plot_acf, plot_pacf +import pmdarima as pm +import plotly.express as px +import plotly.graph_objects as go +from plotly.subplots import make_subplots +import plotly.io as pio +from werkzeug.utils import secure_filename +import matplotlib + +matplotlib.use('Agg') # Use non-interactive backend +import matplotlib.pyplot as plt +import io +import base64 +import numpy as np + +app = Flask(__name__) +app.config['UPLOAD_FOLDER'] = 'Uploads' +app.config['ALLOWED_EXTENSIONS'] = {'csv', 'xls', 'xlsx'} +app.secret_key = 'your-secret-key' # Required for session management + +# Ensure upload folder exists +os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True) + + +def allowed_file(filename): + return '.' in filename and filename.rsplit('.', 1)[1].lower() in app.config['ALLOWED_EXTENSIONS'] + + +def create_acf_pacf_plots(data): + # Create ACF and PACF plots using matplotlib + fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 8)) + + plot_acf(data, ax=ax1, lags=40) + ax1.set_title('Autocorrelation Function') + + plot_pacf(data, ax=ax2, lags=40) + ax2.set_title('Partial Autocorrelation Function') + + # Convert matplotlib plot to Plotly + buf = io.BytesIO() + plt.savefig(buf, format='png') + plt.close(fig) + buf.seek(0) + img_str = base64.b64encode(buf.getvalue()).decode('utf-8') + + # Create Plotly figure with image + fig_plotly = go.Figure() + fig_plotly.add_layout_image( + dict( + source=f'data:image/png;base64,{img_str}', + x=0, + y=1, + xref="paper", + yref="paper", + sizex=1, + sizey=1, + sizing="stretch", + opacity=1, + layer="below" + ) + ) + fig_plotly.update_layout( + height=600, + showlegend=False, + xaxis=dict(visible=False), + yaxis=dict(visible=False) + ) + return pio.to_html(fig_plotly, full_html=False) + + +def process_time_series(filepath, do_decomposition, do_forecasting, do_acf_pacf, train_percent, forecast_periods): + try: + # Read file + if filepath.endswith('.csv'): + df = pd.read_csv(filepath) + else: + df = pd.read_excel(filepath) + + # Ensure datetime column exists + date_col = df.columns[0] # Assume first column is date + value_col = df.columns[1] # Assume second column is value + df[date_col] = pd.to_datetime(df[date_col]) + df.set_index(date_col, inplace=True) + + # Initialize variables + plot_html = None + forecast_html = None + acf_pacf_html = None + summary = df[value_col].describe().to_dict() + arima_params = None + seasonal_params = None + train_size = None + test_size = None + + # Save processed data + processed_df = df.copy() + + # Time series decomposition + if do_decomposition: + decomposition = seasonal_decompose(df[value_col], model='additive', period=12) + fig = make_subplots(rows=4, cols=1, + subplot_titles=('Original Series', 'Trend', 'Seasonality', 'Residuals')) + + fig.add_trace(go.Scatter(x=df.index, y=df[value_col], name='Original'), row=1, col=1) + fig.add_trace(go.Scatter(x=df.index, y=decomposition.trend, name='Trend'), row=2, col=1) + fig.add_trace(go.Scatter(x=df.index, y=decomposition.seasonal, name='Seasonality'), row=3, col=1) + fig.add_trace(go.Scatter(x=df.index, y=decomposition.resid, name='Residuals'), row=4, col=1) + + fig.update_layout(height=800, showlegend=True) + plot_html = pio.to_html(fig, full_html=False) + + processed_df['Trend'] = decomposition.trend + processed_df['Seasonality'] = decomposition.seasonal + processed_df['Residuals'] = decomposition.resid + + # Forecasting + if do_forecasting: + # Split data into train and test + train_size = int(len(df) * train_percent) + test_size = len(df) - train_size + train_data = df[value_col].iloc[:train_size] + test_data = df[value_col].iloc[train_size:] if test_size > 0 else pd.Series() + + # Auto ARIMA for best parameters + model = pm.auto_arima(train_data, + seasonal=True, + m=12, + start_p=0, start_q=0, + max_p=3, max_q=3, + start_P=0, start_Q=0, + max_P=2, max_Q=2, + d=1, D=1, + trace=False, + error_action='ignore', + suppress_warnings=True, + stepwise=True) + + # Fit ARIMA with best parameters + model_fit = model.fit(train_data) + forecast = model_fit.predict(n_periods=forecast_periods) + + # Get ARIMA parameters + arima_params = model.order + seasonal_params = model.seasonal_order + + # Forecast plot + forecast_dates = pd.date_range(start=df.index[-1], periods=forecast_periods + 1, + freq=df.index.inferred_freq)[1:] + forecast_fig = go.Figure() + forecast_fig.add_trace(go.Scatter(x=df.index, y=df[value_col], name='Historical')) + if test_size > 0: + forecast_fig.add_trace( + go.Scatter(x=df.index[train_size:], y=test_data, name='Test Data', line=dict(color='green'))) + forecast_fig.add_trace(go.Scatter(x=forecast_dates, y=forecast, name='Forecast', line=dict(dash='dash'))) + forecast_fig.update_layout(title=f'Forecast (ARIMA{arima_params}, Seasonal{seasonal_params})', height=400) + forecast_html = pio.to_html(forecast_fig, full_html=False) + + # ACF/PACF plots + if do_acf_pacf: + acf_pacf_html = create_acf_pacf_plots(df[value_col]) + + # Save processed data + processed_df.to_csv(os.path.join(app.config['UPLOAD_FOLDER'], 'processed_' + os.path.basename(filepath))) + + return { + 'plot_html': plot_html, + 'forecast_html': forecast_html, + 'acf_pacf_html': acf_pacf_html, + 'summary': summary, + 'filename': 'processed_' + os.path.basename(filepath), + 'arima_params': arima_params, + 'seasonal_params': seasonal_params, + 'train_size': train_size, + 'test_size': test_size + } + + except Exception as e: + return {'error': str(e)} + + +@app.route('/') +def index(): + return render_template('index.html') + + +@app.route('/upload', methods=['POST']) +def upload_file(): + if 'file' not in request.files: + return render_template('index.html', error='No file part') + + file = request.files['file'] + if file.filename == '': + return render_template('index.html', error='No selected file') + + if file and allowed_file(file.filename): + filename = secure_filename(file.filename) + filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename) + file.save(filepath) + session['filepath'] = filepath # Store filepath in session + + # Get user selections + do_decomposition = 'decomposition' in request.form + do_forecasting = 'forecasting' in request.form + do_acf_pacf = 'acf_pacf' in request.form + train_percent = float(request.form.get('train_percent', 80)) / 100 + test_percent = float(request.form.get('test_percent', 20)) / 100 + + # Validate train/test percentages + if abs(train_percent + test_percent - 1.0) > 0.01: # Allow small float precision errors + return render_template('index.html', error='Train and test percentages must sum to 100%') + + session['do_decomposition'] = do_decomposition + session['do_forecasting'] = do_forecasting + session['do_acf_pacf'] = do_acf_pacf + + result = process_time_series(filepath, do_decomposition, do_forecasting, do_acf_pacf, train_percent, + forecast_periods=int(request.form.get('forecast_periods', 12))) + + if 'error' in result: + return render_template('index.html', error=result['error']) + + return render_template('results.html', + do_decomposition=do_decomposition, + do_forecasting=do_forecasting, + do_acf_pacf=do_acf_pacf, + train_percent=train_percent * 100, + test_percent=test_percent * 100, + forecast_periods=int(request.form.get('forecast_periods', 12)), + **result) + + +@app.route('/reforecast', methods=['POST']) +def reforecast(): + filepath = session.get('filepath') + if not filepath or not os.path.exists(filepath): + return render_template('index.html', error='Session expired or file not found. Please upload the file again.') + + # Get user selections from reforecast form + train_percent = float(request.form.get('train_percent', 80)) / 100 + test_percent = float(request.form.get('test_percent', 20)) / 100 + forecast_periods = int(request.form.get('forecast_periods', 12)) + + # Validate train/test percentages + if abs(train_percent + test_percent - 1.0) > 0.01: # Allow small float precision errors + return render_template('index.html', error='Train and test percentages must sum to 100%') + + # Get original selections from session or defaults + do_decomposition = session.get('do_decomposition', False) + do_forecasting = True # Since this is a reforecast + do_acf_pacf = session.get('do_acf_pacf', False) + + result = process_time_series(filepath, do_decomposition, do_forecasting, do_acf_pacf, train_percent, + forecast_periods) + + if 'error' in result: + return render_template('index.html', error=result['error']) + + # Update session with new parameters + session['train_percent'] = train_percent + session['test_percent'] = test_percent + session['forecast_periods'] = forecast_periods + + return render_template('results.html', + do_decomposition=do_decomposition, + do_forecasting=do_forecasting, + do_acf_pacf=do_acf_pacf, + train_percent=train_percent * 100, + test_percent=test_percent * 100, + forecast_periods=forecast_periods, + **result) + + +@app.route('/download/') +def download_file(filename): + filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename) + return send_file(filepath, as_attachment=True) + + +if __name__ == '__main__': + app.run(debug=True) \ No newline at end of file diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 00000000..94543680 --- /dev/null +++ b/templates/index.html @@ -0,0 +1,76 @@ + + + + Time Series Analysis + + + +
+

Time Series Analysis App

+
+
+
Upload Time Series Data
+

Upload a CSV or Excel file with time series data. First column should be dates, second column should be values.

+
+
+ +
+
Select Analyses:
+
+ + +
+
+ + +
+
+ + +
+ + +
+ {% if error %} +
{{ error }}
+ {% endif %} +
+
+
+ + + \ No newline at end of file diff --git a/templates/results.html b/templates/results.html new file mode 100644 index 00000000..44e0d571 --- /dev/null +++ b/templates/results.html @@ -0,0 +1,87 @@ + + + + Analysis Results + + + + +
+

Time Series Analysis Results

+ +
+
+
Summary Statistics
+ + {% for key, value in summary.items() %} + + + + + {% endfor %} +
{{ key }}{{ value|round(2) }}
+
+
+ + {% if do_decomposition %} +
+
+
Time Series Decomposition
+ {{ plot_html | safe }} +
+
+ {% endif %} + + {% if do_forecasting %} +
+
+
Forecast (ARIMA{{ arima_params }}, Seasonal{{ seasonal_params }})
+
+
+ + +
+
+ + +
+
+ + +
+ +
+
+

Training Data: {{ train_percent }}% ({{ train_size }} observations)

+

Test Data: {{ test_percent }}% ({{ test_size }} observations)

+

Forecast Periods: {{ forecast_periods }}

+ {{ forecast_html | safe }} +
+
+ {% endif %} + + {% if do_acf_pacf %} +
+
+
ACF and PACF Plots
+ {{ acf_pacf_html | safe }} +
+
+ {% endif %} + + Download Processed Data + Upload Another File +
+ + + \ No newline at end of file diff --git a/uploads/airline_passengers.csv b/uploads/airline_passengers.csv new file mode 100644 index 00000000..e7eecfea --- /dev/null +++ b/uploads/airline_passengers.csv @@ -0,0 +1,145 @@ +"Month","Thousands of Passengers" +"1949-01",112 +"1949-02",118 +"1949-03",132 +"1949-04",129 +"1949-05",121 +"1949-06",135 +"1949-07",148 +"1949-08",148 +"1949-09",136 +"1949-10",119 +"1949-11",104 +"1949-12",118 +"1950-01",115 +"1950-02",126 +"1950-03",141 +"1950-04",135 +"1950-05",125 +"1950-06",149 +"1950-07",170 +"1950-08",170 +"1950-09",158 +"1950-10",133 +"1950-11",114 +"1950-12",140 +"1951-01",145 +"1951-02",150 +"1951-03",178 +"1951-04",163 +"1951-05",172 +"1951-06",178 +"1951-07",199 +"1951-08",199 +"1951-09",184 +"1951-10",162 +"1951-11",146 +"1951-12",166 +"1952-01",171 +"1952-02",180 +"1952-03",193 +"1952-04",181 +"1952-05",183 +"1952-06",218 +"1952-07",230 +"1952-08",242 +"1952-09",209 +"1952-10",191 +"1952-11",172 +"1952-12",194 +"1953-01",196 +"1953-02",196 +"1953-03",236 +"1953-04",235 +"1953-05",229 +"1953-06",243 +"1953-07",264 +"1953-08",272 +"1953-09",237 +"1953-10",211 +"1953-11",180 +"1953-12",201 +"1954-01",204 +"1954-02",188 +"1954-03",235 +"1954-04",227 +"1954-05",234 +"1954-06",264 +"1954-07",302 +"1954-08",293 +"1954-09",259 +"1954-10",229 +"1954-11",203 +"1954-12",229 +"1955-01",242 +"1955-02",233 +"1955-03",267 +"1955-04",269 +"1955-05",270 +"1955-06",315 +"1955-07",364 +"1955-08",347 +"1955-09",312 +"1955-10",274 +"1955-11",237 +"1955-12",278 +"1956-01",284 +"1956-02",277 +"1956-03",317 +"1956-04",313 +"1956-05",318 +"1956-06",374 +"1956-07",413 +"1956-08",405 +"1956-09",355 +"1956-10",306 +"1956-11",271 +"1956-12",306 +"1957-01",315 +"1957-02",301 +"1957-03",356 +"1957-04",348 +"1957-05",355 +"1957-06",422 +"1957-07",465 +"1957-08",467 +"1957-09",404 +"1957-10",347 +"1957-11",305 +"1957-12",336 +"1958-01",340 +"1958-02",318 +"1958-03",362 +"1958-04",348 +"1958-05",363 +"1958-06",435 +"1958-07",491 +"1958-08",505 +"1958-09",404 +"1958-10",359 +"1958-11",310 +"1958-12",337 +"1959-01",360 +"1959-02",342 +"1959-03",406 +"1959-04",396 +"1959-05",420 +"1959-06",472 +"1959-07",548 +"1959-08",559 +"1959-09",463 +"1959-10",407 +"1959-11",362 +"1959-12",405 +"1960-01",417 +"1960-02",391 +"1960-03",419 +"1960-04",461 +"1960-05",472 +"1960-06",535 +"1960-07",622 +"1960-08",606 +"1960-09",508 +"1960-10",461 +"1960-11",390 +"1960-12",432 \ No newline at end of file diff --git a/uploads/processed_airline_passengers.csv b/uploads/processed_airline_passengers.csv new file mode 100644 index 00000000..8361020e --- /dev/null +++ b/uploads/processed_airline_passengers.csv @@ -0,0 +1,145 @@ +Month,Thousands of Passengers +1949-01-01,112 +1949-02-01,118 +1949-03-01,132 +1949-04-01,129 +1949-05-01,121 +1949-06-01,135 +1949-07-01,148 +1949-08-01,148 +1949-09-01,136 +1949-10-01,119 +1949-11-01,104 +1949-12-01,118 +1950-01-01,115 +1950-02-01,126 +1950-03-01,141 +1950-04-01,135 +1950-05-01,125 +1950-06-01,149 +1950-07-01,170 +1950-08-01,170 +1950-09-01,158 +1950-10-01,133 +1950-11-01,114 +1950-12-01,140 +1951-01-01,145 +1951-02-01,150 +1951-03-01,178 +1951-04-01,163 +1951-05-01,172 +1951-06-01,178 +1951-07-01,199 +1951-08-01,199 +1951-09-01,184 +1951-10-01,162 +1951-11-01,146 +1951-12-01,166 +1952-01-01,171 +1952-02-01,180 +1952-03-01,193 +1952-04-01,181 +1952-05-01,183 +1952-06-01,218 +1952-07-01,230 +1952-08-01,242 +1952-09-01,209 +1952-10-01,191 +1952-11-01,172 +1952-12-01,194 +1953-01-01,196 +1953-02-01,196 +1953-03-01,236 +1953-04-01,235 +1953-05-01,229 +1953-06-01,243 +1953-07-01,264 +1953-08-01,272 +1953-09-01,237 +1953-10-01,211 +1953-11-01,180 +1953-12-01,201 +1954-01-01,204 +1954-02-01,188 +1954-03-01,235 +1954-04-01,227 +1954-05-01,234 +1954-06-01,264 +1954-07-01,302 +1954-08-01,293 +1954-09-01,259 +1954-10-01,229 +1954-11-01,203 +1954-12-01,229 +1955-01-01,242 +1955-02-01,233 +1955-03-01,267 +1955-04-01,269 +1955-05-01,270 +1955-06-01,315 +1955-07-01,364 +1955-08-01,347 +1955-09-01,312 +1955-10-01,274 +1955-11-01,237 +1955-12-01,278 +1956-01-01,284 +1956-02-01,277 +1956-03-01,317 +1956-04-01,313 +1956-05-01,318 +1956-06-01,374 +1956-07-01,413 +1956-08-01,405 +1956-09-01,355 +1956-10-01,306 +1956-11-01,271 +1956-12-01,306 +1957-01-01,315 +1957-02-01,301 +1957-03-01,356 +1957-04-01,348 +1957-05-01,355 +1957-06-01,422 +1957-07-01,465 +1957-08-01,467 +1957-09-01,404 +1957-10-01,347 +1957-11-01,305 +1957-12-01,336 +1958-01-01,340 +1958-02-01,318 +1958-03-01,362 +1958-04-01,348 +1958-05-01,363 +1958-06-01,435 +1958-07-01,491 +1958-08-01,505 +1958-09-01,404 +1958-10-01,359 +1958-11-01,310 +1958-12-01,337 +1959-01-01,360 +1959-02-01,342 +1959-03-01,406 +1959-04-01,396 +1959-05-01,420 +1959-06-01,472 +1959-07-01,548 +1959-08-01,559 +1959-09-01,463 +1959-10-01,407 +1959-11-01,362 +1959-12-01,405 +1960-01-01,417 +1960-02-01,391 +1960-03-01,419 +1960-04-01,461 +1960-05-01,472 +1960-06-01,535 +1960-07-01,622 +1960-08-01,606 +1960-09-01,508 +1960-10-01,461 +1960-11-01,390 +1960-12-01,432