研究方式、研究成果、資料來源我都有在書面詳細交代,這邊基本上所有的圖表和地圖以及統計方式都有在書面中使用,所以也都有解釋到。

#rm(list = ls())
#Sys.getlocale()

library(sf)       # 讀取 shapefile
## Linking to GEOS 3.13.1, GDAL 3.10.2, PROJ 9.5.1; sf_use_s2() is TRUE
library(readr)    # 讀取 csv

# 讀取 shp 檔案
school <- st_read("D:/200學業/270地理系/空間分析/期末考試/SCHOOL/SCHOOL.shp")
## Reading layer `SCHOOL' from data source 
##   `D:\200學業\270地理系\空間分析\期末考試\SCHOOL\SCHOOL.shp' 
##   using driver `ESRI Shapefile'
## Simple feature collection with 148 features and 4 fields
## Geometry type: POINT
## Dimension:     XY
## Bounding box:  xmin: 297078.6 ymin: 2763290 xmax: 312516.7 ymax: 2784542
## Projected CRS: TWD97 / TM2 zone 121
taipei_village <- st_read("D:/200學業/270地理系/空間分析/期末考試/Taipei_village (2)/Taipei_village.shp")
## Reading layer `Taipei_village' from data source 
##   `D:\200學業\270地理系\空間分析\期末考試\Taipei_village (2)\Taipei_village.shp' 
##   using driver `ESRI Shapefile'
## Simple feature collection with 456 features and 32 fields
## Geometry type: POLYGON
## Dimension:     XY
## Bounding box:  xmin: 296103.7 ymin: 2761535 xmax: 317204.1 ymax: 2789175
## Projected CRS: TWD97 / TM2 zone 121
# 讀取 csv 檔案
sports_venues <- read_csv("D:/200學業/270地理系/空間分析/期末考試/體育局列管本市各類運動場館一覽表.csv")
## Rows: 270 Columns: 10
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (6): 公民營, 廠商名稱_市招, 行政區, 經營主體, 聯絡電話, 地址
## dbl (4): 編號, 分機, 經度, 緯度
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
bubble_tea <- read_csv("D:/200學業/270地理系/空間分析/期末考試/手搖杯點位資料(可用).csv")
## Rows: 628 Columns: 5
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (4): Address, Response_Address, Response_X, Response_Y
## dbl (1): id
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
fast_food <- read_csv("D:/200學業/270地理系/空間分析/期末考試/大速食店點位資料(可用).csv")
## Rows: 316 Columns: 5
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (4): Address, Response_Address, Response_X, Response_Y
## dbl (1): id
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
#str(bubble_tea)
#str(fast_food)
# 這段是 R 的程式碼,假設你已經讀取了 bubble_tea
# 生成店家名稱的列表
store_names <- c(
  rep("龜記", 25),
  rep("萬波島嶼", 12),
  rep("可不可", 38),
  rep("清園芋圓", 19),
  rep("五銅號", 24),
  rep("珍煮丹", 17),
  rep("老賴", 17),
  rep("迷克夏", 31),
  rep("砌茶趣", 67),
  rep("comebuy", 57),
  rep("台灣第一味", 11),
  rep("大苑子", 20),
  rep("水巷茶弄", 13),
  rep("日出良太", 19),
  rep("coco", 57),
  rep("清心", 109),
  rep("50嵐", 92)
)

# 確認長度是否為 628
length(store_names)  # 應該是 628
## [1] 628
# 將這個列表加到 bubble_tea 中成為新欄位 "store"
bubble_tea$store <- store_names

#速食店

# 這段是 R 的程式碼,假設你已經讀取了 fast_food
# 生成速食店名稱的列表
store_names_fastfood <- c(
  rep("麥當勞", 63),
  rep("肯德基", 31),
  rep("漢堡王", 24),
  rep("21世紀風味館", 34),
  rep("頂呱呱", 25),
  rep("達美樂", 24),
  rep("必勝客", 40),
  rep("胖老爹", 38),
  rep("炸機大獅", 18),
  rep("派克雞排", 14),
  rep("3Q雞排", 5)
)

# 確認長度是否為 316
length(store_names_fastfood)  # 應該是 316
## [1] 316
# 將這個列表加到 fast_food 中成為新欄位 "store"
fast_food$store <- store_names_fastfood



# 在刪除前,先記錄被刪掉的資料
bubble_tea_removed <- bubble_tea[is.na(bubble_tea$Response_X) | is.na(bubble_tea$Response_Y), ]
fast_food_removed <- fast_food[is.na(fast_food$Response_X) | is.na(fast_food$Response_Y), ]

# 總筆數
cat("bubble_tea 總共被刪掉的筆數:", nrow(bubble_tea_removed), "\n")
## bubble_tea 總共被刪掉的筆數: 19
cat("以下是被刪掉的店家筆數統計:\n")
## 以下是被刪掉的店家筆數統計:
print(table(bubble_tea_removed$store))
## 
##     50嵐     coco  comebuy 日出良太     老賴   砌茶趣     清心 清園芋圓 
##        3        1        2        4        1        6        1        1
cat("\nfast_food 總共被刪掉的筆數:", nrow(fast_food_removed), "\n")
## 
## fast_food 總共被刪掉的筆數: 6
cat("以下是被刪掉的店家筆數統計:\n")
## 以下是被刪掉的店家筆數統計:
print(table(fast_food_removed$store))
## 
##   必勝客 炸機大獅   胖老爹 
##        2        3        1
# 真正執行刪除
bubble_tea <- bubble_tea[!(is.na(bubble_tea$Response_X) | is.na(bubble_tea$Response_Y)), ]
fast_food <- fast_food[!(is.na(fast_food$Response_X) | is.na(fast_food$Response_Y)), ]

# 刪除後的剩餘資料量
cat("\n刪除後 bubble_tea 剩餘筆數:", nrow(bubble_tea), "\n")
## 
## 刪除後 bubble_tea 剩餘筆數: 609
cat("刪除後 fast_food 剩餘筆數:", nrow(fast_food), "\n")
## 刪除後 fast_food 剩餘筆數: 310
library(sf)
library(dplyr)
## 
## 載入套件:'dplyr'
## 下列物件被遮斷自 'package:stats':
## 
##     filter, lag
## 下列物件被遮斷自 'package:base':
## 
##     intersect, setdiff, setequal, union
library(ggplot2)

# 速食店與手搖飲資料:假設座標欄位為 Response_X / Response_Y (且已轉為數值型)

# 將原始 Response_X / Response_Y 轉成數字
bubble_tea$Response_X <- as.numeric(bubble_tea$Response_X)
## Warning: 強制變更過程中產生了 NA
bubble_tea$Response_Y <- as.numeric(bubble_tea$Response_Y)
## Warning: 強制變更過程中產生了 NA
# 找到 X 或 Y 是 NA 的行
na_rows <- which(is.na(bubble_tea$Response_X) | is.na(bubble_tea$Response_Y))

# 總筆數
cat("總共被刪掉的筆數:", length(na_rows), "\n")
## 總共被刪掉的筆數: 10
# 列出被刪掉的店家名稱與筆數
cat("以下是被刪掉的店家筆數統計:\n")
## 以下是被刪掉的店家筆數統計:
print(table(bubble_tea$store[na_rows]))
## 
##     50嵐     coco   大苑子 日出良太   砌茶趣     清心 
##        3        1        1        2        1        2
fast_food$Response_X <- as.numeric(fast_food$Response_X)
## Warning: 強制變更過程中產生了 NA
fast_food$Response_Y <- as.numeric(fast_food$Response_Y)
## Warning: 強制變更過程中產生了 NA
na_rows_fastfood <- which(is.na(fast_food$Response_X) | is.na(fast_food$Response_Y))
cat("\n【fast_food】\n")
## 
## 【fast_food】
cat("總共被刪掉的筆數:", length(na_rows_fastfood), "\n")
## 總共被刪掉的筆數: 9
cat("以下是被刪掉的店家筆數統計:\n")
## 以下是被刪掉的店家筆數統計:
print(table(fast_food$store[na_rows_fastfood]))
## 
## 21世紀風味館       肯德基     派克雞排       胖老爹       頂呱呱       麥當勞 
##            1            1            1            1            1            2 
##       達美樂       漢堡王 
##            1            1
# --- 刪掉沒有座標的資料 ---
bubble_tea <- bubble_tea[!(is.na(bubble_tea$Response_X) | is.na(bubble_tea$Response_Y)), ]
fast_food <- fast_food[!(is.na(fast_food$Response_X) | is.na(fast_food$Response_Y)), ]

# 顯示刪除後剩下的資料筆數
cat("\n刪除後 bubble_tea 剩餘筆數:", nrow(bubble_tea), "\n")
## 
## 刪除後 bubble_tea 剩餘筆數: 599
cat("刪除後 fast_food 剩餘筆數:", nrow(fast_food), "\n")
## 刪除後 fast_food 剩餘筆數: 301
# 轉換 bubble_tea 與 fast_food
bubble_tea_sf <- st_as_sf(bubble_tea, coords = c("Response_X", "Response_Y"), crs = 3826)
fast_food_sf <- st_as_sf(fast_food, coords = c("Response_X", "Response_Y"), crs = 3826)

# 處理運動場館:經度與緯度欄位名稱是中文
# 確保運動場館資料是資料框

colnames(sports_venues)[colnames(sports_venues) == "經度"] <- "Longitude"
colnames(sports_venues)[colnames(sports_venues) == "緯度"] <- "Latitude"
sports_venues <- as.data.frame(sports_venues)
sports_venues$Longitude <- as.numeric(sports_venues$Longitude)
sports_venues$Latitude <- as.numeric(sports_venues$Latitude)
#str(sports_venues)
# 轉換 sports_venues
sports_venues_sf <- st_as_sf(sports_venues, coords = c("Longitude", "Latitude"), crs = 4326)


#st_crs(taipei_village)      # 台北市底圖
#st_crs(sports_venues_sf)    # 運動場館
#st_crs(fast_food_sf)        # 速食店
#st_crs(bubble_tea_sf)       # 飲料店
初步計畫跟圖片蓋覽
#發現有速食店廠商資料灌水
# 找出含有「新竹」或「新北」的行
rows_to_remove <- grepl("新竹", fast_food_sf$Address) | grepl("新北", fast_food_sf$Address)


# 被刪掉的資料
removed_data <- fast_food_sf[rows_to_remove, ]

# 統計被刪掉的品牌筆數
cat("以下是被刪掉的品牌筆數統計:\n")
## 以下是被刪掉的品牌筆數統計:
print(table(removed_data$store))
## 
## 21世紀風味館 
##           10
# 刪掉後的資料(保持變數名稱不變)
fast_food_sf <- fast_food_sf[!rows_to_remove, ]

library(tmap)
   # 使用互動模式



# 把運動場館座標轉成台北市底圖的 CRS (TWD97)
sports_venues_sf <- st_transform(sports_venues_sf, st_crs(taipei_village))

tm_shape(taipei_village) +
  tm_polygons(col = "grey90", border.col = "white") +
  tm_shape(sports_venues_sf) +
  tm_dots(col = "公民營", palette = "Set1", size = 0.3, title = "公/民營") +
  tm_layout(title = "台北市運動場館分布", legend.outside = TRUE)
## 
## ── tmap v3 code detected ───────────────────────────────────────────────────────
## [v3->v4] `tm_polygons()`: use 'fill' for the fill color of polygons/symbols
## (instead of 'col'), and 'col' for the outlines (instead of 'border.col').
## [tm_dots()] Argument `title` unknown.
## [v3->v4] `tm_layout()`: use `tm_title()` instead of `tm_layout(title = )`
## [cols4all] color palettes: use palettes from the R package cols4all. Run
## `cols4all::c4a_gui()` to explore them. The old palette name "Set1" is named
## "brewer.set1"
## This message is displayed once every 8 hours.

library(tmap)
library(dplyr)

# 🔴 過濾掉 3q雞排 和 21世紀風味館
fast_food_filtered <- fast_food_sf %>%
  filter(!store %in% c("3Q雞排", "21世紀風味館"))

# 🔵 切換靜態模式
tmap_mode("plot")
## ℹ tmap mode set to "plot".
# --- 2️⃣ 台北市速食店分布地圖 ---
tm_shape(taipei_village) +
  tm_polygons(col = "grey90", border.col = "white") +
  tm_borders(col = "white", lwd = 0.5) +  # 加上行政邊界
  tm_shape(fast_food_filtered) +
  tm_dots(col = "store",
          palette = "Set3",
          size = 0.3,
          title = "速食品牌",
          alpha = 0.8) +
  tm_layout(
    title = "台北市速食店分布",
    title.size = 1.0,
    legend.outside = TRUE,
    bg.color = "#F0F0F0",
    panel.background = "#F0F0F0",
    outer.bg.color = "white",   # 地圖外部留白
    legend.text.size = 0.6,
    legend.title.size = 0.8,
    frame = TRUE
  )
## 
## ── tmap v3 code detected ───────────────────────────────────────────────────────
## [v3->v4] `tm_dots()`: use `fill_alpha` instead of `alpha`.[tm_dots()] Argument `title` unknown.[v3->v4] `tm_layout()`: use `tm_title()` instead of `tm_layout(title = )`[cols4all] color palettes: use palettes from the R package cols4all. Run
## `cols4all::c4a_gui()` to explore them. The old palette name "Set3" is named
## "brewer.set3"Multiple palettes called "set3" found: "brewer.set3", "hcl.set3". The first one, "brewer.set3", is returned.

# --- 3️⃣ 手搖飲料店圖 ---
tm_shape(taipei_village) +
  tm_polygons(col = "grey90", border.col = "white") +
  tm_borders(col = "white", lwd = 0.5) +                # 🔴 加上邊界
tm_shape(bubble_tea_sf) +
  tm_dots(col = "store", palette = "Set3", size = 0.3, title = "手搖飲品牌", alpha = 0.8) +
  tm_layout(
    title = "台北市手搖飲店分布",
    title.size = 1.0,
    legend.outside = TRUE,
    bg.color = "#F0F0F0",    # 🔴 統一背景顏色
    panel.background = "#F0F0F0",
    outer.bg.color = "white",   # 地圖外部留白
    legend.text.size = 0.6,
    legend.title.size = 0.8,
    frame = TRUE
  )
## 
## ── tmap v3 code detected ───────────────────────────────────────────────────────
## [v3->v4] `tm_dots()`: use `fill_alpha` instead of `alpha`.[tm_dots()] Argument `title` unknown.[v3->v4] `tm_layout()`: use `tm_title()` instead of `tm_layout(title = )`[cols4all] color palettes: use palettes from the R package cols4all. Run
## `cols4all::c4a_gui()` to explore them. The old palette name "Set3" is named
## "brewer.set3"Multiple palettes called "set3" found: "brewer.set3", "hcl.set3". The first one, "brewer.set3", is returned.

開始分析 BUFFER
# --- 1️⃣ 建立 500m buffer ---
school_buffer <- st_buffer(school, dist = 500)


# 先篩選出公立和私立運動場館
public_sports <- sports_venues_sf %>%
  filter(公民營 == "公營") %>%
  st_transform(crs = st_crs(school))  # 轉換成跟 school 一樣的投影座標系統

private_sports <- sports_venues_sf %>%
  filter(公民營 == "民營") %>%
  st_transform(crs = st_crs(school))
# --- 2️⃣ 計算各 buffer 內的點位數 ---
# ⚠️ 改用 st_contains(),只算 buffer 內包含的點(不算邊界模糊重疊)
#str(sports_venues_sf)
bubbletea_counts <- sapply(st_contains(school_buffer, bubble_tea_sf), length)
fastfood_counts <- sapply(st_contains(school_buffer, fast_food_sf), length)
public_counts <- sapply(st_contains(school_buffer, public_sports), length)
private_counts <- sapply(st_contains(school_buffer, private_sports), length)

# --- 3️⃣ 組成 final_counts,補 0(長度相同,對應每個學校)
final_counts <- school %>%
  mutate(
    bubbletea_count = bubbletea_counts,
    fastfood_count = fastfood_counts,
    public_sports_count = public_counts,
    private_sports_count = private_counts,
    unhealthy_index = ifelse(public_sports_count == 0, NA,
                             (bubbletea_count + fastfood_count) / public_sports_count),
    unhealthy_index_combined = ifelse((public_sports_count + private_sports_count) == 0, NA,
                                       (bubbletea_count + fastfood_count) / (public_sports_count + private_sports_count))
  )

# --- 查看結果 ---
print(final_counts)
## Simple feature collection with 148 features and 10 fields
## Geometry type: POINT
## Dimension:     XY
## Bounding box:  xmin: 297078.6 ymin: 2763290 xmax: 312516.7 ymax: 2784542
## Projected CRS: TWD97 / TM2 zone 121
## First 10 features:
##    Type     Name SID    index                 geometry bubbletea_count
## 1  私立 松山國小   1 0.407779 POINT (308286.4 2771496)               5
## 2  公立 西松國小   2 0.500235 POINT (306854.9 2771689)               9
## 3  公立 敦化國小   3 0.440644 POINT (305347.2 2771329)               4
## 4  公立 民生國小   4 0.422485   POINT (305712 2772151)               3
## 5  私立 民權國小   5 0.414474 POINT (306729.6 2772821)               2
## 6  公立 民族國小   6 0.435226 POINT (305664.1 2772492)               2
## 7  私立 三民國小   7 0.405999 POINT (306930.4 2772894)               0
## 8  公立 健康國小   8 0.421402   POINT (306782 2772172)               6
## 9  公立 興雅國小   9 0.420354 POINT (307516.8 2771077)               6
## 10 公立 永春國小  10 0.435220 POINT (308431.8 2770848)              10
##    fastfood_count public_sports_count private_sports_count unhealthy_index
## 1               2                   2                    3             3.5
## 2               6                   0                    4              NA
## 3               2                   0                   11              NA
## 4               2                   0                    3              NA
## 5               2                   0                    2              NA
## 6               1                   0                    3              NA
## 7               1                   0                    0              NA
## 8               4                   0                    3              NA
## 9               3                   0                    2              NA
## 10              5                   0                    5              NA
##    unhealthy_index_combined
## 1                 1.4000000
## 2                 3.7500000
## 3                 0.5454545
## 4                 1.6666667
## 5                 2.0000000
## 6                 1.0000000
## 7                        NA
## 8                 3.3333333
## 9                 4.5000000
## 10                3.0000000
library(spatstat)
## 載入需要的套件:spatstat.data
## 載入需要的套件:spatstat.univar
## spatstat.univar 3.1-3
## 載入需要的套件:spatstat.geom
## spatstat.geom 3.4-1
## 載入需要的套件:spatstat.random
## spatstat.random 3.3-3
## 載入需要的套件:spatstat.explore
## 載入需要的套件:nlme
## 
## 載入套件:'nlme'
## 下列物件被遮斷自 'package:dplyr':
## 
##     collapse
## spatstat.explore 3.4-2
## 載入需要的套件:spatstat.model
## 載入需要的套件:rpart
## spatstat.model 3.3-5
## 載入需要的套件:spatstat.linnet
## spatstat.linnet 3.2-5
## 
## spatstat 3.3-2 
## For an introduction to spatstat, type 'beginner'
library(sf)

#str(sports_venues)
test_NNA <- function(sf_points, layer_name) {
  if (nrow(sf_points) == 0) {
    cat("\n---", layer_name, "---\n")
    cat("⚠️ 這個圖層沒有點,無法進行 NNA 檢定。\n")
    return(NULL)
  }
  
  # 轉成點座標
  coords <- st_coordinates(sf_points)
  
  # 取得邊界
  bbox <- st_bbox(sf_points)
  
  # 檢查 bbox 是否有 NA
  if (any(is.na(bbox))) {
    cat("\n---", layer_name, "---\n")
    cat("⚠️ 這個圖層的邊界有 NA,無法建立窗口。\n")
    return(NULL)
  }
  
  # 建立 owin 窗口
  win <- owin(xrange = as.numeric(c(bbox["xmin"], bbox["xmax"])),
               yrange = as.numeric(c(bbox["ymin"], bbox["ymax"])))
  
  # 建立 ppp 物件
  ppp_obj <- ppp(x = coords[, 1],
                  y = coords[, 2],
                  window = win)
  
  # 計算平均最近鄰距離
  mean_NND <- mean(nndist(ppp_obj))
  
  # 理論隨機平均最近鄰距離 (CSR)
  lambda <- intensity(ppp_obj)  # 點密度
  expected_NND <- 0.5 / sqrt(lambda)
  
  # Z 分數(統計檢定)
  se_NND <- 0.26136 / sqrt(lambda * npoints(ppp_obj))
  z_score <- (mean_NND - expected_NND) / se_NND
  
  cat("\n---", layer_name, "---\n")
  cat("平均最近鄰距離:", mean_NND, "\n")
  cat("理論隨機平均最近鄰距離:", expected_NND, "\n")
  cat("Z分數:", z_score, "\n")
  
  if (z_score < -1.96) {
    cat("結果: 顯著聚集\n")
  } else if (z_score > 1.96) {
    cat("結果: 顯著均勻分布\n")
  } else {
    cat("結果: 沒有顯著差異 (隨機分布)\n")
  }
}

# ⚠️ 最後,請一定檢查 4 個 sf 點圖層是否存在:
print(nrow(public_sports))
## [1] 64
print(nrow(private_sports))
## [1] 206
print(nrow(bubble_tea_sf))
## [1] 599
print(nrow(fast_food_sf))
## [1] 291
# 如果都不是 0,直接執行
test_NNA(public_sports, "公立運動場館")
## Warning: data contain duplicated points
## 
## --- 公立運動場館 ---
## 平均最近鄰距離: 897.3532 
## 理論隨機平均最近鄰距離: 1065.036 
## Z分數: -2.409605 
## 結果: 顯著聚集
test_NNA(private_sports, "私立運動場館")
## Warning: data contain duplicated points
## 
## --- 私立運動場館 ---
## 平均最近鄰距離: 257.1123 
## 理論隨機平均最近鄰距離: 491.4866 
## Z分數: -13.09371 
## 結果: 顯著聚集
test_NNA(bubble_tea_sf, "手搖飲料店")
## Warning: data contain duplicated points
## 
## --- 手搖飲料店 ---
## 平均最近鄰距離: 67.85662 
## 理論隨機平均最近鄰距離: 333.9653 
## Z分數: -37.308 
## 結果: 顯著聚集
test_NNA(fast_food_sf, "速食店")
## Warning: data contain duplicated points
## 
## --- 速食店 ---
## 平均最近鄰距離: 575.2384 
## 理論隨機平均最近鄰距離: 5898.669 
## Z分數: -29.45201 
## 結果: 顯著聚集
###都是顯著聚集,但公家體育館分散一些
畫檢定圖
# 假設 public_sports 是 sf 格式
# --- 1️⃣ 轉成 spatstat 的 ppp 物件 ---
coords <- st_coordinates(public_sports)
win <- owin(xrange = range(coords[,1]), yrange = range(coords[,2]))
ppp_obj <- ppp(x = coords[,1], y = coords[,2], window = win)
## Warning: data contain duplicated points
# --- 2️⃣ 真實資料最近鄰距離 ---
real_nnd <- nndist(ppp_obj)

# --- 3️⃣ 產生隨機 CSR 模擬(強度與窗口一致) ---
csr_ppp <- rpoispp(lambda = intensity(ppp_obj), win = win)
csr_nnd <- nndist(csr_ppp)

# --- 4️⃣ 合併成長格式資料框 ---
ecdf_df <- data.frame(
  distance = c(real_nnd, csr_nnd),
  type = rep(c("真實", "CSR (隨機)"), times = c(length(real_nnd), length(csr_nnd)))
)

# --- 5️⃣ ECDF 疊加圖 ---
ggplot(ecdf_df, aes(x = distance, color = type)) +
  stat_ecdf(geom = "step", size = 1) +
  scale_color_manual(values = c("真實" = "#6FB7B7", "CSR (隨機)" = "#9999CC")) +
  labs(
    title = "公立場館真實vs.隨機CSR累積分布",
    x = "距離 (m)",
    y = "累積機率",
    color = "類別"
  ) +
  theme_minimal(base_family = "STHeiti") +
  theme(
    plot.background = element_rect(fill = "#F0F0F0", color = NA),
    panel.background = element_rect(fill = "#F0F0F0", color = NA),
    text = element_text(color = "#3C3C3C"),
    plot.title = element_text(size = 14, hjust = 0.5, face = "bold"),
    axis.title = element_text(size = 12),
    axis.text = element_text(size = 10),
    legend.position = "bottom",
    legend.text = element_text(size = 10),
    #legend.title = element_blank()  # 不顯示圖例標題
  )
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
## Warning in grid.Call(C_stringMetric, as.graphicsAnnot(x$label)): Windows
## 字型資料庫裡不明的字型系列
## Warning in grid.Call(C_stringMetric, as.graphicsAnnot(x$label)): Windows
## 字型資料庫裡不明的字型系列
## Warning in grid.Call(C_stringMetric, as.graphicsAnnot(x$label)): Windows
## 字型資料庫裡不明的字型系列
## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call(C_stringMetric, as.graphicsAnnot(x$label)): Windows
## 字型資料庫裡不明的字型系列
## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列

剩下三張用function

library(sf)
library(spatstat)
library(ggplot2)
library(dplyr)

# 🔧 定義繪製 ECDF 疊加圖的函數
plot_nna_ecdf <- function(sf_points, layer_name) {
  # 轉成 ppp
  coords <- st_coordinates(sf_points)
  win <- owin(xrange = range(coords[,1]), yrange = range(coords[,2]))
  ppp_obj <- ppp(x = coords[,1], y = coords[,2], window = win)
  
  # 真實資料最近鄰距離
  real_nnd <- nndist(ppp_obj)
  
  # CSR 隨機模擬
  csr_ppp <- rpoispp(lambda = intensity(ppp_obj), win = win)
  csr_nnd <- nndist(csr_ppp)
  
  # 合併資料框
  ecdf_df <- data.frame(
    distance = c(real_nnd, csr_nnd),
    type = rep(c("真實", "CSR (隨機)"), times = c(length(real_nnd), length(csr_nnd)))
  )
  
  # 繪圖
  p <- ggplot(ecdf_df, aes(x = distance, color = type)) +
    stat_ecdf(geom = "step", size = 1) +
    scale_color_manual(values = c("真實" = "#6FB7B7", "CSR (隨機)" = "#9999CC")) +
    labs(
      title = paste0(layer_name, "真實vs.隨機CSR累積分布"),
      x = "距離 (m)",
      y = "累積機率",
      color = NULL
    ) +
    theme_minimal(base_family = "STHeiti") +
    theme(
      plot.background = element_rect(fill = "#F0F0F0", color = NA),
      panel.background = element_rect(fill = "#F0F0F0", color = NA),
      text = element_text(color = "#3C3C3C"),
      plot.title = element_text(size = 14, hjust = 0.5, face = "bold"),
      axis.title = element_text(size = 12),
      axis.text = element_text(size = 10),
      legend.position = "bottom",
      legend.text = element_text(size = 10)
    )
  
  return(p)
}

# ⚡ 分別執行並顯示三張圖
p_private <- plot_nna_ecdf(private_sports, "私立運動場館")
## Warning: data contain duplicated points
p_bubbletea <- plot_nna_ecdf(bubble_tea_sf, "手搖飲料店")
## Warning: data contain duplicated points
p_fastfood <- plot_nna_ecdf(fast_food_sf, "速食店")
## Warning: data contain duplicated points
# 顯示圖
p_private
## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列

p_bubbletea
## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列

p_fastfood
## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列

# 建立多元線性回歸模型 #跟公立運動場不顯著 #速食店邊緣顯著但也很低

model_multi <- lm(private_sports_count ~ fastfood_count + bubbletea_count, data = final_counts)

# 查看模型摘要
summary(model_multi)
## 
## Call:
## lm(formula = private_sports_count ~ fastfood_count + bubbletea_count, 
##     data = final_counts)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -3.1095 -0.8214 -0.4994  0.4435  9.5175 
## 
## Coefficients:
##                 Estimate Std. Error t value Pr(>|t|)    
## (Intercept)      0.49939    0.20190   2.473 0.014538 *  
## fastfood_count   0.16964    0.09036   1.877 0.062469 .  
## bubbletea_count  0.16096    0.04589   3.507 0.000603 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 1.603 on 145 degrees of freedom
## Multiple R-squared:  0.2436, Adjusted R-squared:  0.2331 
## F-statistic: 23.35 on 2 and 145 DF,  p-value: 1.623e-09
# 產生 HTML 格式表格(在 RStudio Viewer / 瀏覽器看)

library(stargazer)
## 
## Please cite as:
##  Hlavac, Marek (2022). stargazer: Well-Formatted Regression and Summary Statistics Tables.
##  R package version 5.2.3. https://CRAN.R-project.org/package=stargazer
# 我們可以把它存在變數內(例如 output_html),再貼到 Word/PPT
# 用 stargazer 產生 HTML 格式
stargazer(model_multi,
           type = "html",
           title = "私立運動場館數量回歸結果",
           dep.var.labels = "私立運動場館數量",
           covariate.labels = c("速食店數量", "手搖飲料店數量"),
           single.row = FALSE,
           digits = 3,
           out = "private_sports_model.html")  # 直接輸出成 HTML 檔
## 
## <table style="text-align:center"><caption><strong>私立運動場館數量回歸結果</strong></caption>
## <tr><td colspan="2" style="border-bottom: 1px solid black"></td></tr><tr><td style="text-align:left"></td><td><em>Dependent variable:</em></td></tr>
## <tr><td></td><td colspan="1" style="border-bottom: 1px solid black"></td></tr>
## <tr><td style="text-align:left"></td><td>私立運動場館數量</td></tr>
## <tr><td colspan="2" style="border-bottom: 1px solid black"></td></tr><tr><td style="text-align:left">速食店數量</td><td>0.170<sup>*</sup></td></tr>
## <tr><td style="text-align:left"></td><td>(0.090)</td></tr>
## <tr><td style="text-align:left"></td><td></td></tr>
## <tr><td style="text-align:left">手搖飲料店數量</td><td>0.161<sup>***</sup></td></tr>
## <tr><td style="text-align:left"></td><td>(0.046)</td></tr>
## <tr><td style="text-align:left"></td><td></td></tr>
## <tr><td style="text-align:left">Constant</td><td>0.499<sup>**</sup></td></tr>
## <tr><td style="text-align:left"></td><td>(0.202)</td></tr>
## <tr><td style="text-align:left"></td><td></td></tr>
## <tr><td colspan="2" style="border-bottom: 1px solid black"></td></tr><tr><td style="text-align:left">Observations</td><td>148</td></tr>
## <tr><td style="text-align:left">R<sup>2</sup></td><td>0.244</td></tr>
## <tr><td style="text-align:left">Adjusted R<sup>2</sup></td><td>0.233</td></tr>
## <tr><td style="text-align:left">Residual Std. Error</td><td>1.603 (df = 145)</td></tr>
## <tr><td style="text-align:left">F Statistic</td><td>23.346<sup>***</sup> (df = 2; 145)</td></tr>
## <tr><td colspan="2" style="border-bottom: 1px solid black"></td></tr><tr><td style="text-align:left"><em>Note:</em></td><td style="text-align:right"><sup>*</sup>p<0.1; <sup>**</sup>p<0.05; <sup>***</sup>p<0.01</td></tr>
## </table>
#可能會分析關係,所以先把st_join做出來

#str(taipei_village)

# 計算有無公立運動場館
final_counts <- final_counts %>%
  mutate(has_public_sports = ifelse(public_sports_count == 0, "無公立運動場館", "有公立運動場館"))

# 先把標籤整理成「數字(百分比%)」格式
pie_data <- final_counts %>%
  mutate(has_public_sports = ifelse(public_sports_count == 0, "無公立運動場館", "有公立運動場館")) %>%
  count(has_public_sports) %>%
  mutate(
    prop = n / sum(n) * 100,
    label = paste0(n, " (", round(prop, 1), "%)")
  )

# 畫圓餅圖
p <- ggplot(pie_data, aes(x = "", y = n, fill = has_public_sports)) +  # 🔴 y 改成 n
  geom_col(width = 1, color = "white") +
  coord_polar(theta = "y") +
  geom_text(
    aes(label = label),
    position = position_stack(vjust = 0.5),
    color = "#3C3C3C",
    size = 6
  ) +
  scale_fill_manual(values = c("#6FB7B7", "#9999CC"), name = NULL) +
  labs(title = "國小周邊 500m 內公立運動場館分布比例") +  # 移除 fill 的圖例標題
  theme_void(base_family = "STHeiti") +
  theme(
    legend.position = "bottom",
    plot.background = element_rect(fill = "#F0F0F0", color = NA),
    panel.background = element_rect(fill = "#F0F0F0", color = NA),
    legend.background = element_rect(fill = "#F0F0F0", color = NA),
    legend.box.background = element_rect(fill = "#F0F0F0", color = NA),
    text = element_text(color = "#3C3C3C"),
    legend.text = element_text(size = 16),
    legend.title = element_blank(),
    plot.title = element_text(hjust = 0.5, size = 14)
  )


print(p)
## Warning in grid.Call(C_stringMetric, as.graphicsAnnot(x$label)): Windows
## 字型資料庫裡不明的字型系列
## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列

ggsave("myplot.png", p, width = 8, height = 6, dpi = 300, bg = "#F0F0F0")

### 各個區也看一下

library(ggplot2)
# 確認 final_counts 中的欄位是數值型態
final_counts <- final_counts %>%
  mutate(has_public_sports = as.numeric(public_sports_count > 0))

# 空間交集:把學校 final_counts 接上行政區名稱
final_counts <- st_join(final_counts, taipei_village["TOWNNAME"], join = st_within)



#str(final_counts)
#str(taipei_village)
# 2️⃣ 按行政區分組,計算各區「有公立運動場館的學校數量」以及「總學校數量」
public_sports_by_area <- final_counts %>%
  group_by(TOWNNAME) %>%
  summarise(
    total_schools = n(),
    schools_with_public_sports = sum(has_public_sports),
    proportion_with_public_sports = schools_with_public_sports / total_schools * 100
  ) %>%
  arrange(desc(proportion_with_public_sports))  # 依照比例排序

# 3️⃣ 畫長條圖
ggplot(public_sports_by_area, aes(x = reorder(TOWNNAME, -proportion_with_public_sports), y = proportion_with_public_sports)) +
  geom_col(fill = "#6FB7B7") +
  geom_text(aes(label = paste0(round(proportion_with_public_sports, 1), "%")),
            vjust = -0.5, size = 3, color = "#3C3C3C") +  # 文字顏色
  labs(
    title = "各行政區內國小500m內公立運動場館涵蓋比例",
    x = NULL,    # 🔴 直接隱藏 X 軸標題
    y = NULL     # 🔴 直接隱藏 Y 軸標題
  ) +
  scale_y_continuous(expand = expansion(mult = c(0, 0.1))) +  # 🔴 上方多留 10% 空間
  theme_minimal(base_family = "STHeiti") +
  theme(
    plot.background = element_rect(fill = "#F0F0F0", color = NA),
    panel.background = element_rect(fill = "#F0F0F0", color = NA),
    text = element_text(color = "#3C3C3C"),
    plot.title = element_text(size = 14, hjust = 0.5, face = "bold"),
    #axis.title = element_text(size = 8),
    axis.text.x = element_text(size = 7, angle = 45, hjust = 1),  # 調小 X 軸文字
    axis.text.y = element_text(size = 8) 
    # 調小 Y 軸文字
  )
## Warning in grid.Call(C_stringMetric, as.graphicsAnnot(x$label)): Windows
## 字型資料庫裡不明的字型系列
## Warning in grid.Call(C_stringMetric, as.graphicsAnnot(x$label)): Windows
## 字型資料庫裡不明的字型系列
## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列

library(dplyr)

library(tidyr)
# 計算每個行政區的總學校數 & 有公立運動場館的學校數
counts_by_area <- final_counts %>%
  group_by(TOWNNAME) %>%
  summarise(
    total_schools = n(),
    schools_with_public_sports = sum(has_public_sports, na.rm = TRUE))
    
# 資料轉長格式,方便 ggplot 畫分堆長條圖
counts_long <- counts_by_area %>%
  pivot_longer(cols = c("total_schools", "schools_with_public_sports"),
               names_to = "category",
               values_to = "count")

# 畫分堆長條圖
ggplot(counts_long, aes(x = TOWNNAME, y = count, fill = category)) +
  geom_col(position = "dodge") +
  labs(
    title = "各行政區國小總數vs.有公立運動場館的國小總數",
    x = NULL,  # 🔴 隱藏 X 軸標題
    y = NULL,  # 🔴 隱藏 Y 軸標題
    fill = "類別"
  ) +
  scale_fill_manual(values = c("#6FB7B7", "#9999CC"),
                    labels = c("有公立運動場館的學校數", "總學校數")) +
  theme_minimal(base_family = "STHeiti") +
  theme(
    plot.background = element_rect(fill = "#F0F0F0", color = NA),
    panel.background = element_rect(fill = "#F0F0F0", color = NA),
    text = element_text(color = "#3C3C3C"),
    plot.title = element_text(size = 14, hjust = 0.5, face = "bold"),
    axis.text.x = element_text(size = 7, angle = 45, hjust = 1),
    axis.text.y = element_text(size = 8),
    legend.position = "bottom",
    legend.text = element_text(size = 12),
    legend.title = element_blank()  # 🔴 隱藏圖例標題
  )
## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列
## Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
## Windows 字型資料庫裡不明的字型系列

library(dplyr)
library(ggplot2)
library(tidyr)
library(patchwork)    # 用來一次合併多個圖表
## 
## 載入套件:'patchwork'
## 下列物件被遮斷自 'package:spatstat.geom':
## 
##     area
# 設定不顯示警告
options(warn = -1)


# 計算每個學校的型態
final_counts <- final_counts %>%
  mutate(
    has_public = public_sports_count > 0,
    has_private = private_sports_count > 0,
    has_any = has_public | has_private,
    type = case_when(
      has_public & !has_private ~ "僅公立",
      !has_public & has_private ~ "僅私立",
      TRUE ~ "無運動場館"
    )
  )

# 產生四種統計資料
total_counts <- final_counts %>%
  group_by(TOWNNAME) %>%
  summarise(count = n(), .groups = "drop") %>%
  mutate(type = "總學校數")

any_counts <- final_counts %>%
  filter(has_any) %>%
  group_by(TOWNNAME) %>%
  summarise(count = n(), .groups = "drop") %>%
  mutate(type = "至少有一家")

only_public <- final_counts %>%
  filter(type == "僅公立") %>%
  group_by(TOWNNAME) %>%
  summarise(count = n(), .groups = "drop") %>%
  mutate(type = "僅公立")

only_private <- final_counts %>%
  filter(type == "僅私立") %>%
  group_by(TOWNNAME) %>%
  summarise(count = n(), .groups = "drop") %>%
  mutate(type = "僅私立")

# 合併四種資料
plot_data <- bind_rows(total_counts, any_counts, only_private, only_public) %>%
  mutate(type = factor(type, levels = c("總學校數", "至少有一家", "僅私立", "僅公立")))

# 把 NA 值補 0
plot_data <- plot_data %>%
  complete(TOWNNAME, type, fill = list(count = 0))

# 繪製每個行政區的單一圖表
# 繪製每個行政區的單一圖表
# 繪製每個行政區的單一圖表
p <- ggplot(plot_data, aes(x = type, y = count, fill = type)) +
  geom_col(position = "dodge") +
  facet_wrap(~ TOWNNAME, ncol = 4) +
  scale_fill_manual(
    values = c(
      "總學校數" = "#6FB7B7",
      "至少有一家" = "#9999CC",
      "僅私立" = "#B766AD",
      "僅公立" = "#AFAF61"
    ),
    name = "類別"
  ) +
  labs(
    x = NULL,
    y = "學校數",
    title = "各行政區國小周邊運動場館分布"
  ) +
  theme_minimal(base_family = "STHeiti") +
  theme(
    plot.background = element_rect(fill = "#F0F0F0", color = NA),   # 🔴 背景顏色
    panel.background = element_rect(fill = "#F0F0F0", color = NA),  # 🔴 面板背景
    legend.background = element_rect(fill = "#F0F0F0", color = NA), # 🔴 圖例背景
    legend.box.background = element_rect(fill = "#F0F0F0", color = NA),
    
    axis.text.x = element_blank(),   # 🔴 x軸文字移除
    axis.text.y = element_text(size = 8, color = "#3C3C3C"),    # y軸文字小一點
    axis.title.y = element_text(size = 10, color = "#3C3C3C"),  # y軸標題縮小 & 顏色統一
    strip.text = element_text(size = 10, color = "#3C3C3C"),    # facet 標題
    legend.position = "right",                                   # 圖例放右側
    legend.title = element_text(size = 10, color = "#3C3C3C"),   # 圖例標題
    legend.text = element_text(size = 8, color = "#3C3C3C"),     # 圖例文字
    plot.title = element_text(size = 12, hjust = 0.5, color = "#3C3C3C") # 標題置中 & 顏色
  )



# 顯示圖
print(p)

library(dplyr)
library(ggplot2)

# 1️⃣ 篩選沒有任何運動場館的學校
no_sports_schools <- final_counts %>%
  filter(public_sports_count == 0 & private_sports_count == 0)

# 2️⃣ 計算加權總分
no_sports_schools <- no_sports_schools %>%
  mutate(total_score = bubbletea_count * 1 + fastfood_count * 2) %>%
  filter(total_score > 0)  # 🔴 只保留總分 > 0


# 3️⃣ 整理標籤(行政區+學校名稱)
no_sports_schools <- no_sports_schools %>%
  mutate(label = paste0(TOWNNAME, " ", Name))

# 4️⃣ 繪製長條圖
ggplot(no_sports_schools, aes(x = reorder(label, total_score), y = total_score)) +
  geom_col(fill = "#6FB7B7") +   # 統一顏色
  coord_flip() +                 # 橫向長條圖
  labs(title = "公私立運動場館都沒有的國小被誘惑排名",
       x = NULL, y = NULL) +     # 🔴 刪掉 x、y 軸標題
  theme_minimal(base_family = "STHeiti") +
  theme(
    plot.background = element_rect(fill = "#F0F0F0", color = NA),  # 背景顏色
    panel.background = element_rect(fill = "#F0F0F0", color = NA),
    axis.text.x = element_text(size = 10, color = "#3C3C3C"),      # x 軸文字
    axis.text.y = element_text(size = 9, color = "#3C3C3C"),       # y 軸文字
    plot.title = element_text(size = 14, hjust = 0.5, face = "bold", color = "#3C3C3C"),  # 標題
    legend.position = "none"   # 不顯示圖例
  )

library(sf)
library(dplyr)
library(tmap)


# 🟩 假設以下資料已存在:
# final_counts:含有學校屬性與外部誘惑分數
# school_buffer:國小 500m buffer
# taipei_village:台北市行政區多邊形

# 1️⃣ 篩選無運動場館的學校 + 計算外部誘惑指數
no_sports_schools <- final_counts %>%
  filter(public_sports_count == 0 & private_sports_count == 0) %>%
  mutate(total_score = bubbletea_count * 1 + fastfood_count * 2,
         tempt_group = ifelse(total_score > 0, "有外部誘惑", "無外部誘惑")) %>%
  select(SID, tempt_group)

# 🟩 直接把這 33 筆 buffer 抓出來
buffer_with_tempt <- school_buffer %>%
  filter(SID %in% no_sports_schools$SID) %>%
  st_join(no_sports_schools, by = "SID")

 #buffer 中心的點(final_counts 也用 33 筆顯示)
school_points <- final_counts %>%
  filter(SID %in% no_sports_schools$SID)

# 🟩 畫圖:33 筆 buffer 分 2 顏色
tmap_mode("plot")  # 靜態地圖模式
## ℹ tmap mode set to "plot".
tm_shape(taipei_village) +
  tm_polygons(col = "grey90", border.col = "white") +  # 灰色行政區邊界

tm_shape(buffer_with_tempt) +
  tm_fill(col = "tempt_group",
          palette = c("無外部誘惑" = "#9999CC", "有外部誘惑" = "#6FB7B7"),
          title = "外部誘惑指數",
          alpha = 0.8) +
  tm_borders(col = "white", lwd = 0.5) +   # buffer 外框

  tm_shape(school_points) +
  tm_dots(col = "black", size = 0.1) +     # 中心點黑點

  tm_layout(
    title = "台北市國小 500m Buffer:\n無運動場館學校依外部誘惑分組",
    title.size = 1.2,
    legend.outside = TRUE,
    bg.color = "#F0F0F0",
    frame = TRUE
  )
## 
## ── tmap v3 code detected ───────────────────────────────────────────────────────
## [v3->v4] `tm_polygons()`: use `fill_alpha` instead of `alpha`.[v3->v4] `tm_polygons()`: migrate the argument(s) related to the legend of the
## visual variable `fill` namely 'title' to 'fill.legend = tm_legend(<HERE>)'[v3->v4] `tm_layout()`: use `tm_title()` instead of `tm_layout(title = )`

# 1️⃣ 篩選「只有私立,沒有公立」的學校
only_private_schools <- final_counts %>%
  filter(public_sports_count == 0 & private_sports_count != 0)

# 2️⃣ 計算加權分數
only_private_schools <- only_private_schools %>%
  mutate(total_score = fastfood_count * 2 + bubbletea_count * 1)

# 3️⃣ 先印出:總共有幾所學校
cat("總共有", nrow(only_private_schools), "所學校\n")
## 總共有 64 所學校
# 4️⃣ 再篩選:只留總分 >0 的
only_private_schools_positive <- only_private_schools %>%
  filter(total_score > 0)

cat("其中有", nrow(only_private_schools_positive), "所學校分數>0\n")
## 其中有 64 所學校分數>0
# 🔴 先篩選:total_score >= 15
only_private_schools_filtered <- only_private_schools %>%
  filter(total_score >= 16) %>%
  mutate(label = paste0(TOWNNAME, " - ", Name))

# 🔵 畫圖
ggplot(only_private_schools_filtered, aes(x = reorder(label, total_score), y = total_score)) +
  geom_col(fill = "#6FB7B7") +   # 🔴 顏色一致
  geom_text(aes(label = total_score), hjust = -0.1, size = 3, color = "black") +  # 🔴 分數標籤
  coord_flip() +
  labs(
    title = "僅私立運動場館(無公立)學校外部誘惑指數 ≥16",
    x = NULL,
    y = NULL
  ) +
  theme_minimal(base_family = "STHeiti") +
  theme(
    axis.text.y = element_text(size = 8, color = "#3C3C3C"),
    axis.text.x = element_text(size = 8, color = "#3C3C3C"),
    plot.background = element_rect(fill = "#F0F0F0", color = NA),
    panel.background = element_rect(fill = "#F0F0F0", color = NA),
    plot.title = element_text(size = 12, hjust = 0.5, face = "bold", color = "#3C3C3C")
  )

# 🟩 篩選:僅有私立運動場館
private_only_schools <- final_counts %>%
  filter(public_sports_count == 0 & private_sports_count > 0) %>%
  mutate(total_score = bubbletea_count * 1 + fastfood_count * 2,
         tempt_group = ifelse(total_score > 0, "有外部誘惑", "無外部誘惑")) %>%
  select(SID, tempt_group)

# 🟩 取得 buffer
buffer_private_only <- school_buffer %>%
  filter(SID %in% private_only_schools$SID) %>%
  st_join(private_only_schools, by = "SID")

# 🟩 中心學校點
school_points_private_only <- final_counts %>%
  filter(SID %in% private_only_schools$SID)

tmap_mode("plot")  # 靜態模式
## ℹ tmap mode set to "plot".
tm_shape(taipei_village) +
  tm_polygons(col = "grey90", border.col = "white") +  # 行政區灰色

  tm_shape(buffer_private_only) +
  tm_fill(col = "tempt_group",
          palette = c("無外部誘惑" = "#9999CC", "有外部誘惑" = "#6FB7B7"),
          title = "外部誘惑指數",
          alpha = 0.8) +
  tm_borders(col = "white", lwd = 0.5) +   # buffer 外框

  tm_shape(school_points_private_only) +
  tm_dots(col = "black", size = 0.1) +     # 中心點黑點

  tm_layout(
    title = "台北市國小 500m Buffer:\n僅有私立運動場館學校依外部誘惑分組",
    title.size = 1.2,
    legend.outside = TRUE,
    bg.color = "#F0F0F0",
    frame = TRUE
  )
## 
## ── tmap v3 code detected ───────────────────────────────────────────────────────
## [v3->v4] `tm_polygons()`: use `fill_alpha` instead of `alpha`.[v3->v4] `tm_polygons()`: migrate the argument(s) related to the legend of the
## visual variable `fill` namely 'title' to 'fill.legend = tm_legend(<HERE>)'[v3->v4] `tm_layout()`: use `tm_title()` instead of `tm_layout(title = )`The visual variable "fill" of the layer "polygons" contains a unique value. Therefore a categorical scale is applied (tm_scale_categorical).

library(sf)
library(dplyr)

# --- 速食店:判斷每間店家是否至少有被一個 buffer 擊中 ---
fastfood_in_buffer_flag <- st_join(fast_food_sf, school_buffer, join = st_intersects) %>%
  st_drop_geometry() %>%
  filter(!is.na(SID)) %>%
  distinct(id, store)

# 移除 3Q雞排 和 21世紀風味館
fastfood_in_buffer_flag <- fastfood_in_buffer_flag %>%
  filter(!store %in% c("3q雞排", "21世紀風味館"))

# --- 速食店:總家數 ---
fastfood_total_counts <- fast_food_sf %>%
  st_drop_geometry() %>%
  filter(!is.na(store)) %>%
  filter(!store %in% c("3Q雞排", "21世紀風味館")) %>%  # 🔴 移除
  count(store, name = "total_count")

# --- 速食店:在 buffer 內的家數 ---
fastfood_in_buffer_counts <- fastfood_in_buffer_flag %>%
  count(store, name = "in_buffer_count")

# --- 合併計算百分比 ---
fastfood_coverage_unique <- left_join(fastfood_total_counts, fastfood_in_buffer_counts, by = "store") %>%
  mutate(in_buffer_count = ifelse(is.na(in_buffer_count), 0, in_buffer_count),
         percentage_in_buffer = (in_buffer_count / total_count) * 100) %>%
  arrange(desc(percentage_in_buffer))

print(fastfood_coverage_unique)
## # A tibble: 9 × 4
##   store    total_count in_buffer_count percentage_in_buffer
##   <chr>          <int>           <int>                <dbl>
## 1 炸機大獅          15              14                 93.3
## 2 達美樂            23              20                 87.0
## 3 必勝客            38              33                 86.8
## 4 派克雞排          13              11                 84.6
## 5 胖老爹            36              30                 83.3
## 6 漢堡王            23              18                 78.3
## 7 麥當勞            61              47                 77.0
## 8 肯德基            30              22                 73.3
## 9 頂呱呱            24              13                 54.2
# --- 手搖飲 ---
bubbletea_in_buffer_flag <- st_join(bubble_tea_sf, school_buffer, join = st_intersects) %>%
  st_drop_geometry() %>%
  filter(!is.na(SID)) %>%
  distinct(id, store)

bubbletea_total_counts <- bubble_tea_sf %>%
  st_drop_geometry() %>%
  filter(!is.na(store)) %>%
  count(store, name = "total_count")

bubbletea_in_buffer_counts <- bubbletea_in_buffer_flag %>%
  count(store, name = "in_buffer_count")

bubbletea_coverage_unique <- left_join(bubbletea_total_counts, bubbletea_in_buffer_counts, by = "store") %>%
  mutate(in_buffer_count = ifelse(is.na(in_buffer_count), 0, in_buffer_count),
         percentage_in_buffer = (in_buffer_count / total_count) * 100) %>%
  arrange(desc(percentage_in_buffer))

  ggplot(fastfood_coverage_unique, aes(x = percentage_in_buffer, y = reorder(store, percentage_in_buffer))) +
  geom_col(fill = "#6FB7B7", alpha = 0.8) +  # 🔵 改成你的顏色,微透明
  geom_text(aes(label = paste0(round(percentage_in_buffer, 1), "%")),
            hjust = -0.1, size = 3, color = "#3C3C3C") +  # 🔵 加上黑灰字
  labs(title = "台北市速食店品牌:國小500m內覆蓋率",
       x = NULL,  # 🔵 拿掉 x 軸標題
       y = NULL) +  # 🔵 拿掉 y 軸標題
  theme_minimal(base_family = "STHeiti") +
  theme(
    plot.background = element_rect(fill = "#F0F0F0", color = NA),  # 🔵 灰背景
    panel.background = element_rect(fill = "#F0F0F0", color = NA), # 🔵 灰背景
    axis.text.x = element_text(size = 10, color = "#3C3C3C"),
    axis.text.y = element_text(size = 8, color = "#3C3C3C"),
    plot.title = element_text(hjust = 0.5, size = 14, face = "bold", color = "#3C3C3C"),
    panel.grid.major = element_line(color = "grey80"),  # 🔵 格線顏色淡化
    panel.grid.minor = element_blank()
  ) +
  xlim(0, max(fastfood_coverage_unique$percentage_in_buffer, na.rm = TRUE) + 5)  # 🔵 保留空間

# --- 手搖飲長條圖 ---
ggplot(bubbletea_coverage_unique, aes(x = percentage_in_buffer, y = reorder(store, percentage_in_buffer))) +
  geom_col(fill = "#6FB7B7", alpha = 0.8) +  # 🔵 改成你的顏色,微透明
  geom_text(aes(label = paste0(round(percentage_in_buffer, 1), "%")),
            hjust = -0.1, size = 3, color = "#3C3C3C") +  # 🔵 加上黑灰字
  labs(title = "台北市手搖飲品牌:國小500m內覆蓋率",
       x = NULL,  # 🔵 拿掉 x 軸標題
       y = NULL) +  # 🔵 拿掉 y 軸標題
  theme_minimal(base_family = "STHeiti") +
  theme(
    plot.background = element_rect(fill = "#F0F0F0", color = NA),  # 🔵 灰背景
    panel.background = element_rect(fill = "#F0F0F0", color = NA), # 🔵 灰背景
    axis.text.x = element_text(size = 10, color = "#3C3C3C"),
    axis.text.y = element_text(size = 8, color = "#3C3C3C"),
    plot.title = element_text(hjust = 0.5, size = 14, face = "bold", color = "#3C3C3C"),
    panel.grid.major = element_line(color = "grey80"),  # 🔵 格線顏色淡化
    panel.grid.minor = element_blank()
  ) +
  xlim(0, max(bubbletea_coverage_unique$percentage_in_buffer, na.rm = TRUE) + 5)  # 🔵 保留空間

library(sf)
library(dplyr)
library(ggplot2)

# 🟡 台北市國小總數
total_schools <- nrow(final_counts)  # 148間

# 🟡 速食店被覆蓋的學校數 (distinct store + SID)
fastfood_school_coverage <- st_join(fast_food_sf, school_buffer, join = st_intersects) %>%
  st_drop_geometry() %>%
  filter(!is.na(SID), !store %in% c("3Q雞排", "21世紀風味館")) %>%
  distinct(store, SID) %>%
  count(store, name = "covered_school_count") %>%
  mutate(percentage_schools = (covered_school_count / total_schools) * 100) %>%
  arrange(desc(percentage_schools))

# 🟡 手搖飲店被覆蓋的學校數
bubbletea_school_coverage <- st_join(bubble_tea_sf, school_buffer, join = st_intersects) %>%
  st_drop_geometry() %>%
  filter(!is.na(SID)) %>%
  distinct(store, SID) %>%
  count(store, name = "covered_school_count") %>%
  mutate(percentage_schools = (covered_school_count / total_schools) * 100) %>%
  arrange(desc(percentage_schools))

# 🟡 覆蓋學校比例長條圖 - 速食店
ggplot(fastfood_school_coverage, aes(x = percentage_schools, y = reorder(store, percentage_schools))) +
  geom_col(fill = "#6FB7B7", alpha = 0.8) +
  geom_text(aes(label = paste0(covered_school_count, "校 (", round(percentage_schools, 1), "%)")),
            hjust = 0, size = 3, color = "#3C3C3C") +
  labs(title = "台北市速食店品牌:國小 500m 內覆蓋學校比例",
       x = NULL, y = NULL) +
  theme_minimal(base_family = "STHeiti") +
  theme(
    plot.background = element_rect(fill = "#F0F0F0", color = NA),
    panel.background = element_rect(fill = "#F0F0F0", color = NA),
    axis.text.x = element_text(size = 10, color = "#3C3C3C"),
    axis.text.y = element_text(size = 8, color = "#3C3C3C"),
    plot.title = element_text(hjust = 0.5, size = 14, face = "bold", color = "#3C3C3C"),
    panel.grid.major = element_line(color = "grey80"),
    panel.grid.minor = element_blank()
  ) +
  xlim(0, max(fastfood_school_coverage$percentage_schools, na.rm = TRUE) + 5)

# 🟡 覆蓋學校比例長條圖 - 手搖飲店
ggplot(bubbletea_school_coverage, aes(x = percentage_schools, y = reorder(store, percentage_schools))) +
  geom_col(fill = "#6FB7B7", alpha = 0.8) +
  geom_text(aes(label = paste0(covered_school_count, "校 (", round(percentage_schools, 1), "%)")),
            hjust = 0, size = 3, color = "#3C3C3C") +
  labs(title = "台北市手搖飲品牌:國小 500m 內覆蓋學校比例",
       x = NULL, y = NULL) +
  theme_minimal(base_family = "STHeiti") +
  theme(
    plot.background = element_rect(fill = "#F0F0F0", color = NA),
    panel.background = element_rect(fill = "#F0F0F0", color = NA),
    axis.text.x = element_text(size = 10, color = "#3C3C3C"),
    axis.text.y = element_text(size = 8, color = "#3C3C3C"),
    plot.title = element_text(hjust = 0.5, size = 14, face = "bold", color = "#3C3C3C"),
    panel.grid.major = element_line(color = "grey80"),
    panel.grid.minor = element_blank()
  ) +
  xlim(0, max(bubbletea_school_coverage$percentage_schools, na.rm = TRUE) + 5)

# 🟩 篩選:同時有公立與私立運動場館的學校
both_sports_schools <- final_counts %>%
  filter(public_sports_count > 0 & private_sports_count > 0) %>%
  mutate(total_score = bubbletea_count * 1 + fastfood_count * 2) %>%
  select(SID, total_score)

# 🟩 合併到 buffer,並手動指定 factor 順序
buffer_both <- school_buffer %>%
  filter(SID %in% both_sports_schools$SID) %>%
  st_join(both_sports_schools, by = "SID") %>%
  mutate(score_group = case_when(
    total_score <= 8 ~ "8分以下",
    total_score <= 16 ~ "16分以下",
    total_score <= 24 ~ "24分以下",
    total_score > 24 ~ "24分以上",
  ),
  # 🔴 這裡手動設定 factor 順序
  score_group = factor(score_group,
                        levels = c("8分以下", "16分以下", "24分以下", "24分以上")))

# 🟩 中心點
school_points_both <- final_counts %>%
  filter(SID %in% both_sports_schools$SID)

tmap_mode("plot")  # 靜態模式
## ℹ tmap mode set to "plot".
tm_shape(taipei_village) +
  tm_polygons(col = "grey90", border.col = "white") +

  tm_shape(buffer_both) +
  tm_fill(col = "score_group",
          palette = c("8分以下" = "#D1E9E9",
                      "16分以下" = "#95CACA",
                      "24分以下" = "#4F9D9D",
                      "24分以上" = "#336666"),
          title = "外部誘惑指數",
          alpha = 0.8) +
  tm_borders(col = "white", lwd = 0.5) +

  tm_shape(school_points_both) +
  tm_dots(col = "black", size = 0.1) +

  tm_layout(
    title = "台北市國小 500m Buffer:\n同時有公私立運動場館學校外部誘惑指數分組",
    title.size = 1.2,
    legend.outside = TRUE,
    bg.color = "#F0F0F0",
    frame = TRUE
  )
## 
## ── tmap v3 code detected ───────────────────────────────────────────────────────
## [v3->v4] `tm_polygons()`: use `fill_alpha` instead of `alpha`.[v3->v4] `tm_polygons()`: migrate the argument(s) related to the legend of the
## visual variable `fill` namely 'title' to 'fill.legend = tm_legend(<HERE>)'[v3->v4] `tm_layout()`: use `tm_title()` instead of `tm_layout(title = )`[plot mode] fit legend/component: Some legend items or map compoments do not
## fit well, and are therefore rescaled.
## ℹ Set the tmap option `component.autoscale = FALSE` to disable rescaling.

# 假設你已經有 final_counts、school_buffer 這兩個資料

# 1️⃣ 篩選出至少有公立運動場館的學校(不管私立)
public_schools <- final_counts %>%
  filter(public_sports_count > 0) %>%
  mutate(total_score = fastfood_count * 2 + bubbletea_count * 1) %>%
  select(SID, total_score)

# 2️⃣ 把總分分成四個等級(注意:這邊名稱要對應下面的圖層色票!)
buffer_public <- school_buffer %>%
  filter(SID %in% public_schools$SID) %>%
  st_join(public_schools, by = "SID") %>%
  mutate(score_group = case_when(
    total_score <= 8 ~ "0~8分",
    total_score <= 16 ~ "9~16分",
    total_score <= 24 ~ "17~24分",
    total_score > 24 ~ "24分以上"
  ),
  # 🔴 設定 factor 順序
  score_group = factor(score_group,
                        levels = c("0~8分", "9~16分", "17~24分", "24分以上")))

# 3️⃣ 中心點層:直接拿 final_counts 中有公立的
school_points_public <- final_counts %>%
  filter(public_sports_count > 0)

# 4️⃣ 繪圖
tmap_mode("plot")  # 靜態模式
## ℹ tmap mode set to "plot".
tm_shape(taipei_village) +
  tm_polygons(col = "grey90", border.col = "white") +     # 灰色底 + 邊框

  tm_shape(buffer_public) +
  tm_fill(col = "score_group",
          palette = c("0~8分" = "#D1E9E9",
                      "9~16分" = "#95CACA",
                      "17~24分" = "#4F9D9D",
                      "24分以上" = "#336666"),
          title = "外部誘惑指數",
          alpha = 0.8) +
  tm_borders(col = "white", lwd = 0.5) +                  # 邊框

  tm_shape(school_points_public) +
  tm_dots(col = "black", size = 0.1) +

  tm_layout(
    title = "台北市國小500m Buffer有公立運動場館國小外部誘惑",
    title.size = 1.2,  # 🔴 標題放大(tmap title.size 是比例數字)
    legend.outside = TRUE,
    bg.color = "#F0F0F0",    # 🔴 背景灰色底
    panel.background = "#F0F0F0",
    legend.text.size = 0.6,
    legend.title.size = 0.8,
    frame = TRUE
  )
## 
## ── tmap v3 code detected ───────────────────────────────────────────────────────
## [v3->v4] `tm_polygons()`: use `fill_alpha` instead of `alpha`.[v3->v4] `tm_polygons()`: migrate the argument(s) related to the legend of the
## visual variable `fill` namely 'title' to 'fill.legend = tm_legend(<HERE>)'[v3->v4] `tm_layout()`: use `tm_title()` instead of `tm_layout(title = )`[plot mode] fit legend/component: Some legend items or map compoments do not
## fit well, and are therefore rescaled.
## ℹ Set the tmap option `component.autoscale = FALSE` to disable rescaling.

# 1️⃣ 篩選有公立運動場館的學校
public_schools <- final_counts %>%
  filter(public_sports_count > 0) %>%
  mutate(total_score = fastfood_count * 2 + bubbletea_count * 1) %>%
  filter(total_score <= 4) %>%      # 🔴 只留最高 4 分以內
  mutate(label = paste0(TOWNNAME, " - ", Name)) %>%  # 建立標籤
  arrange(total_score)

# 2️⃣ 繪製橫向長條圖
ggplot(public_schools, aes(x = reorder(label, total_score), y = total_score)) +
  geom_col(fill = "#6FB7B7") +  # 統一顏色
  geom_text(aes(label = total_score),
            hjust = -0.1, size = 3, color = "black") +
  coord_flip() +
  labs(
    title = "台北市有公立運動場館國小500m",
    x = NULL,
    y = NULL
  ) +
  theme_minimal(base_family = "STHeiti") +
  theme(
    plot.background = element_rect(fill = "#F0F0F0", color = NA),
    panel.background = element_rect(fill = "#F0F0F0", color = NA),
    axis.text.y = element_text(size = 8, color = "#3C3C3C"),
    axis.text.x = element_text(size = 8, color = "#3C3C3C"),
    plot.title = element_text(size = 12, hjust = 0.5, face = "bold", color = "#3C3C3C")
  )

# 🟩 總共有公立運動場館的學校
n_public_schools <- final_counts %>%
  filter(public_sports_count > 0) %>%
  nrow()

# 🟩 只有公立(沒有私立)的學校
n_only_public_schools <- final_counts %>%
  filter(public_sports_count > 0 & private_sports_count == 0) %>%
  nrow()

# 輸出
cat("總共有公立運動場館的學校數量:", n_public_schools, "\n")
## 總共有公立運動場館的學校數量: 51
cat("其中只有公立運動場館的學校數量:", n_only_public_schools, "\n")
## 其中只有公立運動場館的學校數量: 22
library(ggplot2)
library(dplyr)




tempt_scores_16up <- final_counts %>%
  filter(public_sports_count > 0 & private_sports_count > 0) %>%
  mutate(total_score = fastfood_count * 2 + bubbletea_count * 1) %>%
  filter(total_score > 16) %>%
  mutate(label = paste0(TOWNNAME, " - ", Name)) %>%
  select(label, total_score)

library(ggplot2)

ggplot(tempt_scores_16up, aes(x = reorder(label, total_score), y = total_score)) +
  geom_col(fill = "#6FB7B7") +  # 用同樣顏色
  geom_text(aes(label = total_score), hjust = -0.1, size = 3, color = "black") +
  coord_flip() +
  labs(
    title = "同時有公私立運動場館學校外部誘惑指數 ≥16 分",
    x = NULL,
    y = NULL
  ) +
  theme_minimal(base_family = "STHeiti") +
  theme(
    axis.text.y = element_text(size = 8, color = "#3C3C3C"),
    axis.text.x = element_text(size = 8, color = "#3C3C3C"),
    plot.background = element_rect(fill = "#F0F0F0", color = NA),
    panel.background = element_rect(fill = "#F0F0F0", color = NA),
    plot.title = element_text(size = 12, hjust = 0.5, face = "bold", color = "#3C3C3C")
  )

# 🔵 僅有公立運動場館的學校(無私立)
tempt_scores_16up <- final_counts %>%
  filter(public_sports_count > 0 & private_sports_count == 0) %>%
  mutate(total_score = fastfood_count * 2 + bubbletea_count * 1) %>%
  filter(total_score > 0) %>%
  mutate(label = paste0(TOWNNAME, " - ", Name)) %>%
  select(label, total_score)

library(ggplot2)

ggplot(tempt_scores_16up, aes(x = reorder(label, total_score), y = total_score)) +
  geom_col(fill = "#6FB7B7") +  # 用統一顏色
  geom_text(aes(label = total_score), hjust = -0.1, size = 3, color = "black") +
  coord_flip() +
  labs(
    title = "僅有公立運動場館學校外部誘惑指數 ",
    x = NULL,
    y = NULL
  ) +
  theme_minimal(base_family = "STHeiti") +
  theme(
    axis.text.y = element_text(size = 8, color = "#3C3C3C"),
    axis.text.x = element_text(size = 8, color = "#3C3C3C"),
    plot.background = element_rect(fill = "#F0F0F0", color = NA),
    panel.background = element_rect(fill = "#F0F0F0", color = NA),
    plot.title = element_text(size = 12, hjust = 0.5, face = "bold", color = "#3C3C3C")
  )

# 🔵 只要有公立運動場館的學校(有無私立皆可),且誘惑指數 >= 16
tempt_scores_16up <- final_counts %>%
  filter(public_sports_count > 0) %>%  # 🟢 只要有公立
  mutate(total_score = fastfood_count * 2 + bubbletea_count * 1) %>%
  filter(total_score >= 16) %>%        # 🟢 誘惑指數 >= 16
  mutate(label = paste0(TOWNNAME, " - ", Name)) %>%
  select(label, total_score)

library(ggplot2)

ggplot(tempt_scores_16up, aes(x = reorder(label, total_score), y = total_score)) +
  geom_col(fill = "#6FB7B7") +  # 用統一顏色
  geom_text(aes(label = total_score), hjust = -0.1, size = 3, color = "black") +
  coord_flip() +
  labs(
    title = "有公立運動場館學校外部誘惑指數 ≥16 分",
    x = NULL,
    y = NULL
  ) +
  theme_minimal(base_family = "STHeiti") +
  theme(
    axis.text.y = element_text(size = 8, color = "#3C3C3C"),
    axis.text.x = element_text(size = 8, color = "#3C3C3C"),
    plot.background = element_rect(fill = "#F0F0F0", color = NA),
    panel.background = element_rect(fill = "#F0F0F0", color = NA),
    plot.title = element_text(size = 12, hjust = 0.5, face = "bold", color = "#3C3C3C")
  )

# 🟠 速食店:有/沒有 的比例
fastfood_pie <- final_counts %>%
  mutate(has_fastfood = ifelse(fastfood_count > 0, "有速食店", "無速食店")) %>%
  count(has_fastfood, name = "n") %>%
  mutate(prop = n / sum(n) * 100)

# 🟠 飲料店:有/沒有 的比例
bubbletea_pie <- final_counts %>%
  mutate(has_bubbletea = ifelse(bubbletea_count > 0, "有飲料店", "無飲料店")) %>%
  count(has_bubbletea, name = "n") %>%
  mutate(prop = n / sum(n) * 100)

# 🔵 速食店圓餅圖
p1 <- ggplot(fastfood_pie, aes(x = "", y = prop, fill = has_fastfood)) +
  geom_col(width = 1, color = "white") +
  coord_polar(theta = "y") +
  geom_text(aes(label = paste0(n, " (", round(prop, 1), "%)")),
             position = position_stack(vjust = 0.5),
             color = "#3C3C3C", size = 5) +
  scale_fill_manual(values = c("有速食店" = "#6FB7B7", "無速食店" = "#9999CC"), name = NULL) +
  labs(title = "國小周邊 500m 內速食店有無比例") +
  theme_void() +
  theme(
    plot.background = element_rect(fill = "#F0F0F0", color = NA),
    panel.background = element_rect(fill = "#F0F0F0", color = NA),
    legend.position = "bottom",
    legend.text = element_text(size = 12, color = "#3C3C3C"),
    plot.title = element_text(hjust = 0.5, size = 14, face = "bold", color = "#3C3C3C")
  )

# 🔵 飲料店圓餅圖
p2 <- ggplot(bubbletea_pie, aes(x = "", y = prop, fill = has_bubbletea)) +
  geom_col(width = 1, color = "white") +
  coord_polar(theta = "y") +
  geom_text(aes(label = paste0(n, " (", round(prop, 1), "%)")),
             position = position_stack(vjust = 0.5),
             color = "#3C3C3C", size = 5) +
  scale_fill_manual(values = c("有飲料店" = "#6FB7B7", "無飲料店" = "#9999CC"), name = NULL) +
  labs(title = "國小周邊 500m 內飲料店有無比例") +
  theme_void() +
  theme(
    plot.background = element_rect(fill = "#F0F0F0", color = NA),
    panel.background = element_rect(fill = "#F0F0F0", color = NA),
    legend.position = "bottom",
    legend.text = element_text(size = 12, color = "#3C3C3C"),
    plot.title = element_text(hjust = 0.5, size = 14, face = "bold", color = "#3C3C3C")
  )




# ➜ 並排顯示
p1 

p2

ggsave("fastfood_pie.png", plot = p1, width = 6, height = 6, dpi = 300)
ggsave("bubbletea_pie.png", plot = p2, width = 6, height = 6, dpi = 300)
# 🟠 至少有一間速食店或手搖飲料店
tempt_pie <- final_counts %>%
  mutate(has_anytempt = ifelse(fastfood_count > 0 | bubbletea_count > 0, "有外部誘惑", "無外部誘惑")) %>%
  count(has_anytempt, name = "n") %>%
  mutate(prop = n / sum(n) * 100)

# 🔵 外部誘惑圓餅圖
p3  <- ggplot(tempt_pie, aes(x = "", y = prop, fill = has_anytempt)) +
  geom_col(width = 1, color = "white") +
  coord_polar(theta = "y") +
  geom_text(aes(label = paste0(n, " (", round(prop, 1), "%)")),
             position = position_stack(vjust = 0.5),
             color = "#3C3C3C", size = 5) +
  scale_fill_manual(values = c("有外部誘惑" = "#6FB7B7", "無外部誘惑" = "#9999CC"), name = NULL) +
  labs(title = "國小周邊 500m 內外部誘惑(速食或手搖)有無比例") +
  theme_void() +
  theme(
    plot.background = element_rect(fill = "#F0F0F0", color = NA),
    panel.background = element_rect(fill = "#F0F0F0", color = NA),
    legend.position = "bottom",
    legend.text = element_text(size = 12, color = "#3C3C3C"),
    plot.title = element_text(hjust = 0.5, size = 14, face = "bold", color = "#3C3C3C")
  )

ggsave("p3_tempt_pie_chart.png", p3, width = 8, height = 6, dpi = 300)
sports_count <- data.frame(
  category = c("公立運動場館", "私立運動場館"),
  count = c(nrow(public_sports), nrow(private_sports))
)

# 計算百分比,並組合數字+百分比的標籤
sports_count <- sports_count %>%
  mutate(
    prop = count / sum(count) * 100,
    label = paste0(count, " (", round(prop, 1), "%)")
  )

# 畫圓餅圖
p2 <- ggplot(sports_count, aes(x = "", y = count, fill = category)) +
  geom_col(width = 1, color = "white") +
  coord_polar(theta = "y") +
  geom_text(
    aes(label = label),
    position = position_stack(vjust = 0.5),
    color = "#3C3C3C",
    size = 5
  ) +
  scale_fill_manual(values = c("#6FB7B7", "#9999CC")) +
  labs(
    title = "台北市公私立運動場館數量分布",
    fill = NULL  # 隱藏圖例標題
  ) +
  theme_void(base_family = "STHeiti") +
  theme(
    plot.background = element_rect(fill = "#F0F0F0", color = NA),
    panel.background = element_rect(fill = "#F0F0F0", color = NA),
    text = element_text(color = "#3C3C3C"),
    plot.title = element_text(size = 18, hjust = 0.5, face = "bold"),
    legend.text = element_text(size = 12),
    legend.position = "bottom"
  )
print(p2)

ggsave("public_private_sports_pie.png", plot = p2, width = 8, height = 6, dpi = 300, bg = "#F0F0F0")
library(sf)
library(dplyr)
library(tmap)


# 🔴 確認公民營欄位:公立 / 私立
public_sports <- sports_venues_sf %>% filter(公民營 == "公營")
private_sports <- sports_venues_sf %>% filter(公民營 == "民營")

# 1️⃣ 私立運動場館交集
private_joined <- st_join(school_buffer, private_sports, join = st_intersects, left = TRUE) %>%
  mutate(has_private = ifelse(is.na(編號), "無私立運動場館", "有私立運動場館")) %>%
  select(SID, has_private, geometry) %>%
  group_by(SID, has_private) %>%
  summarise(geometry = st_union(geometry), .groups = "drop")

# 2️⃣ 公立運動場館交集
public_joined <- st_join(school_buffer, public_sports, join = st_intersects, left = TRUE) %>%
  mutate(has_public = ifelse(is.na(編號), "無公立運動場館", "有公立運動場館")) %>%
  select(SID, has_public, geometry) %>%
  group_by(SID, has_public) %>%
  summarise(geometry = st_union(geometry), .groups = "drop")

tmap_mode("plot")  # 靜態模式
## ℹ tmap mode set to "plot".
# 🔵 私立運動場館圖
p1 <- tm_shape(taipei_village) +
  tm_polygons(col = "grey90", border.col = "white", alpha = 1) +
  tm_shape(private_joined) +
  tm_fill(col = "has_private",
          palette = c("有私立運動場館" = "#6FB7B7", "無私立運動場館" = "#9999CC"),
          title = "私立運動場館") +
  tm_shape(final_counts) +
  tm_dots(col = "black", size = 0.1) +
  tm_layout(
    title = "台北市國小 500m Buffer 是否有私立運動場館",
    title.size = 4, 
    legend.outside = TRUE,
    bg.color = "#F0F0F0"      # 🔴 這邊加上背景顏色
  )
## 
## ── tmap v3 code detected ───────────────────────────────────────────────────────
## [v3->v4] `tm_polygons()`: use `fill_alpha` instead of `alpha`.[v3->v4] `tm_polygons()`: migrate the argument(s) related to the legend of the
## visual variable `fill` namely 'title' to 'fill.legend = tm_legend(<HERE>)'[v3->v4] `tm_layout()`: use `tm_title()` instead of `tm_layout(title = )`
# 🔵 公立運動場館圖
p2 <- tm_shape(taipei_village) +
  tm_polygons(col = "grey90", border.col = "white", alpha = 1) +
  tm_shape(public_joined) +
  tm_fill(col = "has_public",
          palette = c("有公立運動場館" = "#6FB7B7", "無公立運動場館" = "#9999CC"),
          title = "公立運動場館") +
  tm_shape(final_counts) +
  tm_dots(col = "black", size = 0.1) +
  tm_layout(
    title = "台北市國小 500m Buffer 是否有公立運動場館",
    main.title.size = 8,  # 🔴 這裡調整標題大小,數字愈大,字愈大(預設 1)
    legend.outside = TRUE,
    bg.color = "#F0F0F0"      # 🔴 這邊加上背景顏色
  )
## [v3->v4] `tm_polygons()`: use `fill_alpha` instead of `alpha`.
## [v3->v4] `tm_polygons()`: migrate the argument(s) related to the legend of the
## visual variable `fill` namely 'title' to 'fill.legend = tm_legend(<HERE>)'
## [v3->v4] `tm_layout()`: use `tm_title()` instead of `tm_layout(title = )`
# 顯示地圖
p1
## [plot mode] fit legend/component: Some legend items or map compoments do not
## fit well, and are therefore rescaled.
## ℹ Set the tmap option `component.autoscale = FALSE` to disable rescaling.

p2
## [plot mode] fit legend/component: Some legend items or map compoments do not
## fit well, and are therefore rescaled.
## ℹ Set the tmap option `component.autoscale = FALSE` to disable rescaling.

盒需圖

# 🔴 移除 sf geometry(轉成一般 data.frame)
final_counts_df <- st_drop_geometry(final_counts)

boxplot_data <- final_counts_df %>%
  select(fastfood_count, bubbletea_count) %>%
  pivot_longer(cols = everything(), names_to = "type", values_to = "count") %>%
  mutate(type = case_when(
    type == "fastfood_count" ~ "速食店",
    type == "bubbletea_count" ~ "手搖飲料店",
    TRUE ~ type  # 預防萬一有其他值
  ))

# 2️⃣ 計算統計量(最小值、Q1、中位數、Q3、最大值)
box_stats <- boxplot_data %>%
  group_by(type) %>%
  summarise(
    ymin = min(count),
    lower = quantile(count, 0.25),
    middle = median(count),
    upper = quantile(count, 0.75),
    ymax = max(count)
  )

# 3️⃣ 畫盒鬚圖,並加上數值標註
ggplot(boxplot_data, aes(x = type, y = count, fill = type)) +
  geom_boxplot(width = 0.5, alpha = 0.6, color = "black") +
  
  # "手搖飲料店" Q1
  geom_text(data = filter(box_stats, type == "手搖飲料店"),
             aes(y = lower, label = paste("Q1:", lower)),
             vjust = -0.3, hjust = -0.3, size = 3, color = "black") +
  # "速食店" Q1
  geom_text(data = filter(box_stats, type == "速食店"),
             aes(y = lower, label = paste("Q1:", lower)),
             vjust = -0.5, hjust = -1.2, size = 3, color = "black") +
  
  # "手搖飲料店" Q3
  geom_text(data = filter(box_stats, type == "手搖飲料店"),
             aes(y = upper, label = paste("Q3:", upper)),
             vjust = -0.3, hjust = -0.3, size = 3, color = "black") +
  # "速食店" Q3
  geom_text(data = filter(box_stats, type == "速食店"),
             aes(y = upper, label = paste("Q3:", upper)),
             vjust = -0.7, hjust = -2.2, size = 3, color = "black") +
  
  # "手搖飲料店" Median
  geom_text(data = filter(box_stats, type == "手搖飲料店"),
             aes(y = middle, label = paste("Med:", middle)),
             vjust = -0.3, hjust = 0, size = 3, color = "black") +
  # "速食店" Median
  geom_text(data = filter(box_stats, type == "速食店"),
             aes(y = middle, label = paste("Med:", middle)),
             vjust = -0.6, hjust = -1.7, size = 3, color = "black") +
  
  # "手搖飲料店" Min
  geom_text(data = filter(box_stats, type == "手搖飲料店"),
             aes(y = ymin, label = paste("Min:", ymin)),
             vjust = 1.2, hjust = -0.2, size = 3, color = "black") +
  # "速食店" Min
  geom_text(data = filter(box_stats, type == "速食店"),
             aes(y = ymin, label = paste("Min:", ymin)),
             vjust = 1.2, hjust = -2, size = 3, color = "black") +
  
  # "手搖飲料店" Max
  geom_text(data = filter(box_stats, type == "手搖飲料店"),
             aes(y = ymax, label = paste("Max:", ymax)),
             vjust = 1, hjust = -0.1, size = 3, color = "black") +
  # "速食店" Max
  geom_text(data = filter(box_stats, type == "速食店"),
             aes(y = ymax, label = paste("Max:", ymax)),
             vjust = -0.5, hjust = -0.1, size = 3, color = "black") +
  
  labs(title = "台灣國小周邊速食店與手搖飲料店分布比較", y = "店家數量", x = NULL) +
  theme_minimal(base_family = "STHeiti") +
  theme(
    plot.background = element_rect(fill = "#F0F0F0", color = NA),
    panel.background = element_rect(fill = "#F0F0F0", color = NA),
    axis.text.x = element_text(size = 10),
    axis.text.y = element_text(size = 10),
    plot.title = element_text(size = 14, hjust = 0.5, face = "bold"),
    legend.position = "none"
  )

library(sf)
library(dplyr)
library(tmap)

# 假設你已經有 taipei_village、school_buffer、fast_food_sf、bubble_tea_sf、final_counts

# 🔴 速食店交集
fastfood_joined <- st_join(school_buffer, fast_food_sf, join = st_intersects, left = TRUE) %>%
  mutate(has_fastfood = ifelse(is.na(id), "無速食店", "有速食店")) %>%
  select(SID, has_fastfood, geometry) %>%
  group_by(SID, has_fastfood) %>%
  summarise(geometry = st_union(geometry), .groups = "drop")

# 🔴 飲料店交集
bubbletea_joined <- st_join(school_buffer, bubble_tea_sf, join = st_intersects, left = TRUE) %>%
  mutate(has_bubbletea = ifelse(is.na(id), "無飲料店", "有飲料店")) %>%
  select(SID, has_bubbletea, geometry) %>%
  group_by(SID, has_bubbletea) %>%
  summarise(geometry = st_union(geometry), .groups = "drop")

tmap_mode("plot")  # 靜態模式
## ℹ tmap mode set to "plot".
# 🔵 速食店圖
p1 <- tm_shape(taipei_village) +
  tm_polygons(col = "grey90", border.col = "white", alpha = 1) +
  tm_shape(fastfood_joined) +
  tm_fill(col = "has_fastfood",
          palette = c("有速食店" = "#AFAF61", "無速食店" = "#B87070"),
          title = "速食店") +
  tm_shape(final_counts) +
  tm_dots(col = "black", size = 0.1) +
  tm_layout(
    title = "台北市國小 500m Buffer 是否有速食店",
    title.size = 4,
    legend.outside = TRUE,
    bg.color = "#F0F0F0"
  )
## 
## ── tmap v3 code detected ───────────────────────────────────────────────────────
## [v3->v4] `tm_polygons()`: use `fill_alpha` instead of `alpha`.[v3->v4] `tm_polygons()`: migrate the argument(s) related to the legend of the
## visual variable `fill` namely 'title' to 'fill.legend = tm_legend(<HERE>)'[v3->v4] `tm_layout()`: use `tm_title()` instead of `tm_layout(title = )`
# 🔵 飲料店圖
p2 <- tm_shape(taipei_village) +
  tm_polygons(col = "grey90", border.col = "white", alpha = 1) +
  tm_shape(bubbletea_joined) +
  tm_fill(col = "has_bubbletea",
          palette = c("有飲料店" = "#AFAF61", "無飲料店" = "#B87070"),
          title = "飲料店") +
  tm_shape(final_counts) +
  tm_dots(col = "black", size = 0.1) +
  tm_layout(
    title = "台北市國小 500m Buffer 是否有飲料店",
    main.title.size = 8,
    legend.outside = TRUE,
    bg.color = "#F0F0F0"
  )
## [v3->v4] `tm_polygons()`: use `fill_alpha` instead of `alpha`.
## [v3->v4] `tm_polygons()`: migrate the argument(s) related to the legend of the
## visual variable `fill` namely 'title' to 'fill.legend = tm_legend(<HERE>)'
## [v3->v4] `tm_layout()`: use `tm_title()` instead of `tm_layout(title = )`
# 顯示地圖
p1
## [plot mode] fit legend/component: Some legend items or map compoments do not
## fit well, and are therefore rescaled.
## ℹ Set the tmap option `component.autoscale = FALSE` to disable rescaling.

p2

# 如果需要儲存
# tmap_save(p1, "Taipei_FastFood_Buffer_Map.png", width = 8, height = 10, dpi = 300)
# tmap_save(p2, "Taipei_BubbleTea_Buffer_Map.png", width = 8, height = 10, dpi = 300)

速食店和飲料店國小覆蓋率

library(dplyr)
library(ggplot2)

# --- 速食店品牌分布 ---
fastfood_in_buffer <- st_join(school_buffer, fast_food_sf, join = st_intersects)



fastfood_brand_counts <- fastfood_in_buffer %>%
  st_drop_geometry() %>%
  filter(!is.na(store)) %>%      # 🔴 去掉 NA 品牌
  count(store) %>%
  mutate(percentage = n / sum(n) * 100) %>%
  arrange(desc(percentage))

# 移除指定品牌
fastfood_brand_counts <- fastfood_brand_counts %>%
  filter(!store %in% c("3Q雞排", "21世紀風味館"))

str(fastfood_brand_counts)
## 'data.frame':    9 obs. of  3 variables:
##  $ store     : chr  "麥當勞" "必勝客" "胖老爹" "達美樂" ...
##  $ n         : int  67 50 46 37 33 23 19 18 17
##  $ percentage: num  21.1 15.8 14.5 11.7 10.4 ...
ggplot(fastfood_brand_counts, aes(x = percentage, y = reorder(store, percentage))) +
  geom_col(fill = "steelblue") +
  geom_text(aes(label = paste0(round(percentage, 1), "%")),
            hjust = -0.1, size = 3) +     # 🔴 在柱狀圖外顯示比例
  labs(title = "國小500m內速食店品牌分布",
       x = "百分比(%)",
       y = "品牌") +
  theme_minimal(base_family = "STHeiti") +
  theme(axis.text.x = element_text(size = 10),
        axis.text.y = element_text(size = 8),
        plot.title = element_text(hjust = 0.5)) +
  xlim(0, max(fastfood_brand_counts$percentage) + 5)   # 🔴 給文字標籤留空間

# --- 手搖杯品牌分布 ---
bubbletea_in_buffer <- st_join(school_buffer, bubble_tea_sf, join = st_intersects)

bubbletea_brand_counts <- bubbletea_in_buffer %>%
  st_drop_geometry() %>%
  filter(!is.na(store)) %>%     # 🔴 去掉 NA 品牌
  count(store) %>%
  mutate(percentage = n / sum(n) * 100) %>%
  arrange(desc(percentage))

ggplot(fastfood_brand_counts, aes(x = percentage, y = reorder(store, percentage))) +
  geom_col(fill = "#6FB7B7", alpha = 0.8) +
  geom_text(aes(label = paste0(round(percentage, 1), "%")),
            hjust = -0.1, size = 3, color = "#3C3C3C") +
  labs(title = "國小500m內速食店品牌分布",
       x = NULL, y = NULL) +
  theme_minimal(base_family = "STHeiti") +
  theme(
    plot.background = element_rect(fill = "#F0F0F0", color = NA),
    panel.background = element_rect(fill = "#F0F0F0", color = NA),
    axis.text.x = element_text(size = 10, color = "#3C3C3C"),
    axis.text.y = element_text(size = 8, color = "#3C3C3C"),
    plot.title = element_text(hjust = 0.5, size = 14, face = "bold", color = "#3C3C3C"),
    panel.grid.major = element_line(color = "grey80"),
    panel.grid.minor = element_blank()
  ) +
  xlim(0, max(fastfood_brand_counts$percentage) + 5)

ggplot(bubbletea_brand_counts, aes(x = percentage, y = reorder(store, percentage))) +
  geom_col(fill = "#6FB7B7", alpha = 0.8) +
  geom_text(aes(label = paste0(round(percentage, 1), "%")),
            hjust = -0.1, size = 3, color = "#3C3C3C") +
  labs(title = "國小500m內手搖飲品牌分布",
       x = NULL, y = NULL) +
  theme_minimal(base_family = "STHeiti") +
  theme(
    plot.background = element_rect(fill = "#F0F0F0", color = NA),
    panel.background = element_rect(fill = "#F0F0F0", color = NA),
    axis.text.x = element_text(size = 10, color = "#3C3C3C"),
    axis.text.y = element_text(size = 8, color = "#3C3C3C"),
    plot.title = element_text(hjust = 0.5, size = 14, face = "bold", color = "#3C3C3C"),
    panel.grid.major = element_line(color = "grey80"),
    panel.grid.minor = element_blank()
  ) +
  xlim(0, max(bubbletea_brand_counts$percentage) + 5)

ggplot(fastfood_brand_counts, aes(x = n, y = reorder(store, n))) +
  geom_col(fill = "#6FB7B7", alpha = 0.8) +
  geom_text(aes(label = n),
            hjust = -0.1, size = 3, color = "#3C3C3C") +
  labs(title = "國小500m內速食店品牌數量分布",
       x = NULL, y = NULL) +
  theme_minimal(base_family = "STHeiti") +
  theme(
    plot.background = element_rect(fill = "#F0F0F0", color = NA),
    panel.background = element_rect(fill = "#F0F0F0", color = NA),
    axis.text.x = element_text(size = 10, color = "#3C3C3C"),
    axis.text.y = element_text(size = 8, color = "#3C3C3C"),
    plot.title = element_text(hjust = 0.5, size = 14, face = "bold", color = "#3C3C3C"),
    panel.grid.major = element_line(color = "grey80"),
    panel.grid.minor = element_blank()
  ) +
  xlim(0, max(fastfood_brand_counts$n, na.rm = TRUE) + 5)

ggplot(bubbletea_brand_counts, aes(x = n, y = reorder(store, n))) +
  geom_col(fill = "#6FB7B7", alpha = 0.8) +
  geom_text(aes(label = n),
            hjust = -0.1, size = 3, color = "#3C3C3C") +
  labs(title = "國小500m內手搖飲品牌數量分布",
       x = NULL, y = NULL) +
  theme_minimal(base_family = "STHeiti") +
  theme(
    plot.background = element_rect(fill = "#F0F0F0", color = NA),
    panel.background = element_rect(fill = "#F0F0F0", color = NA),
    axis.text.x = element_text(size = 10, color = "#3C3C3C"),
    axis.text.y = element_text(size = 8, color = "#3C3C3C"),
    plot.title = element_text(hjust = 0.5, size = 14, face = "bold", color = "#3C3C3C"),
    panel.grid.major = element_line(color = "grey80"),
    panel.grid.minor = element_blank()
  ) +
  xlim(0, max(bubbletea_brand_counts$n, na.rm = TRUE) + 5)

library(ggplot2)
library(dplyr)
library(tidyr)

# 假設兩個資料框:
# 1️⃣ bubbletea_brand_counts (store, n)
# 2️⃣ bubbletea_in_buffer_counts (store, n)
# 計算每家店的 500m 內比例(%)


# 1️⃣ 先合併資料

bubbletea_brand_merged <- bubbletea_brand_counts %>%
  rename(total_count = n) %>%  # ✅ 把 n 改成 total_count
  left_join(
    bubbletea_in_buffer_counts %>% rename(buffer_count = in_buffer_count),  # ✅ 這裡正確用 in_buffer_count
    by = "store"
  ) %>%
  mutate(buffer_count = ifelse(is.na(buffer_count), 0, buffer_count)) %>%
  filter(!store %in% c("日出良太"))  # ✅ 移除不要的品牌

# 2️⃣ 長格式(為了畫雙色柱狀圖)
bubbletea_long <- bubbletea_brand_merged %>%
  pivot_longer(cols = c(total_count, buffer_count),
                names_to = "type",
                values_to = "count") %>%
  mutate(type = factor(type,
                       levels = c("total_count", "buffer_count"),
                       labels = c("總店面數", "500m內店面數")))


bubbletea_labels <- bubbletea_brand_merged %>%
  mutate(percent = round(buffer_count / total_count * 100, 1)) %>%
  select(store, percent)

# 加入百分比到長格式資料中
bubbletea_long <- bubbletea_long %>%
  left_join(bubbletea_labels, by = "store")

max_val <- max(bubbletea_brand_merged$total_count, bubbletea_brand_merged$buffer_count, na.rm = TRUE)


# 3️⃣ 繪製 4x4 Facet Grid
ggplot(bubbletea_long, aes(x = type, y = count, fill = type)) +
  geom_col(width = 0.6) +
  # 每個長條圖的數字
  geom_text(aes(label = count), vjust = -0.5, size = 2.5, color = "#3C3C3C") +
  # 顯示 500m 內比例
  geom_text(
    data = bubbletea_labels,
    aes(x = 1.5, y = max_val + 20, label = paste0(percent, "%")),
    inherit.aes = FALSE, size = 3, color = "#3C3C3C", fontface = "bold"
  ) +
  facet_wrap(~ store, ncol = 4) +
  scale_y_continuous(limits = c(0, max_val + 50)) +
  scale_fill_manual(values = c("總店面數" = "#9999CC", "500m內店面數" = "#6FB7B7")) +
  labs(title = "台北市手搖飲品牌店面總數 vs 學校500m內店面數比例",
       x = NULL, y = NULL, fill = NULL) +
  theme_minimal(base_family = "STHeiti") +
  theme(
    strip.background = element_blank(),
    strip.text = element_text(size = 8, face = "bold", color = "#3C3C3C"),
    axis.text.x = element_blank(),
    axis.ticks.x = element_blank(),
    axis.text.y = element_blank(),
    axis.ticks = element_blank(),
    legend.position = "bottom",
    legend.text = element_text(size = 8, color = "#3C3C3C"),
    plot.title = element_text(hjust = 0.5, size = 12, face = "bold", color = "#3C3C3C"),
    plot.background = element_rect(fill = "#F0F0F0", color = NA),
    panel.background = element_rect(fill = "#F0F0F0", color = NA),
    panel.grid.major = element_line(color = "grey80"),
    panel.grid.minor = element_blank()
  )

# 4️⃣ 如果需要另存圖檔
# ggsave("bubbletea_4x4_brand_comparison.png", width = 10, height = 8, dpi = 300)
# 計算每家店的 500m 內店面比例
friedchicken_summary <- fastfood_brand_counts %>%
  rename(total_count = n) %>%
  left_join(
    fastfood_in_buffer_counts %>% rename(buffer_count = in_buffer_count),
    by = "store"
  ) %>%
  mutate(
    buffer_count = ifelse(is.na(buffer_count), 0, buffer_count),
    percent = round(buffer_count / total_count * 100, 1)  # 百分比
  )

friedchicken_long <- friedchicken_summary %>%
  pivot_longer(
    cols = c(total_count, buffer_count),
    names_to = "type",
    values_to = "count"
  ) %>%
  mutate(
    type = factor(type,
                   levels = c("total_count", "buffer_count"),
                   labels = c("總店面數", "500m內店面數"))
  )

store_labels <- friedchicken_long %>%
  group_by(store) %>%
  summarise(
    total_count = max(count[type == "總店面數"], na.rm = TRUE),
    buffer_count = max(count[type == "500m內店面數"], na.rm = TRUE),
    percent = round(buffer_count / total_count * 100, 1)
  ) %>%
  mutate(
    store_label = paste0(percent, "%")
  )

# 把 store_labels 合併回主資料框
friedchicken_long <- friedchicken_long %>%
  left_join(store_labels %>% select(store, store_label), by = "store")

friedchicken_long <- friedchicken_long %>%
  mutate(type = factor(type, levels = c("總店面數", "500m內店面數")))

# 畫圖
ggplot(friedchicken_long, aes(x = type, y = count, fill = type)) +
  geom_col(width = 0.6) +
  # 每個 bar 上顯示數字
  geom_text(aes(label = count), vjust = -0.5, size = 2.5, color = "#3C3C3C") +
  # 在最上面顯示 500m 內店面比例
  geom_text(
    data = store_labels,
    aes(x = 1.5, y = max(friedchicken_long$count, na.rm = TRUE) + 5, label = store_label),
    inherit.aes = FALSE, size = 3, color = "#3C3C3C", fontface = "bold"
  ) +
  facet_wrap(~ store, ncol = 3) +
  scale_y_continuous(limits = c(0, max(friedchicken_long$count, na.rm = TRUE) + 10)) +
  scale_fill_manual(values = c( "500m內店面數" = "#6FB7B7","總店面數" = "#9999CC")) +
  labs(
    title = "台北市炸雞速食店面總數 vs 學校500m內店面數比例",
    x = NULL, y = NULL, fill = NULL
  ) +
  theme_minimal(base_family = "STHeiti") +
  theme(
    strip.background = element_blank(),
    strip.text = element_text(size = 8, face = "bold", color = "#3C3C3C"),
    axis.text.x = element_blank(),
    axis.ticks.x = element_blank(),
    axis.text.y = element_blank(),
    axis.ticks = element_blank(),
    legend.position = "bottom",
    legend.text = element_text(size = 8, color = "#3C3C3C"),
    plot.title = element_text(
      hjust = 0.5, size = 12, face = "bold", color = "#3C3C3C"
    ),
    plot.background = element_rect(fill = "#F0F0F0", color = NA),
    panel.background = element_rect(fill = "#F0F0F0", color = NA),
    panel.grid.major = element_line(color = "grey80"),
    panel.grid.minor = element_blank()
  )

# 1️⃣ 合併 buffer 與 final_counts,產生分組
school_buffer_with_type <- school_buffer %>%
  st_join(final_counts %>% select(SID, has_public, has_private), by = "SID") %>%
  mutate(
    sports_group = case_when(
      has_public & has_private ~ "同時有公立與私立",
      has_public & !has_private ~ "只有公立",
      !has_public & has_private ~ "只有私立",
      TRUE ~ "無運動場館"
    )
  )

# 2️⃣ 繪圖
tmap_mode("plot")  # 靜態模式
## ℹ tmap mode set to "plot".
p <- tm_shape(taipei_village) +
  tm_polygons(col = "grey90", border.col = "white", alpha = 1) +

  tm_shape(school_buffer_with_type) +
  tm_fill(
    col = "sports_group",
    palette = c(
      "同時有公立與私立" = "#6FB7B7",
      "只有私立" = "#9999CC",
      "只有公立" = "#B766AD",
      "無運動場館" = "#AFAF61"
    ),
    title = "運動場館分組"
  ) +

  tm_shape(final_counts) +
  tm_dots(col = "black", size = 0.1, alpha = 0.8) +

  tm_layout(
    title = "台北市國小 500m Buffer:運動場館資源分布",
    title.size = 1,
    legend.outside = TRUE,
    bg.color = "#F0F0F0",
    panel.background = "#F0F0F0",
    legend.text.size = 0.6,
    legend.title.size = 0.8,
    frame = TRUE      # 🔴 保留邊框
  )
## 
## ── tmap v3 code detected ───────────────────────────────────────────────────────
## [v3->v4] `tm_polygons()`: use `fill_alpha` instead of `alpha`.[v3->v4] `tm_polygons()`: migrate the argument(s) related to the legend of the
## visual variable `fill` namely 'title' to 'fill.legend = tm_legend(<HERE>)'[v3->v4] `tm_dots()`: use `fill_alpha` instead of `alpha`.[v3->v4] `tm_layout()`: use `tm_title()` instead of `tm_layout(title = )`
p