Ανάλυση Χρονοσειρών στην Python: Οδηγός με Pandas, ARIMA και Πρακτικά Παραδείγματα

Πλήρης οδηγός ανάλυσης χρονοσειρών με Python, pandas 3.0 και statsmodels. Καλύπτει DatetimeIndex, resampling, rolling averages, αποδόμηση εποχικότητας, ARIMA, SARIMA και lag features για μηχανική μάθηση — με παραδείγματα κώδικα που τρέχουν.

Αν δουλεύετε με δεδομένα που αλλάζουν στον χρόνο — πωλήσεις, θερμοκρασίες, τιμές μετοχών, επισκεψιμότητα — τότε σίγουρα χρειάζεστε ανάλυση χρονοσειρών. Η Python, με τις βιβλιοθήκες pandas και statsmodels, δίνει ένα εξαιρετικά δυνατό οικοσύστημα για να φορτώνετε, να επεξεργάζεστε και να προβλέπετε χρονικά δεδομένα.

Σε αυτόν τον οδηγό θα καλύψουμε τα πάντα: από τα βασικά του DatetimeIndex μέχρι μοντέλα ARIMA και SARIMA, με πλήρη παραδείγματα κώδικα που τρέχουν στο pandas 3.0. Λοιπόν, ας ξεκινήσουμε.

Τι Είναι μια Χρονοσειρά και Γιατί Μας Ενδιαφέρει

Μια χρονοσειρά (time series) είναι μια ακολουθία παρατηρήσεων καταγεγραμμένων σε χρονολογική σειρά. Κάθε τιμή συνδέεται με μια χρονική στιγμή — ώρα, ημέρα, μήνα ή έτος.

Τα παραδείγματα είναι κυριολεκτικά παντού:

  • Χρηματοοικονομικά: Ημερήσιες τιμές κλεισίματος μετοχών
  • Μετεωρολογία: Ωριαίες μετρήσεις θερμοκρασίας
  • E-commerce: Μηνιαίες πωλήσεις ανά κατηγορία
  • IoT: Δεδομένα αισθητήρων σε πραγματικό χρόνο
  • Web analytics: Ημερήσια επισκεψιμότητα ιστοσελίδας

Ουσιαστικά, η ανάλυση χρονοσειρών μας βοηθάει να εντοπίσουμε τάσεις (trends), εποχικότητα (seasonality) και κυκλικά μοτίβα. Και το πιο ενδιαφέρον; Μας επιτρέπει να κάνουμε προβλέψεις για το μέλλον.

Στήσιμο Περιβάλλοντος: Εγκατάσταση Βιβλιοθηκών

Πριν ξεκινήσουμε, πρέπει να εγκαταστήσουμε τα εργαλεία μας. Δεν είναι πολλά, ευτυχώς:

# Εγκατάσταση βιβλιοθηκών
pip install pandas==3.0.1 numpy matplotlib statsmodels scikit-learn

Και οι βασικές εισαγωγές που θα χρειαστούμε σε όλο τον οδηγό:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from statsmodels.tsa.seasonal import seasonal_decompose
from statsmodels.tsa.arima.model import ARIMA
from statsmodels.tsa.statespace.sarimax import SARIMAX
from statsmodels.tsa.stattools import adfuller

print(f"pandas version: {pd.__version__}")
# pandas version: 3.0.1

DatetimeIndex: Η Βάση Κάθε Χρονοσειράς στο Pandas

Το πρώτο βήμα σε κάθε ανάλυση χρονοσειράς είναι η σωστή μετατροπή των ημερομηνιών. Αυτό ακούγεται τετριμμένο, αλλά πιστέψτε με — πολλά bugs ξεκινούν ακριβώς εδώ. Το pandas χρησιμοποιεί τον τύπο DatetimeIndex για να αναπαραστήσει χρονικά δεδομένα, κάτι που ξεκλειδώνει ισχυρές λειτουργίες ευρετηρίασης και αναδειγματοληψίας.

Δημιουργία DatetimeIndex από Αρχεία

# Φόρτωση CSV με αυτόματη αναγνώριση ημερομηνιών
df = pd.read_csv("sales_data.csv", parse_dates=["date"], index_col="date")
print(df.index)
# DatetimeIndex(['2024-01-01', '2024-01-02', ...], dtype='datetime64[us]', name='date', freq=None)

# Στο pandas 3.0, η ανάλυση ημερομηνιών χρησιμοποιεί
# μικροδευτερόλεπτα (us) αντί νανοδευτερόλεπτα (ns)
print(df.index.dtype)
# datetime64[us]

Δημιουργία Χρονοσειράς από το Μηδέν

Αν δεν έχετε πραγματικά δεδομένα ακόμα (ή θέλετε απλά να πειραματιστείτε), μπορείτε να φτιάξετε συνθετικά δεδομένα αρκετά εύκολα:

# Δημιουργία εύρους ημερομηνιών
dates = pd.date_range(start="2024-01-01", periods=365, freq="D")

# Συνθετικά δεδομένα πωλήσεων με τάση και εποχικότητα
np.random.seed(42)
trend = np.linspace(100, 200, 365)
seasonality = 30 * np.sin(2 * np.pi * np.arange(365) / 365)
noise = np.random.normal(0, 10, 365)
sales = trend + seasonality + noise

ts = pd.Series(sales, index=dates, name="sales")
print(ts.head())
# 2024-01-01    104.967142
# 2024-01-02    103.106346
# 2024-01-03    107.426904
# 2024-01-04    117.662568
# 2024-01-05    101.070885
# Freq: D, Name: sales, dtype: float64

Ευρετηρίαση και Τεμαχισμός Χρονοσειρών

Μόλις έχετε DatetimeIndex, μπορείτε να κάνετε εξαιρετικά εκφραστικές επιλογές. Αυτό είναι ίσως από τα πιο βολικά χαρακτηριστικά του pandas:

# Επιλογή συγκεκριμένου μήνα
jan_data = ts["2024-01"]
print(f"Ιανουάριος 2024: {len(jan_data)} εγγραφές")

# Επιλογή εύρους ημερομηνιών
q1 = ts["2024-01":"2024-03"]
print(f"Q1 2024: {len(q1)} εγγραφές")

# Επιλογή με loc και Timestamp
from_date = pd.Timestamp("2024-06-01")
to_date = pd.Timestamp("2024-06-30")
june = ts.loc[from_date:to_date]
print(f"Ιούνιος 2024: {len(june)} εγγραφές")

Σημείωση pandas 3.0: Η τεμαχισμός με datetime.date αντικείμενα είναι πλέον deprecated. Χρησιμοποιήστε πάντα pd.Timestamp ή strings — αλλιώς θα δείτε warnings.

Resampling: Αλλαγή Συχνότητας Χρονοσειράς

Η αναδειγματοληψία (resampling) είναι ειλικρινά μια από τις πιο χρήσιμες λειτουργίες στην ανάλυση χρονοσειρών. Σας επιτρέπει να αλλάξετε τη συχνότητα των δεδομένων — από ημερήσια σε εβδομαδιαία, μηνιαία ή ετήσια — με μία γραμμή κώδικα.

Downsampling: Από Ημερήσια σε Μηνιαία

# Μηνιαίος μέσος όρος πωλήσεων
monthly_avg = ts.resample("ME").mean()
print(monthly_avg.head())
# 2024-01-31    112.345678
# 2024-02-29    118.234567
# 2024-03-31    128.456789
# ...

# Μηνιαίο σύνολο πωλήσεων
monthly_sum = ts.resample("ME").sum()

# Εβδομαδιαίος μέσος όρος
weekly_avg = ts.resample("W").mean()

# Τριμηνιαίες στατιστικές
quarterly = ts.resample("QE").agg(["mean", "std", "min", "max"])
print(quarterly)

Upsampling: Από Μηνιαία σε Ημερήσια

Μερικές φορές χρειάζεστε το αντίθετο — να πάτε από αραιά σε πιο πυκνά δεδομένα. Εδώ η παρεμβολή κάνει τη δουλειά:

# Upsampling με παρεμβολή (interpolation)
monthly = ts.resample("ME").mean()
daily_interp = monthly.resample("D").interpolate(method="linear")
print(f"Πριν: {len(monthly)} εγγραφές → Μετά: {len(daily_interp)} εγγραφές")

Rolling Windows: Κυλιόμενοι Μέσοι Όροι

Οι κυλιόμενοι μέσοι όροι (rolling averages) είναι ένα απλό αλλά πανίσχυρο εργαλείο. Εξομαλύνουν τον θόρυβο στα δεδομένα και αποκαλύπτουν την υποκείμενη τάση. Τους βλέπετε παντού — από εξερευνητική ανάλυση μέχρι trading strategies.

# Κυλιόμενος μέσος όρος 7 ημερών
rolling_7 = ts.rolling(window=7).mean()

# Κυλιόμενος μέσος όρος 30 ημερών
rolling_30 = ts.rolling(window=30).mean()

# Οπτικοποίηση
fig, ax = plt.subplots(figsize=(14, 6))
ax.plot(ts, alpha=0.4, label="Πραγματικές τιμές")
ax.plot(rolling_7, label="Κυλιόμενος ΜΟ (7 ημέρες)", linewidth=2)
ax.plot(rolling_30, label="Κυλιόμενος ΜΟ (30 ημέρες)", linewidth=2, color="red")
ax.set_title("Πωλήσεις με Κυλιόμενους Μέσους Όρους")
ax.set_xlabel("Ημερομηνία")
ax.set_ylabel("Πωλήσεις")
ax.legend()
plt.tight_layout()
plt.show()

Expanding Window: Σωρευτικές Στατιστικές

Εκτός από rolling, υπάρχει και το expanding window — υπολογίζει στατιστικά από την αρχή της χρονοσειράς μέχρι κάθε σημείο. Αρκετά χρήσιμο για σωρευτικές αναλύσεις:

# Σωρευτικός μέσος όρος (από την αρχή μέχρι κάθε σημείο)
cumulative_mean = ts.expanding().mean()

# Σωρευτικό μέγιστο
cumulative_max = ts.expanding().max()

# Εκθετικός κινητός μέσος (EMA) — δίνει περισσότερο βάρος σε πρόσφατα δεδομένα
ema_20 = ts.ewm(span=20).mean()

Αποδόμηση Χρονοσειράς (Seasonal Decomposition)

Η αποδόμηση χωρίζει μια χρονοσειρά στα τρία βασικά της συστατικά: τάση (trend), εποχικότητα (seasonal) και υπόλοιπο (residual). Ομολογώ ότι αυτή η τεχνική ήταν μια αποκάλυψη για μένα — βλέπεις ξεκάθαρα τι συμβαίνει κάτω από την επιφάνεια των δεδομένων.

from statsmodels.tsa.seasonal import seasonal_decompose

# Αποδόμηση — πρέπει η χρονοσειρά να έχει σταθερή συχνότητα
ts_monthly = ts.resample("ME").mean()

# Αθροιστικό μοντέλο: Y = Trend + Seasonal + Residual
decomposition = seasonal_decompose(ts_monthly, model="additive", period=12)

# Οπτικοποίηση
fig = decomposition.plot()
fig.set_size_inches(14, 10)
fig.suptitle("Αποδόμηση Χρονοσειράς Πωλήσεων", fontsize=14, y=1.02)
plt.tight_layout()
plt.show()

# Πρόσβαση στα επιμέρους συστατικά
print("Τάση:", decomposition.trend.dropna().head())
print("Εποχικότητα:", decomposition.seasonal.head())
print("Υπόλοιπο:", decomposition.resid.dropna().head())

Αν η διακύμανση αυξάνεται ανάλογα με την τάση, χρησιμοποιήστε model="multiplicative" αντί για "additive". Αυτό είναι πιο συνηθισμένο απ' ό,τι νομίζετε.

Έλεγχος Στασιμότητας: Augmented Dickey-Fuller Test

Πριν εφαρμόσουμε μοντέλα όπως ARIMA, πρέπει να ελέγξουμε αν η χρονοσειρά είναι στάσιμη (stationary). Τι σημαίνει αυτό πρακτικά; Μια στάσιμη χρονοσειρά έχει σταθερό μέσο όρο και διακύμανση στον χρόνο — δηλαδή δεν παρουσιάζει τάσεις ή εποχικά μοτίβα.

Ο ADF test είναι ο πιο συνηθισμένος τρόπος ελέγχου:

from statsmodels.tsa.stattools import adfuller

def check_stationarity(series, name=""):
    """Εκτελεί τον Augmented Dickey-Fuller test."""
    result = adfuller(series.dropna())
    print(f"=== ADF Test: {name} ===")
    print(f"ADF Statistic: {result[0]:.4f}")
    print(f"p-value: {result[1]:.6f}")
    print(f"Κρίσιμες τιμές:")
    for key, value in result[4].items():
        print(f"  {key}: {value:.4f}")

    if result[1] <= 0.05:
        print("→ Η χρονοσειρά ΕΙΝΑΙ στάσιμη (απορρίπτουμε τη H0)")
    else:
        print("→ Η χρονοσειρά ΔΕΝ είναι στάσιμη (δεν απορρίπτουμε τη H0)")
    print()

# Έλεγχος αρχικής χρονοσειράς
check_stationarity(ts, "Αρχική χρονοσειρά")

# Αν δεν είναι στάσιμη, εφαρμόζουμε διαφόριση (differencing)
ts_diff = ts.diff().dropna()
check_stationarity(ts_diff, "Μετά από 1η διαφόριση")

Αν το p-value ≤ 0.05, η χρονοσειρά θεωρείται στάσιμη. Αν όχι, χρειάζεται διαφόριση (differencing) — και αυτό ακριβώς κάνει η παράμετρος d στο ARIMA.

ACF και PACF: Εύρεση Παραμέτρων p και q

Εδώ μπαίνουμε σε λίγο πιο θεωρητικά νερά, αλλά μην αγχώνεστε. Τα γραφήματα Αυτοσυσχέτισης (ACF) και Μερικής Αυτοσυσχέτισης (PACF) μας βοηθούν να επιλέξουμε τις παραμέτρους p και q του μοντέλου ARIMA.

from statsmodels.graphics.tsaplots import plot_acf, plot_pacf

fig, axes = plt.subplots(1, 2, figsize=(14, 5))

# ACF — βοηθά στον καθορισμό του q (Moving Average)
plot_acf(ts_diff, lags=40, ax=axes[0])
axes[0].set_title("Αυτοσυσχέτιση (ACF)")

# PACF — βοηθά στον καθορισμό του p (AutoRegressive)
plot_pacf(ts_diff, lags=40, ax=axes[1])
axes[1].set_title("Μερική Αυτοσυσχέτιση (PACF)")

plt.tight_layout()
plt.show()

Ο κανόνας (σε απλά ελληνικά): η PACF δείχνει σημαντική συσχέτιση στα πρώτα p lags, ενώ η ACF στα πρώτα q lags. Οτιδήποτε πέφτει εντός του μπλε διαστήματος εμπιστοσύνης δεν θεωρείται σημαντικό.

Μοντέλο ARIMA: Βήμα προς Βήμα

Φτάσαμε στο "ψωμί και βούτυρο" της πρόβλεψης χρονοσειρών. Το ARIMA (AutoRegressive Integrated Moving Average) είναι το πιο δημοφιλές στατιστικό μοντέλο και ελέγχεται από τρεις παραμέτρους:

  • p (AutoRegressive): Πόσες προηγούμενες τιμές χρησιμοποιούνται
  • d (Integrated): Πόσες φορές εφαρμόζεται διαφόριση
  • q (Moving Average): Πόσα προηγούμενα σφάλματα χρησιμοποιούνται

Εφαρμογή ARIMA στα Δεδομένα μας

from statsmodels.tsa.arima.model import ARIMA

# Διαχωρισμός σε train/test
train = ts[:"2024-10-31"]
test = ts["2024-11-01":]

print(f"Train: {len(train)} εγγραφές")
print(f"Test: {len(test)} εγγραφές")

# Εκπαίδευση μοντέλου ARIMA(2,1,2)
model = ARIMA(train, order=(2, 1, 2))
fitted = model.fit()

# Σύνοψη μοντέλου
print(fitted.summary())

Πρόβλεψη και Αξιολόγηση

Η στιγμή της αλήθειας — πόσο καλά προβλέπει το μοντέλο μας;

# Πρόβλεψη για την περίοδο test
forecast = fitted.forecast(steps=len(test))

# Μετρικές αξιολόγησης
from sklearn.metrics import mean_absolute_error, mean_squared_error

mae = mean_absolute_error(test, forecast)
rmse = np.sqrt(mean_squared_error(test, forecast))
mape = np.mean(np.abs((test - forecast) / test)) * 100

print(f"MAE:  {mae:.2f}")
print(f"RMSE: {rmse:.2f}")
print(f"MAPE: {mape:.2f}%")

# Οπτικοποίηση
fig, ax = plt.subplots(figsize=(14, 6))
ax.plot(train[-60:], label="Εκπαίδευση (τελευταίες 60 ημέρες)")
ax.plot(test, label="Πραγματικές τιμές", color="green")
ax.plot(test.index, forecast, label="Πρόβλεψη ARIMA(2,1,2)", color="red", linestyle="--")
ax.set_title("Πρόβλεψη ARIMA vs Πραγματικές Τιμές")
ax.legend()
plt.tight_layout()
plt.show()

Αυτόματη Εύρεση Βέλτιστων Παραμέτρων

Η χειροκίνητη δοκιμή παραμέτρων γίνεται γρήγορα κουραστική. Ευτυχώς, μπορείτε να αυτοματοποιήσετε τη διαδικασία χρησιμοποιώντας AIC (Akaike Information Criterion):

import itertools
import warnings
warnings.filterwarnings("ignore")

# Πλέγμα παραμέτρων
p_range = range(0, 4)
d_range = range(0, 2)
q_range = range(0, 4)

best_aic = float("inf")
best_order = None

for p, d, q in itertools.product(p_range, d_range, q_range):
    try:
        model = ARIMA(train, order=(p, d, q))
        result = model.fit()
        if result.aic < best_aic:
            best_aic = result.aic
            best_order = (p, d, q)
    except Exception:
        continue

print(f"Βέλτιστη παράμετρος: ARIMA{best_order}")
print(f"AIC: {best_aic:.2f}")

# Εκπαίδευση με τις βέλτιστες παραμέτρους
best_model = ARIMA(train, order=best_order)
best_fitted = best_model.fit()
best_forecast = best_fitted.forecast(steps=len(test))

SARIMA: Πρόβλεψη με Εποχικότητα

Αν τα δεδομένα σας εμφανίζουν εποχικά μοτίβα (π.χ. αυξημένες πωλήσεις κάθε Δεκέμβριο ή αυξημένη κίνηση κάθε καλοκαίρι), το απλό ARIMA δεν αρκεί. Χρειάζεστε το SARIMA (Seasonal ARIMA), που προσθέτει εποχικές παραμέτρους (P, D, Q, s).

Στην πράξη, αυτό κάνει τεράστια διαφορά στην ακρίβεια.

from statsmodels.tsa.statespace.sarimax import SARIMAX

# SARIMA με μηνιαία εποχικότητα (s=12)
# order: (p, d, q) = (1, 1, 1)
# seasonal_order: (P, D, Q, s) = (1, 1, 1, 12)
ts_monthly = ts.resample("ME").mean()
train_m = ts_monthly[:"2024-09"]
test_m = ts_monthly["2024-10":]

model_sarima = SARIMAX(
    train_m,
    order=(1, 1, 1),
    seasonal_order=(1, 1, 1, 12),
    enforce_stationarity=False,
    enforce_invertibility=False
)
sarima_fitted = model_sarima.fit(disp=False)

# Πρόβλεψη
sarima_forecast = sarima_fitted.forecast(steps=len(test_m))

# Οπτικοποίηση με διάστημα εμπιστοσύνης
pred_ci = sarima_fitted.get_forecast(steps=len(test_m)).conf_int()

fig, ax = plt.subplots(figsize=(14, 6))
ax.plot(train_m, label="Εκπαίδευση")
ax.plot(test_m, label="Πραγματικές τιμές", color="green")
ax.plot(test_m.index, sarima_forecast, label="SARIMA", color="red", linestyle="--")
ax.fill_between(
    pred_ci.index,
    pred_ci.iloc[:, 0],
    pred_ci.iloc[:, 1],
    color="red",
    alpha=0.15,
    label="95% Διάστημα Εμπιστοσύνης"
)
ax.set_title("Πρόβλεψη SARIMA με Διάστημα Εμπιστοσύνης")
ax.legend()
plt.tight_layout()
plt.show()

Πρακτικό Παράδειγμα: Ανάλυση Θερμοκρασίας

Ας δούμε τώρα ένα ολοκληρωμένο παράδειγμα, από τη φόρτωση μέχρι την πρόβλεψη. Χρησιμοποιούμε ένα ρεαλιστικό σενάριο ανάλυσης θερμοκρασίας στην Αθήνα — κάτι που θα μπορούσε κάλλιστα να βγει σε πραγματικό project.

import pandas as pd
import numpy as np
from statsmodels.tsa.statespace.sarimax import SARIMAX
from statsmodels.tsa.seasonal import seasonal_decompose
from sklearn.metrics import mean_absolute_error

# 1. Δημιουργία ρεαλιστικών δεδομένων θερμοκρασίας (Αθήνα)
np.random.seed(123)
dates = pd.date_range("2020-01-01", "2025-12-31", freq="D")
n = len(dates)

# Εποχικό μοτίβο θερμοκρασίας
day_of_year = dates.dayofyear
temp_base = 18  # Μέσος ετήσιος ΜΟ για Αθήνα
amplitude = 12  # Διαφορά θερμοκρασίας καλοκαίρι-χειμώνας
seasonality = amplitude * np.sin(2 * np.pi * (day_of_year - 80) / 365)
trend = np.linspace(0, 1.5, n)  # Μικρή ανοδική τάση (κλιματική αλλαγή)
noise = np.random.normal(0, 2.5, n)

temperature = temp_base + seasonality + trend + noise
ts_temp = pd.Series(temperature, index=dates, name="temperature_c")

print(ts_temp.describe())
print(f"\nΕύρος: {ts_temp.min():.1f}°C - {ts_temp.max():.1f}°C")

# 2. Μηνιαίος μέσος όρος
monthly_temp = ts_temp.resample("ME").mean()

# 3. Αποδόμηση
decomp = seasonal_decompose(monthly_temp, model="additive", period=12)
fig = decomp.plot()
fig.set_size_inches(14, 10)
plt.suptitle("Αποδόμηση Μηνιαίας Θερμοκρασίας Αθήνας", fontsize=14, y=1.02)
plt.tight_layout()
plt.show()

# 4. Εκπαίδευση SARIMA
train_temp = monthly_temp[:"2024-12"]
test_temp = monthly_temp["2025-01":]

model = SARIMAX(
    train_temp,
    order=(1, 0, 1),
    seasonal_order=(1, 1, 1, 12)
)
result = model.fit(disp=False)

# 5. Πρόβλεψη 12 μηνών
forecast_temp = result.forecast(steps=12)
mae = mean_absolute_error(test_temp, forecast_temp)
print(f"\nMAE πρόβλεψης: {mae:.2f}°C")

# 6. Οπτικοποίηση
fig, ax = plt.subplots(figsize=(14, 6))
ax.plot(monthly_temp, label="Πραγματική θερμοκρασία")
ax.plot(forecast_temp, label="Πρόβλεψη SARIMA", color="red", linestyle="--", marker="o")
ax.axvline(x=pd.Timestamp("2025-01-01"), color="gray", linestyle=":", label="Αρχή πρόβλεψης")
ax.set_title("Πρόβλεψη Θερμοκρασίας Αθήνας 2025")
ax.set_ylabel("Θερμοκρασία (°C)")
ax.legend()
plt.tight_layout()
plt.show()

Διαχείριση Ελλιπών Τιμών σε Χρονοσειρές

Στον πραγματικό κόσμο (κι εδώ μιλάω από εμπειρία), τα δεδομένα χρονοσειρών σχεδόν πάντα έχουν κενά. Χαμένοι αισθητήρες, downtime σέρβερ, αργίες — πάντα κάτι λείπει. Η σωστή διαχείριση αυτών των κενών είναι κρίσιμη για αξιόπιστα αποτελέσματα.

# Δημιουργία χρονοσειράς με κενά
ts_with_gaps = ts.copy()
# Αφαίρεση τυχαίων σημείων
drop_idx = np.random.choice(ts_with_gaps.index, size=30, replace=False)
ts_with_gaps[drop_idx] = np.nan

print(f"Ελλιπείς τιμές: {ts_with_gaps.isna().sum()}")

# Μέθοδος 1: Forward fill (χρησιμοποιεί την τελευταία γνωστή τιμή)
ts_ffill = ts_with_gaps.ffill()

# Μέθοδος 2: Γραμμική παρεμβολή (ιδανική για χρονοσειρές)
ts_interp = ts_with_gaps.interpolate(method="linear")

# Μέθοδος 3: Χρονική παρεμβολή (λαμβάνει υπόψη τις χρονικές αποστάσεις)
ts_time_interp = ts_with_gaps.interpolate(method="time")

# Μέθοδος 4: Spline παρεμβολή (πιο ομαλή)
ts_spline = ts_with_gaps.interpolate(method="spline", order=3)

# Σύγκριση μεθόδων
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
methods = [
    (ts_ffill, "Forward Fill"),
    (ts_interp, "Γραμμική Παρεμβολή"),
    (ts_time_interp, "Χρονική Παρεμβολή"),
    (ts_spline, "Spline Παρεμβολή")
]
for ax, (filled, name) in zip(axes.flat, methods):
    ax.plot(ts[:50], label="Αρχικά", alpha=0.5)
    ax.plot(filled[:50], label=name, linewidth=2)
    ax.set_title(name)
    ax.legend()
plt.suptitle("Σύγκριση Μεθόδων Συμπλήρωσης Κενών", fontsize=14)
plt.tight_layout()
plt.show()

Shift και Lag Features για Μηχανική Μάθηση

Αν προτιμάτε μοντέλα μηχανικής μάθησης (π.χ. Random Forest, XGBoost) για πρόβλεψη χρονοσειρών, πρέπει πρώτα να μετατρέψετε τα δεδομένα σε μορφή supervised learning. Πώς; Χρησιμοποιώντας lag features — δηλαδή τιμές από προηγούμενες χρονικές στιγμές.

# Δημιουργία lag features
df_ml = pd.DataFrame({"value": ts})
df_ml["lag_1"] = df_ml["value"].shift(1)
df_ml["lag_7"] = df_ml["value"].shift(7)
df_ml["lag_30"] = df_ml["value"].shift(30)
df_ml["rolling_7_mean"] = df_ml["value"].rolling(7).mean()
df_ml["rolling_30_mean"] = df_ml["value"].rolling(30).mean()
df_ml["day_of_week"] = df_ml.index.dayofweek
df_ml["month"] = df_ml.index.month
df_ml = df_ml.dropna()

print(df_ml.head(10))
print(f"\nΣτήλες: {list(df_ml.columns)}")
print(f"Γραμμές: {len(df_ml)}")

# Εκπαίδευση Random Forest
from sklearn.ensemble import RandomForestRegressor
from sklearn.metrics import mean_absolute_error

features = ["lag_1", "lag_7", "lag_30", "rolling_7_mean",
            "rolling_30_mean", "day_of_week", "month"]
target = "value"

train_ml = df_ml[:"2024-10-31"]
test_ml = df_ml["2024-11-01":]

rf = RandomForestRegressor(n_estimators=100, random_state=42)
rf.fit(train_ml[features], train_ml[target])

rf_predictions = rf.predict(test_ml[features])
rf_mae = mean_absolute_error(test_ml[target], rf_predictions)
print(f"\nRandom Forest MAE: {rf_mae:.2f}")

# Feature importance
importance = pd.Series(rf.feature_importances_, index=features)
importance.sort_values(ascending=True).plot(kind="barh", title="Σημαντικότητα Features")
plt.tight_layout()
plt.show()

Συμβουλές Απόδοσης για Μεγάλες Χρονοσειρές

Όταν δουλεύετε με χρονοσειρές εκατομμυρίων εγγραφών, η απόδοση γίνεται πραγματικά ζήτημα. Εδώ είναι μερικά πράγματα που έχω βρει ότι κάνουν πραγματική διαφορά:

  • Χρησιμοποιήστε PyArrow backend: Στο pandas 3.0, το PyArrow backend μειώνει δραματικά τη χρήση μνήμης για στήλες κειμένου.
  • Φορτώστε μόνο ό,τι χρειάζεστε: Τα parse_dates και usecols είναι φίλοι σας κατά τη φόρτωση αρχείων.
  • Προτιμήστε Parquet αντί CSV: Τα αρχεία Parquet είναι 5-10x μικρότερα και πολύ ταχύτερα. Σοβαρά, αξίζει τη μετάβαση.
  • Chunking: Για πολύ μεγάλα αρχεία, χρησιμοποιήστε chunksize στο read_csv().
# Αποδοτική φόρτωση μεγάλων χρονοσειρών
# Αντί για CSV, χρησιμοποιήστε Parquet
ts.to_frame().to_parquet("sales_timeseries.parquet")
df_fast = pd.read_parquet("sales_timeseries.parquet")

# Chunked ανάγνωση CSV
chunks = pd.read_csv(
    "huge_timeseries.csv",
    parse_dates=["timestamp"],
    usecols=["timestamp", "value"],
    chunksize=100_000
)

results = []
for chunk in chunks:
    chunk = chunk.set_index("timestamp")
    monthly = chunk.resample("ME").mean()
    results.append(monthly)

final = pd.concat(results).groupby(level=0).mean()

Συνήθεις Ερωτήσεις (FAQ)

Ποια είναι η διαφορά μεταξύ ARIMA και SARIMA;

Το ARIMA χειρίζεται χρονοσειρές χωρίς εποχικά μοτίβα, χρησιμοποιώντας τρεις παραμέτρους (p, d, q). Το SARIMA επεκτείνει το ARIMA προσθέτοντας εποχικές παραμέτρους (P, D, Q, s), ώστε να αποτυπώνει επαναλαμβανόμενα μοτίβα — π.χ. μηνιαία εποχικότητα με s=12. Αν βλέπετε ξεκάθαρη εποχικότητα στα δεδομένα σας, πηγαίνετε κατευθείαν σε SARIMA.

Πώς ελέγχω αν μια χρονοσειρά είναι στάσιμη;

Ο πιο διαδεδομένος τρόπος είναι ο Augmented Dickey-Fuller (ADF) test της βιβλιοθήκης statsmodels. Αν το p-value βγει ≤ 0.05, η χρονοσειρά θεωρείται στάσιμη. Διαφορετικά, εφαρμόστε διαφόριση — αφαιρέστε κάθε τιμή από την προηγούμενή της με τη μέθοδο .diff() του pandas.

Μπορώ να χρησιμοποιήσω μηχανική μάθηση αντί για ARIMA;

Απολύτως! Μοντέλα όπως Random Forest και Gradient Boosting λειτουργούν περίφημα, αρκεί να μετατρέψετε το πρόβλημα σε supervised learning με lag features. Πολλές φορές δίνουν εξαιρετικά αποτελέσματα, ειδικά με πολλαπλές εξωτερικές μεταβλητές. Ωστόσο, τα στατιστικά μοντέλα παραμένουν πιο ερμηνεύσιμα και πιο εύκολα στο debugging.

Πώς χειρίζομαι ελλιπείς τιμές σε χρονοσειρές;

Η γραμμική παρεμβολή (.interpolate(method="linear")) είναι συνήθως η ασφαλέστερη επιλογή για χρονοσειρές. Για δεδομένα με ισχυρή εποχικότητα, δοκιμάστε method="spline" ή ακόμα και forward fill (.ffill()) αν οι τιμές αλλάζουν αργά στον χρόνο.

Τι άλλαξε στο pandas 3.0 για χρονοσειρές;

Οι κυριότερες αλλαγές: (1) η ανάλυση ημερομηνιών χρησιμοποιεί πλέον μικροδευτερόλεπτα αντί νανοδευτερόλεπτα, (2) η τεμαχισμός με datetime.date είναι deprecated — χρησιμοποιήστε pd.Timestamp, και (3) η προσθαφαίρεση Day offset σε timezone-aware DatetimeIndex δεν διατηρεί πλέον τη συχνότητα. Το Copy-on-Write που ενεργοποιήθηκε εξ ορισμού βελτιώνει επίσης αισθητά την απόδοση σε pipeline με πολλά βήματα.

Σχετικά με τον Συγγραφέα Editorial Team

Our team of expert writers and editors.