Chuyển đến nội dung chính

Trung bình Động trong pandas

Tìm hiểu cách bạn có thể nắm bắt xu hướng và hiểu dữ liệu chuỗi thời gian với sự hỗ trợ của trung bình động hoặc trung bình cuộn.
· 8 phút đọc

Khám phá cùng AI

Giới thiệu

Trung bình động, còn gọi là trung bình cuộn hoặc trung bình chạy, được dùng để phân tích dữ liệu chuỗi thời gian bằng cách tính trung bình của các tập con khác nhau trong toàn bộ tập dữ liệu. Vì liên quan đến việc lấy trung bình dữ liệu theo thời gian, nó cũng được gọi là trung bình động (MM) hoặc trung bình cuộn.

Có nhiều cách để tính trung bình cuộn, nhưng một cách là lấy một tập con cố định từ toàn bộ dãy số. Giá trị trung bình động đầu tiên được tính bằng cách lấy trung bình của tập con cố định đầu tiên, sau đó tập con được thay đổi bằng cách di chuyển tiến tới tập con cố định tiếp theo (bao gồm giá trị tương lai trong nhóm nhỏ trong khi loại trừ số trước đó khỏi dãy).

Trung bình động thường được dùng với dữ liệu chuỗi thời gian để nắm bắt các dao động ngắn hạn trong khi tập trung vào các xu hướng dài hạn.

Một vài ví dụ về dữ liệu chuỗi thời gian có thể là giá cổ phiếu, báo cáo thời tiết, chất lượng không khí, tổng sản phẩm quốc nội, việc làm, v.v.

Nhìn chung, trung bình động giúp làm trơn dữ liệu.

Trung bình động là nền tảng của nhiều thuật toán, trong đó có Mô hình Trung bình Động Tích hợp Tự hồi quy (ARIMA), sử dụng trung bình động để dự báo dữ liệu chuỗi thời gian.

Có nhiều loại trung bình động:

  • Trung bình Động Đơn giản (SMA): Trung bình Động Đơn giản (SMA) dùng một cửa sổ trượt để lấy trung bình trong một số khoảng thời gian nhất định. Đây là trung bình có trọng số bằng nhau của n dữ liệu trước đó.

    Để hiểu rõ hơn về SMA, hãy lấy ví dụ một dãy gồm n giá trị:

    sequence of n values

    khi đó, trung bình cuộn có trọng số bằng nhau cho n điểm dữ liệu về bản chất sẽ là trung bình của M điểm dữ liệu trước đó, trong đó M là kích thước cửa sổ trượt:

    simple moving average calculation 1

    Tương tự, để tính các giá trị trung bình cuộn tiếp theo, một giá trị mới sẽ được thêm vào tổng, và giá trị của giai đoạn trước sẽ bị loại ra; vì bạn đã có trung bình của các giai đoạn trước nên không cần tính tổng đầy đủ mỗi lần:

    simple moving average calculation 2
  • Trung bình Động Tích lũy (CMA): Không giống trung bình động đơn giản vốn loại bỏ quan sát cũ nhất khi thêm quan sát mới, trung bình động tích lũy xét tất cả các quan sát trước đó. CMA không phải là kỹ thuật tốt để phân tích xu hướng và làm trơn dữ liệu. Lý do là nó lấy trung bình của toàn bộ dữ liệu trước đó cho đến điểm dữ liệu hiện tại, vì vậy trung bình có trọng số bằng nhau của dãy n giá trị:
    sequence of n values 2
    tính đến thời điểm hiện tại được cho bởi:
    cumulative moving average 1
    Tương tự, để cập nhật trung bình tích lũy cho mỗi giá trị mới đến có thể tính bằng công thức dưới đây:
    cumulative moving average 2
  • Trung bình Động Hàm mũ (EMA): Khác với SMA và CMA, trung bình động hàm mũ đặt trọng số lớn hơn cho các mức giá gần đây và vì vậy có thể là mô hình tốt hơn hoặc nắm bắt chuyển động xu hướng nhanh hơn. Phản ứng của EMA tỷ lệ thuận với mô hình dữ liệu.

Vì EMA đặt trọng số cao hơn cho dữ liệu gần đây so với dữ liệu cũ, chúng phản ứng nhanh hơn với các thay đổi giá mới nhất so với SMA, điều này khiến kết quả từ EMA kịp thời hơn và do đó EMA thường được ưa chuộng hơn các kỹ thuật khác.

Lý thuyết đủ rồi, phải không? Hãy chuyển sang triển khai thực tế trung bình động.

Triển khai Trung bình Động trên Dữ liệu Chuỗi thời gian

Trung bình Động Đơn giản (SMA)

Trước tiên, hãy tạo dữ liệu chuỗi thời gian giả và thử triển khai SMA chỉ bằng Python.

Giả sử có nhu cầu cho một sản phẩm và nó được quan sát trong 12 tháng (1 năm), và bạn cần tìm trung bình động cho các cửa sổ 3 và 4 tháng.

Nhập mô-đun

import pandas as pd
import numpy as np
 
product = {'month' : [1,2,3,4,5,6,7,8,9,10,11,12],'demand':[290,260,288,300,310,303,329,340,316,330,308,310]}
df = pd.DataFrame(product)
df.head()
 

Chạy và chỉnh sửa mã từ hướng dẫn trực tuyến này

Chạy mã
 monthdemand
01290
12260
23288
34300
45310

Hãy tính SMA cho kích thước cửa sổ là 3, nghĩa là mỗi lần bạn sẽ xét ba giá trị để tính trung bình động, và với mỗi giá trị mới, giá trị cũ nhất sẽ bị bỏ qua.

Để triển khai điều này, bạn sẽ dùng hàm iloc của pandas; vì cột demand là thứ bạn cần, bạn sẽ cố định vị trí của cột đó trong hàm iloc còn hàng sẽ là biến i mà bạn sẽ lặp cho đến cuối dataframe.

for i in range(0,df.shape[0]-2):
    df.loc[df.index[i+2],'SMA_3'] = np.round(((df.iloc[i,1]+ df.iloc[i+1,1] +df.iloc[i+2,1])/3),1)
 monthdemandSMA_3
01290NaN
12260NaN
23288279.3
34300282.7
45310299.3

Để kiểm tra cho chắc, hãy dùng thêm hàm dựng sẵn rolling của pandas và xem liệu nó có khớp với trung bình động đơn giản do bạn tự viết bằng python hay không.

df['pandas_SMA_3'] = df.iloc[:,1].rolling(window=3).mean()
df.head()
 monthdemandSMA_3pandas_SMA_3
01290NaNNaN
12260NaNNaN
23288279.3279.333333
34300282.7282.666667
45310299.3299.333333

Tuyệt, như bạn thấy, trung bình động tự viết và của pandas khớp nhau chính xác, nghĩa là bạn đã triển khai SMA đúng.

Hãy nhanh chóng tính thêm trung bình động đơn giản cho window_size là 4.

for i in range(0,df.shape[0]-3):
    df.loc[df.index[i+3],'SMA_4'] = np.round(((df.iloc[i,1]+ df.iloc[i+1,1] +df.iloc[i+2,1]+df.iloc[i+3,1])/4),1)
df.head()
 monthdemandSMA_3pandas_SMA_3SMA_4
01290NaNNaNNaN
12260NaNNaNNaN
23288279.3279.333333NaN
34300282.7282.666667284.5
45310299.3299.333333289.5
df['pandas_SMA_4'] = df.iloc[:,1].rolling(window=4).mean()
df.head()
 monthdemandSMA_3pandas_SMA_3SMA_4pandas_SMA_4
01290NaNNaNNaNNaN
12260NaNNaNNaNNaN
23288279.3279.333333NaNNaN
34300282.7282.666667284.5284.5
45310299.3299.333333289.5289.5

Bây giờ, bạn sẽ vẽ dữ liệu của các trung bình động đã tính.

import matplotlib.pyplot as plt
%matplotlib inline
plt.figure(figsize=[15,10])
plt.grid(True)
plt.plot(df['demand'],label='data')
plt.plot(df['SMA_3'],label='SMA 3 Months')
plt.plot(df['SMA_4'],label='SMA 4 Months')
plt.legend(loc=2)
<matplotlib.legend.Legend at 0x11fe15080>
calculated moving averages line graph

Trung bình Động Tích lũy

Tôi nghĩ giờ chúng ta đã sẵn sàng chuyển sang một tập dữ liệu thực.

Đối với trung bình động tích lũy, hãy sử dụng một tập dữ liệu chất lượng không khí có thể tải về từ liên kết này.

df = pd.read_csv("AirQualityUCI/AirQualityUCI.csv", sep = ";", decimal = ",")
df = df.iloc[ : , 0:14]
df.head()
 DateTimeCO(GT)PT08.S1(CO)NMHC(GT)C6H6(GT)PT08.S2(NMHC)NOx(GT)PT08.S3(NOx)NO2(GT)PT08.S4(NO2)PT08.S5(O3)TRH
010/03/200418.00.002.61360.0150.011.91046.0166.01056.0113.01692.01268.013.648.9
110/03/200419.00.002.01292.0112.09.4955.0103.01174.092.01559.0972.013.347.7
210/03/200420.00.002.21402.088.09.0939.0131.01140.0114.01555.01074.011.954.0
310/03/200421.00.002.21376.080.09.2948.0172.01092.0122.01584.01203.011.060.0
410/03/200422.00.001.61272.051.06.5836.0131.01205.0116.01490.01110.011.259.6

Tiền xử lý là bước thiết yếu mỗi khi bạn làm việc với dữ liệu. Với dữ liệu số, một trong những bước tiền xử lý phổ biến nhất là kiểm tra các giá trị NaN (Null). Nếu có giá trị NaN, bạn có thể thay thế chúng bằng 0, hoặc bằng trung bình, hoặc bằng giá trị liền trước hay liền sau, hoặc thậm chí loại bỏ chúng. Dù thay thế thường là lựa chọn tốt hơn so với loại bỏ, nhưng vì tập dữ liệu này có ít giá trị NULL, việc loại bỏ chúng sẽ không ảnh hưởng đến tính liên tục của chuỗi.

df.isna().sum()
Date             114
Time             114
CO(GT)           114
PT08.S1(CO)      114
NMHC(GT)         114
C6H6(GT)         114
PT08.S2(NMHC)    114
NOx(GT)          114
PT08.S3(NOx)     114
NO2(GT)          114
PT08.S4(NO2)     114
PT08.S5(O3)      114
T                114
RH               114
dtype: int64

Từ kết quả trên, bạn có thể thấy có khoảng 114 giá trị NaN trên tất cả các cột, tuy nhiên bạn sẽ nhận ra rằng chúng đều nằm ở cuối chuỗi thời gian, nên hãy nhanh chóng loại bỏ chúng.

df.dropna(inplace=True)
df.isna().sum()
Date             0
Time             0
CO(GT)           0
PT08.S1(CO)      0
NMHC(GT)         0
C6H6(GT)         0
PT08.S2(NMHC)    0
NOx(GT)          0
PT08.S3(NOx)     0
NO2(GT)          0
PT08.S4(NO2)     0
PT08.S5(O3)      0
T                0
RH               0
dtype: int64

Bạn sẽ áp dụng trung bình động tích lũy lên cột Nhiệt độ (T), vì vậy hãy nhanh chóng tách cột đó ra khỏi toàn bộ dữ liệu.

df_T = pd.DataFrame(df.iloc[:,-2])
df_T.head()
 T
013.6
113.3
211.9
311.0
411.2

Bây giờ, bạn sẽ dùng phương thức expanding của pandas để tìm trung bình tích lũy của dữ liệu trên. Nếu bạn nhớ phần giới thiệu, khác với trung bình động đơn giản, trung bình động tích lũy xét tất cả các giá trị trước đó khi tính trung bình.

df_T['CMA_4'] = df_T.expanding(min_periods=4).mean()
df_T.head(10)
 TCMA_4
013.6NaN
113.3NaN
211.9NaN
311.012.450000
411.212.200000
511.212.033333
611.311.928571
710.711.775000
810.711.655556
910.311.520000

Dữ liệu chuỗi thời gian được vẽ theo thời gian, nên hãy kết hợp cột ngày và giờ rồi chuyển thành đối tượng datetime. Để làm điều này, bạn sẽ dùng mô-đun datetime của python (Nguồn: Hướng dẫn Chuỗi thời gian).

import datetime

df['DateTime'] = (df.Date) + ' ' + (df.Time)
df.DateTime = df.DateTime.apply(lambda x: datetime.datetime.strptime(x, '%d/%m/%Y %H.%M.%S'))

Hãy đổi chỉ mục của dataframe temperature sang datetime.

df_T.index = df.DateTime

Giờ hãy vẽ nhiệt độ thực tế và trung bình động tích lũy theo thời gian.

plt.figure(figsize=[15,10])
plt.grid(True)
plt.plot(df_T['T'],label='temperature')
plt.plot(df_T['CMA_4'],label='CMA_4')
plt.legend(loc=2)
<matplotlib.legend.Legend at 0x1210a2d30>
time series data graph

Trung bình Động Hàm mũ

df_T['EMA'] = df_T.iloc[:,0].ewm(span=40,adjust=False).mean()
df_T.head()
 TCMA_4EMA
DateTime   
2004-03-10 18:00:0013.6NaN13.600000
2004-03-10 19:00:0013.3NaN13.585366
2004-03-10 20:00:0011.9NaN13.503153
2004-03-10 21:00:0011.012.4513.381048
2004-03-10 22:00:0011.212.2013.274655
plt.figure(figsize=[15,10])
plt.grid(True)
plt.plot(df_T['T'],label='temperature')
plt.plot(df_T['CMA_4'],label='CMA_4')
plt.plot(df_T['EMA'],label='EMA')
plt.legend(loc=2)
<matplotlib.legend.Legend at 0x14b2a41d0>
time series data graph 2

Wow! Như bạn có thể quan sát từ biểu đồ trên, Trung bình Động Hàm mũ (EMA) làm rất tốt việc nắm bắt mô hình của dữ liệu trong khi Trung bình Động Tích lũy (CMA) kém hơn đáng kể.

Tiến xa hơn!

Chúc mừng bạn đã hoàn thành hướng dẫn.

Hướng dẫn này là điểm khởi đầu tốt để bạn có thể tính trung bình động cho dữ liệu của mình và hiểu được ý nghĩa của nó.

Hãy thử viết mã Python cho trung bình động tích lũy và trung bình động hàm mũ mà không dùng thư viện pandas. Điều đó sẽ giúp bạn hiểu sâu hơn cách chúng được tính và chúng khác nhau như thế nào.

Vẫn còn rất nhiều điều để thử nghiệm. Hãy thử tính tự tương quan riêng phần giữa dữ liệu đầu vào và trung bình động, và cố gắng tìm một số mối quan hệ giữa hai thứ đó.

Nếu bạn muốn tìm hiểu thêm về DataFrame trong pandas, hãy học khóa học tương tác Nền tảng pandas của DataCamp.

Tài liệu tham khảo:

Vui lòng đặt mọi câu hỏi liên quan đến hướng dẫn này trong phần bình luận bên dưới.

Chủ đề

Liên quan