関数名: 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万人)以上の乗降客数です。
以上です。

