Rで自作関数:ksj_station_passenger_data_parser

関数名: ksj_station_passenger_data_parser

国土交通省が公開している国土数値情報(KSJ)の駅別乗降客数データ(S12)をXML形式から読み込み、Rのデータフレームに変換する関数です。

具体的には、全国の各駅について、駅名・駅コード・運営会社・路線名などの基本情報と、年別乗降客数を1行1駅の形式に整形します。

また、各駅の位置情報(緯度・経度)をXML内の路線ジオメトリデータと結合することで、地図上への可視化や空間分析にも活用できる形で出力します。

Rコード

ksj_station_passenger_data_parser <- function(xml_file){
  
# 国土数値情報(駅別乗降客数)のxmlデータ をデータフレームに変換する
# 必要パッケージ: xml2

library(xml2)

# -------------------------------------------------------
# 1. ファイル読み込み
# -------------------------------------------------------
doc <- read_xml(xml_file)

# -------------------------------------------------------
# 2. 名前空間の定義
# -------------------------------------------------------
ns <- c(
  ksj   = "http://nlftp.mlit.go.jp/ksj/schemas/ksj-app",
  gml   = "http://www.opengis.net/gml/3.2",
  xlink = "http://www.w3.org/1999/xlink"
)

# -------------------------------------------------------
# 3. gml:Curve から緯度・経度を抽出
# -------------------------------------------------------
# posList には複数の座標ペア(緯度 経度 緯度 経度 ...)が入っているため、
# 全座標の平均値を代表点(中点)として使用する

curves <- xml_find_all(doc, "//gml:Curve", ns)

coords_df <- do.call(rbind, lapply(curves, function(cv) {
  cv_id    <- xml_attr(cv, "id")
  pos_node <- xml_find_first(cv, ".//gml:posList", ns)
  pos_text <- xml_text(pos_node)
  vals     <- as.numeric(strsplit(trimws(pos_text), "\\s+")[[1]])
  # 座標順は「緯度 経度 緯度 経度 ...」
  lat_vals  <- vals[seq(1, length(vals), by = 2)]
  lon_vals  <- vals[seq(2, length(vals), by = 2)]
  data.frame(
    cv_id     = cv_id,
    latitude  = mean(lat_vals),
    longitude = mean(lon_vals),
    stringsAsFactors = FALSE
  )
}))

# -------------------------------------------------------
# 4. 駅ノードを取得
# -------------------------------------------------------
stations <- xml_find_all(
  doc,
  "//ksj:TheNumberofTheStationPassengersGettingonandoff",
  ns
)

# -------------------------------------------------------
# 5. 各ノードからフィールドを抽出するヘルパー関数
# -------------------------------------------------------
get_text <- function(node, xpath, ns) {
  result <- xml_find_first(node, xpath, ns)
  if (is.na(result)) return(NA_character_)
  xml_text(result)
}

# -------------------------------------------------------
# 6. 乗降客数の年リストを動的に取得
# -------------------------------------------------------
# 最初の駅ノードの子要素名から passengerYYYY タグを特定する
# (XPathのワイルドカード * は属性・名前空間付きノードに非対応のため)
first_station  <- stations[[1]]
child_names    <- xml_name(xml_children(first_station))
passenger_tags <- child_names[grepl("^passengers\\d{4}$", child_names)]
years <- sort(as.integer(sub("passengers", "", passenger_tags)))

# -------------------------------------------------------
# 7. 駅データフレームの構築
# -------------------------------------------------------
records <- lapply(stations, function(node) {

  # station_ref は "#cv1" の形式 → "#" を除いて cv_id とする
  station_href <- xml_attr(xml_find_first(node, "ksj:station", ns), "href")
  cv_id <- sub("^#", "", station_href)

  base <- list(
    gml_id                        = xml_attr(node, "id"),
    cv_id                         = cv_id,
    stationName                   = get_text(node, "ksj:stationName",                   ns),
    stationCode                   = get_text(node, "ksj:stationCode",                   ns),
    groupCode                     = get_text(node, "ksj:groupCode",                     ns),
    administrationCompany         = get_text(node, "ksj:administrationCompany",         ns),
    routeName                     = get_text(node, "ksj:routeName",                     ns),
    railroadDivision              = get_text(node, "ksj:railroadDivision",              ns),
    railroadCompanyClassification = get_text(node, "ksj:railroadCompanyClassification", ns)
  )

  # 年別乗降客数
  pax <- lapply(years, function(yr) {
    as.integer(get_text(node, paste0("ksj:passengers", yr), ns))
  })
  names(pax) <- paste0("passengers", years)

  c(base, pax)
})

df_stations <- as.data.frame(
  do.call(rbind, lapply(records, as.data.frame, stringsAsFactors = FALSE)),
  stringsAsFactors = FALSE
)

# 乗降客数列を整数型に変換
int_cols <- grep("^passengers", names(df_stations), value = TRUE)
df_stations[int_cols] <- lapply(df_stations[int_cols], as.integer)

# -------------------------------------------------------
# 8. 緯度・経度を cv_id で結合
# -------------------------------------------------------
df <- merge(df_stations, coords_df, by = "cv_id", all.x = TRUE)

# 列順を整える(cv_id の直後に latitude / longitude が来るように)
col_order <- c("gml_id", "cv_id", "latitude", "longitude",
               "stationName", "stationCode", "groupCode",
               "administrationCompany", "routeName",
               "railroadDivision", "railroadCompanyClassification",
               paste0("passengers", years))
df <- df[, col_order]

# gml_id の番号順にソート
df <- df[order(as.integer(sub("sp", "", df$gml_id))), ]
rownames(df) <- NULL

# df を返す
return(df)
  
}

実行例

国土交通省ウェブサイト https://nlftp.mlit.go.jp/ksj/gml/datalist/KsjTmplt-S12-2024.html のデータから、2024年度(令和6年度)データ(2025年度整備データ)を確認します。

XMLデータの取得

xml_file <- 'D:/S12-25_GML/UTF-8/S12-25.xml'
df <- ksj_station_passenger_data_parser(xml_file = xml_file)

2024年度の駅別乗降客数(人/日)上位30駅

library(dplyr)

cat(paste0('2024年度データの総駅数: ',nrow(df),'\n'))

cat('\n--- 2024年度の乗降客数(人/日)上位30駅 ---\n')

df$passengers2024 %>% order(decreasing = T) %>% df[.,c("stationName","administrationCompany","routeName","passengers2024")] %>% head(30)
2024年度データの総駅数: 10534

--- 2024年度の乗降客数(人/日)上位30駅 ---
     stationName administrationCompany       routeName passengers2024
6046        渋谷              東急電鉄          東横線        1770430
7515        新宿        東日本旅客鉄道          中央線        1333618
6852        池袋        東日本旅客鉄道          山手線         998256
7634        東京        東日本旅客鉄道        東海道線         869128
4356        大阪        西日本旅客鉄道        東海道線         751006
7637        横浜        東日本旅客鉄道        東海道線         746020
624         新宿              京王電鉄          京王線         728874
8245      北千住              東武鉄道        伊勢崎線         688265
6862        渋谷        東日本旅客鉄道          山手線         648828
6859        品川        東日本旅客鉄道          山手線         575878
6124        池袋            東京地下鉄   4号線丸ノ内線         518135
6053        横浜              東急電鉄          東横線         514860
7848        大宮        東日本旅客鉄道          東北線         508440
7631        新橋        東日本旅客鉄道        東海道線         463256
2241    大阪梅田              阪急電鉄          神戸線         451568
3017        新宿            小田急電鉄        小田原線         450952
7389      秋葉原        東日本旅客鉄道          総武線         442842
4787        池袋              西武鉄道          池袋線         428121
4995        梅田    大阪市高速電気軌道 1号線(御堂筋線)         420718
8315        池袋              東武鉄道        東上本線         419499
5802      名古屋          東海旅客鉄道        東海道線         416867
7500        中野        東日本旅客鉄道          中央線         406312
7065      北千住        東日本旅客鉄道          常磐線         397464
7900        川崎        東日本旅客鉄道          南武線         388782
6217        綾瀬            東京地下鉄   9号線千代田線         384554
6166      西船橋            東京地下鉄     5号線東西線         383395
6040      中目黒              東急電鉄          東横線         381738
6067        目黒              東急電鉄          目黒線         379056
9390      名古屋              名古屋市     1号線東山線         369436
6853    高田馬場        東日本旅客鉄道          山手線         359332

最多は東急電鉄が運営する東横線の渋谷駅。1日当たりの乗降客数は177万430人。福岡県福岡市の人口(約160万人)以上の乗降客数です。

以上です。