import gradio as gr import pandas as pd import numpy as np from statsmodels.tsa.arima.model import ARIMA from sklearn.metrics import mean_squared_error, mean_absolute_error import matplotlib.pyplot as plt import io import base64 # ✅ Mapping of best ARIMA orders per part number # (replace with your actual best parameters from the notebook) BEST_ARIMA_PARAMS = { "TE50011": (2, 0, 1), "TE50012": (2, 1, 1), "TE50013": (1, 1, 1), "TE50014": (0, 0, 0), "TE50015": (0, 1, 0), "TE50016": (0, 1, 1), "TE50017": (2, 1, 0), "TE50018": (1, 1, 2), "TE50019": (2, 1, 0), "TE50020": (2, 1, 1), "TE50021": (0, 0, 0), "TE50022": (2, 1, 2), "TE50023": (1, 0, 0), "TE50024": (1, 1, 2), "TE50025": (1, 1, 0), "TE50026": (2, 0, 0), "TE50027": (0, 0, 2), "TE50028": (1, 0, 1), "TE50029": (0, 1, 0), "TE50030": (0, 1, 0), "TE50031": (1, 1, 0), "TE50032": (1, 1, 0), "TE50033": (2, 1, 1), "TE50034": (0, 0, 0), "TE50035": (2, 1, 0), "TE50036": (0, 1, 0), "TE50037": (0, 0, 2), "TE50038": (2, 1, 1), "TE50039": (2, 0, 1), "TE50040": (1, 1, 1), "TE50041": (1, 1, 0), "TE50042": (0, 1, 0), "TE50043": (2, 1, 0), "TE50044": (0, 1, 0), "TE50045": (1, 0, 0), "TE50046": (0, 1, 0), "TE50047": (1, 1, 2), "TE50048": (0, 1, 0), "TE50049": (2, 1, 2), "TE50050": (2, 0, 2), "TE50051": (0, 1, 0), "TE50052": (2, 0, 1), "TE50053": (1, 0, 2), "TE50054": (1, 1, 0), "TE50055": (0, 0, 0), "TE50056": (2, 0, 2), "TE50057": (0, 1, 0), "TE50058": (1, 1, 0), "TE50059": (0, 1, 0), "TE50060": (1, 1, 2), "TE50061": (2, 1, 0), "TE50062": (1, 1, 2), "TE50063": (0, 0, 2), "TE50064": (0, 1, 0), "TE50065": (0, 1, 0), "TE50066": (2, 1, 0), "TE50067": (2, 1, 0), "TE50068": (1, 1, 0), "TE50069": (0, 1, 2), "TE50070": (1, 1, 1), "TE50071": (2, 1, 2), "TE50072": (2, 0, 2), "TE50073": (2, 0, 1), "TE50074": (0, 1, 1), "TE50075": (2, 1, 0), "TE50076": (0, 1, 0), "TE50077": (0, 1, 2), "TE50078": (2, 1, 0), "TE50079": (0, 1, 0), "TE50080": (2, 1, 2), "TE50081": (2, 1, 0), "TE50082": (0, 1, 0), "TE50083": (2, 1, 0), "TE50084": (0, 1, 0), "TE50085": (2, 1, 0), "TE50086": (0, 1, 1), "TE50087": (2, 0, 1), "TE50088": (2, 1, 0), "TE50089": (2, 0, 2), "TE50090": (0, 1, 1), "TE50091": (0, 1, 0), "TE50092": (0, 1, 0), "TE50093": (2, 0, 2), "TE50094": (2, 0, 1), "TE50095": (1, 0, 0), "TE50096": (0, 1, 0), "TE50097": (0, 1, 0), "TE50098": (1, 1, 0), "TE50099": (0, 1, 0), "TE50100": (2, 0, 2), "TE50101": (2, 0, 2), "TE50102": (1, 1, 1), "TE50103": (0, 0, 1), "TE50104": (0, 1, 0), "TE50105": (2, 0, 0), "TE50106": (2, 0, 0), "TE50107": (2, 0, 0), "TE50108": (2, 1, 0), "TE50109": (2, 1, 0), "TE50110": (0, 1, 0), "TE50111": (0, 1, 2), "TE50112": (2, 1, 2), "TE50113": (0, 1, 0), "TE50114": (0, 1, 0), "TE50115": (1, 1, 0), "TE50116": (1, 0, 0), "TE50117": (2, 1, 0), "TE50118": (2, 1, 1), "TE50119": (2, 1, 0), "TE50120": (2, 1, 1), "TE50121": (0, 1, 0), "TE50122": (1, 1, 2), "TE50123": (2, 1, 1), "TE50124": (1, 0, 2), "TE50125": (1, 0, 2), "TE50126": (0, 0, 0), "TE50127": (0, 1, 0), "TE50128": (2, 1, 0), "TE50129": (0, 1, 0), "TE50130": (1, 1, 0), "TE50131": (0, 1, 1), "TE50132": (0, 1, 0), "TE50133": (2, 0, 2), "TE50134": (1, 1, 0), "TE50135": (0, 1, 0), "TE50136": (0, 1, 2), "TE50137": (2, 0, 0), "TE50138": (2, 1, 0), "TE50139": (0, 1, 0), "TE50140": (0, 1, 0), "TE50141": (0, 1, 0), "TE50142": (2, 1, 2), "TE50143": (0, 1, 0), "TE50144": (0, 1, 0), "TE50145": (1, 0, 2), "TE50146": (0, 0, 0), "TE50147": (0, 0, 2), "TE50148": (2, 0, 2), "TE50149": (1, 0, 2), "TE50150": (2, 1, 1), "TE50151": (2, 0, 2), "TE50152": (2, 0, 1), "TE50153": (2, 0, 2), "TE50154": (2, 0, 2), "TE50155": (0, 0, 0), "TE50156": (0, 0, 0), "TE50157": (0, 1, 2), "TE50158": (1, 0, 2), "TE50159": (2, 1, 0), "TE50160": (2, 1, 0), "TE50161": (2, 0, 2), "TE50162": (0, 1, 0), "TE50163": (0, 1, 0), "TE50164": (1, 0, 0), "TE50165": (0, 1, 0), "TE50166": (2, 0, 2), "TE50167": (0, 1, 2), "TE50168": (0, 1, 0), "TE50169": (0, 1, 0), "TE50170": (1, 1, 0), "TE50171": (2, 1, 2), "TE50172": (2, 1, 2), "TE50173": (0, 1, 0), "TE50174": (2, 1, 0), "TE50175": (0, 1, 0), "TE50176": (2, 0, 2), "TE50177": (2, 0, 2), "TE50178": (1, 1, 0), "TE50179": (1, 0, 1), "TE50180": (0, 1, 0), "TE50181": (0, 1, 0), "TE50182": (1, 1, 0), "TE50183": (2, 1, 1), "TE50184": (0, 1, 0), "TE50185": (1, 1, 0), "TE50186": (2, 0, 2), "TE50187": (0, 1, 0), "TE50188": (0, 1, 1), "TE50189": (2, 0, 2), "TE50190": (1, 1, 0), "TE50191": (0, 1, 0), "TE50192": (0, 0, 1), "TE50193": (1, 1, 0), "TE50194": (2, 1, 0), "TE50195": (2, 0, 2), "TE50196": (0, 1, 0), "TE50197": (0, 0, 1), "TE50198": (0, 1, 0), "TE50199": (0, 1, 0), "TE50200": (0, 1, 0), "TE50201": (0, 1, 0), "TE50202": (0, 1, 0), "TE50203": (0, 1, 0), "TE50204": (2, 1, 2), "TE50205": (2, 0, 1), "TE50206": (1, 1, 1), "TE50207": (2, 0, 0), "TE50208": (0, 1, 0), "TE50209": (2, 0, 2), "TE50210": (0, 1, 0), "TE50211": (0, 1, 0), "TE50212": (0, 1, 0), "TE50213": (0, 1, 0), "TE50214": (0, 1, 0), "TE50215": (0, 1, 0), "TE50216": (0, 1, 0), "TE50217": (0, 1, 0), "TE50218": (2, 0, 1), "TE50219": (2, 1, 0), "TE50220": (2, 1, 0), "TE50221": (1, 1, 2), "TE50222": (0, 1, 0), "TE50223": (0, 1, 1), "TE50224": (0, 1, 0), "TE50225": (0, 1, 0), "TE50226": (0, 1, 0), "TE50227": (0, 1, 0), "TE50228": (0, 1, 0), "TE50229": (0, 1, 0), "TE50230": (0, 1, 0), "TE50231": (0, 1, 0), "TE50232": (0, 1, 1), "TE50233": (1, 1, 2), "TE50234": (0, 1, 0), "TE50235": (0, 1, 0), "TE50236": (0, 1, 0), "TE50237": (0, 1, 0), "TE50238": (0, 0, 2), "TE50239": (0, 1, 0), "TE50240": (0, 1, 0), "TE50241": (0, 1, 0), "TE50242": (0, 1, 0), "TE50243": (0, 1, 0), "TE50244": (0, 1, 0), "TE50245": (0, 1, 0), "TE50246": (0, 1, 0), "TE50247": (0, 1, 0), "TE50248": (0, 1, 0), "TE50249": (0, 1, 0), "TE50250": (2, 0, 1), "TE50251": (2, 0, 1), "TE50252": (0, 1, 0), "TE50253": (0, 1, 0), "TE50254": (0, 1, 0), "TE50255": (0, 1, 0), "TE50256": (0, 1, 0), "TE50257": (0, 1, 0), "TE50258": (0, 1, 0), "TE50259": (0, 1, 0), "TE50260": (0, 1, 0), "TE50261": (0, 1, 0), "TE50262": (0, 1, 0), "TE50263": (0, 1, 0), "TE50264": (0, 1, 0), "TE50265": (0, 1, 0), "TE50266": (0, 1, 0), "TE50267": (0, 1, 0), "TE50268": (0, 1, 0), "TE50269": (0, 1, 0), "TE50270": (0, 1, 0), "TE50271": (0, 1, 0), "TE50272": (0, 1, 0), "TE50273": (0, 1, 0), "TE50274": (0, 1, 0), "TE50275": (0, 1, 0), "TE50276": (0, 1, 0), "TE50277": (0, 1, 0), "TE50278": (0, 1, 0), "TE50279": (0, 1, 0), "TE50280": (0, 1, 0), "TE50281": (0, 1, 0), "TE50282": (0, 1, 0), "TE50283": (0, 1, 0), "TE50284": (0, 1, 0), "TE50285": (0, 1, 0), "TE50286": (0, 1, 0), "TE50287": (0, 1, 0), "TE50288": (0, 1, 0), "TE50289": (0, 1, 0), "TE50290": (0, 1, 0), "TE50291": (0, 1, 0), "TE50292": (0, 1, 0), "TE50293": (0, 1, 0), "TE50294": (0, 1, 0), "TE50295": (0, 1, 0), "TE50296": (0, 1, 0), "TE50297": (0, 1, 0), "TE50298": (0, 1, 0), "TE50299": (0, 1, 0), "TE50300": (0, 1, 0), "TE50301": (0, 1, 0), "TE50302": (0, 1, 0), "TE50303": (0, 1, 0), "TE50304": (0, 1, 0), "TE50305": (0, 1, 0), "TE50306": (0, 1, 0), "TE50307": (0, 1, 0), "TE50308": (0, 1, 0), "TE50309": (0, 1, 0), "TE50310": (0, 1, 0), "TE50311": (0, 1, 0), "TE50312": (0, 1, 0), "TE50313": (0, 1, 0), "TE50314": (0, 1, 0), "TE50315": (0, 1, 0), "TE50316": (0, 1, 0), "TE50317": (0, 1, 0), "TE50318": (0, 1, 0), "TE50319": (0, 1, 0), "TE50320": (0, 1, 0), "TE50321": (0, 1, 0), "TE50322": (0, 1, 0), "TE50323": (0, 1, 0), "TE50324": (0, 1, 0), "TE50325": (0, 1, 0), "TE50326": (0, 1, 0), "TE50327": (0, 1, 0), "TE50328": (2, 0, 2), "TE50329": (0, 1, 0), "TE50330": (0, 1, 0), "TE50331": (0, 1, 0), "TE50332": (0, 1, 0), "TE50333": (0, 1, 0), "TE50334": (0, 1, 0), "TE50335": (0, 1, 1), "TE50336": (0, 1, 1), "TE50337": (0, 1, 0), "TE50338": (0, 1, 0), "TE50339": (0, 1, 0), "TE50340": (0, 1, 0), "TE50341": (0, 1, 0), "TE50342": (0, 1, 0), "TE50343": (0, 1, 0), "TE50344": (0, 1, 0), "TE50345": (0, 1, 0), "TE50346": (0, 1, 0), "TE50346": (0, 1, 0), "TE50347": (0, 1, 0), "TE50348": (0, 1, 0), "TE50349": (0, 1, 0), "TE50350": (0, 1, 0), "TE50351": (0, 1, 0), "TE50352": (0, 1, 0), "TE50353": (0, 1, 0) } # Load and preprocess data def load_data(): df = pd.read_csv("data (3).csv", skiprows=2, header=None) header_df = pd.read_csv("data (3).csv", nrows=2, header=None) column_names = ['PN', 'Dummy_Project'] + [ f"{header_df.iloc[0, i]}_{header_df.iloc[1, i]}" for i in range(2, len(header_df.columns)) ] df.columns = column_names id_vars = ['PN', 'Dummy_Project'] value_vars = column_names[2:] df_long = df.melt( id_vars=id_vars, value_vars=value_vars, var_name='Time_Period', value_name='y' ) df_long[['Year', 'Month']] = df_long['Time_Period'].str.split('_', expand=True) df_long.drop(columns=['Time_Period'], inplace=True) df_long['Year'] = df_long['Year'].astype(int) df_long['Month'] = df_long['Month'].astype(str) df_long['Date'] = pd.to_datetime( df_long['Year'].astype(str) + '-' + df_long['Month'] + '-01', format='%Y-%b-%d', errors='coerce' ) df_long.dropna(subset=['y'], inplace=True) df_long.reset_index(drop=True, inplace=True) return df_long # Get available part numbers def get_part_numbers(df): return df['PN'].unique().tolist() # Train ARIMA model and make predictions def train_arima(series, order=(5,1,0)): model = ARIMA(series, order=order) model_fit = model.fit() forecast = model_fit.forecast(steps=10) return model_fit, forecast # Create plot def create_plot(historical, forecast, freq='M'): plt.figure(figsize=(14, 7)) plt.plot(historical.index, historical, label='Historical', linewidth=2) forecast_index = pd.date_range( start=historical.index[-1] + pd.tseries.frequencies.to_offset(freq), periods=len(forecast), freq=freq ) plt.plot(forecast_index, forecast, label='Forecast', color='orange', linewidth=2) plt.legend(fontsize=12) plt.title('Time Series Forecast', fontsize=16) plt.xlabel('Time Period', fontsize=14) plt.ylabel('Value', fontsize=14) plt.grid(True, alpha=0.3) buf = io.BytesIO() plt.savefig(buf, format='png', bbox_inches="tight") buf.seek(0) img_str = base64.b64encode(buf.read()).decode('utf-8') buf.close() plt.close() return f'' # Main prediction function def predict(part_number, model_name): df = load_data() df_part = df[df['PN'] == part_number].copy() # Prepare time series start_date = '2021-10-09' date_range = pd.date_range(start=start_date, periods=len(df_part), freq='W') df_part['Date'] = date_range df_part.set_index('Date', inplace=True) series = df_part['y'].astype(float) freq = pd.infer_freq(series.index) or 'M' # ✅ Choose ARIMA order from mapping (fallback = (5,1,0)) order = BEST_ARIMA_PARAMS.get(part_number, (5,1,0)) if model_name == 'ARIMA': model, forecast = train_arima(series, order=order) plot_html = create_plot(series, forecast, freq=freq) # Evaluate train_size = int(len(series) * 0.8) train, test = series[:train_size], series[train_size:] model_eval = ARIMA(train, order=order) model_fit_eval = model_eval.fit() predictions = model_fit_eval.forecast(steps=len(test)) rmse = np.sqrt(mean_squared_error(test, predictions)) mae = mean_absolute_error(test, predictions) metrics = f""" Best ARIMA Order for {part_number}: {order} Model Performance Metrics: - RMSE: {rmse:.2f} - MAE: {mae:.2f} Forecast for next 10 periods: {', '.join([f'{x:.2f}' for x in forecast])} """ return metrics, plot_html # Create Gradio interface def create_interface(): df = load_data() part_numbers = get_part_numbers(df) with gr.Blocks() as demo: gr.Markdown("# Time Series Forecasting Dashboard") with gr.Row(): part_dropdown = gr.Dropdown(choices=part_numbers, label="Select Part Number") model_dropdown = gr.Dropdown(choices=['ARIMA'], label="Select Model") predict_btn = gr.Button("Predict") with gr.Row(): metrics_output = gr.Textbox(label="Metrics and Forecast") plot_output = gr.HTML(label="Forecast Plot") predict_btn.click( fn=predict, inputs=[part_dropdown, model_dropdown], outputs=[metrics_output, plot_output] ) return demo if __name__ == "__main__": demo = create_interface() demo.launch()