項目 內容
組別名稱(報告題目) 晴天騎、雨天停?天氣條件對首爾共享單車租借量的影響
小組成員 胡嘉成、蔡昀展、蔡浚庭、方世燊
學生/學號 方世燊/113612037
研究主題 共享單車的使用情況與天氣條件的關聯
標題 是否已涵蓋
研究主題 ✅
研究動機 ✅
研究依據 ✅
研究問題 ✅
研究假設 ✅
研究方法 ✅
研究結果 ✅
結論與討論 ✅
參考資料 ✅
程式碼 ✅
相關截圖/連結 ✅

研究主題

本專題探討共享單車的使用情況與天氣條件的關聯。我們使用首爾市共享單車(Ttareungyi)一整年的每小時租借資料,檢驗溫度、濕度、降雨與季節是否和租借量有關,並比較哪一個天氣因素的影響最大。

一、研究動機

共享單車已經是許多城市通勤和短程移動的重要工具,台灣的 YouBike、首爾的 Ttareungyi 都是例子。我們自己也常騎單車,發現天氣很會影響騎不騎:天氣太熱、太冷或下雨時,就會改搭其他交通工具。

這個問題也有實際用途。單車營運單位需要知道哪些時候需求高、哪些時候需求低,才能安排車輛調度和維修。如果能用統計方法確認天氣和時間對租借量的影響,就能提供具體的數據依據。

我們選擇首爾共享單車資料,是因為它公開可得、沒有遺漏值,且同時有數值變數(溫度、濕度、降雨量)和類別變數(季節、假日),可以完整練習本學期學到的統計方法。

二、研究依據

過去研究一致發現,天氣是影響共享單車使用量的重要因素。

  • Gebhart 與 Noland(2014)分析美國華盛頓特區的共享單車資料,將每小時天氣與使用量配對。結果顯示低溫、降雨和高濕度都會降低使用機率和騎乘時間。
  • Sathishkumar 與 Cho(2020)使用首爾共享單車和華盛頓 Capital Bikeshare 的資料,結合天氣變數建立規則式迴歸模型來預測租借需求。
  • Sathishkumar、Park 與 Cho(2020)以首爾租借資料結合天氣與時間變數,比較多種資料探勘模型預測租借需求的表現。

研究缺口:首爾資料的既有研究多以機器學習追求預測準確度,較少用基礎統計檢定個別回答「每個天氣因素是否顯著影響租借量、影響有多大」。我們用初等統計方法,逐一比較溫度、濕度、降雨量和租借量的關係,讓結果更容易解釋。

【研究問題】與【研究假設】

# 研究問題 研究假設
Q1 溫度和每小時租借量是否有關? 溫度與租借量呈正相關:溫度越高,租借量越多
Q2 濕度和每小時租借量是否有關? 濕度與租借量呈負相關
Q3 有下雨和沒下雨的時段,平均租借量是否不同? 下雨時段的平均租借量顯著低於沒下雨時段
Q4 四個季節的平均租借量是否不同? 至少有一個季節不同,預期冬季最低
Q5 同時考慮溫度、濕度、降雨量時,哪個因素對租借量的影響最大? 三者都有顯著影響,其中溫度的影響最大

三、研究方法

資料來源

UCI Machine Learning Repository 的 Seoul Bike Sharing Demand 資料集(CC BY 4.0 授權)。資料涵蓋 2017 年 12 月 1 日至 2018 年 11 月 30 日,共 8,760 筆,每筆是一個小時的首爾共享單車租借量,遺漏值 0 筆。

資料清理

  1. 刪除「系統未營運」(Functioning Day = No)的時段,共 295 筆,這些時段的租借量合計為 0,保留下來會把平均數拉低。清理後的分析樣本為 8,465 筆。
  2. 降雨量大多為 0,另外建立「是否下雨」類別變數(降雨量 > 0 為有雨)。
  3. 季節轉成「春、夏、秋、冬」的有序類別。
  4. 用直方圖和盒鬚圖檢查分布與極端值。濕度有 17 筆為 0%,全部集中在 2018 年 5 月的 5 天,疑似感測器異常;因為只占樣本的 0.20%,我們保留這些資料,並在結果中說明排除後的結果。
  5. 每小時租借量明顯右偏,另外建立 log(租借量 + 1),作為穩健性檢查使用。

研究變項

變項 角色 類型 單位/類別
每小時租借量(Rented Bike Count) 應變數 數值 台次
溫度(Temperature) 自變數 數值 °C
濕度(Humidity) 自變數 數值 %
降雨量(Rainfall) 自變數 數值 mm
是否下雨 自變數 類別 有雨/沒雨(由降雨量轉換)
季節(Seasons) 自變數 類別 春、夏、秋、冬

統計方法

顯著水準設為 α = .05,所有分析以 R(4.3.1)完成。

研究問題 統計方法 虛無假設 H₀
全部 敘述統計(平均數、中位數、標準差、偏態、峰度)、直方圖、盒鬚圖、散佈圖 —
Q1 溫度 Pearson 相關係數、簡單線性迴歸;補充:加入溫度平方項檢查曲線關係 ρ = 0
Q2 濕度 Pearson 相關係數、簡單線性迴歸 ρ = 0
Q3 下雨 Levene 變異數同質性檢定 → 兩獨立樣本 t 檢定(變異數不同質時用 Welch t);效果量 Cohen’s d μ有雨 = μ沒雨
Q4 季節 Levene 檢定 → 單因子 ANOVA(變異數不同質時以 Welch ANOVA 為主);事後比較 Tukey HSD 與 Games-Howell;效果量 η² 四季平均數相等
Q5 綜合 複迴歸(溫度、濕度、降雨量 → 租借量)、標準化係數 β、VIF、殘差診斷;穩健性:以 log(租借量 + 1) 重跑 各迴歸係數 = 0

前提假設的處理方式:樣本數超過八千,依中央極限定理,平均數與迴歸係數的抽樣分布會接近常態,所以租借量本身右偏不會讓 t 檢定和迴歸失效。不過我們仍以 log(租借量 + 1) 重跑主要分析,確認結論不受右偏影響。變異數同質性則用 Levene 檢定判斷,再決定要用哪個版本的檢定。

四、研究結果

(一)敘述統計

表 1 數值變項的敘述統計(N = 8,465)
變項 N 平均數 中位數 標準差 最小值 最大值 偏態 峰度
每小時租借量(台) 8,465 729.16 542.0 642.35 2.0 3,556.0 1.14 0.82
溫度(°C) 8,465 12.77 13.5 12.10 -17.8 39.4 -0.17 -0.89
濕度(%) 8,465 58.15 57.0 20.48 0.0 98.0 0.07 -0.81
降雨量(mm) 8,465 0.15 0.0 1.13 0.0 35.0 14.61 289.72

清理後共 8,465 個營運時段。每小時租借量平均 729.2 台,中位數 542 台,標準差 642.4 台。平均數大於中位數,偏態 1.14,表示租借量右偏:大部分時段只有幾百台,少數尖峰時段超過三千台(圖 1 左)。取對數後分布變得較對稱(圖 1 右)。

溫度從 -17.8°C 到 39.4°C,平均 12.8°C,分布接近對稱。濕度平均 58.1%。降雨量極度右偏(偏態 14.6),只有 516 個時段(6.1%)有下雨,所以額外用「是否下雨」來分析(圖 2)。四季的時段數分別為春 2160、夏 2208、秋 1937、冬 2160。

(二)Q1:溫度與租借量

溫度與每小時租借量呈中度正相關,r(8463) = .56,95% CI [.55, .58],p < .001。簡單線性迴歸為:

租借量 = 347.77 + 29.86 × 溫度,R² = .32

也就是溫度每升高 1°C,每小時租借量平均增加約 30 台;溫度單獨可以解釋租借量 31.7% 的變異。拒絕 H₀,支持研究假設 Q1。

不過圖 3 的 LOESS 曲線顯示關係不是一直線:約 25–30°C 時租借量最高,再熱就開始下降。圖 4 把溫度每 5°C 分一組,平均租借量在 25–30°C 達到 1,235 台,到 35°C 以上降為 900 台。加入溫度平方項後,平方項係數為負且顯著(p = .018),但 R² 幾乎沒有增加(.3167 → .3171),表示曲線趨勢存在、但幅度不大,主要只發生在少數高溫時段(35°C 以上只有 89 個時段)。

(三)Q2:濕度與租借量

濕度與每小時租借量呈弱負相關,r(8463) = -.20,95% CI [-.22, -.18],p < .001。迴歸斜率為 -6.33,即濕度每增加 1%,租借量平均減少約 6.3 台,R² = .04(只解釋 4.1% 的變異)。拒絕 H₀,支持研究假設 Q2,但關聯強度明顯弱於溫度。排除濕度 = 0% 的 17 筆疑似異常資料後,r = -.21,結論不變。

(四)Q3:有雨與沒雨時段的租借量

表 2 有雨與沒雨時段的租借量
是否下雨 N 平均數 標準差 中位數
沒雨 7,949 765.6 642.7 591
有雨 516 167.3 263.9 71

Levene 檢定顯示兩組變異數不同質,F(1, 8463) = 358.60,p < .001(有雨時段的標準差只有沒雨時段的一半不到),因此採用 Welch t 檢定:

t(978.5) = 43.77,p < .001,平均差 = 598.4 台,95% CI [571.5, 625.2]

沒雨時段平均每小時租借 765.6 台,有雨時段只有 167.3 台,約為沒雨時的 22%。效果量 Cohen’s d = 0.96,屬於大效果。改用 log(租借量 + 1) 重做 Welch t 檢定,結果同樣顯著(t(561.6) = 32.82,p < .001)。拒絕 H₀,支持研究假設 Q3。

(五)Q4:四季的平均租借量

表 3 四季每小時租借量
季節 N 平均數 標準差 95% CI 下限 95% CI 上限
春 2,160 746.3 618.7 720.1 772.4
夏 2,208 1,034.1 690.2 1,005.3 1,062.9
秋 1,937 924.1 617.5 896.6 951.6
冬 2,160 225.5 150.4 219.2 231.9

Levene 檢定顯示四季變異數不同質,F(3, 8461) = 663.52,p < .001(冬季標準差遠小於其他季節),所以以 Welch ANOVA 為主要結果:

Welch F(3, 3,809.7) = 2,030.6,p < .001

傳統單因子 ANOVA 也得到相同結論,F(3, 8461) = 875.6,p < .001,效果量 η² = .24,表示季節可以解釋租借量約 24% 的變異,屬於大效果。拒絕 H₀,支持研究假設 Q4。

表 4 四季兩兩事後比較
比較 平均差 Games-Howell p Tukey HSD p
夏 − 春 287.8 < .001 < .001
秋 − 春 177.9 < .001 < .001
冬 − 春 -520.7 < .001 < .001
秋 − 夏 -110.0 < .001 < .001
冬 − 夏 -808.5 < .001 < .001
冬 − 秋 -698.6 < .001 < .001

事後比較中,Games-Howell(不假設變異數相等)和 Tukey HSD 的六組比較全部顯著。平均租借量由高到低為:夏(1,034 台)> 秋(924 台)> 春(746 台)> 冬(226 台)。冬季最低,只有夏季的 22%,與假設相符。

(六)Q5:複迴歸——哪個天氣因素影響最大?

表 5 複迴歸結果(依變數:每小時租借量)
預測變項 B 標準誤 95% CI β t p
截距 834.84 16.66 [802.19, 867.50] — 50.12 < .001
溫度 32.61 0.45 [31.74, 33.49] 0.61 72.87 < .001
濕度 -8.84 0.27 [-9.38, -8.31] -0.28 -32.53 < .001
降雨量 -53.57 4.89 [-63.14, -43.99] -0.09 -10.96 < .001

以溫度、濕度、降雨量同時預測租借量,整體模型顯著,F(3, 8461) = 1,999.7,p < .001,R² = .41(調整後 R² = .41),三個天氣變數共同解釋了 41.5% 的變異。

在控制其他兩個變數之下:

  • 溫度:每升高 1°C,租借量增加 32.6 台(β = .61,p < .001)
  • 濕度:每增加 1%,租借量減少 8.8 台(β = -.28,p < .001)
  • 降雨量:每增加 1 mm,租借量減少 53.6 台(β = -.09,p < .001)

三個係數都顯著,標準化係數絕對值的大小順序為 溫度(.61)> 濕度(.28)> 降雨量(.09)(圖 8)。溫度的影響約為濕度的 2.2 倍。拒絕 H₀,支持研究假設 Q5。值得注意的是,濕度在複迴歸中的 β(-.28)比它和租借量的單純相關(-.20)更強,表示在溫度相同的條件下,濕度的負面影響更明顯。

前提假設檢查:

  • 共線性:VIF 介於 1.03 到 1.09,都遠小於 10,三個自變數沒有共線性問題。
  • 殘差:圖 9a 中殘差隨預測值增加而擴散(呈漏斗狀),表示變異數不同質;圖的下方有一條斜直線的界線,因為租借量不可能小於 0(殘差最低只能到「−預測值」);另外有 185 個時段的預測值是負數(多為零下低溫或大雨時段),顯示線性模型在極端天氣下不太合理。圖 9b 的 Q-Q 圖右尾上翹,殘差右偏。

穩健性檢查:由於殘差不完全符合常態與等變異,我們改用 log(租借量 + 1) 當依變數重跑複迴歸(表 6)。模型 R² 提高到 .50,三個係數的方向與顯著性都不變,β 的大小順序也仍是溫度(.63)> 濕度(-.33)> 降雨量(-.21),表示結論不受右偏影響。以對數模型來解釋:溫度每升高 1°C,租借量約增加 6.2%;降雨量每增加 1 mm,租借量約減少 19.7%。

表 6 穩健性檢查:依變數為 log(租借量 + 1)
預測變項 B 標準誤 95% CI β t p
截距 6.453 0.028 [6.399, 6.507] — 233.766 < .001
溫度 0.060 0.001 [0.058, 0.061] 0.628 80.762 < .001
濕度 -0.019 0.000 [-0.020, -0.018] -0.334 -41.712 < .001
降雨量 -0.219 0.008 [-0.235, -0.203] -0.214 -27.038 < .001

(七)研究假設檢驗摘要

# 研究假設 主要統計結果 是否支持
Q1 溫度與租借量呈正相關 r = .56,p < .001;高溫(> 30°C)時略為下降 ✅ 支持
Q2 濕度與租借量呈負相關 r = -.20,p < .001 ✅ 支持(弱相關)
Q3 下雨時段平均租借量較低 Welch t = 43.77,p < .001,d = 0.96 ✅ 支持
Q4 四季不同,冬季最低 Welch F = 2,030.6,p < .001,η² = .24;夏 > 秋 > 春 > 冬 ✅ 支持
Q5 三者皆顯著,溫度影響最大 R² = .41;β:溫度 .61、濕度 -.28、降雨量 -.09 ✅ 支持

五、結論與討論

主要發現

  1. 溫度是影響租借量最大的天氣因素。溫度與租借量呈中度正相關(r = .56),在複迴歸中的標準化係數(β = .61)也遠高於濕度和降雨量。但這個關係不是單純的「越熱越多」:25–30°C 時租借量最高,超過 30°C 後下降,符合我們在計劃書中「太熱反而不騎」的預期。
  2. 下雨的影響非常明顯。有雨時段的平均租借量(167 台)只有沒雨時段(766 台)的約 22%,Cohen’s d = 0.96。
  3. 濕度有負面影響,但比溫度弱。單純相關只有 -.20,但控制溫度與降雨後,濕度的影響變得更清楚(β = -.28)。
  4. 季節差異很大。夏季最高、冬季最低,季節可以解釋約 24% 的變異。季節差異很大一部分其實就反映了溫度差異。

「降雨量」與「是否下雨」為什麼看起來不一致?

Q3 中下雨的效果很大(d = 0.96),但在 Q5 複迴歸裡降雨量的 β 只有 -.09,是三個變數中最小的。我們認為原因是降雨量有 93.9% 的時段都是 0,分布極度右偏:真正影響騎車意願的是「有沒有下雨」,而不是「下了幾毫米」。從 0 mm 變成 1 mm 時,租借量就已經大幅下降;之後雨再變大,可以再減少的空間就不多了。用「每增加 1 mm 減少多少台」的線性模型,無法描述這種「有或無」的效果,所以標準化係數被低估。這也是我們在計劃書中另外建立「是否下雨」變數的原因。

與過去研究的比較

我們的結果與 Gebhart 與 Noland(2014)在華盛頓特區的發現方向一致:低溫、降雨和高濕度都會降低共享單車的使用。Sathishkumar 與 Cho(2020)及 Sathishkumar、Park 與 Cho(2020)以機器學習模型預測首爾的租借需求,天氣變數是模型中的重要預測變項。本研究用初等統計方法回到「每個因素各自有多大影響」這個問題,得到容易解釋的結論:溫度最重要、下雨的效果最大但以「有無」的形式出現、濕度較弱。

實務意涵

對營運單位來說,可以依照天氣預報調整車輛調度:溫暖(約 20–30°C)的晴天要準備更多車;下雨或冬季時段需求只剩一般時段的二到三成,可以趁這些時段進行車輛維修與重新分配。

研究限制

  1. 觀測值不獨立:這是每小時的時間序列資料,相鄰一小時租借量的自我相關高達 .90,不符合 t 檢定、ANOVA 與迴歸「觀測值獨立」的假設,因此 p 值可能被低估(看起來比實際更顯著)。
  2. 樣本很大,幾乎都會顯著:N = 8,465 時,很小的差異也會達到統計顯著,所以我們更重視效果量(r、d、η²、β)而非 p 值。
  3. 沒有控制時段:租借量有很強的通勤尖峰(早上與傍晚),而一天中的時段同時和溫度有關(白天較熱),本研究沒有控制「小時」,溫度的效果可能部分來自時段的影響。
  4. 推論範圍有限:資料只有首爾一年,結果不一定能推論到台灣或其他年份;且屬觀察型資料,只能說明關聯,不能確認因果。
  5. 資料品質:濕度有 17 筆為 0%,疑似感測器異常;排除後結果不變,但仍代表天氣資料可能有測量誤差。

未來可以加入小時、假日等時間變數,或改用日資料降低自我相關,以更嚴謹地估計天氣的影響。

六、參考資料

  1. Gebhart, K., & Noland, R. B. (2014). The impact of weather conditions on bikeshare trips in Washington, DC. Transportation, 41(6), 1205–1225. https://doi.org/10.1007/s11116-014-9540-7
  2. Sathishkumar V E, & Cho, Y. (2020). A rule-based model for Seoul Bike sharing demand prediction using weather data. European Journal of Remote Sensing, 53(sup1), 166–183. https://doi.org/10.1080/22797254.2020.1725789
  3. Sathishkumar V E, Park, J., & Cho, Y. (2020). Using data mining techniques for bike sharing demand prediction in metropolitan city. Computer Communications, 153, 353–366. https://doi.org/10.1016/j.comcom.2020.02.007
  4. Seoul Bike Sharing Demand [Dataset]. (2020). UCI Machine Learning Repository. https://doi.org/10.24432/C5F62R
  5. R Core Team. (2023). R: A language and environment for statistical computing. R Foundation for Statistical Computing. https://www.R-project.org/

七、程式碼

以下為完整的 R 程式碼(Final_analysis.R)。本報告的所有數字、表格與圖都由這份程式碼產生,資料檔需放在 data/SeoulBikeData.csv。

# =====================================================================
# 115-1 初等統計 期末專題
# 晴天騎、雨天停?天氣條件對首爾共享單車租借量的影響
# 資料:UCI Seoul Bike Sharing Demand(SeoulBikeData.csv)
# 顯著水準 α = .05
# =====================================================================

# ---- 0. 套件 --------------------------------------------------------
# 若尚未安裝,先執行 install.packages(c("dplyr", "ggplot2", "car", "psych"))
library(dplyr)    # 資料整理
library(ggplot2)  # 畫圖
library(car)      # leveneTest()、vif()
library(psych)    # describe():偏態、峰度

# 中文字型(Windows 用微軟正黑體,其他系統用預設字型)
if (.Platform$OS.type == "windows") {
  windowsFonts(JH = windowsFont("Microsoft JhengHei"))
  base_font <- "JH"
} else {
  base_font <- ""
}
theme_set(
  theme_minimal(base_size = 12, base_family = base_font) +
    theme(panel.grid.minor = element_blank(),
          panel.grid.major = element_line(colour = "grey90", linewidth = 0.3),
          plot.title = element_text(face = "bold"),
          legend.position = "top")
)

# 固定配色:同一個類別在每張圖都用同一個顏色
col_season <- c("春" = "#2a78d6", "夏" = "#eb6834", "秋" = "#1baf7a", "冬" = "#eda100")
col_rain   <- c("沒雨" = "#2a78d6", "有雨" = "#eb6834")
col_point  <- "#2a78d6"
col_ink    <- "#0b0b0b"

# ---- 1. 讀取資料 ----------------------------------------------------
# 原始檔的欄位名稱有 Latin-1 的度數符號,所以用 latin1 讀進來,再依位置改成英文短名
raw <- read.csv("data/SeoulBikeData.csv", fileEncoding = "latin1",
                check.names = FALSE)
names(raw) <- c("Date", "Count", "Hour", "Temp", "Humidity", "Wind",
                "Visibility", "DewPoint", "Solar", "Rainfall", "Snowfall",
                "Seasons", "Holiday", "Functioning")

n_raw        <- nrow(raw)
n_missing    <- sum(is.na(raw))
n_nonfunc    <- sum(raw$Functioning == "No")
sum_nonfunc  <- sum(raw$Count[raw$Functioning == "No"])  # 未營運時段的租借量總和(應為 0)

# ---- 2. 資料整理 ----------------------------------------------------
# (1) 刪除系統未營運(Functioning Day = No)的時段
# (2) 建立「是否下雨」:降雨量 > 0 為有雨
# (3) 季節轉成有順序的因子,並改成中文標籤
bike <- raw %>%
  filter(Functioning == "Yes") %>%
  mutate(
    Rain    = factor(ifelse(Rainfall > 0, "有雨", "沒雨"),
                     levels = c("沒雨", "有雨")),
    Season  = factor(Seasons,
                     levels = c("Spring", "Summer", "Autumn", "Winter"),
                     labels = c("春", "夏", "秋", "冬")),
    LogCount = log(Count + 1)   # 右偏時使用的對數轉換
  )
n_clean <- nrow(bike)

# ---- 3. 敘述統計 ----------------------------------------------------
num_vars <- c("Count", "Temp", "Humidity", "Rainfall")
desc_tab <- psych::describe(bike[, num_vars])
desc_tab <- data.frame(
  變項   = c("每小時租借量(台)", "溫度(°C)", "濕度(%)", "降雨量(mm)"),
  N      = desc_tab$n,
  平均數 = desc_tab$mean,
  中位數 = desc_tab$median,
  標準差 = desc_tab$sd,
  最小值 = desc_tab$min,
  最大值 = desc_tab$max,
  偏態   = desc_tab$skew,
  峰度   = desc_tab$kurtosis
)

# 類別變項次數
rain_freq   <- table(bike$Rain)
season_freq <- table(bike$Season)

# 圖 1:租借量直方圖(原始 vs. 取對數)
hist_df <- rbind(
  data.frame(值 = bike$Count,    尺度 = "原始租借量(台)"),
  data.frame(值 = bike$LogCount, 尺度 = "log(租借量 + 1)")
)
hist_df$尺度 <- factor(hist_df$尺度, levels = c("原始租借量(台)", "log(租借量 + 1)"))
fig_hist <- ggplot(hist_df, aes(x = 值)) +
  geom_histogram(bins = 40, fill = col_point, colour = "white", linewidth = 0.2) +
  facet_wrap(~ 尺度, scales = "free") +
  labs(title = "圖 1 每小時租借量的分布", x = NULL, y = "時段數")

# 圖 2:四個數值變項的盒鬚圖(各自尺度)
box_df <- rbind(
  data.frame(變項 = "租借量(台)", 值 = bike$Count),
  data.frame(變項 = "溫度(°C)",   值 = bike$Temp),
  data.frame(變項 = "濕度(%)",    值 = bike$Humidity),
  data.frame(變項 = "降雨量(mm)", 值 = bike$Rainfall)
)
box_df$變項 <- factor(box_df$變項, levels = unique(box_df$變項))
fig_box <- ggplot(box_df, aes(x = "", y = 值)) +
  geom_boxplot(fill = "#dbe8f8", colour = col_point, width = 0.5,
               outlier.size = 0.6, outlier.alpha = 0.4) +
  facet_wrap(~ 變項, scales = "free_y", nrow = 1) +
  labs(title = "圖 2 各數值變項的盒鬚圖", x = NULL, y = NULL)

# ---- 4. Q1 溫度與租借量:Pearson 相關 + 簡單線性迴歸 --------------
cor_temp <- cor.test(bike$Count, bike$Temp, method = "pearson")
lm_temp  <- lm(Count ~ Temp, data = bike)
sum_temp <- summary(lm_temp)

# 補充:檢查「太熱反而下降」的曲線關係(加入溫度平方項)
lm_temp2  <- lm(Count ~ Temp + I(Temp^2), data = bike)
cmp_temp2 <- anova(lm_temp, lm_temp2)   # 加入平方項是否顯著改善模型

# 依溫度每 5°C 分組,看平均租借量
temp_bin <- bike %>%
  mutate(溫度區間 = cut(Temp, breaks = seq(-20, 40, 5))) %>%
  group_by(溫度區間) %>%
  summarise(時段數 = n(), 平均租借量 = mean(Count), .groups = "drop") %>%
  mutate(組中點 = seq(-17.5, 37.5, 5)[as.integer(溫度區間)])

# 圖 3:溫度 vs. 租借量散佈圖
fig_temp <- ggplot(bike, aes(x = Temp, y = Count)) +
  geom_point(colour = col_point, alpha = 0.12, size = 0.8) +
  geom_smooth(aes(colour = "線性迴歸"), method = "lm", se = FALSE,
              linewidth = 0.9) +
  geom_smooth(aes(colour = "LOESS 平滑曲線"), method = "loess", se = FALSE,
              linewidth = 0.9) +
  scale_colour_manual(name = NULL,
                      values = c("線性迴歸" = col_ink, "LOESS 平滑曲線" = "#eb6834"),
                      breaks = c("線性迴歸", "LOESS 平滑曲線")) +
  labs(title = "圖 3 溫度與每小時租借量",
       x = "溫度(°C)", y = "每小時租借量(台)")

# 圖 4:依溫度分組的平均租借量
fig_tempbin <- ggplot(temp_bin, aes(x = 組中點, y = 平均租借量)) +
  geom_line(colour = col_point, linewidth = 0.8) +
  geom_point(colour = col_point, size = 2.5) +
  geom_text(aes(label = round(平均租借量)), vjust = -1, size = 3,
            family = base_font, colour = "grey30") +
  scale_x_continuous(breaks = seq(-20, 40, 5)) +
  scale_y_continuous(expand = expansion(mult = c(0.05, 0.15))) +
  labs(title = "圖 4 依溫度分組(每 5°C)的平均租借量",
       x = "溫度區間組中點(°C)", y = "平均租借量(台)")

# ---- 5. Q2 濕度與租借量:Pearson 相關 + 簡單線性迴歸 --------------
cor_hum <- cor.test(bike$Count, bike$Humidity, method = "pearson")
lm_hum  <- lm(Count ~ Humidity, data = bike)
sum_hum <- summary(lm_hum)

# 濕度 = 0% 的時段(集中在 2018 年 5 月少數幾天,疑似感測器異常)
n_hum0      <- sum(bike$Humidity == 0)
dates_hum0  <- unique(bike$Date[bike$Humidity == 0])
cor_hum_nz  <- cor.test(bike$Count[bike$Humidity > 0],
                        bike$Humidity[bike$Humidity > 0])   # 排除後再算一次

# 圖 5:濕度 vs. 租借量散佈圖
fig_hum <- ggplot(bike, aes(x = Humidity, y = Count)) +
  geom_point(colour = col_point, alpha = 0.12, size = 0.8) +
  geom_smooth(method = "lm", se = FALSE, colour = col_ink, linewidth = 0.9) +
  labs(title = "圖 5 濕度與每小時租借量(黑線為線性迴歸)",
       x = "濕度(%)", y = "每小時租借量(台)")

# ---- 6. Q3 有雨 vs. 沒雨:兩獨立樣本 t 檢定 --------------------------
rain_desc <- bike %>%
  group_by(Rain) %>%
  summarise(N = n(), 平均數 = mean(Count), 標準差 = sd(Count),
            中位數 = median(Count), .groups = "drop")

lev_rain     <- car::leveneTest(Count ~ Rain, data = bike, center = median)
lev_rain_p   <- lev_rain$`Pr(>F)`[1]
t_student    <- t.test(Count ~ Rain, data = bike, var.equal = TRUE)
t_welch      <- t.test(Count ~ Rain, data = bike, var.equal = FALSE)
t_rain       <- if (lev_rain_p < .05) t_welch else t_student  # 依 Levene 結果選版本

# 效果量 Cohen's d(合併標準差)
n1 <- rain_desc$N[1];  n2 <- rain_desc$N[2]
s1 <- rain_desc$標準差[1]; s2 <- rain_desc$標準差[2]
sd_pooled <- sqrt(((n1 - 1) * s1^2 + (n2 - 1) * s2^2) / (n1 + n2 - 2))
d_rain    <- (rain_desc$平均數[1] - rain_desc$平均數[2]) / sd_pooled

# 補充:取對數後再做一次 Welch t 檢定(處理右偏)
t_rain_log <- t.test(LogCount ~ Rain, data = bike, var.equal = FALSE)

# 圖 6:有雨 vs. 沒雨盒鬚圖
fig_rain <- ggplot(bike, aes(x = Rain, y = Count, fill = Rain)) +
  geom_boxplot(width = 0.5, colour = "grey25", outlier.size = 0.6,
               outlier.alpha = 0.3, show.legend = FALSE) +
  stat_summary(fun = mean, geom = "point", shape = 23, size = 3,
               fill = "white", colour = col_ink) +
  scale_fill_manual(values = col_rain) +
  labs(title = "圖 6 有雨與沒雨時段的租借量(◇為平均數)",
       x = NULL, y = "每小時租借量(台)")

# ---- 7. Q4 四季:單因子 ANOVA + 事後比較 ----------------------------
season_desc <- bike %>%
  group_by(Season) %>%
  summarise(N = n(), 平均數 = mean(Count), 標準差 = sd(Count),
            se = sd(Count) / sqrt(n()), .groups = "drop") %>%
  mutate(CI下限 = 平均數 - qt(.975, N - 1) * se,
         CI上限 = 平均數 + qt(.975, N - 1) * se)

lev_season   <- car::leveneTest(Count ~ Season, data = bike, center = median)
lev_season_p <- lev_season$`Pr(>F)`[1]
aov_season   <- aov(Count ~ Season, data = bike)
aov_tab      <- summary(aov_season)[[1]]
eta2_season  <- aov_tab$`Sum Sq`[1] / sum(aov_tab$`Sum Sq`)   # η²
welch_season <- oneway.test(Count ~ Season, data = bike, var.equal = FALSE)
tukey_season <- TukeyHSD(aov_season)

# Games-Howell 事後比較(不假設變異數相等)
games_howell <- function(y, g) {
  g   <- droplevels(g)
  lev <- levels(g); k <- length(lev)
  m   <- tapply(y, g, mean); v <- tapply(y, g, var); n <- tapply(y, g, length)
  out <- NULL
  for (i in 1:(k - 1)) for (j in (i + 1):k) {
    diff <- m[j] - m[i]
    se   <- sqrt(v[i] / n[i] + v[j] / n[j])
    df   <- (v[i] / n[i] + v[j] / n[j])^2 /
            ((v[i] / n[i])^2 / (n[i] - 1) + (v[j] / n[j])^2 / (n[j] - 1))
    q    <- abs(diff) / (se / sqrt(2))
    p    <- ptukey(q, nmeans = k, df = df, lower.tail = FALSE)
    out  <- rbind(out, data.frame(比較 = paste0(lev[j], " − ", lev[i]),
                                  平均差 = diff, 標準誤 = se, 自由度 = df,
                                  p值 = p))
  }
  rownames(out) <- NULL
  out
}
gh_season <- games_howell(bike$Count, bike$Season)

# 圖 7:四季平均租借量與 95% 信賴區間
fig_season <- ggplot(season_desc, aes(x = Season, y = 平均數, fill = Season)) +
  geom_col(width = 0.6, show.legend = FALSE) +
  geom_errorbar(aes(ymin = CI下限, ymax = CI上限), width = 0.15,
                colour = col_ink, linewidth = 0.5) +
  geom_text(aes(label = round(平均數), y = CI上限), vjust = -0.6, size = 3.5,
            family = base_font, colour = "grey20") +
  scale_fill_manual(values = col_season) +
  scale_y_continuous(expand = expansion(mult = c(0, 0.1))) +
  labs(title = "圖 7 四季平均每小時租借量(誤差線為 95% 信賴區間)",
       x = "季節", y = "平均租借量(台)")

# ---- 8. Q5 複迴歸:溫度、濕度、降雨量 → 租借量 ----------------------
lm_multi  <- lm(Count ~ Temp + Humidity + Rainfall, data = bike)
sum_multi <- summary(lm_multi)
vif_multi <- car::vif(lm_multi)

# 標準化迴歸係數 β:把所有變數轉成 z 分數再跑迴歸
bike_z <- bike %>%
  mutate(across(c(Count, LogCount, Temp, Humidity, Rainfall),
                ~ as.numeric(scale(.x))))
lm_multi_z <- lm(Count ~ Temp + Humidity + Rainfall, data = bike_z)
beta_multi <- coef(lm_multi_z)[-1]

# 穩健性檢查:依變數改用 log(租借量 + 1)
lm_multi_log   <- lm(LogCount ~ Temp + Humidity + Rainfall, data = bike)
sum_multi_log  <- summary(lm_multi_log)
beta_multi_log <- coef(lm(LogCount ~ Temp + Humidity + Rainfall, data = bike_z))[-1]

# 整理係數表
make_coef_tab <- function(fit, beta, ci_digits = 2) {
  s  <- summary(fit)$coefficients
  ci <- confint(fit)
  fmt <- paste0("[%.", ci_digits, "f, %.", ci_digits, "f]")
  data.frame(
    預測變項 = c("截距", "溫度", "濕度", "降雨量"),
    B        = s[, 1],
    標準誤   = s[, 2],
    `95% CI` = sprintf(fmt, ci[, 1], ci[, 2]),
    β        = c(NA, beta),
    t        = s[, 3],
    p        = s[, 4],
    check.names = FALSE, row.names = NULL
  )
}
coef_multi     <- make_coef_tab(lm_multi, beta_multi)
coef_multi_log <- make_coef_tab(lm_multi_log, beta_multi_log, ci_digits = 3)

# 圖 8:標準化迴歸係數比較
beta_df <- data.frame(
  變項 = factor(c("溫度", "濕度", "降雨量"), levels = c("溫度", "濕度", "降雨量")),
  β    = beta_multi
)
fig_beta <- ggplot(beta_df, aes(x = 變項, y = β)) +
  geom_col(width = 0.5, fill = col_point) +
  geom_hline(yintercept = 0, colour = "grey40", linewidth = 0.4) +
  geom_text(aes(label = sprintf("%.2f", β), vjust = ifelse(β > 0, -0.5, 1.4)),
            size = 3.8, family = base_font, colour = "grey20") +
  scale_y_continuous(limits = c(-0.45, 0.75)) +
  labs(title = "圖 8 複迴歸的標準化係數 β", x = NULL, y = "標準化係數 β")

# 圖 9:殘差診斷(殘差 vs. 預測值、殘差 Q-Q 圖)
diag_df <- data.frame(預測值 = fitted(lm_multi), 殘差 = resid(lm_multi))
fig_resid <- ggplot(diag_df, aes(x = 預測值, y = 殘差)) +
  geom_point(colour = col_point, alpha = 0.12, size = 0.8) +
  geom_hline(yintercept = 0, colour = col_ink, linewidth = 0.5) +
  labs(title = "圖 9a 殘差 vs. 預測值", x = "預測租借量", y = "殘差")
fig_qq <- ggplot(diag_df, aes(sample = 殘差)) +
  stat_qq(colour = col_point, alpha = 0.3, size = 0.8) +
  stat_qq_line(colour = col_ink, linewidth = 0.5) +
  labs(title = "圖 9b 殘差常態 Q-Q 圖", x = "理論分位數", y = "樣本分位數")

# ---- 9. 研究限制:相鄰時段的自我相關 --------------------------------
acf_lag1 <- acf(bike$Count, plot = FALSE)$acf[2]   # 落後 1 小時的自我相關

# ---- 10. 單獨執行本檔時,把圖存到 figures/ ---------------------------
if (!isTRUE(getOption("knitr.in.progress"))) {
  dir.create("figures", showWarnings = FALSE)
  figs <- list(fig1_hist = fig_hist, fig2_box = fig_box, fig3_temp = fig_temp,
               fig4_tempbin = fig_tempbin, fig5_hum = fig_hum,
               fig6_rain = fig_rain, fig7_season = fig_season,
               fig8_beta = fig_beta, fig9a_resid = fig_resid, fig9b_qq = fig_qq)
  for (nm in names(figs)) {
    ggsave(file.path("figures", paste0(nm, ".png")), figs[[nm]],
           width = 7, height = 4.5, dpi = 200, bg = "white")
  }
  print(sum_multi); print(vif_multi); print(beta_multi)
}

八、相關截圖/連結

AI 使用揭露

這堂課鼓勵大家可以與 AI 進行合作,但同時希望大家以負責任的方式揭露使用過程。

你是否有使用 AI 工具協助完成作業? ■是 □否

使用的 AI 工具:Claude(Anthropic)

使用 AI 的部分與幫助:

  1. 選題與計劃書:和 Claude 討論可用的公開資料集,比較後選定首爾共享單車資料;協助整理研究問題、假設與統計方法的對應表。
  2. R 語法與資料轉換:協助處理原始 CSV 欄位名稱的編碼問題(Latin-1 度數符號)、用 dplyr 刪除未營運時段並建立「是否下雨」變數,以及撰寫 Games-Howell 事後比較的函數。
  3. 方法理解:討論 Levene 檢定顯著時應改用 Welch t 檢定與 Welch ANOVA、如何計算標準化迴歸係數比較影響大小,以及租借量右偏時用對數轉換做穩健性檢查。
  4. 結果解釋:協助撰寫結果段落初稿,並討論「降雨量 β 很小,但是否下雨的效果很大」的原因。

所有分析結果都由小組成員實際執行 R 程式碼確認,報告內容也經過小組討論與修改。

AI 對話連結:______________(或附上截圖)