iT邦幫忙

2025 iThome 鐵人賽

DAY 18
0
Modern Web

每天一點 API:打造我的生活小工具系列 第 18

Day18 — 天氣數據視覺化:從 API 到折線圖

  • 分享至 

  • xImage
  •  

昨天我們已經知道 API 抓回來的數字資料,直接看並不直觀。

因此今天我要用之前練習過的天氣 API 當例子,實作把資料轉換成折線圖,讓趨勢更清楚。

為什麼要把天氣數據視覺化?

把天氣數據用圖表表示出來,有幾個重要原因:

  1. 容易看到異常值

比方說某一天的溫度突然降很多或升很多,透過圖表可以馬上發現,不需要一筆筆數字比對。

  1. 可以快速比對不同時間區間

如果想知道這一週和上一週的氣溫有什麼不一樣,用圖表比用表格看來得直覺。

  1. 有助於做決策

像是決定今天要不要帶傘、要不要開冷氣,甚至可以幫忙我們規劃假日出遊的時間,視覺化資料提供很好的參考。

  1. 更容易比較

透過圖表,我們可以快速比較不同日期的高低溫差,或者同一天不同時間的氣溫變化。

小實作:用 Matplotlib 畫天氣折線圖

步驟 1:準備環境

  • 先安裝需要用到的套件:
pip install requests matplotlib
  • 建立一個 Python 檔名叫 day18_weather_plot.py

步驟 2:呼叫天氣 API

  • 用免費且免金鑰的 Open-Meteo API,查詢台北市過去14天的最高氣溫。

  • 這些資料是以 JSON 格式回傳的,裡面包含日期(time)和當天最高溫度(temperature_2m_max)。

import argparse
from datetime import date, timedelta
import requests
import matplotlib
import matplotlib.pyplot as plt

matplotlib.rcParams['font.sans-serif'] = [
    'Microsoft JhengHei', 'PingFang TC', 'Noto Sans CJK TC',
    'SimHei', 'WenQuanYi Zen Hei', 'DejaVu Sans'
]## 設定中文字型,避免中文變成亂碼或空白框。

matplotlib.rcParams['axes.unicode_minus'] = False

GEO_API = "https://geocoding-api.open-meteo.com/v1/search" 
# Open-Meteo 的地理編碼 API,把地名換成經緯度
WX_API  = "https://api.open-meteo.com/v1/forecast" 
# Open-Meteo 的天氣預報 API,用經緯度查天氣
# 1) 地名換成經緯度 
def geocode_city(name: str, count=1, lang="zh", timeout=10):
    # 用 Open-Meteo Geocoding 取得經緯度(取最符合的第一筆)
    params = {"name": name, "count": count, "language": lang, "format": "json"}
    # 組出要帶給地理編碼 API 的查詢參數(地名、筆數、語言、回傳格式)
    r = requests.get(GEO_API, params=params, timeout=timeout)
    # 發送 GET 請求到 GEO_API,把 params 放到 query string,並設定逾時
    r.raise_for_status() # 若回應不是 2xx,直接丟出例外
    data = r.json() # 把回應內容轉成 Python 物件 
    results = data.get("results") or [] # 回應中取出 results 清單;如果沒有,給空清單避免報錯
    if not results:
        raise ValueError(f"查無地名:{name}") #如果清單是空的,代表找不到地名,主動拋出錯誤訊息
    top = results[0] # 取清單的第一筆,最符和的那一筆。
    return {
        "name": top.get("name"),
        "lat":  top["latitude"],
        "lon":  top["longitude"],
        "country": top.get("country", ""),
    } # 回傳一個字典,包含城市名稱、緯度 lat、經度 lon,以及國家名稱

步驟 3:整理資料

  • 把日期放到 X 軸當作時間軸。

  • 把氣溫放到 Y 軸表示溫度。

這對應資料會像這樣,例如:
2025-09-17 → 36.2℃
2025-09-18 → 35.5℃

# 2) 抓近 14 天的 daily 資料 
DAILY_MAP = {
    "temp_max": "temperature_2m_max",
    "temp_min": "temperature_2m_min",
    "precip":   "precipitation_sum",
}

Y_LABEL = {
    "temp_max": "日最高氣溫 (°C)",
    "temp_min": "日最低氣溫 (°C)",
    "precip":   "日累積降水量 (mm)",
}

def fetch_daily_series(lat, lon, metric_key: str, start: date, end: date, timeout=10):
    """抓指定日期區間的 daily 欄位,回傳 (dates, values)"""
    daily_var = DAILY_MAP[metric_key]  # 例如 temperature_2m_max
    params = {
        "latitude":  lat,
        "longitude": lon,
        "daily":     daily_var,
        "start_date": start.isoformat(),
        "end_date":   end.isoformat(),
        "timezone":  "auto",  # 依座標自動挑時區
    }
    r = requests.get(WX_API, params=params, timeout=timeout)
    r.raise_for_status()
    js = r.json()
    daily = js.get("daily") or {}
    dates = daily.get("time") or [] # 把日期清單放進 dates
    values = daily.get(daily_var) or [] # 把對應欄位的數值清單放進 values
    if not dates or not values:
        raise ValueError("API 回傳無資料,請換指標或日期範圍再試")
    return dates, values

步驟 4:畫折線圖

  • 使用 Matplotlib 的 plot() 函式畫出折線圖。

  • 加上圖表標題、X 軸(日期)、Y 軸(氣溫),還有網格線讓圖更清楚。

# 3) 畫圖(折線 / 長條)
def plot_line(dates, values, title, ylabel): 
# 定義一個函式:輸入 X 軸日期 dates、Y 軸數值 values、標題 title、以及 Y 軸標籤 ylabel
    plt.figure() # 開一張新的圖,避免跟上一張重疊
    plt.plot(dates, values, marker="o") # 畫折線圖;marker="o" 讓每個點加上圓點
    plt.title(title) # 設定圖表標題 
    plt.xlabel("日期") # 設定 X 軸標籤為日期
    plt.ylabel(ylabel) # 設定 Y 軸標籤為日最高氣溫 (°C)
    plt.grid(True) # 開啟網格線,方便讀數
    plt.xticks(rotation=45, ha="right")
    plt.tight_layout() # 開啟網格線,方便讀數
    plt.show()

def plot_bar(dates, values, title, ylabel):
    plt.figure()
    plt.bar(dates, values) # 畫長條圖
    plt.title(title)
    plt.xlabel("日期")
    plt.ylabel(ylabel)
    plt.xticks(rotation=45, ha="right")
    plt.tight_layout()
    plt.show()

步驟 5:執行成果

  • 在執行 Python 程式後,螢幕會跳出一張折線圖

  • X 軸是過去14天的日期,Y 軸是每天的最高氣溫

  • 折線連接每天溫度的變化趨勢

# CLI 入口 
def main():
    ap = argparse.ArgumentParser(description="近兩週天氣圖表(Open-Meteo)")
    # 建立參數解析器,讓我們可以用指令列選項控制城市、圖表型態等
    ap.add_argument("--city", default="Taipei", help="地名(支援中文/英文,預設 Taipei)")
    ap.add_argument("--metric", choices=["temp_max","temp_min","precip"],
                    default="temp_max", help="指標:temp_max / temp_min / precip")
    ap.add_argument("--chart", choices=["line","bar"], default="line",
                    help="圖表:line=折線 / bar=長條") # 決定用折線圖還是長條圖,預設是折線圖
    ap.add_argument("--days", type=int, default=14, help="回溯天數(預設 14)")
    # 要看幾天的資料,這裡是設定 14 天
    args = ap.parse_args() # 實際解析指令列輸入,把結果放進 args

    # 計算日期區間(含今天)
    end = date.today()
    start = end - timedelta(days=args.days - 1)

    try:
        place = geocode_city(args.city)
        dates, values = fetch_daily_series(place["lat"], place["lon"], args.metric, start, end)
    except requests.exceptions.RequestException as e: # 錯誤處理
        print("API 連線失敗:", e)
        return
    except Exception as e:
        print("錯誤:", e)
        return

    title = f"{place['name']} 近 {args.days} 天 — " + {
        "temp_max":"日最高溫",
        "temp_min":"日最低溫",
        "precip":"日降水量",
    }[args.metric]
    ylabel = Y_LABEL[args.metric]

    if args.chart == "line": # 依 chart 選擇要畫折線或長條,帶入剛組好的標題與 Y 軸標籤
        plot_line(dates, values, title, ylabel)
    else:
        plot_bar(dates, values, title, ylabel)

if __name__ == "__main__":
    main()

https://ithelp.ithome.com.tw/upload/images/20250930/20178708VVrl6y5XL3.png

畫圖小技巧

  • 畫圖時,如果標題或是文字是中文,要記得設定中文字體,不然會顯示成看不懂的方塊。
    https://ithelp.ithome.com.tw/upload/images/20250930/20178708HMcRhmo3Ua.png

  • 如果是想要把折線圖呈現更清楚,可以在每個點上加一個小圓點(marker='o')。

  • 畫好圖後,可以用 plt.savefig("weather.png") 把圖存成圖片檔,這樣可以更方便我們傳送,或是放在報告裡。

今日總結

  • 學會呼叫天氣 API,抓取歷史氣溫。

  • 練習用 Matplotlib 畫出折線圖。


上一篇
Day17 — 從數字到圖表:Python 資料視覺化基礎
下一篇
Day19 — API 數據怎麼解讀?描述性統計的基礎練習
系列文
每天一點 API:打造我的生活小工具19
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言