I'm working on a precipitation prediction project using time series models like LSTM, ARIMA, and Prophet in Google Colab. The dataset I'm using contains daily precipitation values, but the vast majority of the entries are zero — since on most days, it doesn't rain. Because of this extreme imbalance (many zeros and very few rainy days), my models often perform poorly. For instance: LSTM tends to output constant or near-zero predictions, likely overfitting to the frequent zero values. ARIMA struggles to capture meaningful patterns and ends up producing flat or noisy forecasts. Prophet also fails to model the few rainfall spikes correctly and often underestimates any precipitation. I want to get better model and predict for example 5 years forecast or I don't know exactly how much of an estimate I can make ? How can I better model or preprocess such sparse time series data for precipitation prediction? Are there any recommended techniques to handle the zero-inflated nature of such data in time series forecasting? Any advice or shared experience would be appreciated. Thanks in advance! Hear is my colab code: import numpy as np import pandas as pd import matplotlib.pyplot as plt plt.style.use('fivethirtyeight') from sklearn.metrics import mean_squared_error import seaborn as sns from statsmodels.tsa.arima_model import ARIMA from statsmodels.tsa.stattools import adfuller, acf, pacf from statsmodels.graphics.tsaplots import plot_acf, plot_pacf weather_df = pd.read_csv('sample_data/preprocessed.csv', parse_dates=['DATE'], index_col='DATE') weather_df.head() def list_and_visualize_missing_data(dataset): # Listing total null items and its percent with respect to all nulls total = dataset.isnull().sum().sort_values(ascending=False) percent = ((dataset.isnull().sum())/(dataset.isnull().count())).sort_values(ascending=False) percent = percent*100 print('Count of missing data : \n',total) print('% of missing data : \n',percent) list_and_visualize_missing_data(weather_df) #check trend and seasonality of weather data weather_df.plot(subplots=True, figsize=(20,12)) #detailed view of 2015 year weather_df['2015':'2016'].resample('D') weather_df['2015':'2016'].fillna(weather_df['2015':'2016']['PRECTOTCORR'].mean()) weather_df['2015':'2016'].plot(subplots=True, figsize=(20,12)) print(weather_df) weather_df.index = pd.to_datetime(weather_df.index, format='%Y-%m-%d') # İndeks olarak tarih formatına dönüştür precip = weather_df[['PRECTOTCORR']] # Tarih indeksine göre sıralı olduğundan emin ol precip = precip.sort_index() # Veri setinin uzunluğunu al n = len(precip) train_size = int(n * 0.8) # %80 eğitim, %20 test verisi olarak ayır train_df = precip.iloc[:train_size] test_df = precip.iloc[train_size:] train_df.describe() print(train_df) # check rolling mean and rolling standard deviation def plot_rolling_mean_std(ts): rolling_mean = ts.rolling(12).mean() rolling_std = ts.rolling(12).std() plt.figure(figsize=(22,10)) plt.plot(ts, label='Actual Mean') plt.plot(rolling_mean, label='Rolling Mean') plt.plot(rolling_std, label = 'Rolling Std') plt.xlabel("Date") plt.ylabel("Mean Temperature") plt.title('Rolling Mean & Rolling Standard Deviation') plt.legend() plt.show() # check stationary: mean, variance(std)and adfuller test plot_rolling_mean_std(train_df.PRECTOTCORR) #Plot ACF and PACF acf_lag = acf(train_df.values, nlags=20) pacf_lag = pacf(train_df.values, nlags=20, method='ols') plt.figure(figsize=(22,10)) plt.subplot(1,2,1) plt.plot(acf_lag) plt.axhline(y=0,linestyle='--',color='silver') plt.axhline(y=-1.96/np.sqrt(len(train_df.values)),linestyle='--',color='silver') plt.axhline(y=1.96/np.sqrt(len(train_df.values)),linestyle='--',color='silver') plt.title("Autocorrelation Function") plt.subplot(1,2,2) plt.plot(pacf_lag) plt.axhline(y=0,linestyle='--',color='silver') plt.axhline(y=-1.96/np.sqrt(len(train_df.values)),linestyle='--',color='silver') plt.axhline(y=1.96/np.sqrt(len(train_df.values)),linestyle='--',color='silver') plt.title("Partial Autocorrelation Function") plt.tight_layout() #ARIMA model from statsmodels.tsa.arima.model import ARIMA print("train_df values:",train_df) model = ARIMA(train_df.values, order=(2, 0, 2)) model_fit = model.fit() print(model_fit.summary()) # Plot residual errors plt.rcParams.update({'figure.figsize'12,3), 'figure.dpi':120}) residuals = pd.DataFrame(model_fit.resid) fig, ax = plt.subplots(1,2) residuals.plot(title="Residuals", ax=ax[0]) residuals.plot(kind='kde', title='Density', ax=ax[1]) plt.show() # Actual vs Fitted fig, ax = plt.subplots(figsize=(10, 5)) plot_predict(model_fit, dynamic=False, ax=ax) # Başlık ve etiketler ax.set_title("Gerçek ve Tahmin Edilen Değerler (Actual vs Fitted)", fontsize=14) ax.set_ylabel("Değer", fontsize=12) ax.legend(["Gerçek Değer", "Tahmin"], loc="upper left") plt.tight_layout() plt.show() forecast_steps = len(test_df) forecast_index = test_df.index # 16 dönemlik tahmin ve güven aralığı forecast_obj = model_fit.get_forecast(steps=forecast_steps) fc = forecast_obj.predicted_mean conf = forecast_obj.conf_int(alpha=0.05) # Pandas serileri oluştur fc_series = pd.Series(fc, index=forecast_index) lower_series = pd.Series(conf[:, 0], index=forecast_index) upper_series = pd.Series(conf[:, 1], index=forecast_index) # Grafik plt.figure(figsize=(12,5), dpi=100) plt.plot(train_df, label='Test Value') plt.plot(test_df, label='Actual Values') plt.plot(fc_series, label='Forecast') plt.fill_between(lower_series.index, lower_series, upper_series, color='k', alpha=.15) plt.title('Forecast vs Actual') plt.legend(loc='upper left', fontsize=8) plt.show() # plot Testing and Forecasted data plt.plot(test_df, label='Actual') plt.plot(fc_series, label='Forecast', color='red') plt.legend(loc='upper left', fontsize=8) plt.show() error = mean_squared_error(test_df, fc_series) print('Test Mean Squared Error: ',error) df_result = pd.DataFrame({ "Actual": test_df["PRECTOTCORR"], # test_df'deki hedef sütunu yaz "Forecast": fc_series }) df_result.head(100) This is the code github link where I get and adjust for my need: https://github.com/Niwedita17/Weather-Forecasting-using-ARIMA some screen shots: Continue reading...