Trong chuỗi bài viết này mình sẽ trình bày các kiến thức cơ bản, khái niệm cũng như các phương pháp để phân tích và dự báo chuỗi thời gian (time series). Trước hết ta sẽ tìm hiểu các khái niệm cơ bản liên quan đến chuỗi thời gian.

1. Chuỗi time series là gì?

  • Là chuỗi các điểm dữ liệu được đo theo từng khoảng thời gian liền nhau, khoảng cách giữa các lần đo là bằng nhau. (Lưu ý rằng dữ liệu mà ta đang nói ở đây phải là tập các biến ngẫu nhiên - stochastic process).
  • Ví dụ:

2. Các khái niệm liên quan tới chuỗi time series

  • Stationary: Tính dừng.
    Một chuỗi thời gian có tính dừng khi các giá trị mean, variance, autocorrelation không thay đổi theo thời gian.
    Với hầu hết các phương pháp thống kê dự báo, ta đều phải đảm bảo tính dừng của chuỗi dữ liệu vì thế việc kiểm tra tính dừng là rất quan trọng.
  • Lag:
    Lag-1 của chuỗi thời gian có thể thu được bằng cách dịch chuyển thời gian về quá khứ 1 đơn vị. Tương tự Lag-n thu được bằng cách dịch chuyển n đơn vị thời gian về quá khứ.
    Ví dụ:
    Khi kí hiệu chuỗi thời gian là X(t) thì ta có lag-n của chuỗi thời gian là: X(t-n)
  • Specification:
    Là bước kiểm tra mối quan hệ tuyến tính hay phi tuyến của các biến phụ thuộc bằng cách sử dụng các models như ARIMA, ARCH, GARCH, VAR, Co-integration, etc
  • Differencing:
    Là phương pháp để biến đổi một chuỗi time series thành chuỗi dừng (stationary), để loại bỏ xu hướng (trend), hay sự tự tương quan (auto-correlation).
    Ví dụ:
    Differencing bậc 1 với khoảng interval 1 của chuỗi thời gian X(t) được tính bằng công thức:
    D_1 = X(t)-X(t-1)

3. Thực hành

Sau đây ta sẽ thử kiểm tra các khái niệm bên trên bằng các bài toán thực tế. Để kiểm tra tính dừng của một chuỗi ta có thể sử dụng hàm adfuller của thư viện statsmodel.

Remove trend from a time series:

Giả sử chuỗi time series là:

s = np.array([i + np.random.rand() for i in range(100)])

Đồ thị của chuỗi như sau:
uc?id=19Zc_4qkrVJRjZXW-w0dY1G6ro_Yjfa9P&export=download

plt.plot(s, color='green')

Ta có thể thấy chuỗi tăng dần theo thời gian (trend series) do đó nó không là chuỗi dừng.
Sử dụng hàm adfuller để kiểm tra lại nhận định trên:

from statsmodels.tsa.stattools import adfuller
r = adfuller(s)
print('ADF Statistic: {}'.format(r[0]))
print('p-value: {}'.format(r[1]))
print('Critical Values:')
for key, value in r[4].items():
    print('\t{}: {}'.format(key, value))

Thu được kết quả:

ADF Statistic: 1.4366704172755116
p-value: 0.9972745258766317
Critical Values:
	1%: -3.506057133647011
	5%: -2.8946066061911946
	10%: -2.5844100201994697

Do p-value=0.9972 >> 0.05 nên ta có thể kết luận đây là chuỗi không dừng, đúng với nhận định bên trên.

Tạo hàm để tính chuỗi difference:

def differencing(s, interval=1):
  diff = [s[i]-s[i-interval] for i in range(interval, len(s))]
  return diff

Tính differencing bậc 1 của chuỗi:

ds_1 = differencing(s, 1)

Kiểm tra tính dừng của chuỗi đã được differencing:

r2 = adfuller(ds_1)
print('ADF Statistic: {}'.format(r2[0]))
print('p-value: {}'.format(r2[1]))
print('Critical Values:')
for key, value in r2[4].items():
    print('\t{}: {}'.format(key, value))

Thu được kết quả:

ADF Statistic: -6.303149317251657
p-value: 3.373465505030078e-08
Critical Values:
	1%: -3.506057133647011
	5%: -2.8946066061911946
	10%: -2.5844100201994697

Do p-value=3.37e-08 << 0.05 nên ta có thể kết luận đây là chuỗi dừng. Như vậy chỉ cần sau một bước thực hiện differencing, ta đã triệt tiêu thành phần trend (xu hướng) của chuỗi, và thu được kết quả là một chuỗi dừng.
Lưu ý:
Ngoài cách viết hàm differencing như bên trên, ta có thể sử dụng hàm diff của module pandas.

Remove seasonality from a time series:

Một chuỗi gọi là season series khi mà giá trị của nó có tính lặp lại theo chu kì (season). Ví dụ đơn giản như thời tiết, mực nước biển, lượng băng tan đều là các đại lượng thay đổi theo mùa, ...
Sau đây ta sẽ phân tích chuỗi time series của nhiệt độ trung bình theo tháng của thủ đô Hà Nội. Trước hết ta sẽ chuẩn bị và xử lý data.

# load data
df = pd.read_csv('/content/drive/My Drive/data/temperature/hanoi_temp.csv')
df.head(3)

Kết quả:
uc?id=1RE35xu0lLJ1-qj6O6MsJkUBHI9wdy8Nk&export=download

Combine nhiệt độ các năm thành một chuỗi time series duy nhất:

temp_series = pd.Series()
for i in range(len(temp_df)):
  temp_series = temp_series.append(temp_df.loc[i], ignore_index=True)

plt.plot(temp_series)

,uc?id=1h8uNLPOMrJ2s86iEVofpX6QHv9fIeFFL&export=download

Dễ nhận thấy tính seasonality ở chuỗi bên trên, chu kì của chuỗi là 12 (một năm).
Ta tính defferencing bậc 1 với interval 12 của chuỗi để loại trừ tính seasonality theo năm:

year_diff = temp_series.diff(periods=12)
plt.plot(year_diff)

Kết quả:
uc?id=1uZkHR3RtNxKvZ5bpAjAqyfHzjmjThsVh&export=download

Cách làm như trên được gọi là phương pháp Seasonal adjustment.
Lưu ý:
Ta có thể sử dụng hàm adfuller như bên trên để kiểm tra tính dừng của chuỗi trước và sau khi loại bỏ thành phần season.

Kết Luận

Như vậy ta đã tìm hiểu qua các khái niệm cơ bản liên quan đến time series, cách remove trend và seasonality khỏi chuỗi time series. Trong bài tiếp theo ta sẽ tìm hiểu về kĩ thuật Moving Average Model.

Reference:

https://vi.wikipedia.org/wiki/Chuỗi_thời_gian
https://www.statisticssolutions.com/time-series-analysis/
https://people.duke.edu/~rnau/411diff.htm
https://machinelearningmastery.com/difference-time-series-dataset-python/
https://machinelearningmastery.com/remove-trends-seasonality-difference-transform-python/

Dữ liệu thời tiết:
https://www.gso.gov.vn/default_en.aspx?tabid=773

Source code:
https://github.com/phavaha1/Time-Series