資料探勘研究與實務_空氣品質預報Time Series Regression_part2

2. 時間序列
a.預測目標
1. 將未來第一個小時當預測目標
取6小時為一單位切割,例如第一筆資料為第0~5小時的資料(X[0]),去預測第6小時(未來第一小時)的PM2.5值(Y[0]),下一筆資料為第1~6小時的資料(X[1])去預測第7 小時的PM2.5值(Y[1])  *hint: 切割後X的長度應為1464-6=1458

2. 將未來第六個小時當預測目標
取6小時為一單位切割,例如第一筆資料為第0~5小時的資料(X[0]),去預測第11小時(未來第六小時)的PM2.5值(Y[0]),下一筆資料為第1~6小時的資料(X[1])去預測第12 小時的PM2.5值(Y[1])  *hint: 切割後X的長度應為1464-11=1453


 b. X請分別取
1. 只有PM2.5 (e.g. X[0]會有6個特徵,即第0~5小時的PM2.5數值)
2. 所有18種屬性 (e.g. X[0]會有18*6個特徵,即第0~5小時的所有18種屬性數值)

 c. 使用兩種模型 Linear Regression 和 XGBoost 建模
 d. 用測試集資料計算MAE (會有8個結果, 2種X資料 * 2種Y資料 * 2種模型)




確定PM2.5的位置,因為這是我們的目標變量。
在此發現一個小問題就是多於空白



在檢索 'PM2.5' 索引之前先將所有測項的名稱進行空白處理。
這可以通過 strip 函數來移除字符串前後的空白字符。



第5階段程式碼
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
import pandas as pd
import numpy as np
# 讀取檔案
file_path = r'E:\交大學分班\557607資料探勘研究與實務\HW3_11134225_周冠羽\新竹_2021.xls'

data = pd.read_excel(file_path, engine='xlrd')


# 清理數據:移除不需要的第一橫列,設置正確的列名
data_cleaned = data.iloc[1:].copy()  # 使用 .copy() 來創建副本,避免後續操作持續冒出 SettingWithCopyWarning:A value is trying to be set on a copy of a slice from a DataFrame.
data_cleaned.reset_index(drop=True, inplace=True)
correct_hour_columns = ['測站', '日期', '測項'] + [str(i) for i in range(24)]
data_cleaned.columns = correct_hour_columns


# 轉換日期格式並篩選出10、11、12月的資料
data_cleaned['日期'] = pd.to_datetime(data_cleaned['日期'])
#data_filtered = data_cleaned[data_cleaned['日期'].dt.month.isin([10, 11, 12])]
# 使用 .copy() 來創建副本,避免後續操作持續冒出 SettingWithCopyWarning:A value is trying to be set on a copy of a slice from a DataFrame.
data_filtered = data_cleaned[data_cleaned['日期'].dt.month.isin([10, 11, 12])].copy()
data_filtered.reset_index(drop=True, inplace=True)


# 將 'NR' 替換為 0,然後將 *, #, x 替換為 NaN。
data_filtered.replace({'NR': 0, '*': pd.NA, '#': pd.NA, 'x': pd.NA}, inplace=True)

# 確保所有數據列都是數值型
for col in data_filtered.columns[3:]:
    data_filtered[col] = pd.to_numeric(data_filtered[col], errors='coerce')

# 處理缺失值:使用前後一小時的平均值填充缺失值
#對於每個缺失的數據點,我們往前和往後尋找非缺失的值來計算平均值進行填充。如果前面沒有有效值,則僅使用後面的值;如果後面也沒有有效值,則僅使用前面的值。最後,我們檢查整個 DataFrame 確保沒有剩餘的缺失值。
for col in data_filtered.columns[3:]:
    for i in range(len(data_filtered)):
        if pd.isna(data_filtered.at[i, col]):
            # 往前找到非缺失值
            prev_index = i - 1
            while prev_index >= 0 and pd.isna(data_filtered.at[prev_index, col]):
                prev_index -= 1
            # 往後找到非缺失值
            next_index = i + 1
            while next_index < len(data_filtered) and pd.isna(data_filtered.at[next_index, col]):
                next_index += 1

            # 計算平均值並填充
            prev_val = data_filtered.at[prev_index, col] if prev_index >= 0 else None
            next_val = data_filtered.at[next_index, col] if next_index < len(data_filtered) else None
            if prev_val is not None and next_val is not None:
                data_filtered.at[i, col] = (prev_val + next_val) / 2
            elif prev_val is not None:
                data_filtered.at[i, col] = prev_val
            elif next_val is not None:
                data_filtered.at[i, col] = next_val
# 檢查是否還有缺失值
missing_values_after_fill = data_filtered.isnull().sum().sum()
print(f'After filling, the number of missing values is: {missing_values_after_fill}')

#根據月份將資料分成訓練集(10月和11月)和測試集(12月)
train_data = data_filtered[data_filtered['日期'].dt.month.isin([10, 11])]
test_data = data_filtered[data_filtered['日期'].dt.month == 12]

#將訓練集的資料轉換成時序資料,其中行代表18種屬性,列代表每小時的數據。

# 首先我們需要確定屬性的唯一值
unique_items = train_data['測項'].unique()

# 建立一個空的DataFrame來保存時序格式的數據
time_series_data = pd.DataFrame()

# 對於訓練集中的每一種屬性
for item in unique_items:
    # 挑選出該屬性的所有行
    item_data = train_data[train_data['測項'] == item].iloc[:, 3:].reset_index(drop=True)
    # 由於每一天的數據在單獨的行中,我們需要將它們連接起來
    item_data = item_data.melt(var_name='hour', value_name=item)
    # 重排列索引,使其成為連續的時序數據
    item_data = item_data.sort_values(by='hour').reset_index(drop=True)
    time_series_data[item] = item_data[item]

# 設定正確的行索引
time_series_data.index = pd.date_range(start=train_data['日期'].min(), periods=len(time_series_data), freq='H')

# 現在,time_series_data 已經是一個時序資料集,行代表時間,列代表18種屬性的測量值
print(time_series_data.shape)  # 應該是 (18, 61*24) 如果包括10月和11月的所有小時

# 檢查維度是否符合預期(應該有61天*24小時=1464個時刻)
#assert time_series_data.shape == (1464, len(unique_items)), "維度不匹配"


#unique_items = train_data['測項'].unique()
#print(unique_items)

# 移除測項名稱的前後空白
unique_items = [item.strip() for item in unique_items]

pm25_index = unique_items.index('PM2.5')

# 創建特徵和標籤集
window_size = 6  # 使用6小時的窗口大小
X = []  # 特徵集
Y_future_1hr = []  # 未來第一個小時的PM2.5值
Y_future_6hr = []  # 未來第六個小時的PM2.5值


# 遍歷時序數據集,創建特徵和標籤
for i in range(time_series_data.shape[0] - window_size):
    # 提取6小時的特徵
    X.append(time_series_data.iloc[i:i+window_size].values.flatten())

    # 提取未來第一個小時和第六個小時的PM2.5值作為標籤
    Y_future_1hr.append(time_series_data.iloc[i+window_size, pm25_index])
    if i+window_size+5 < time_series_data.shape[0]:
        Y_future_6hr.append(time_series_data.iloc[i+window_size+5, pm25_index])

# 轉換成numpy數組便於後續處理
X = np.array(X)
Y_future_1hr = np.array(Y_future_1hr)
Y_future_6hr = np.array(Y_future_6hr[:1458])  # 確保與X的形狀一致

# 確認形狀
print(f'X shape: {X.shape}')
print(f'Y_future_1hr shape: {Y_future_1hr.shape}')
print(f'Y_future_6hr shape: {Y_future_6hr.shape}')




第6階段程式碼
2. 時間序列
a.預測目標
1. 將未來第一個小時當預測目標
取6小時為一單位切割,例如第一筆資料為第0~5小時的資料(X[0]),去預測第6小時(未來第一小時)的PM2.5值(Y[0]),下一筆資料為第1~6小時的資料(X[1])去預測第7 小時的PM2.5值(Y[1])  *hint: 切割後X的長度應為1464-6=1458

2. 將未來第六個小時當預測目標
取6小時為一單位切割,例如第一筆資料為第0~5小時的資料(X[0]),去預測第11小時(未來第六小時)的PM2.5值(Y[0]),下一筆資料為第1~6小時的資料(X[1])去預測第12 小時的PM2.5值(Y[1])  *hint: 切割後X的長度應為1464-11=1453


b. X請分別取
1. 只有PM2.5 (e.g. X[0]會有6個特徵,即第0~5小時的PM2.5數值)
2. 所有18種屬性 (e.g. X[0]會有18*6個特徵,即第0~5小時的所有18種屬性數值)

 c. 使用兩種模型 Linear Regression 和 XGBoost 建模
 d. 用測試集資料計算MAE (會有8個結果, 2種X資料 * 2種Y資料 * 2種模型)


  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
import pandas as pd
import numpy as np
# 讀取檔案
file_path = r'E:\交大學分班\557607資料探勘研究與實務\HW3_11134225_周冠羽\新竹_2021.xls'

data = pd.read_excel(file_path, engine='xlrd')


# 清理數據:移除不需要的第一橫列,設置正確的列名
data_cleaned = data.iloc[1:].copy()  # 使用 .copy() 來創建副本,避免後續操作持續冒出 SettingWithCopyWarning:A value is trying to be set on a copy of a slice from a DataFrame.
data_cleaned.reset_index(drop=True, inplace=True)
correct_hour_columns = ['測站', '日期', '測項'] + [str(i) for i in range(24)]
data_cleaned.columns = correct_hour_columns


# 轉換日期格式並篩選出10、11、12月的資料
data_cleaned['日期'] = pd.to_datetime(data_cleaned['日期'])
#data_filtered = data_cleaned[data_cleaned['日期'].dt.month.isin([10, 11, 12])]
# 使用 .copy() 來創建副本,避免後續操作持續冒出 SettingWithCopyWarning:A value is trying to be set on a copy of a slice from a DataFrame.
data_filtered = data_cleaned[data_cleaned['日期'].dt.month.isin([10, 11, 12])].copy()
data_filtered.reset_index(drop=True, inplace=True)


# 將 'NR' 替換為 0,然後將 *, #, x 替換為 NaN。
data_filtered.replace({'NR': 0, '*': pd.NA, '#': pd.NA, 'x': pd.NA}, inplace=True)

# 確保所有數據列都是數值型
for col in data_filtered.columns[3:]:
    data_filtered[col] = pd.to_numeric(data_filtered[col], errors='coerce')

# 處理缺失值:使用前後一小時的平均值填充缺失值
#對於每個缺失的數據點,我們往前和往後尋找非缺失的值來計算平均值進行填充。如果前面沒有有效值,則僅使用後面的值;如果後面也沒有有效值,則僅使用前面的值。最後,我們檢查整個 DataFrame 確保沒有剩餘的缺失值。
for col in data_filtered.columns[3:]:
    for i in range(len(data_filtered)):
        if pd.isna(data_filtered.at[i, col]):
            # 往前找到非缺失值
            prev_index = i - 1
            while prev_index >= 0 and pd.isna(data_filtered.at[prev_index, col]):
                prev_index -= 1
            # 往後找到非缺失值
            next_index = i + 1
            while next_index < len(data_filtered) and pd.isna(data_filtered.at[next_index, col]):
                next_index += 1

            # 計算平均值並填充
            prev_val = data_filtered.at[prev_index, col] if prev_index >= 0 else None
            next_val = data_filtered.at[next_index, col] if next_index < len(data_filtered) else None
            if prev_val is not None and next_val is not None:
                data_filtered.at[i, col] = (prev_val + next_val) / 2
            elif prev_val is not None:
                data_filtered.at[i, col] = prev_val
            elif next_val is not None:
                data_filtered.at[i, col] = next_val
# 檢查是否還有缺失值
missing_values_after_fill = data_filtered.isnull().sum().sum()
print(f'After filling, the number of missing values is: {missing_values_after_fill}')

#根據月份將資料分成訓練集(10月和11月)和測試集(12月)
train_data = data_filtered[data_filtered['日期'].dt.month.isin([10, 11])]
test_data = data_filtered[data_filtered['日期'].dt.month == 12]

#將訓練集的資料轉換成時序資料,其中行代表18種屬性,列代表每小時的數據。

# 首先我們需要確定屬性的唯一值
unique_items = train_data['測項'].unique()

# 建立一個空的DataFrame來保存時序格式的數據
time_series_data = pd.DataFrame()

# 對於訓練集中的每一種屬性
for item in unique_items:
    # 挑選出該屬性的所有行
    item_data = train_data[train_data['測項'] == item].iloc[:, 3:].reset_index(drop=True)
    # 由於每一天的數據在單獨的行中,我們需要將它們連接起來
    item_data = item_data.melt(var_name='hour', value_name=item)
    # 重排列索引,使其成為連續的時序數據
    item_data = item_data.sort_values(by='hour').reset_index(drop=True)
    time_series_data[item] = item_data[item]

# 設定正確的行索引
time_series_data.index = pd.date_range(start=train_data['日期'].min(), periods=len(time_series_data), freq='H')

# 現在,time_series_data 已經是一個時序資料集,行代表時間,列代表18種屬性的測量值
print(time_series_data.shape)  # 應該是 (18, 61*24) 如果包括10月和11月的所有小時

# 檢查維度是否符合預期(應該有61天*24小時=1464個時刻)
#assert time_series_data.shape == (1464, len(unique_items)), "維度不匹配"


#unique_items = train_data['測項'].unique()
#print(unique_items)

# 移除測項名稱的前後空白
unique_items = [item.strip() for item in unique_items]

pm25_index = unique_items.index('PM2.5')

# 創建特徵和標籤集
window_size = 6  # 使用6小時的窗口大小
X = []  # 特徵集
Y_future_1hr = []  # 未來第一個小時的PM2.5值
Y_future_6hr = []  # 未來第六個小時的PM2.5值


# 遍歷時序數據集,創建特徵和標籤
for i in range(time_series_data.shape[0] - window_size):
    # 提取6小時的特徵
    X.append(time_series_data.iloc[i:i+window_size].values.flatten())

    # 提取未來第一個小時和第六個小時的PM2.5值作為標籤
    Y_future_1hr.append(time_series_data.iloc[i+window_size, pm25_index])
    if i+window_size+5 < time_series_data.shape[0]:
        Y_future_6hr.append(time_series_data.iloc[i+window_size+5, pm25_index])

# 轉換成numpy數組便於後續處理
X = np.array(X)
Y_future_1hr = np.array(Y_future_1hr)
Y_future_6hr = np.array(Y_future_6hr[:1458])  # 確保與X的形狀一致

# 確認形狀
print(f'X shape: {X.shape}')
print(f'Y_future_1hr shape: {Y_future_1hr.shape}')
print(f'Y_future_6hr shape: {Y_future_6hr.shape}')

# 獲取 PM2.5 數據的索引
pm25_index = unique_items.index('PM2.5')

# 創建只包含 PM2.5 的特徵集
X_pm25_only = []

for i in range(0, time_series_data.shape[0] - window_size):
    # 提取過去 6 小時的 PM2.5 數據
    X_pm25_only.append(time_series_data.iloc[i:i + window_size, pm25_index].values.flatten())

# 轉換成 NumPy 數組
X_pm25_only = np.array(X_pm25_only)

# 打印形狀以確認
#只包含PM2.5的特徵集,其形狀為 (1458, 6)。這代表了1458個樣本,每個樣本有6個小時的PM2.5數據。
print(f'X_pm25_only shape: {X_pm25_only.shape}')


# 創建包含所有屬性的特徵集
X_all_features = []

for i in range(0, time_series_data.shape[0] - window_size):
    # 提取過去 6 小時的所有屬性數據
    X_all_features.append(time_series_data.iloc[i:i + window_size].values.flatten())

# 轉換成 NumPy 數組
X_all_features = np.array(X_all_features)

# 打印形狀以確認
#包含所有18種屬性的特徵集,其形狀應該與 X 相同,也就是 (1458, 108)。
print(f'X_all_features shape: {X_all_features.shape}')

#更新標籤集,使它們匹配特徵集的數量。
Y_future_1hr_trimmed = Y_future_1hr[:len(X_pm25_only)]
Y_future_6hr_trimmed = Y_future_6hr[:len(X_pm25_only)]

# 打印形狀以確認
#未來第一小時的PM2.5標籤集,其形狀為 (1458,),與特徵集 X 一樣有1458個標籤。
print(f'Y_future_1hr shape: {Y_future_1hr_trimmed.shape}')
#未來第六小時的PM2.5標籤集,其形狀為 (1453,)。
print(f'Y_future_6hr shape: {Y_future_6hr_trimmed.shape}')



對於 Y_future_1hr 和 Y_future_6hr 的標籤集,由於我們無法預測最後5個小時的PM2.5值(因為沒有足夠的未來數據),所以 Y_future_6hr 的數量會比 Y_future_1hr 少5個值,這也是合理的。

而 X 和 X_all_features 應該是相同的,因為它們都是基於所有屬性的6小時窗口大小的特徵集。


第7階段程式碼



2. 時間序列
a.預測目標
1. 將未來第一個小時當預測目標
取6小時為一單位切割,例如第一筆資料為第0~5小時的資料(X[0]),去預測第6小時(未來第一小時)的PM2.5值(Y[0]),下一筆資料為第1~6小時的資料(X[1])去預測第7 小時的PM2.5值(Y[1])  *hint: 切割後X的長度應為1464-6=1458

2. 將未來第六個小時當預測目標
取6小時為一單位切割,例如第一筆資料為第0~5小時的資料(X[0]),去預測第11小時(未來第六小時)的PM2.5值(Y[0]),下一筆資料為第1~6小時的資料(X[1])去預測第12 小時的PM2.5值(Y[1])  *hint: 切割後X的長度應為1464-11=1453


b. X請分別取
1. 只有PM2.5 (e.g. X[0]會有6個特徵,即第0~5小時的PM2.5數值)
2. 所有18種屬性 (e.g. X[0]會有18*6個特徵,即第0~5小時的所有18種屬性數值)
c. 使用兩種模型 Linear Regression 和 XGBoost 建模
d. 用測試集資料計算MAE (會有8個結果, 2種X資料 * 2種Y資料 * 2種模型)

記得pip install xgboost 不然會報錯
ModuleNotFoundError: No module named 'xgboost' 




ValueError: Found input variables with inconsistent numbers of samples: [1458, 1453]
[1458, 1453]」表示你嘗試用於訓練或預測的特徵矩陣 X 與目標向量 Y 有不一致的樣本數量。在這種情況下,X 有 1458 個樣本,而 Y 只有 1453 個樣本。

要解決這個問題,你需要確保 X 和 Y 有相同數量的樣本。這通常意味著你需要裁剪或擴展其中一個向量,以便它們匹配。例如,如果 Y_future_6hr 只有 1453 個樣本,你應該只使用 X 的前 1453 行來匹配 Y。














留言

張貼留言

這個網誌中的熱門文章

何謂淨重(Net Weight)、皮重(Tare Weight)與毛重(Gross Weight)

Architecture(架構) 和 Framework(框架) 有何不同?_軟體設計前的事前規劃的藍圖概念

經得起原始碼資安弱點掃描的程式設計習慣培養(五)_Missing HSTS Header