資料科學 金融分析 PYTHON 資料清理 量化交易

學習資料科學:yfinance 金融數據擷取與 Pandas 分析實戰

yfinance 專案介紹與實作範例

本篇文章將根據 yfinance GitHub 連結介紹此專案,並示範如何使用它來擷取股票與期貨資料,並運用 Pandas 進行資料分析。

專案簡介

yfinance 是一個 Python 開源函式庫,其主要功能是從 Yahoo Finance! 網站上抓取金融市場數據。透過 yfinance,使用者可以輕鬆地取得股票、指數、貨幣、加密貨幣、ETF、基金以及期貨等金融商品的:

  • 歷史價格
  • 基本面資料
  • 公司資訊
  • …等金融數據

重要聲明yfinance 專案並非由 Yahoo! 公司官方維護或贊助。它是由社群開發者維護,並基於 Yahoo Finance! 提供的公開 API 介面進行開發。因此,使用 yfinance 需注意以下幾點:

  • 非官方: yfinance 與 Yahoo! 公司沒有官方關聯。
  • 社群維護: 專案的維護與更新仰賴社群貢獻。
  • 服務條款: 使用 Yahoo Finance! 數據時,仍需遵守 Yahoo! 服務條款。

專案特色

yfinance 函式庫具有以下特色:

  • Pythonic 介面: 提供簡潔易用的 Python 介面,方便使用者快速上手。
  • 多元資料: 支援多種金融市場數據,包含歷史價格、股息、股票分割、財報、公司資訊等。
  • 靈活應用: 可應用於量化交易、投資分析、數據視覺化等領域。

GitHub 專案結構 (main branch)

雖然我無法直接瀏覽 GitHub 頁面,但根據一般 GitHub 專案的結構, main 分支通常包含以下內容:

  • yfinance 函式庫程式碼
    • 核心程式碼,包含各種模組和函數,用於資料抓取、處理和分析。
  • 範例程式碼 (examplesnotebooks 資料夾)
    • 示範如何使用 yfinance 進行資料擷取的範例程式。
  • 文件 (README.md, doc 資料夾)
    • 專案說明文件、安裝指南、API 文件等,幫助使用者了解和使用 yfinance。
  • 授權檔案 (LICENSE.txt)
    • 說明專案的授權條款, yfinance 通常使用 Apache Software License。
  • 其他設定檔
    • 例如 .gitignore (排除不需納入版本控制的檔案)、 setup.py (專案安裝設定檔) 等。

實作範例:擷取 Nvidia 股票與黃金期貨資料

以下將示範如何使用 yfinance 擷取 Nvidia 股票 (NVDA) 和黃金期貨 (GC=F) 的歷史價格資料。

1. 安裝 yfinance

首先,您需要在 Python 環境中安裝 yfinance 函式庫。您可以使用 pip 指令進行安裝:

pip install yfinance

2. Python 程式碼範例

import yfinance as yf

# 擷取 Nvidia 股票 (NVDA) 歷史資料
nvda = yf.Ticker("NVDA")
nvda_history = nvda.history(period="1mo")  # 擷取近一個月的資料

print("Nvidia 股票近一個月歷史資料:")
print(nvda_history)

# 擷取 黃金期貨 (GC=F) 歷史資料
gold = yf.Ticker("GC=F")
gold_history = gold.history(period="1mo") # 擷取近一個月的資料

print("\n黃金期貨近一個月歷史資料:")
print(gold_history)

程式碼說明

  • import yfinance as yf: 導入 yfinance 函式庫,並簡稱為 yf
  • yf.Ticker("NVDA"): 建立 Nvidia 股票的 Ticker 物件。股票代碼 (Ticker Symbol) 為 "NVDA"
  • yf.Ticker("GC=F"): 建立 黃金期貨 的 Ticker 物件。黃金期貨在 Yahoo Finance! 的代碼為 "GC=F"
  • .history(period="1mo"): 呼叫 history() 方法,設定 period="1mo" 參數,表示擷取 “近一個月” 的歷史資料。您可以調整 period 參數來設定不同的時間範圍 (例如 "1d", "5d", "1wk", "1mo", "3mo", "6mo", "1y", "2y", "5y", "10y", "ytd", "max")。
  • print(nvda_history)print(gold_history): 印出擷取到的歷史資料,資料格式為 Pandas DataFrame。

3. 執行結果

執行上述 Python 程式碼後,您將在終端機看到類似以下的輸出結果 (資料會隨著時間變動):

Nvidia 股票近一個月歷史資料:
                  Open        High         Low       Close    Volume  Dividends  Stock Splits
Date
2025-02-10  708.529999  727.880005  708.529999  721.280029  43575300        0.0           0.0
2025-02-11  724.440002  736.479980  715.539978  726.659973  45874900        0.0           0.0
...
2025-03-07  868.340027  875.239990  855.140015  875.200012  46497500        0.0           0.0
2025-03-08  880.000000  888.559998  869.500000  875.760010  42888700        0.0           0.0

黃金期貨近一個月歷史資料:
                  Open        High         Low       Close    Volume  Dividends  Stock Splits
Date
2025-02-10  2024.700073  2035.599976  2014.099976  2024.099976    140.0        0.0           0.0
2025-02-11  2023.900024  2033.599976  2016.500000  2029.400024    148.0        0.0           0.0
...
2025-03-07  2158.000000  2174.500000  2154.199951  2165.299805    155.0        0.0           0.0
2025-03-08  2170.000000  2178.199951  2155.000000  2160.399902    131.0        0.0           0.0

4. 進階應用

除了歷史價格, yfinance 還可以擷取更多資料,例如:

  • 股息與股票分割: 使用 .dividends.splits 屬性。
  • 公司資訊: 使用 .info 屬性,包含公司簡介、產業、員工數等。
  • 財報: 使用 .financials, .balancesheet, .cashflow, .earnings 等屬性。
  • 即時報價: 使用 .fast_info 屬性 (可能需要搭配其他方法或調整參數)。

假設資料已成功載入 Pandas DataFrame,並說明如何使用 Pandas 進行資料分析處理,包含您提及的資料結合、操作、聚合、遺漏值與異常值處理、時間序列資料處理以及移動平均等方法。

請您先將 CSV 檔案下載至您的環境中,並使用 Pandas 載入資料,即可參考以下說明進行資料分析。

假設情境:

假設您已將 CSV 檔案 SOLB-BR-1d-bad-div-fixed.csv 讀取到 Pandas DataFrame 中,並命名為 df

import pandas as pd

# 假設已讀取 CSV 檔案
# df = pd.read_csv('SOLB-BR-1d-bad-div-fixed.csv')
# 為了示範,我們建立一個簡化的 DataFrame
data = {'Date': pd.to_datetime(['2024-01-01', '2024-01-02', '2024-01-03', '2024-01-04', '2024-01-05']),
        'Open': [10, 11, 12, 13, 14],
        'High': [15, 16, 17, 18, 19],
        'Low': [9, 10, 11, 12, 13],
        'Close': [12, 13, 14, 15, 16],
        'Volume': [100, 150, 200, 250, 300],
        'Dividends': [0.1, 0, 0, 0.2, 0],
        'Stock Splits': [1, 1, 1, 2, 1]}
df = pd.DataFrame(data)

print(df)

資料分析處理說明 (基於 Pandas DataFrame df)

1. 資料的結合

情境: 假設您有另一個包含補充資訊的 DataFrame,例如基本面資料或額外的技術指標,您想將其與 df 結合。

方法: Pandas 提供 pd.concat()pd.merge() 函數進行資料結合。

  • pd.concat(): 沿著軸線 (行或列) 將 DataFrame 或 Series 物件串聯起來。
  • pd.merge(): 根據共同欄位 (類似 SQL 的 JOIN 操作) 將 DataFrame 合併。

範例 (假設 df2 是另一個 DataFrame,且有共同的 'Date' 欄位):

# 假設 df2 是另一個 DataFrame
data2 = {'Date': pd.to_datetime(['2024-01-01', '2024-01-02', '2024-01-03', '2024-01-06']),
           'Indicator': [20, 21, 22, 23]}
df2 = pd.DataFrame(data2)


# 使用 pd.merge() 根據 'Date' 欄位結合 df 和 df2 (inner join)
df_merged = pd.merge(df, df2, on='Date', how='inner')
print("合併後的 DataFrame (inner join):")
print(df_merged)

# 使用 pd.concat() 沿著列方向堆疊 DataFrame (axis=0)
df_concat_row = pd.concat([df, df2], axis=0, ignore_index=True)
print("\n沿著列方向串聯後的 DataFrame:")
print(df_concat_row)

# 使用 pd.concat() 沿著欄方向堆疊 DataFrame (axis=1) - 注意 Date 欄位重複
df_concat_col = pd.concat([df, df2], axis=1)
print("\n沿著欄方向串聯後的 DataFrame:")
print(df_concat_col)

2. 資料的操作與變換

情境: 您可能需要對現有資料進行計算、轉換格式或新增欄位。

方法: Pandas 提供了豐富的 DataFrame 和 Series 操作方法。

  • 欄位選取df['欄位名稱']df[['欄位名稱1', '欄位名稱2']]
  • 條件篩選df[df['欄位名稱'] > 某值]
  • 新增欄位df['新欄位名稱'] = 計算公式
  • 函數應用.apply(), .map(), .applymap()

範例

# 計算每日價格波動幅度 (High - Low)
df['Price_Range'] = df['High'] - df['Low']
print("新增 'Price_Range' 欄位後的 DataFrame:")
print(df)

# 將 'Volume' 欄位轉換為千股單位
df['Volume_K'] = df['Volume'] / 1000
print("\n新增 'Volume_K' 欄位後的 DataFrame:")
print(df)

# 使用 apply() 函數將 'Date' 欄位格式化為 'YYYY/MM/DD'
df['Date_Formatted'] = df['Date'].apply(lambda x: x.strftime('%Y/%m/%d'))
print("\n新增 'Date_Formatted' 欄位後的 DataFrame:")
print(df)

3. 資料的聚合與群組運算

情境: 您可能需要對資料進行分組,並計算各組的統計量 (例如平均值、總和、最大值等)。

方法: Pandas 的 groupby() 方法用於分組,並可搭配聚合函數 (例如 .mean(), .sum(), .max(), .min(), .count(), .agg()) 進行群組運算。

範例 (假設資料包含不同股票的資料,新增 'Stock' 欄位):

# 假設資料包含不同股票,新增 'Stock' 欄位
stock_data = {'Date': pd.to_datetime(['2024-01-01', '2024-01-02', '2024-01-01', '2024-01-02']),
                'Stock': ['NVDA', 'NVDA', 'AAPL', 'AAPL'],
                'Close': [120, 125, 170, 175],
                'Volume': [1000, 1500, 2000, 2500]}
df_stocks = pd.DataFrame(stock_data)
print("\n包含不同股票的 DataFrame:")
print(df_stocks)


# 根據 'Stock' 欄位分組,計算各股票的平均收盤價
grouped_mean_close = df_stocks.groupby('Stock')['Close'].mean()
print("\n各股票的平均收盤價:")
print(grouped_mean_close)

# 根據 'Stock' 欄位分組,計算各股票的總成交量和最大收盤價 (使用 agg() 函數)
grouped_agg = df_stocks.groupby('Stock').agg({'Volume': 'sum', 'Close': 'max'})
print("\n各股票的總成交量和最大收盤價:")
print(grouped_agg)

4. 遺漏資料與異常值處理的基礎

  • 遺漏資料 (Missing Data): 資料中某些欄位的值缺失,常見原因包括資料收集錯誤、資料損壞等。
  • 異常值 (Outliers): 資料中數值明顯偏離正常範圍的值,可能是錯誤或特殊事件造成。

5. 遺漏資料的處理方法

檢查遺漏值

  • .isnull(): 判斷 DataFrame 或 Series 中的每個元素是否為遺漏值 (NaN)。
  • .notnull(): 與 .isnull() 相反,判斷是否為非遺漏值。
  • .isna(): .isnull() 的別名。
  • .notna(): .notnull() 的別名。
  • .isnull().sum(): 統計每欄遺漏值的數量。
  • .isnull().any(): 檢查 DataFrame 是否有任何遺漏值。

處理方法

  • 刪除遺漏值
    • .dropna(): 刪除包含遺漏值的行或列。
  • 填補遺漏值
    • .fillna(value): 使用指定值填補遺漏值。
    • .fillna(method='ffill'): 使用前一個非遺漏值填補 (向前填補)。
    • .fillna(method='bfill'): 使用後一個非遺漏值填補 (向後填補)。
    • .fillna(df.mean()): 使用該欄的平均值填補。
    • .fillna(df.median()): 使用該欄的中位數填補。
    • .fillna(df.mode()): 使用該欄的眾數填補。

範例 (假設 df 中存在遺漏值):

# 假設 df 中存在遺漏值
df.loc[2, 'Open'] = None
df.loc[4, 'Volume'] = None
print("\n包含遺漏值的 DataFrame:")
print(df)

# 檢查每欄遺漏值數量
print("\n每欄遺漏值數量:")
print(df.isnull().sum())

# 刪除包含遺漏值的行
df_dropna = df.dropna()
print("\n刪除遺漏值後的 DataFrame:")
print(df_dropna)

# 使用 0 填補遺漏值
df_fillna_0 = df.fillna(0)
print("\n使用 0 填補遺漏值後的 DataFrame:")
print(df_fillna_0)

# 使用前一個值向前填補遺漏值
df_fillna_ffill = df.fillna(method='ffill')
print("\n向前填補遺漏值後的 DataFrame:")
print(df_fillna_ffill)

# 使用平均值填補遺漏值
df_fillna_mean = df.fillna(df.mean(numeric_only=True)) # numeric_only=True 避免對非數值欄位計算平均值
print("\n使用平均值填補遺漏值後的 DataFrame:")
print(df_fillna_mean)

6. 異常資料的處理方法

檢查異常值

  • 視覺化方法: 盒鬚圖 (Box Plot)、散佈圖 (Scatter Plot) 等。
  • 統計方法: 標準差 (Standard Deviation)、Z-score、四分位距 (IQR) 等。

處理方法

  • 刪除異常值: 直接移除異常值所在的資料點。
  • 替換異常值: 使用平均值、中位數、邊界值等替換異常值。
  • 保留異常值: 若異常值具有實際意義,則可保留並進行特殊處理。

範例 (基於標準差判斷異常值,並替換為邊界值):

# 假設 'Close' 欄位存在異常值
df.loc[1, 'Close'] = 100  # 模擬異常值
print("\n包含異常值的 DataFrame:")
print(df)

# 計算 'Close' 欄位的平均值和標準差
mean_close = df['Close'].mean()
std_close = df['Close'].std()

# 設定異常值的上下界 (例如:平均值 +/- 3 個標準差)
lower_bound = mean_close - 3 * std_close
upper_bound = mean_close + 3 * std_close

print(f"\n收盤價正常範圍: {lower_bound:.2f} ~ {upper_bound:.2f}")

# 找出 'Close' 欄位中的異常值
outliers = df[(df['Close'] < lower_bound) | (df['Close'] > upper_bound)]
print("\n偵測到的異常值:")
print(outliers)

# 將異常值替換為邊界值
df['Close_Capped'] = df['Close'].clip(lower=lower_bound, upper=upper_bound)
print("\n異常值替換為邊界值後的 DataFrame:")
print(df)

7. 時間序列資料處理的基礎

  • 時間序列資料 (Time Series Data): 以時間順序排列的資料點序列。在金融領域,股票價格、交易量等都是時間序列資料。
  • Pandas 時間序列功能: Pandas 針對時間序列資料提供了強大的處理功能。
    • DatetimeIndex: Pandas 使用 DatetimeIndex 作為時間序列資料的索引,方便進行時間相關的操作。
    • 時間戳記 (Timestamp): 表示單一時間點。
    • 時間區間 (Period): 表示一段時間範圍 (例如:日、月、年)。
    • 時間間隔 (Timedelta): 表示時間長度。

範例 (將 'Date' 欄位設定為索引):

# 將 'Date' 欄位設定為 DataFrame 的索引
df_timeseries = df.set_index('Date')
print("\n將 'Date' 設定為索引的 DataFrame:")
print(df_timeseries)

# 檢查索引類型 (DatetimeIndex)
print("\n索引類型:")
print(df_timeseries.index)

8. 時間序列資料的處理與變換

  • 時間序列索引操作: 利用 DatetimeIndex 可以方便地進行時間序列的選取、切片、重採樣等操作。
    • 時間範圍選取df_timeseries['2024-01-03'], df_timeseries['2024-01-01':'2024-01-04'], df_timeseries['2024-01']
    • 重採樣 (Resampling): 改變時間序列的頻率 (例如:將日資料轉換為週資料或月資料)。 .resample('頻率').聚合函數()
    • 時間序列位移 (Shifting): 將時間序列資料向前或向後移動。 .shift(periods=n)

範例

df_timeseries = df.set_index('Date') # 確保 Date 是索引

# 選取特定日期的資料
jan_3_data = df_timeseries.loc['2024-01-03']
print("\n2024-01-03 的資料:")
print(jan_3_data)

# 選取日期範圍的資料
date_range_data = df_timeseries.loc['2024-01-01':'2024-01-04']
print("\n2024-01-01 到 2024-01-04 的資料:")
print(date_range_data)

# 將日資料重採樣為週資料,並計算每週的平均收盤價
weekly_avg_close = df_timeseries.resample('W')['Close'].mean()
print("\n週平均收盤價 (重採樣):")
print(weekly_avg_close)

# 將收盤價時間序列向前移動一天
close_shifted_forward = df_timeseries['Close'].shift(periods=1)
print("\n收盤價向前移動一天:")
print(close_shifted_forward)

# 將收盤價時間序列向後移動一天
close_shifted_backward = df_timeseries['Close'].shift(periods=-1)
print("\n收盤價向後移動一天:")
print(close_shifted_backward)

9. 移動平均 (Moving Average)

  • 移動平均: 一種常用的時間序列平滑技術,用於消除短期波動,突顯長期趨勢。
  • Pandas 移動平均.rolling(window=窗口大小).mean()

範例

df_timeseries = df.set_index('Date') # 確保 Date 是索引

# 計算 3 日移動平均收盤價
rolling_mean_3d = df_timeseries['Close'].rolling(window=3).mean()
print("\n3 日移動平均收盤價:")
print(rolling_mean_3d)

# 計算 5 日移動平均成交量
rolling_mean_5d_volume = df_timeseries['Volume'].rolling(window=5).mean()
print("\n5 日移動平均成交量:")
print(rolling_mean_5d_volume)

總結

以上範例涵蓋了使用 Pandas 進行股票交易數據分析處理的基礎方法,包括資料結合、操作、聚合、遺漏值與異常值處理以及時間序列資料處理和移動平均。

請注意,由於我無法直接讀取您提供的 CSV 檔案,上述程式碼範例是基於簡化資料的示範。實際應用時,您需要根據您的資料集調整程式碼。

建議您可以參考 Pandas 官方文件以及網路上的 Pandas 教學資源,深入學習 Pandas 的各種功能,以便更有效地進行資料分析。

import pandas as pd
import io
import requests

# **步驟 1: 載入資料**
# 假設 CSV 檔案可以從提供的 GitHub 連結直接下載
# (Colab 可以直接讀取 URL,但如果 GitHub 限制存取,可能需要先下載到 Colab 環境)

csv_url = "[https://raw.githubusercontent.com/ranaroussi/yfinance/main/tests/data/SOLB-BR-1d-bad-div-fixed.csv](https://raw.githubusercontent.com/ranaroussi/yfinance/main/tests/data/SOLB-BR-1d-bad-div-fixed.csv)"

try:
    response = requests.get(csv_url)
    response.raise_for_status()  # 檢查 HTTP 請求是否成功
    csv_text = response.text
    df = pd.read_csv(io.StringIO(csv_text)) # 使用 io.StringIO 處理字串資料
    print("資料載入成功!")
except requests.exceptions.RequestException as e:
    print(f"資料載入失敗,請確認網路連線或連結是否正確: {e}")
    # 如果載入失敗,使用簡化的 DataFrame 進行示範
    data = {'Date': pd.to_datetime(['2024-01-01', '2024-01-02', '2024-01-03', '2024-01-04', '2024-01-05', '2024-01-06', '2024-01-07', '2024-01-08', '2024-01-09', '2024-01-10']),
            'Open': [10, 11, 12, 13, 14, 15, 16, 17, 18, 19],
            'High': [15, 16, 17, 18, 19, 20, 21, 22, 23, 24],
            'Low': [9, 10, 11, 12, 13, 14, 15, 16, 17, 18],
            'Close': [12, 13, 14, 15, 16, 17, 18, 19, 20, 21],
            'Volume': [100, 150, 200, 250, 300, 350, 400, 450, 500, 550],
            'Dividends': [0.1, 0, 0, 0.2, 0, 0, 0.3, 0, 0, 0.4],
            'Stock Splits': [1, 1, 1, 2, 1, 1, 1, 2, 1, 1]}
    df = pd.DataFrame(data)
    print("使用簡化資料進行示範。")

print("原始 DataFrame:")
print(df)

# **步驟 2: 資料的結合 (示範)**
# 建立第二個 DataFrame 作為示範
data2 = {'Date': pd.to_datetime(['2024-01-01', '2024-01-03', '2024-01-05', '2024-01-11']),
         'Indicator': [20, 22, 24, 25]}
df2 = pd.DataFrame(data2)

# 使用 pd.merge() 根據 'Date' 欄位結合 df 和 df2 (left join,保留 df 的所有日期)
df_merged = pd.merge(df, df2, on='Date', how='left')
print("\n合併後的 DataFrame (left join):")
print(df_merged)

# **步驟 3: 資料的操作與變換**

# 計算每日價格波動幅度 (High - Low)
df_merged['Price_Range'] = df_merged['High'] - df_merged['Low']
print("\n新增 'Price_Range' 欄位後的 DataFrame:")
print(df_merged)

# 將 'Volume' 欄位轉換為千股單位
df_merged['Volume_K'] = df_merged['Volume'] / 1000
print("\n新增 'Volume_K' 欄位後的 DataFrame:")
print(df_merged)

# 使用 apply() 函數將 'Date' 欄位格式化為 'YYYY/MM/DD' (建立新欄位)
df_merged['Date_Formatted'] = df_merged['Date'].apply(lambda x: x.strftime('%Y/%m/%d'))
print("\n新增 'Date_Formatted' 欄位後的 DataFrame:")
print(df_merged)

# **步驟 4: 資料的聚合與群組運算 (示範)**
# 假設資料包含不同股票,新增 'Stock' 欄位 (僅為示範,原始資料可能不包含)
df_merged['Stock'] = 'SOLB' # 假設所有資料都是 SOLB 股票

stock_data = {'Date': pd.to_datetime(['2024-01-01', '2024-01-02', '2024-01-01', '2024-01-02']),
              'Stock': ['NVDA', 'NVDA', 'AAPL', 'AAPL'],
              'Close': [120, 125, 170, 175],
              'Volume': [1000, 1500, 2000, 2500]}
df_stocks = pd.DataFrame(stock_data)
df_combined_stocks = pd.concat([df_merged, df_stocks], ignore_index=True) # 合併示範股票資料

print("\n包含不同股票的 DataFrame (示範合併):")
print(df_combined_stocks)

# 根據 'Stock' 欄位分組,計算各股票的平均收盤價
grouped_mean_close = df_combined_stocks.groupby('Stock')['Close'].mean()
print("\n各股票的平均收盤價:")
print(grouped_mean_close)

# 根據 'Stock' 欄位分組,計算各股票的總成交量和最大收盤價 (使用 agg() 函數)
grouped_agg = df_combined_stocks.groupby('Stock').agg({'Volume': 'sum', 'Close': 'max'})
print("\n各股票的總成交量和最大收盤價:")
print(grouped_agg)

# **步驟 5: 遺漏資料處理 (示範)**
df_missing = df_merged.copy() # 複製 DataFrame 進行遺漏值處理示範
df_missing.loc[2:4, 'Open'] = None  # 模擬 'Open' 欄位遺漏值
df_missing.loc[7:9, 'Volume'] = None # 模擬 'Volume' 欄位遺漏值
print("\n包含遺漏值的 DataFrame (示範):")
print(df_missing)

# 檢查每欄遺漏值數量
print("\n每欄遺漏值數量:")
print(df_missing.isnull().sum())

# 使用 0 填補 'Open' 欄位遺漏值
df_fillna_0_open = df_missing.copy()
df_fillna_0_open['Open'] = df_fillna_0_open['Open'].fillna(0)
print("\n'Open' 欄位使用 0 填補遺漏值:")
print(df_fillna_0_open[['Date', 'Open']]) # 只顯示 Date 和 Open 欄位方便查看

# 使用前一個值向前填補 'Volume' 欄位遺漏值
df_fillna_ffill_volume = df_missing.copy()
df_fillna_ffill_volume['Volume'] = df_fillna_ffill_volume['Volume'].fillna(method='ffill')
print("\n'Volume' 欄位向前填補遺漏值:")
print(df_fillna_ffill_volume[['Date', 'Volume']]) # 只顯示 Date 和 Date 欄位方便查看

# 使用平均值填補 'Indicator' 欄位遺漏值
df_fillna_mean_indicator = df_missing.copy()
df_fillna_mean_indicator['Indicator'] = df_fillna_mean_indicator['Indicator'].fillna(df_fillna_mean_indicator['Indicator'].mean())
print("\n'Indicator' 欄位使用平均值填補遺漏值:")
print(df_fillna_mean_indicator[['Date', 'Indicator']]) # 只顯示 Date 和 Indicator 欄位方便查看

# **步驟 6: 異常值處理 (示範 - 基於 'Close' 欄位)**
df_outlier = df_merged.copy() # 複製 DataFrame 進行異常值處理示範
df_outlier.loc[5, 'Close'] = df_outlier['Close'].mean() + 5 * df_outlier['Close'].std() # 模擬 'Close' 欄位異常值 (極端值)
print("\n包含異常值的 DataFrame (示範):")
print(df_outlier[['Date', 'Close']]) # 只顯示 Date 和 Close 欄位方便查看

# 計算 'Close' 欄位的平均值和標準差
mean_close = df_outlier['Close'].mean()
std_close = df_outlier['Close'].std()

# 設定異常值的上下界 (例如:平均值 +/- 2.5 個標準差)
lower_bound = mean_close - 2.5 * std_close
upper_bound = mean_close + 2.5 * std_close
print(f"\n收盤價正常範圍 (平均值 +/- 2.5 個標準差): {lower_bound:.2f} ~ {upper_bound:.2f}")

# 將 'Close' 欄位中的異常值替換為邊界值
df_outlier['Close_Capped'] = df_outlier['Close'].clip(lower=lower_bound, upper=upper_bound)
print("\n'Close' 欄位異常值替換為邊界值:")
print(df_outlier[['Date', 'Close', 'Close_Capped']]) # 顯示 Date, Close, Close_Capped 欄位方便查看

# **步驟 7: 時間序列資料處理**
df_timeseries = df_merged.copy()
df_timeseries = df_timeseries.set_index('Date') # 將 'Date' 欄位設定為 DataFrame 的索引
print("\n將 'Date' 設定為索引的 DataFrame:")
print(df_timeseries)

# 檢查索引類型 (DatetimeIndex)
print("\n索引類型:")
print(df_timeseries.index)

# **步驟 8: 時間序列操作**

# 選取特定日期範圍的資料 (例如 2024-01-03 到 2024-01-07)
date_range_data = df_timeseries.loc['2024-01-03':'2024-01-07']
print("\n2024-01-03 到 2024-01-07 的資料:")
print(date_range_data)

# 將日資料重採樣為週資料,並計算每週的平均收盤價
weekly_avg_close = df_timeseries.resample('W')['Close'].mean()
print("\n週平均收盤價 (重採樣):")
print(weekly_avg_close)

# 將收盤價時間序列向前移動一天
close_shifted_forward = df_timeseries['Close'].shift(periods=1)
print("\n收盤價向前移動一天 (顯示 Date 和原始 Close 與位移後的 Close):")
print(pd.concat([df_timeseries['Close'], close_shifted_forward.rename('Close_Forward')], axis=1))

# **步驟 9: 移動平均**

# 計算 3 日移動平均收盤價
rolling_mean_3d = df_timeseries['Close'].rolling(window=3).mean()
print("\n3 日移動平均收盤價 (顯示 Date 和原始 Close 與 3日移動平均):")
print(pd.concat([df_timeseries['Close'], rolling_mean_3d.rename('MA_3D')], axis=1))

# 計算 5 日移動平均成交量
rolling_mean_5d_volume = df_timeseries['Volume'].rolling(window=5).mean()
print("\n5 日移動平均成交量 (顯示 Date 和原始 Volume 與 5日移動平均):")
print(pd.concat([df_timeseries['Volume'], rolling_mean_5d_volume.rename('MA_5D_Volume')], axis=1))

print("\n**程式碼執行完成!** 以上範例示範了 Pandas 在股市交易數據分析中的基本操作。")