ついついブログの文量が多めになる岩石です。今回は特に行数が長いです!おそらく当ブログで長さだけなら最長!挨拶短めにさっさと本文です。最後までお付き合いくださいませ。
昨年の夏は暑かったですね。今年も暑くなるんでしょうね。気象データを調べたくなりますよね?ブログの定番ネタではありますが、オープンデータを活用して「本当に例年より暑かった?」ってことを振り返りたいと思います。オープンデータはCSV形式で入手できるのですが、Excelなどではちょっと処理に手間が必要なので、Google Colaboratory上でR言語を使ってさくっと処理してみました。この記事ではその手順を紹介します。
R言語については過去にこんな記事を書いていますので、興味のある方はあわせてお読みください。
まず、データの取得です。今回は気象庁のサイトで公開されているデータを使用します。
気象庁|過去の気象データ・ダウンロード
取得できたCSVファイルの中身はこんな感じです。この選択項目だと10年分が1度でダウンロードできる最大量のようです。
オープンデータの形式は提供元の都合などにより利用したい自分の望む形式でないこともあります。今回取得したデータはそれほどではないですが、発行元URLや条件など様々なメタ情報が付帯していることも多く、出力データとしての必要性は理解できますが、利用する場面で不要な情報を含んでいることは珍しくありません。CSV形式の場合はExcelなどの表計算アプリで取り扱うことができますが、行と列の作りが意図した並びではなく、事前処理が面倒だったりします。その他、年や年度またぎなどの処理も結構大変です。JSONなどの表計算アプリでは取り扱えない形式の場合もあります(おそらく。未調査です)。
その点、様々な形式のデータを何度でも確認しながら処理できるRはとても使いやすいです。また数年前よりGoogle Colaboratoryで使えるようになったこともあり、導入の面倒さもなくなりました。コード化することでの使い回しや再現などのメリットもありますね。
Google Colaboratory(通称: Colab)は、ブラウザ上でコードを実行できるノートブック環境です。Googleアカウントがあればすぐに使え、ローカルへのインストールや環境構築は不要です。
以前よりPython利用でよく使われていましたが、数年前からRも利用できるようになりました。RStudioなどをインストールせずに、ブラウザだけでRのコードを試せる点は、時々だけ簡単に試したい用途にはとても助かります。
今回の記事では、「環境構築を省いて、オープンデータの処理そのものに集中する」という目的でColabを使います。本格的なR言語での開発などにはRStudioの方が向いていると思いますが、CSVの集計や可視化であればColabでも十分でした。なお今回の記事ではColabの使い方そのものについてはあまり書きません。
環境の準備としてノートブックを新規作成しておきます。ColabはデフォルトではPythonが動作する環境になっています。実行時に %%R をコードの文頭につけておくとR言語のコードとして処理されますが、毎回面倒なのでランタイムを変更しておきます。
Colabは立ち上げたそのままではグラフに日本語が表示できません。今回手間を省いて英語での表示にしようと思ったのですが、英語表記に心が折れたので日本語が使えるようにします。ターミナルを開き、 apt install -y fonts-ipafont-gothic を実行してIPAフォントをインストールします。
データ処理と日時の取り扱いのためのライブラリを呪文のように呟いておきます。
library(tidyverse)
library(lubridate)
グラフでのデフォルトフォントをIPAフォントにするため、コードセルで下記を実行します。
install.packages("showtext")
library(showtext)
font_add("ipa", "/usr/share/fonts/truetype/ipafont-gothic/ipag.ttf")
showtext_auto()
theme_set(theme_minimal(base_family = "ipa"))
取得したCSVファイルを読み込みます。ファイル中の上6行は処理そのものには不要なのでスキップします。
file <- "data.csv"
col_names <- c(
"date",
"tmax", "tmax_q", "tmax_h",
"tmin", "tmin_q", "tmin_h",
"rain", "rain_no_phen", "rain_q", "rain_h"
)
raw <- read_csv(
file,
skip = 6,
col_names = col_names,
locale = readr::locale(encoding = "CP932"),
show_col_types = FALSE
)
df <- raw %>%
transmute(
date = ymd(date),
tmax = as.numeric(tmax),
tmin = as.numeric(tmin),
rain = as.numeric(rain)
) %>%
filter(!is.na(date))
print(head(df, 10)) #10行確認のための表示
無事読み込めてますね。
1年単位で猛暑日(最高気温が35℃以上)と熱帯夜(最低気温が25℃以上)を集計します。
(最低気温が夕〜翌朝に計測とは限らないのですが、「ざっくり」ということで...)
yearly <- df %>%
mutate(
year = year(date),
hot_day = tmax >= 35, # 猛暑日
summer_day = tmax >= 30, # 真夏日
tropical_night = tmin >= 25 # 熱帯夜
) %>%
group_by(year) %>%
summarise(
hot_days = sum(hot_day, na.rm = TRUE),
summer_days = sum(summer_day, na.rm = TRUE),
tropical_nights = sum(tropical_night, na.rm = TRUE),
rain_total_mm = sum(rain, na.rm = TRUE),
tmax_mean = mean(tmax, na.rm = TRUE),
tmin_mean = mean(tmin, na.rm = TRUE),
.groups = "drop"
)
print(yearly)
グラフに表示します。
p1 <- yearly %>%
pivot_longer(cols = c(hot_days, summer_days, tropical_nights),
names_to = "metric", values_to = "count") %>%
mutate(metric = recode(metric,
hot_days = "猛暑日(最高35℃以上)",
summer_days = "真夏日(最高30℃以上)",
tropical_nights = "熱帯夜(最低25℃以上)")) %>%
ggplot(aes(x = year, y = count, group = metric)) +
geom_line() +
geom_point() +
labs(x = NULL, y = "日数", title = "年別:猛暑日・真夏日・熱帯夜の回数", subtitle = "(気象庁公開データ)") +
theme_minimal()
print(p1)
ggsave("yearly_hotdays.png", p1, width = 8, height = 4, dpi = 160)
グラフの線は、上から真夏日・熱帯夜・猛暑日です。ここ数年の猛暑日の数も多いのですが、真夏日と熱帯夜の増え方がすごいですね。
p2 <- ggplot(yearly, aes(x = year, y = rain_total_mm)) +
geom_col() +
labs(x = NULL, y = "降水量合計(mm)", title = "年別:降水量合計") +
theme_minimal()
print(p2)
ggsave("yearly_rain_total.png", p2, width = 8, height = 4, dpi = 160)
降水量はそれほど変化無いようですね。ふむふむ。
次に、最近は雪の話題も多いので、同サイトから積雪に関するデータを取得し、いろいろ調べてみました。
取得できたCSVファイルの中身はこんな感じです。この選択項目だと2011年のデータまで取得可能でした。
気温と降雨については年で集計しましたが、雪については年を跨いでシーズンとして集計した方が感覚に合います。毎年11月から翌年10月までを1シーズンとして集計することにしました。
file2 <- "data2.csv"
snow <- read_csv(
file2,
skip = 6,
col_names = c(
"date",
"snowfall", "snowfall_no_phen", "snowfall_q", "snowfall_h",
"snow_depth", "snow_depth_q", "snow_depth_h"
),
locale = locale(encoding = "CP932"),
show_col_types = FALSE
) %>%
transmute(
date = ymd(date),
snowfall = as.numeric(snowfall),
snow_depth = as.numeric(snow_depth)
) %>%
filter(!is.na(date)) %>%
mutate(
season = if_else(month(date) >= 10, year(date), year(date) - 1)
)
「雪が降った日」と「積雪がある日」は違うので分けて日数を表示します。
season_days <- snow %>%
mutate(
snowfall_day = snowfall > 0,
snow_cover_day = snow_depth > 0
) %>%
group_by(season) %>%
summarise(
snowfall_days = sum(snowfall_day, na.rm = TRUE),
snow_cover_days = sum(snow_cover_day, na.rm = TRUE),
.groups = "drop"
)
season_days
可視化します。
ggplot(
season_days %>%
pivot_longer(c(snowfall_days, snow_cover_days),
names_to = "type", values_to = "days") %>%
mutate(type = recode(type,
snowfall_days = "降雪日数",
snow_cover_days = "積雪日数")),
aes(x = season, y = days, group = type)
) +
geom_line() +
geom_point() +
labs(x = "シーズン開始年", y = "日数") +
theme_minimal()
「よく降るシーズン」と「シーズンごとの最深積雪」はどうだったかを調べます。
season_summary <- snow %>%
group_by(season) %>%
summarise(
snowfall_total = sum(snowfall, na.rm = TRUE), # 降雪量合計
max_snow_depth = max(snow_depth, na.rm = TRUE), # 最大積雪
.groups = "drop"
)
season_summary
scale_factor <- max(season_summary$snowfall_total, na.rm = TRUE) /
max(season_summary$max_snow_depth, na.rm = TRUE)
ggplot(season_summary, aes(x = season)) +
geom_col(aes(y = snowfall_total),
fill = "grey70") +
geom_line(aes(y = max_snow_depth * scale_factor),
color = "black") +
geom_point(aes(y = max_snow_depth * scale_factor),
color = "black") +
scale_y_continuous(
name = "降雪量合計 (cm)",
sec.axis = sec_axis(
~ . / scale_factor,
name = "最大積雪 (cm)"
)
) +
labs(x = "シーズン開始年") +
theme_minimal()
雪の量は年によって、かなりばらつきありますね。また雪の量は同じぐらいでも積もったシーズンとそうでないシーズンがあるのがわかります。
ggplot(season_summary,
aes(x = snowfall_total, y = max_snow_depth)) +
geom_point() +
geom_smooth(method = "lm") +
labs(
x = "降雪量合計 (cm)",
y = "最深積雪 (cm)"
) +
theme_minimal()
おおよそは関係性ありそうですが、外れるシーズンも結構ありますね。こういった関係性を調べるのは面白いですね。
最後に雪が積もっている(最初の積雪から積雪記録がそれ以降なくなる)期間はどれくらいか。初雪や終雪の時期は、冬タイヤの交換タイミングを検討する際のリアルな指標になりそうです。
snow_season <- snow %>%
filter(snow_depth > 0) %>%
mutate(
season = if_else(month(date) >= 10,
year(date),
year(date) - 1)
) %>%
group_by(season) %>%
summarise(
first_day = min(date),
last_day = max(date),
duration_days = as.integer(last_day - first_day) + 1,
.groups = "drop"
)
snow_season
ggplot(snow_season, aes(x = season, y = duration_days)) +
geom_col() +
labs(x = "シーズン開始年", y = "積雪期間(日)") +
theme_minimal()
せっかく気温のデータと降雪のデータがあるので、冬の気温と降雪の関係について調べてみました。
まずこちらのデータもシーズン集計にします。
temp_season <- df %>% # 前半で使った気象データ
filter(month(date) %in% c(12, 1, 2)) %>% # 冬(12–2月)
mutate(
season = if_else(month(date) >= 10,
year(date),
year(date) - 1)
) %>%
group_by(season) %>%
summarise(
tmax_mean = mean(tmax, na.rm = TRUE),
tmin_mean = mean(tmin, na.rm = TRUE),
.groups = "drop"
)
temp_season
気温と積雪のデータをつなげます。
snow_season <- snow %>%
group_by(season) %>%
summarise(
snowfall_total = sum(snowfall, na.rm = TRUE),
max_snow_depth = max(snow_depth, na.rm = TRUE),
.groups = "drop"
)
winter_summary <- temp_season %>%
inner_join(snow_season, by = "season")
winter_summary
ggplot(winter_summary,
aes(x = tmin_mean, y = max_snow_depth)) +
geom_point() +
geom_smooth(method = "lm") +
labs(
x = "冬季の平均最低気温 (℃)",
y = "最大積雪 (cm)"
) +
theme_minimal()
ggplot(winter_summary,
aes(x = tmin_mean, y = snowfall_total)) +
geom_point() +
geom_smooth(method = "lm") +
labs(
x = "冬季の平均最低気温 (℃)",
y = "降雪量合計 (cm)"
) +
theme_minimal()
cor(
winter_summary$tmin_mean,
winter_summary$max_snow_depth,
use = "complete.obs"
)
それほど相関関係は強く無さそうです。(なんとなく予想はしていましたが)
印象として近年は夏の暑さは顕著にデータにも現れていますが、雪の量や降った期間はあまりデータでは変わって見えないと感じました。
今回ColabでRを動かして気象のオープンデータを使った簡単な調査をしてみました。まず気象データからの集計を可視化したことにより、近年の猛暑についてはっきりと感じ取ることができました。著しく暑い地点のデータではもっと顕著な結果になったかもしれません。また積雪については年によるばらつきが大きいなと感じました。島根県松江市のデータで調べたのですが、 極端に暑くはならない ・ 雪は積もるけど豪雪ではない 地点でのデータなのでばらつきが大きかったのかもしれないです。
Google ColaboratoryでRを動かしてみたことについては、端末へのインストールが不要で準備が楽なだけでなく、修正しながらの処理も楽しくできました。また、デバッグやわからないところについてGeminiに手伝ってもらうことができ、短時間で処理を進めることができました。これは本当に助かりました。
現実的な話をすると、こう言った分析はAIサービスに投げかけてしまえば、このようなことをしなくてももっと深い解析までしてくれます。しかしながら、AIのOUTPUTは自分の成果ではなくあくまで参考情報なので、その裏付けになるものを用意する必要があると思います。そういったシーンでは何かしらのツールを使って、自分で読み解いておく必要があると思ってます。
表計算アプリは身近で触れやすいですが、同じ処理を全体的にちょっと変更するなどの処理も大変なので、Excel猛者の方にもお勧めできると感じました。
【スタッフ募集中】
「My Redmine」など弊社提供サービスのお問い合わせ対応を担当するカスタマーサポートスタッフを募集しています。
弊社での勤務に関心をお持ちの方は、知り合いの弊社社員・関係者を通じてご連絡ください。採用情報の詳細
|
JSON以外にもXMLやCSVの処理もできます。解析に強い言語なので、様々な形式のデータを連携させた活用に向いてます。 |
|
今後の人員増加を見据えて本社オフィスを増床しました。社員皆が快適に働ける環境を整えていきたいです。 |
|
AWSサポートを利用してAmazon ECS Execが動作しない問題が解決できました。 |
|
Redmineプラグインが本体の変更等で動かなくなっていることを早く検知できるように、プラグインの自動テスト環境を整備して、結果をSlackに通知するようにしました。 |
|
|
スマートバンドで睡眠計測をしてみて、自分が思っているよりしっかり眠れていることがわかりました。 |
|
My Redmine 2026 新春アップデートのお知らせ(RedMica 4.0対応) 2025年12月にMy Redmine 2026 新春アップデートを実施しました。 |
|
プロジェクト管理ツール「RedMica」バージョン 4.0.0をリリース Redmine互換のオープンソースソフトウェア 今日使える明日のRedmine「RedMica」のバージョン4.0.0をリリースしました。 |
|
Redmineの最新情報をメールでお知らせする「Redmine News」配信中 新バージョンやセキュリティ修正のリリース情報、そのほか最新情報を迅速にお届け |

