Phân tích chuỗi thời gian (Time series analysis - TSA) là kỹ thuật phân tích, tìm hiểu dữ liệu để tìm hiểu xu hướng, chu kỳ, bất thường của tập dữ liệu dữ liệu được ghi lại liên tục theo thời gian và đưa ra dự báo về giá trị của dữ liệu trong tương lai. TSA dùng mô hình như AR, MA, ARIMA, VAR, VECM ... Hãy cùng khám phá về nó trong series bài viết về time series analysis cùng 200lab nhé!
1. Time Series Analysis là gì?
1.1. Giới thiệu về chuỗi thời gian (Time series)
Bước đầu tiên trong việc hiểu và thực hiện phân tích, dự báo chuỗi thời gian là nắm vững khái niệm về "chuỗi thời gian (time series) là gì?".
Đơn giản, chuỗi thời gian là một tập hợp các điểm dữ liệu được sắp xếp theo thời gian. Dữ liệu thường được cách đều về thời gian, có nghĩa là khoảng cách giữa mỗi điểm dữ liệu là bằng nhau.
Nói một cách đơn giản, dữ liệu về một đối tượng được quan sát nào đó có thể được ghi lại mỗi giờ hoặc mỗi phút, ... hoặc nó có thể được lấy trung bình qua mỗi tháng hoặc năm ...
Một số ví dụ điển hình về chuỗi thời gian bao gồm:
- Lượng điện tiêu thụ của một hộ gia đình trong một tháng
- Nhiệt độ trung bình ở thành phố Hồ Chí Minh hằng ngày
- Tổng Sản phẩm quốc nội (GDP): dữ liệu theo quý hoặc theo năm.
- Tỉ lệ lạm phát hoặc thất nghiệp: dữ liệu hàng tháng, hàng quý, hàng năm.
- Chỉ số chứng khoản, tỷ giá hối đoái: dữ liệu hàng ngày, hàng tuần, hàng tháng, hàng quý, hàng năm.
- Lợi nhuận doanh nghiệp, cổ tức, v.v.: dữ liệu hàng quý, hàng năm ...
Hình 1 là đồ thị biểu diễn lợi nhuận hằng quý cho mỗi cổ phiếu tính USD của cổ phiếu Johnson & Johnson từ năm 1960 đến năm 1980. Dữ liệu được cách đều theo thời gian vào cuối mỗi quý của mỗi năm.
1.2. Các thành phần của dữ liệu chuỗi thời gian
Độ hiệu quả của các phân tích dữ liệu chuỗi thời gian phụ thuộc khá nhiều vào việc các bạn phân tích, khám phá về các thành phần của chuỗi thời gian đó là Trends, Seasonality, Cycles và Iregular.
Việc phân tích này giúp các bạn tìm ra quy luật lặp lại tác động tới chuỗi thời gian, xu hướng thay đổi của dữ liệu trong tương lai giúp cho chúng ta phân tích và dự báo số liệu trong tương lai chính xác hơn.
Xu hướng (Trends): đề cập đến hướng di chuyển của dữ liệu qua thời gian, có thể là tăng lên hoặc giảm đi. Điều này thường được nhận biết dựa trên độ dốc của đồ thị biểu diễn dữ liệu.
Một số xu hướng phổ biến của Time Series data:
- Upward Trend: dữ liệu có xu hướng tăng theo thời gian
- Downward Trend: dữ liệu có xu hướng giảm theo thời gian
- Horizontal Trend: Dữ liệu duy trì ổn định hoặc biến đổi rất ít theo thời gian.
- Damped Trend: Dữ liệu giảm dần theo thời gian, nhưng tốc độ giảm đi chậm dần.
- Non-linear Trend: Dữ liệu biến đổi không tuân theo một mô hình tuyến tính đơn giản mà có các biến đổi phức tạp hơn, bao gồm cả sự tăng, giảm, hoặc thay đổi không đồng đều, đột biến theo thời gian (VD: dữ liệu biến đổi theo đồ thị hình sin, cos, hàm số mũ ....)
# Code vẽ đồ thị các xu hướng của time series data
import plotly.graph_objects as go
import numpy as np
import plotly.io as pio
# Tạo dữ liệu mô phỏng cho các loại xu hướng
x = np.arange(1000)
# Upward Trend
y_upward = np.cumsum(np.random.randn(100)) + np.linspace(0, 50, 100)
# Downward Trend
y_downward = np.flip(np.cumsum(np.random.randn(100)) + np.linspace(0, 50, 100))
# Horizontal Trend with Variation
y_horizontal_variation = np.linspace(30, 30, 100) + 2 * np.sin(np.linspace(0, 10, 100))
# Damped Trend
y_damped = np.cumsum(np.random.randn(100)) + np.sin(np.linspace(0, 10, 100))
# Non-linear Trend
y_nonlinear = np.cumsum(np.random.randn(100)) + 20 * np.sin(np.linspace(0, 10, 100))
# Tạo đối tượng fig và subplot
fig = go.Figure()
# Add traces
fig.add_trace(go.Scatter(x=x, y=y_upward, mode='lines', name='Upward Trend'))
fig.add_trace(go.Scatter(x=x, y=y_downward, mode='lines', name='Downward Trend'))
fig.add_trace(go.Scatter(x=x, y=y_horizontal_variation, mode='lines', name='Horizontal Trend with Variation'))
fig.add_trace(go.Scatter(x=x, y=y_damped, mode='lines', name='Damped Trend'))
fig.add_trace(go.Scatter(x=x, y=y_nonlinear, mode='lines', name='Non-linear Trend'))
fig.update_layout(
title='Different Trends in Time Series Data',
xaxis_title='Time',
yaxis_title='Value',
font=dict(
family="Arial, sans-serif",
size=28, # Kích thước chữ của title và label
color="black"
)
)
Seasonality (Tính mùa vụ): trong dữ liệu chuỗi thời gian đề cập đến các biến động tăng hoặc giảm mà diễn ra đều đặn và lặp lại trong một khoảng thời gian.
Một số tính mùa vụ phổ biến khi phân tích Time Series data:
- Holiday Seasonality: Sự thay đổi này thường được gây ra bởi các sự kiện đặc biệt như ngày lễ, sự kiện đặc biệt nào đó. Ví dụ: Doanh số bán hàng tăng mạnh vào dịp cuối năm gần Tết.
# Code vẽ đồ thị Holiday Seasonality
import plotly.graph_objs as go
import pandas as pd
import numpy as np
# Tạo dữ liệu doanh số bán lẻ với sự tăng đột biến ở các tháng gần tết
num_years = 5
num_months = 12 * num_years
dates = pd.date_range(start='1/1/2018', periods=num_months, freq='MS')
base_sales = np.random.normal(loc=10000, scale=1500, size=(num_months,))
holiday_spike_multiplier = 5
holiday_spike = ((dates.month == 1) | (dates.month == 12)) # Spike in January and December for holiday season
sales = base_sales + holiday_spike * base_sales * holiday_spike_multiplier
# Dataframe to plot
sales_data = pd.DataFrame({'Date': dates, 'Sales': sales})
# Plot
fig = go.Figure()
fig.add_trace(go.Scatter(x=sales_data['Date'], y=sales_data['Sales'], mode='lines+markers', name='Retail Sales'))
# Add annotations for holiday spikes
for date, sale in sales_data[sales_data['Sales'] > base_sales * holiday_spike_multiplier].itertuples(index=False):
fig.add_annotation(
x=date, y=sale,
text='Holiday Spike', ax=-15, ay=-30, textangle=90,
showarrow=True, arrowhead=2, arrowsize=1, arrowcolor='red',
)
fig.update_layout(
title='Holiday Seasonality in Retail Sales',
xaxis_title='Date',
yaxis_title='Sales',
autosize=False,
width=1200,
height=500
)
# Hiển thị biểu đồ
fig.show()
- Weekly Seasonality: Sự thay đổi lặp lại trong khoảng thời gian 7 ngày. Ví dụ: số lượng vé xem phim tại các rạp tăng mạnh vào các dịp cuối tuần.
# Code vẽ đồ thị Weekly Seasonality
import plotly.graph_objects as go
import numpy as np
import pandas as pd
import random
def generate_weekly_values(num_weeks):
values = []
for _ in range(num_weeks):
# Random start value từ 90 đến 110 cho mỗi tuần
current_value = random.randint(90, 110)
daily_increase = np.sin(np.linspace(0, np.pi, 7)) * 10 * (np.linspace(1, 2, 7)) # Tăng biên độ theo từng ngày trong tuần
for increase in daily_increase:
values.append(current_value)
current_value += increase
return values
# Tạo dữ liệu mẫu cho 4 tuần, bắt đầu từ giá trị 100
weekly_values = generate_weekly_values(4)
num_days = 7
num_weeks = 4 * 7 # 4 tuần
dates = pd.date_range(start='2024-04-01', periods=num_days * num_weeks, freq='D')
# Tạo danh sách các ngày trong tuần
weekdays = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
# Tạo danh sách label cho mỗi điểm
labels = [f"{date.strftime('%Y-%m-%d')} ({weekdays[date.weekday()]})" for date in dates]
# Tạo đối tượng đồ thị
fig = go.Figure()
# Thêm biểu đồ sóng
fig.add_trace(go.Scatter(x=dates, y=weekly_values,
mode='lines+markers',
hoverinfo='text', # Chỉ ra rằng tooltip sẽ chứa text
text=labels)) # Sử dụng danh sách label làm nội dung của tooltip
# Cài đặt tiêu đề và nhãn trục
fig.update_layout(title='Weekly Viewership of Movies',
xaxis_title='Date',
yaxis_title='Number of Viewers',
font=dict(
family="Arial, sans-serif",
size=20, # Kích thước chữ cho title và lable
color="black"))
# Hiển thị đồ thị
fig.show()
- Monthly Seasonality: Sự thay đổi lặp lại trong khoảng thời gian 30 hoặc 31 ngày. Ví dụ mọi người có xu hướng mua nhiều hàng online khi nhận lương vào đầu tháng, và sẽ giảm mua lại ở giai đoạn cuối tháng khi sắp hết tiền :))))).
# Code vẽ đồ thị Monthly Seasonality
import plotly.graph_objs as go
import pandas as pd
from datetime import datetime, timedelta
import numpy as np
# Tạo dữ liệu giả định cho doanh số hàng ngày trong 5 tháng liên tiếp
num_days = 30 # Số ngày trong một tháng giả định
dates = []
sales = []
initial_date = datetime.now().replace(day=1, hour=0, minute=0, second=0, microsecond=0)
for month in range(5): # Dữ liệu cho 5 tháng
for day in range(num_days):
date = initial_date + timedelta(days=day + month * num_days)
# Tạo đợt bán hàng cao vào đầu tháng và giảm dần về cuối tháng
daily_sales = (np.sin(2 * np.pi/num_days * day) + 1) * 500 + 1000
dates.append(date)
sales.append(daily_sales)
# Tạo DataFrame
sales_df = pd.DataFrame({'Date': dates, 'Sales': sales})
# Vẽ đồ thị
fig = go.Figure()
fig.add_trace(go.Scatter(x=sales_df['Date'], y=sales_df['Sales'],
mode='lines+markers',
name='Daily Sales'))
fig.update_layout(title='Monthly Sales Seasonality Over 5 Months',
xaxis_title='Date',
yaxis_title='Sales',
xaxis=dict(
tickformat='%d-%b-%y'
),
yaxis=dict(showgrid=True, zeroline=False),
showlegend=True)
fig.show()
- Annual Seasonality: Sự thay đổi lặp lại trong khoảng một năm. Chẳng hạn như số lượng du khách tăng đột ngột vào các tháng hè mỗi năm.
# Code vẽ đồ thị Annual Seasonality
import plotly.graph_objs as go
import pandas as pd
import numpy as np
# Tạo dữ liệu giả định về số lượng khách du lịch hàng tháng trong 5 năm với đột biến mùa hè
years = 5
months = pd.date_range(start='2016-01-01', periods=years * 12, freq='MS')
base_visitors = np.random.normal(200, 20, size=(years * 12,))
# Điều chỉnh ở đây để tạo ra sự tăng vọt đột biến mùa hè
summer_peak = ((months.month >= 6) & (months.month <= 8)).astype(int) * 3
visitors = base_visitors + summer_peak * base_visitors
# Tạo DataFrame
visitors_data = pd.DataFrame({'Month': months.strftime('%Y-%m'), 'Visitors': visitors, 'Summer_Peak': summer_peak})
# Vẽ biểu đồ
fig = go.Figure()
fig.add_trace(go.Scatter(x=visitors_data['Month'], y=visitors_data['Visitors'],
mode='markers+lines',
name='Tourism'))
# Thêm labels trên các điểm Summer Peak
for i, row in visitors_data.iterrows():
if row['Summer_Peak']:
fig.add_annotation(
x=row['Month'],
y=row['Visitors'],
text="Summer Peak",
showarrow=True,
arrowhead=1,
textangle=90, # Hiển thị chữ dọc
ax=0,
ay=-50,
standoff=30,
font=dict(color='red') # Đổi màu label
)
fig.update_layout(title='5-Year Tourism Seasonality with Labels on Summer Peaks',
xaxis_title='Month',
yaxis_title='Number of Visitors',
autosize=False,
width=2500,
height=500)
# Hiển thị biểu đồ
fig.show()
Cycles (Tính chu kỳ) trong dữ liệu chuỗi thời gian đề cập đến những biến động lên xuống lặp lại, hoặc những thay đổi định kỳ, có thể kéo dài trong nhiều năm và chuyển từ giai đoạn này qua giai đoạn khác.
# Code vẽ đồ thị mô tả chu kỳ kinh tế từ năm 2000 đến năm 2020
import plotly.graph_objs as go
import numpy as np
from datetime import datetime, timedelta
# Tạo dữ liệu giả định cho chu kỳ kinh tế
start_date = datetime(2000, 1, 1)
end_date = datetime(2020, 1, 1)
total_days = (end_date - start_date).days
dates = [start_date + timedelta(days=x) for x in range(total_days)]
# Tạo chuỗi thời gian không đều để phản ánh thực tế hơn
# Tín hiệu chu kỳ có thể thay đổi về biên độ (amplitude) và tần số (frequency)
amplitudes = np.random.uniform(low=50, high=250, size=(total_days,)) # Biên độ thay đổi ngẫu nhiên
cycle_length = 365*4 # khoảng chu kỳ, khoảng 4 năm sẽ hình thành 1 chu kì
cycles = np.sin(2 * np.pi / cycle_length * np.arange(total_days) * np.random.uniform(0.5, 2.5)) * amplitudes
# Biến động bất thường
cycle_peaks = np.random.choice(dates, size=(int(total_days*0.1),), replace=False) # Khoảng 10% ngày có biến động lớn
for peak in cycle_peaks:
noise = np.random.normal(0, amplitudes[dates.index(peak)] * 0.5)
cycles[dates.index(peak)] += noise
# Vẽ đồ thị
fig = go.Figure()
fig.add_trace(go.Scatter(x=dates, y=cycles, mode='lines', name='Cycles'))
fig.update_layout(
title='Economic Cycle Simulation over 20 Years',
xaxis_title='Date',
yaxis_title='Economic Index',
xaxis=dict(
rangeselector=dict(
buttons=list([
dict(count=1, label='1Y', step='year', stepmode='backward'),
dict(count=5, label='5Y', step='year', stepmode='backward'),
dict(count=10, label='10Y', step='year', stepmode='backward'),
dict(count=20, label='All', step='all')
])
),
rangeslider=dict(
visible=True
),
type='date'
),
yaxis=dict(
fixedrange=False
)
)
fig.show()
Iregular (hay còn gọi là noise): Sự bất thường trong dữ liệu chuỗi thời gian đề cập đến những sự thay đổi bất thường của dữ liệu, xảy ra một cách ngẫu nhiên, có thể trái ngược hoàn toàn với các dữ liệu trong quá khứ, khó có thể giải thích và không dự đoán được trước.
Sự bất thường này có thể do sự sai sót trong đo lường dữ liệu, hoặc những sự kiện bất ngờ diễn ra và có thể vô tình làm ảnh hưởng đến tính chính xác khi đánh giá dữ liệu chuỗi thời gian.
# Code vẽ đồ thị Iregular trong time series data
import plotly.graph_objs as go
import numpy as np
# Tạo dữ liệu mẫu cho sự bất thường trong dữ liệu chuỗi thời gian
np.random.seed(0)
n_points = 100
time_series = np.random.normal(loc=0, scale=1, size=n_points)
abnormal_indices = np.random.choice(range(n_points), size=5, replace=False)
time_series[abnormal_indices] += 5 # Tạo ra sự bất thường ở các điểm dữ liệu ngẫu nhiên
# Tạo biểu đồ đường
trace = go.Scatter(x=np.arange(n_points), y=time_series, mode='lines', name='Time Series')
# Tạo dữ liệu cho các điểm dữ liệu bất thường
abnormal_data = [time_series[i] for i in abnormal_indices]
abnormal_trace = go.Scatter(x=abnormal_indices, y=abnormal_data, mode='markers',
marker=dict(color='red', size=10), name='Abnormal Points')
# Tạo layout cho biểu đồ
layout = go.Layout(
title='Sự bất thường trong dữ liệu chuỗi thời gian',
xaxis=dict(title='Thời gian'),
yaxis=dict(title='Giá trị'),
)
# Tạo figure và hiển thị biểu đồ
fig = go.Figure(data=[trace, abnormal_trace], layout=layout)
fig.show()
3. Kết luận
Phân tích chuỗi thời gian đã trở thành một công cụ quan trọng cho các công ty muốn đưa ra quyết định tốt hơn dựa trên dữ liệu. Nó giúp biến dữ liệu thô thành thông tin chuyên sâu mà các tổ chức, công ty có thể sử dụng để cải thiện hiệu suất trong tương lai và theo dõi, đánh giá kết quả lịch sử.
Phân tích chuỗi thời gian là một chủ đề rộng và được ứng dụng vào rất nhiều lĩnh vực trên thực tế như kinh tế, xã hội, an toàn thông tin, y học, thuỷ văn .... Tuy nhiên, bài viết này nhằm mục đích giới thiệu nhẹ nhàng về khái niệm và các tính chất đặc trưng của time series data. Trong các bài viết sau mình sẽ gửi đến các bạn nội dung về các loại TSA phổ biến nhé!
Các bài viết liên quan bạn có thể tham khảo tại blog 200Lab:
Bài viết liên quan
Database (Cơ sở dữ liệu) là gì? Những loại Database phổ biến nhất hiện nay
Sep 01, 2024 • 11 min read
Python là gì? Những đặc điểm nổi bật và Ứng dụng của Python
Aug 28, 2024 • 14 min read
Ứng dụng Hypothesis Testing - Kiểm định giả thuyết trong Y học
Jul 18, 2024 • 8 min read
Google Colab là gì? Hướng dẫn sử dụng Google Colab cho người mới
Jul 02, 2024 • 10 min read
Hướng dẫn cách lấy dữ liệu Facebook Ads Tự động Mỗi ngày Miễn phí - Phần 2
Jun 24, 2024 • 6 min read
Hướng dẫn cách lấy dữ liệu Facebook Ads Tự động Mỗi ngày Miễn phí- Phần 1
Jun 24, 2024 • 11 min read