AIが生成しがちな「いかにもAIっぽい」日本語の定型表現・水増し・空疎な形容を取り除き、読者の信頼を損なわない自然な文章に直す。記事・原稿・プレスリリース・告知・SNS投稿など、AIの支援を受けて書いた文章を公開前に点検するときに使う。「AI臭を消したい」「文章が機械っぽい」「人が書いたように直したい」などの依頼で使う。
data-hodo
データ報道(データジャーナリズム)の手順を支援する。データセットの分析、グラフや地図の作成、雑然としたデータのクリーニング、統計の算出、データに基づく記事の構築に使う。「e-Statのデータを分析したい」「人口あたりで比較したい」「グラフが誤解を招かないか確認したい」「統計から記事を組み立てたい」などの依頼で使う。
記者・編集者・調査担当向け。
実行スコア 未検証
インストール方法を見る含まれるファイル(1)
- SKILL.md50.0 KB
SKILL.md(原文)
インストールする前に、エージェントに与えられる指示の中身を確認できます。
データ報道(データジャーナリズム)
データを見つけ、素性を確かめ、クリーニングし、分析し、可視化し、出典を明記して記事にするまでの体系的な手順。 記者の問いを聞き取り、フェアな比較・誤読防止・再現性を満たす形で支援する。数値の最終確認と公表判断は記者が行う。
注意:統計の更新頻度・定義・公表日程は変わる。重要な数値は公表元の最新資料で必ず確認すること。本スキルは一般的な手引きであり、法的・統計的助言ではない。
データ報道の記事設計
方法論の枠組み
データジャーナリズムの土台は、米国の記者フィリップ・マイヤーが著書『The New Precision Journalism(精密ジャーナリズム)』で示した「報道を科学のように扱う」という考え方にある。科学的手法を取り入れる。
- 観察し、問いを立てる
- 問いを調べ、データを収集・保存・参照できるようにする
- 仮説を立てる
- 仮説を検証する(取材・文書などの定性的手法と、データ分析などの定量的手法の両方で)
- 結果を分析し、最も重要な発見に絞り込む
- 読者に提示する
この過程は一直線ではなく、行き来しながら繰り返す。
データ記事の構成
1. つかみ(核心の一段落)
- 最も重要な発見は何か
- なぜ読者にとって重要か
- 人々への影響は何か
2. 証拠
- データを示す
- 手法を説明する
- 限界を認める
3. 文脈
- 過去と比べてどうか
- 他の地域と比べてどうか
- 趨勢(トレンド)はどうか
4. 人間の要素
- データを体現する個別の事例
- 専門家の解釈
- 当事者の声
5. 含意
- これは今後何を意味するか
- どんな問いが残るか
- どんな行動につながりうるか
6. 手法の注記欄(メソドロジー・ボックス)
- データはどこから来たか
- どう分析したか
- 限界は何か
- 読者がさらに調べるには
手法の開示テンプレート
## この分析の手法
### データの出典
[すべての出典を、リンクと取得日とともに列挙]
### 対象期間
[正確にどの期間を対象にしたか]
### 定義
[重要な用語と、それをどう操作的に定義したか]
### 分析の手順
1. [分析の最初の手順]
2. [2番目の手順]
3. [以降続く…]
### 限界
- [限界1]
- [限界2]
### 除外したものとその理由
- [除外した区分]:[理由]
### 検証
[発見をどう検証・確認したか]
### コードとデータの公開
[コードやデータを共有する場合はリポジトリ等のリンク]
### 連絡先
[読者が問い合わせる方法]
データの入手
最新データの取得(鮮度の鉄則)
最初に「これは最新の調査年か」を必ず確認する。 古いデータを使うのはデータ報道の重大な失敗。 e-Stat API は次の手順で最新表を選ぶ(最初の検索ヒットをそのまま使わない)。
- getStatsList で候補を出し、
SURVEY_DATE降順に並べるか、surveyYears(例:直近数年)で絞って最新調査年の表を選ぶ。- 取得の3段:getStatsList(最新表を決定)→ getMetaInfo(分類・表章のコード特定)→ getStatsData(取得)。
- 分母(人口)は「人口推計」の最新年を使う(年次更新)。古い国勢調査(5年毎)で代用しない。
- 人口あたり等の指標は、分子と分母の年を揃える(例:分子2024 → 分母も2024の人口推計)。
- データ年・調査年を必ず明記。現在から大きく古い場合は、最新を探すか、古い理由を述べる。
- 定義の粒度に注意:産業分類は 大分類>中分類>小分類>細分類。対象が変わると数も順位も変わる。
- 例:純粋な書店=細分類「書籍・雑誌小売業(古本を除く)」。**小分類「書籍・文房具小売業」**は文房具・古本・新聞を含み広い。
- 集計の但し書き(例:「雇用者のいない個人経営を除く」)も数値を左右する。「どの定義の・いつの数字か」を必ず添える。
反面教師:2026年に書店データを2016年(経済センサス)・人口を2010年(国勢調査)で作ってしまった失敗例がある。 実際には2024年(経済センサス基礎調査)・人口推計2024が普通に取得できた。最新確認を省くと古い数字で報じてしまう。
公的データの主な入手先
国の統計・データ
総合窓口:
- e-Stat(政府統計の総合窓口、e-stat.go.jp) — 日本の政府統計を横断的に検索・取得できる中核サイト。多くの統計が CSV・Excel・データベース API で提供される。API 機能(アプリケーションID登録制)で機械的に取得できる。
- DATA.GO.JP(データカタログサイト) — 各府省のオープンデータを横断検索する政府のカタログ。
- RESAS(地域経済分析システム、resas.go.jp) — 内閣府・経済産業省。人口・産業・観光・雇用などを地域別に可視化。API も提供。
- 地方公共団体のオープンデータポータル — 各都道府県・市区町村が公開(検索:「[自治体名] オープンデータ」)。様式・粒度は自治体ごとに異なる。
分野別:
- 総務省統計局 — 国勢調査、人口推計、労働力調査、家計調査、消費者物価指数(CPI)、小売物価統計など。
- 内閣府 — 国民経済計算(GDP・SNA)、景気動向指数、消費動向調査。
- 厚生労働省 — 人口動態統計(出生・死亡・婚姻等)、毎月勤労統計(賃金・労働時間)、賃金構造基本統計、患者調査、各種保健・医療・福祉統計。
- 日本銀行(時系列統計データ検索サイト) — 金融・物価・国際収支・資金循環などのマクロ統計。
- 財務省 — 貿易統計、国債・財政、予算・決算。
- 国税庁 — 民間給与実態統計調査、税収統計。
- 経済産業省 — 鉱工業指数、商業動態統計、各種産業統計。
- 農林水産省 — 作物統計、漁業・水産統計、食料需給表。
- 国土交通省 — 建築着工統計、自動車保有・リコール情報、交通・物流統計。国土数値情報(行政界・道路・施設等のGISデータ)。
- 気象庁 — 過去の気象データ(気温・降水量・台風等)、地震・火山、防災情報。観測データは CSV ダウンロード可。
- 環境省 — 大気・水質などの環境数値データベース、温室効果ガス排出量。
企業・金融の開示:
- EDINET(金融庁) — 上場企業等の有価証券報告書・四半期報告書・大量保有報告書。XBRL/API で機械取得可能。
- 法人番号公表サイト(国税庁) — 法人名・所在地・法人番号の照合に有用。
- gBizINFO(経済産業省) — 法人活動情報(補助金・調達・許認可等)を横断。
医薬・健康・安全:
- PMDA(医薬品医療機器総合機構) — 医薬品・医療機器の添付文書、副作用報告、回収情報。
- 感染症(感染症発生動向調査 等) — 国の感染症サーベイランス。厚労省のオープンデータも参照。
- ITARDA(交通事故総合分析センター)/警察庁 — 交通事故統計。
裁判・行政:
- 裁判所「裁判例検索」 — 判例の検索。事件記録そのものの閲覧は別途の手続による。
- 官報 — 法令・公告・破産・叙位叙勲等の公示。
政治資金・選挙:
- 総務省/各都道府県選挙管理委員会 — 政治資金収支報告書、選挙結果。一部はPDF中心のため後述のPDF処理が必要なことが多い。
国際比較:
- OECD、世界銀行オープンデータ(World Bank Open Data)、Eurostat、UN Data、IMF — 国際比較に使える主要データ。国ごとの定義差に注意。
学術・ミクロデータ:
- SSJDA(東京大学社会科学研究所 附属社会調査・データアーカイブ) — 社会調査の二次分析用データ(申請制)。
- 各大学・研究機関のデータアーカイブ。
削除・改廃されたデータの保全(出典が消えた場合に使う):
- 国立国会図書館 インターネット資料収集保存事業(WARP) — 府省・自治体サイトの過去スナップショット。
- Internet Archive(Wayback Machine) — 個別ページ単位の復元に有用。
- 統計は改定・系列変更・調査の中止が起こりうる。**取得時点で必ず保存(ダウンロード+取得日記録)**しておく。
注:米国固有のデータ源(Data.gov、Census Bureau、BLS、FRED、SEC EDGAR、CDC WONDER、PACER 等)は日本の相当機関に置換した。日本に相当物がない一部の制度別表は割愛している。
データを請求して入手する
公的に未公開のデータは情報公開請求で取得できることがある。請求書の起草・不開示への対応・審査請求などの手続は joho-kokai-seikyu(情報公開請求)スキルを参照。データ特有のコツ:
- 文書だけでなく**データベース(表形式の生データ)**を請求する。
- **データ定義書(コード表・項目定義・レイアウト)**もあわせて請求する。
- **元の形式(CSV、Excel、データベースのダンプ)**を求める。PDF やスキャン画像は避ける。
- 必要な項目を列単位で具体的に指定し、計算済みの列が欲しい場合はその旨を書く。
- 更新され続けるデータは更新頻度(日次・月次・四半期)を尋ね、継続取材なら定期的な提供を求める。
自前でデータセットを作る
- 公開情報のスクレイピング(robots.txt・利用規約・アクセス頻度の制限を尊重する)
- 読者からの投稿によるクラウドソーシング
- 文書の体系的なレビュー
- 手法を文書化したアンケート調査
報道機関向けの商用データ
- 日経テレコン、商用の企業・信用情報データベース、業界専門データベースなど。
- 所属機関の図書館・契約サービス経由で使えるものがある。
データのクリーニングと前処理
よくあるデータの問題
from typing import Any
import pandas as pd
import numpy as np
from rapidfuzz import fuzz
from itertools import combinations
# 物価調整(実質化)用。日本のCPIは総務省統計局が公表。
# World Bank の指標(country='JP')でも近似できる。後述の adjust_for_inflation 参照。
import wbdata
def standardize_name(name: Any) -> str | None:
"""氏名の表記を統一する。"""
if pd.isna(name):
return None
name = str(name).strip()
# 全角・半角スペースの揺れを統一
name = name.replace(" ", " ")
# 「姓, 名」形式を「姓 名」に直す
if ',' in name:
parts = name.split(',')
name = f"{parts[0].strip()} {parts[1].strip()}"
return name
def parse_date(date_str: Any) -> pd.Timestamp | None:
"""さまざまな形式の日付を解釈する(和暦混在時は事前に西暦へ変換しておく)。"""
if pd.isna(date_str):
return None
formats = [
'%Y-%m-%d', '%Y/%m/%d', '%Y年%m月%d日',
'%m/%d/%Y', '%d-%b-%y', '%m-%d-%Y'
]
for fmt in formats:
try:
return pd.to_datetime(date_str, format=fmt)
except:
continue
# 最後に pandas の汎用パーサーで試す
try:
return pd.to_datetime(date_str)
except:
return None
def handle_missing(
df: pd.DataFrame,
thresh: int | None = None,
per_thresh: float | None = None,
required_col: str | None = None,
) -> pd.DataFrame:
"""欠損が閾値を超える場合に、必須列が欠けた行を除外する。"""
if required_col is None or df.empty:
return df
if required_col not in df.columns:
return df
missing = df[required_col].isna().sum()
if thresh is not None and missing >= thresh:
return df.dropna(subset=[required_col]).reset_index(drop=True).copy()
if per_thresh is not None and (missing / len(df) * 100) >= per_thresh:
return df.dropna(subset=[required_col]).reset_index(drop=True).copy()
return df
def handle_duplicates(df: pd.DataFrame, thresh: int | None = None) -> pd.DataFrame:
"""重複行が閾値を超える場合に重複を除去する。"""
if thresh is not None and df.duplicated().sum() >= thresh:
return df.drop_duplicates().reset_index(drop=True).copy()
return df
def flag_similar_names(df: pd.DataFrame, name_col: str, threshold: int = 85) -> pd.DataFrame:
"""類似した氏名(表記揺れによる重複候補)にフラグを立てる。"""
names = df[name_col].dropna().unique()
# combinations() で入れ子ループと重複比較を避ける
dup_names: set[Any] = {
name
for name1, name2 in combinations(names, 2)
if fuzz.ratio(str(name1).lower(), str(name2).lower()) >= threshold
for name in (name1, name2)
}
df['has_similar_name'] = df[name_col].isin(dup_names)
return df
def flag_outliers(series: pd.Series, method: str = 'iqr', threshold: float = 1.5) -> pd.Series:
"""統計的な外れ値にフラグを立てる。"""
if method == 'iqr':
Q1 = series.quantile(0.25)
Q3 = series.quantile(0.75)
IQR = Q3 - Q1
lower = Q1 - threshold * IQR
upper = Q3 + threshold * IQR
return (series < lower) | (series > upper)
elif method == 'zscore':
z_scores = np.abs((series - series.mean()) / series.std())
return z_scores > threshold
# わかりやすい変数名を使い、メソッドチェーンでつなぐ
data_clean = (pd
# 雑然としたデータを読み込む(raw_data は仮の置き換え対象)
# ファイル形式に合った読み込み関数を使うこと
.read_csv('../data/raw/raw_data.csv')
# データ型の補正
# 分析に適した型にそろえる
.assign(# 数値に変換(エラーは欠損に)
amount = lambda x: pd.to_numeric(x['amount'], errors='coerce'),
# カテゴリ型に変換(メモリ節約・順序付けが可能)
status = lambda x: pd.Categorical(x['status']))
.assign(
# 表記の不統一
# 問題:氏名の形式がばらばら
# 例:「山田, 太郎」「山田 太郎」「山田 太郎」
name_clean = lambda x: standardize_name(x['name']),
# 日付の不統一
# 問題:複数の日付形式が混在
# 例:「2024-01-15」「2024/01/15」「2024年1月15日」
parse_date = lambda x: parse_date(x['date']),
# 外れ値
# 入力ミスの可能性がある値を識別
amount_outlier = lambda x: flag_outliers(x['amount']),
)
# あいまい重複(同一ではないが似ている)
# レコードリンケージや目視確認を併用する
.pipe(flag_similar_names, name_col='name_clean', threshold=85)
# 欠損値
# 対処法は文脈次第。不完全な行を落とす必要があるときに required_col を指定
.pipe(handle_missing, required_col='amount', per_thresh=20.0)
# 重複 — 重複行を見つけて処理
.pipe(handle_duplicates, thresh=1)
.reset_index(drop=True)
.copy())
補足:日本のデータ特有の前処理として、(1) 和暦→西暦の変換(「令和6年」等)、(2) 全角数字・全角スペースの半角化、(3) 自治体コード・郵便番号のゼロ落ち(先頭0の消失)対策で文字列として読み込む、(4) 「-」「…」「*」「秘匿」など欠測・秘匿記号**を欠損に変換、(5) 結合用キー(市区町村名)の新旧合併・表記揺れの突き合わせ、がよく必要になる。
分析前のデータ検証チェックリスト
## 分析前のデータ検証
### 構造の確認
- [ ] 行数が想定どおりか
- [ ] 列数と列名が正しいか
- [ ] データ型が適切か
- [ ] 想定外の空列がないか
### 内容の確認
- [ ] 日付の範囲が妥当か
- [ ] 数値が想定の範囲内か
- [ ] カテゴリの値が想定の選択肢と一致するか
- [ ] 地理データ(住所・自治体コード)が正しく解決するか
- [ ] 一意であるべきIDが一意か
### 整合性の確認
- [ ] 合計が想定値と一致するか
- [ ] クロス集計の縦横が合うか
- [ ] 関連する項目どうしが矛盾しないか
- [ ] 時系列が連続しているか(系列変更・調査中断がないか)
### 出典の確認
- [ ] 元の出典まで遡れるか
- [ ] 手法が文書化されているか
- [ ] 既知の限界が把握できているか
- [ ] 更新頻度を理解しているか
AIを使った分析の注意点
AIツールは探索・コード生成・パターン発見を速めるが、記者が警戒すべき特有の失敗様式がある。海外では、AIが捏造した存在しない判例引用が法廷で問題になった事例(Mata v. Avianca、2023年)や、チャットボットが述べた誤った払い戻し方針が企業を拘束すると判断された事例(エア・カナダ、2024年)が、「LLMの捏造が公表事実として扱われた」典型例として知られる。
LLMが確実に間違えること
- 大量計算 — それらしく見える合計・割合・比率が1〜15%ずれることがある。LLMが出した数値は、公表前に必ず自分で pandas・SQL・R などで計算し直す。
- 出典の引用 — 存在しないURL・論文名・データセット名・条項を、もっともらしく捏造する。引用された出典は一つずつ実際に開いて確認する。
- データの列構成 — データセットの構造を説明させると、存在しない列を作り出すことがある。実際のスキーマ(
df.dtypes、df.columns.tolist())と突き合わせる。 - 統計的推論 — 相関と因果を混同し、標本統計と母集団のパラメータを取り違え、検定を誤用する。分析上の主張は「発見」ではなく「検証すべき仮説」として扱う。
手法の開示
データのクリーニング・分析・可視化・原稿作成のいずれかでAIを使った場合は、手法の注記欄に明示する。どの工程に人間の確認が入り、どの工程が自動だったかを、編集者と読者が知る必要がある。
- ツール名とバージョンを書く(例:「クリーニング処理のコード草案に Claude を使用。コードは[記者名]が確認・実行した」)。
- 何を検証したかを書く(例:「すべての数値結果を pandas で再計算し、引用した出典はすべて個別に確認した」)。
- 必要に応じて、検証していない部分も書く。
再現性
AIに分析コードを生成させたときは、プロンプト・モデル名・バージョンをコードと一緒に保存する。AIが生成したコードも手法の一部であり、同じデータで別の記者が再現できる状態にしておく。
報道のための統計分析
文脈を伴う基本統計
# どんなデータセットにも使える基本統計
def describe_for_journalism(df: pd.DataFrame, col: str) -> pd.DataFrame:
"""記者にわかりやすい統計量を生成する。"""
stats = df[col].describe(percentiles=[0.25, 0.5, 0.75, 0.9, 0.99])
# describe() の出力に歪度(分布の偏り)を追加
stats['skewness'] = df[col].skew()
return stats.to_frame(name=col)
# 解釈の例
stats = describe_for_journalism(salaries, 'salary')
print(f"""
分析結果
---------------
{stats['count']:,.0f} 件の給与記録を分析した。
中央値は {stats['50%']:,.0f} 円。すなわち働く人の半数がこれより多く、
半数がこれより少なく稼いでいる。
平均値は {stats['mean']:,.0f} 円で、中央値より
{'高い' if stats['mean'] > stats['50%'] else '低い'}。これは分布が
{'右に裾を引いている(高所得層に引き上げられている)' if stats['skewness'] > 0 else '左に裾を引いている'}ことを示す。
上位10%は少なくとも {stats['90%']:,.0f} 円稼いでいる。
上位1%は少なくとも {stats['99%']:,.0f} 円稼いでいる。
""")
平均だけで語らない。中央値は外れ値に強く、「ふつうの人」の姿を表しやすい。所得・地価・売上など裾の長い分布では、平均が実感とずれる。中央値・分位点・分布の形(歪度)を併せて見る。
比較と文脈(フェアな比較)
# ある列の変化指標を計算する
def calculate_change(df: pd.DataFrame, col: str, periods: int = 1) -> pd.DataFrame:
"""pandas の組み込みメソッドで変化指標を追加する。
引数:
df: 入力データフレーム
col: 変化を計算する対象列
periods: 何行前と比べるか(1=前の行、12=月次データの前年同月比)
"""
return df.assign(
absolute_change=df[col].diff(periods),
percent_change=df[col].pct_change(periods) * 100,
direction=np.sign(df[col].diff(periods)).map({1: '増加', -1: '減少', 0: '横ばい'})
)
# 使い方:
# changes = data_clean.pipe(calculate_change, 'revenue', periods=12) # 月次データの前年同月比
# 人口あたりの計算(フェアな比較に不可欠)
def per_capita(value: float, population: float, multiplier: int = 100000) -> float:
"""人口あたりの比率を計算する。"""
return (value / population) * multiplier # 人口10万人あたりが標準
# 例:犯罪発生率
city_a = {'crimes': 5000, 'population': 100000}
city_b = {'crimes': 8000, 'population': 500000}
rate_a = per_capita(city_a['crimes'], city_a['population'])
rate_b = per_capita(city_b['crimes'], city_b['population'])
print(f"A市:人口10万人あたり {rate_a:.1f} 件")
print(f"B市:人口10万人あたり {rate_b:.1f} 件")
# 総数はB市が多いが、発生率はA市の方が高い!
def adjust_for_inflation(
amount: float | pd.Series,
from_year: int | pd.Series,
to_year: int,
country: str = 'JP'
) -> float | pd.Series:
"""物価変動を調整する(実質化)。スカラーでも Series でも動く(.assign 用)。
引数:
amount: 調整する金額
from_year: 元の金額の年
to_year: いつの価格水準に合わせるか
country: ISO 2文字の国コード(既定 'JP')。World Bank の消費者物価指数
(FP.CPI.TOTL)を用いる。日本でより正確な値が必要なら、
総務省統計局の消費者物価指数(CPI、2020年基準等)に置き換えること。
"""
cpi_data = wbdata.get_dataframe(
{'FP.CPI.TOTL': 'cpi'},
country=country
)['cpi'].to_dict()
from_cpi = pd.Series(from_year).map(cpi_data) if isinstance(from_year, pd.Series) else cpi_data[from_year]
to_cpi = cpi_data[to_year]
return amount * (to_cpi / from_cpi)
# 使い方:
# adjust_for_inflation(100, 2000, 2024) # 日本(既定)
# adjust_for_inflation(100, 2000, 2024, country='US') # 米国
# df.assign(real24=lambda x: adjust_for_inflation(x['amount'], x['year'], 2024))
# 年をまたいで金額を比べるときは必ず実質化する!
フェアな比較の原則:規模の違う対象を総数で比べない。
- 人口あたり・件数あたり・面積あたりに直す(10万人あたりの犯罪、1床あたりの医師など)。
- 実質化する。複数年の金額は物価で割り戻し、同じ価格水準にそろえる。賃金は名目と実質を区別する。
- 季節調整を考える。月次・四半期データは季節要因(年度末、夏季、年末商戦等)で増減する。前年同月比を使うか、季節調整済み系列(公表されていればそれ)を使う。
- 割合の落とし穴。小さい母数の「○%増」は誇張になりやすい(2件→3件で「50%増」)。分母と実数を併記する。
- 定義をそろえる。自治体・年・調査で集計区分や定義が変わっていないか確認する(市町村合併、年齢区分の変更など)。
相関と因果
## 相関を責任を持って報じる
### 言ってよいこと
- 「XとYには相関がある」
- 「Xが増えると、Yも増える傾向がある」
- 「Xが高い地域は、Yも高い傾向がある」
- 「XはYと関連している」
### (さらなる根拠なしには)言ってはいけないこと
- 「XがYを引き起こす」
- 「XがYにつながる」
- 「XのせいでYが起きる」
### 因果をほのめかす前に問うこと
1. もっともらしい仕組み(メカニズム)があるか
2. 時間の順序は合っているか(原因が結果より先か)
3. 量反応関係があるか(Xが多いほどYも大きいか)
4. その発見は再現されているか
5. 交絡変数は統制されているか
6. 別の説明はありえないか
### 見せかけの相関を疑う兆候
- 無関係なものとの極端に高い相関(r > 0.95)
- 変数間に論理的なつながりがない
- 第3の変数が両方を説明しうる
- ばらつきが大きいのに標本が小さい
統計の落とし穴:シンプソンのパラドックス(全体と層別で傾向が逆転する)、生存者バイアス(残ったものだけ見ている)、選択バイアス、平均への回帰にも注意。集計の粒度を変えると結論が変わることがある。
データの可視化
グラフ選びの指針
## 適切なグラフを選ぶ
### 比較
- **棒グラフ**:カテゴリの比較
- **グループ化棒グラフ**:グループをまたいだカテゴリ比較
- **弾丸グラフ**:実績と目標
### 時間変化
- **折れ線グラフ**:時系列の趨勢
- **面グラフ**:時間に沿った累積
- **スロープグラフ**:2時点間の変化
### 分布
- **ヒストグラム**:1変数の分布
- **箱ひげ図**:グループ間の分布比較
- **バイオリン図**:分布の形を詳しく
### 関係
- **散布図**:2変数の関係
- **バブルチャート**:3変数(x・y・大きさ)
- **連結散布図**:関係の時間変化
### 構成(割合)
- **円グラフ**:全体に対する部分(ほぼ使わない。最大5分割まで、ドーナツの方がよい)
- **ドーナツグラフ**:全体に対する部分
- **積み上げ棒グラフ**:カテゴリ別の内訳
- **ツリーマップ**:階層的な構成
### 地理
- **コロプレス(階級区分図)**:地域別の値(必ず正規化したデータで!)
- **ドットマップ**:個々の地点
- **比例シンボル**:地点ごとの大きさ
探索的なインタラクティブ可視化(Plotly Express)
import plotly.express as px
import plotly.graph_objects as go
# すべてのグラフの既定テンプレートを設定
px.defaults.template = 'simple_white'
def create_bar_chart(
data: pd.DataFrame,
title: str,
source: str,
x_val: str,
y_val: str,
x_lab: str | None = None,
y_lab: str | None = None,
text_col: str | None = None,
) -> go.Figure:
"""出典クレジットを脚注に付けた棒グラフを作る。"""
fig = px.bar(
data,
x=x_val,
y=y_val,
text=text_col,
title=title,
labels={x_val: x_lab or x_val, y_val: y_lab or y_val},
)
fig.add_annotation(
text=f"出典:{source}",
showarrow=False,
xref='paper', yref='paper',
x=0, y=-0.15,
xanchor='left', yanchor='top',
font=dict(size=10, color='gray'),
)
return fig
# 例
fig = create_bar_chart(
data,
title='年別の生産台数',
source='○○省 ××統計、2024年',
x_val='year',
y_val='units_prod',
x_lab='年',
y_lab='生産台数',
)
fig.show() # インタラクティブ表示
日本語フォントの注意:Matplotlib 等で図を作ると日本語が「□(豆腐)」になることがある。
japanize-matplotlibを使うか、matplotlib.rcParams['font.family']に IPAex ゴシック等の日本語フォントを指定する。Plotly は通常そのまま日本語が表示できる。
⚠️ フォントキャッシュでハングする環境がある(日本版・検証済みメモ 2026-07)。 一部の環境(この devcontainer で実測)では、Matplotlib が初回の日本語フォントキャッシュ生成でハングし、
cairosvgは出力が空(真っ白)になるという不具合に当たった。 静的な図表を確実に出したいときは、純Python で SVG を組み立て →rsvg-convert(librsvg2-bin)で PNG 化するのが最も堅牢だった。matplotlib/cairosvgに依存せず、日本語ラベルもフォント指定で確実に描ける。ブラウザ表示だけなら SVG をそのまま HTML に埋め込めばよい。
公開用の自動データ可視化(Datawrapper)
import pandas as pd
import datawrapper as dw
# 認証:環境変数 DATAWRAPPER_ACCESS_TOKEN を設定するか、
# ファイルから読み込んで create() に渡す
with open('datawrapper_api_key.txt', 'r') as f:
api_key = f.read().strip()
# データを読み込む
data = pd.read_csv('../data/raw/data.csv')
# 新しいオブジェクト指向APIで棒グラフを作成
chart = dw.BarChart(
title='棒グラフのタイトル',
intro='サブタイトルや説明文',
data=data,
# 書式設定
value_label_format=dw.NumberFormat.ONE_DECIMAL,
show_value_labels=True,
value_label_alignment='left',
sort_bars=True, # 値の大きい順に並べる
reverse_order=False,
# 出典の明記
source_name='データの出典',
source_url='https://example.com',
byline='作成者名',
# 任意:基本の色
base_color='#1d81a2'
)
# 作成して公開(DATAWRAPPER_ACCESS_TOKEN 環境変数を使う。トークンを渡してもよい)
chart.create(access_token=api_key)
chart.publish()
# グラフのURLと埋め込みコードを取得
print(f"Chart ID: {chart.chart_id}")
print(f"Chart URL: https://datawrapper.dwcdn.net/{chart.chart_id}")
iframe_code = chart.get_iframe_code(responsive=True)
# 既存グラフを新データで更新(自動更新グラフ向け)
existing_chart = dw.get_chart('YOUR_CHART_ID') # IDで取得
existing_chart.data = new_df # 新しいデータフレームを割り当て
existing_chart.title = '更新後のタイトル' # プロパティを変更
existing_chart.update() # Datawrapper に反映
existing_chart.publish() # 再公開して反映
# 任意 — 画像として書き出し
chart.export(filepath='chart.png', width=800, height=600)
# グラフを表示
chart
誤解を招く可視化を避ける
## グラフの誠実さチェックリスト
### 軸
- [ ] 棒グラフのY軸はゼロから始まっているか
- [ ] 軸ラベルが明確か
- [ ] 目盛りが適切か(誇張のための途中省略をしていないか)
- [ ] 両軸に単位を入れているか
### データの表現
- [ ] すべてのデータ点が見えているか
- [ ] 色が区別できるか(色覚多様性に配慮しているか)
- [ ] 比率が正確か
- [ ] 3D効果で見え方を歪めていないか
### 文脈
- [ ] タイトルが「結論」ではなく「示している内容」を述べているか
- [ ] 対象期間が明示されているか
- [ ] 出典が明記されているか
- [ ] 必要なら標本数・手法を注記しているか
- [ ] 適切な箇所で不確実性を示しているか
### 誠実さ
- [ ] 都合のよい期間の切り取り(チェリーピッキング)を避けているか
- [ ] 外れ値を隠さず説明しているか
- [ ] 二軸グラフは正当か(通常は避ける)
- [ ] 注釈が誤解を招かないか
特に注意:棒グラフのY軸は必ずゼロ起点にする(途中から始めると差を誇張する)。折れ線は趨勢を示す目的ならゼロ起点でなくてよいが、その場合も目盛りを明示する。面積で量を表すときは半径ではなく面積を値に比例させる。
地理空間データの扱い
ジオコーディング(住所→緯度経度)
国内の住所マッチング
最適な用途: 日本国内の住所。
- 東京大学CSIS「アドレスマッチングサービス(シンプルジオコーディング実験)」 — 住所文字列に緯度経度と街区レベルの情報を付与。CSVの一括処理に対応(無償・研究用途中心、利用条件を確認)。
- 国土地理院 ジオコーディング(地理院地図/Web API) — 住所・地名から座標を取得。
- これらは国勢調査の**統計地理(小地域・町丁字、地域メッシュ)との結合に有用。結合キーは全国地方公共団体コード(自治体コード)**や町丁字コードを使う。
import pandas as pd
import requests
from typing import Optional
def geocode_jp_csis(address: str) -> Optional[dict]:
"""
東大CSIS アドレスマッチングのシングル照会の概念例。
実際のエンドポイント・パラメータ・利用規約は公式サイトで最新を確認すること。
一括処理は公式のCSVアップロード機能を使う方が確実。
"""
# 注:URL・仕様は変わりうるため、本番では公式ドキュメントを参照
endpoint = "https://geocode.csis.u-tokyo.ac.jp/cgi-bin/simple_geocode.cgi"
try:
r = requests.get(endpoint, params={"addr": address, "charset": "UTF8"}, timeout=10)
r.raise_for_status()
# 返却はXML等。実際の構造に合わせてパースする
return {"raw": r.text}
except Exception:
return None
Google Maps Geocoder
最適な用途: 海外の住所、高い一致率、雑然とした非標準の住所。
長所: 整っていない住所でも高い一致率。世界中で使える。速くて安定。豊富なメタデータ(場所の種別・住所要素・place ID)を返す。
短所: 有料(無料枠を超えると1,000件あたり約5米ドル)。APIキーと課金アカウントが必要。日本の統計地理コードは返さないため、別途、空間結合が必要。
使うとき: 海外の住所、国内ジオコーダーで一致しない雑然とした住所、最高の一致率が必要で予算がある場合。
import googlemaps
from typing import Optional
def geocode_address_google(address: str, api_key: str) -> Optional[dict]:
"""
Google Maps API で住所をジオコーディングする。
Geocoding API を有効にしたAPIキーが必要。
"""
gmaps = googlemaps.Client(key=api_key)
result = gmaps.geocode(address, language='ja', region='jp')
if result:
location = result[0]['geometry']['location']
return {
'formatted_address': result[0]['formatted_address'],
'lat': location['lat'],
'lon': location['lng'],
'place_id': result[0]['place_id']
}
return None
# データフレームを一括ジオコーディング
def batch_geocode(df: pd.DataFrame, address_col: str, api_key: str) -> pd.DataFrame:
gmaps = googlemaps.Client(key=api_key)
results = []
for address in df[address_col]:
try:
result = gmaps.geocode(address, language='ja', region='jp')
if result:
loc = result[0]['geometry']['location']
results.append({'lat': loc['lat'], 'lon': loc['lng']})
else:
results.append({'lat': None, 'lon': None})
except Exception:
results.append({'lat': None, 'lon': None})
return pd.concat([df, pd.DataFrame(results)], axis=1)
米国の Census Geocoder(連邦のセンサス地理コードを返す)は日本に相当機能がないため、上記の国内サービス(CSIS/国土地理院)に置き換えた。
GeoPandas
import geopandas as gpd
import pandas as pd
from shapely.geometry import Point
# さまざまな形式から読み込む
gdf = gpd.read_file('data.geojson') # GeoJSON
gdf = gpd.read_file('data.shp') # シェープファイル
gdf = gpd.read_file('https://example.com/data.geojson') # URL から
gdf = gpd.read_parquet('data.parquet') # GeoParquet(高速!)
# 注:国土数値情報・国勢調査小地域の境界はシェープファイルやGeoJSONで提供される
# 緯度経度を持つデータフレームを GeoDataFrame に変換
df = pd.read_csv('locations.csv')
geometry = [Point(xy) for xy in zip(df['longitude'], df['latitude'])]
gdf = gpd.GeoDataFrame(df, geometry=geometry)
# 座標参照系(CRS)を設定
# EPSG:4326 = WGS84(標準的な緯度・経度)
gdf = gdf.set_crs('EPSG:4326')
# 面積・距離の計算には投影座標系に変換する
# 簡便にはメートル単位の投影系へ変換する(日本では平面直角座標系も使える)
gdf_projected = gdf.to_crs('EPSG:3857') # Web メルカトル(メートル単位)
# 基本的な空間操作
# 面積を求める
gdf['area'] = gdf_projected.geometry.area
# 図形の中心点を求める
gdf['centroid'] = gdf.geometry.centroid
# 点の周囲に1kmの境界を描く
gdf['buffer_1km'] = gdf_projected.geometry.buffer(1000) # CRS 3857 のとき
# 空間結合:ポリゴン内の点を見つける
points = gpd.read_file('points.geojson')
polygons = gpd.read_file('boundaries.geojson')
joined = gpd.sjoin(points, polygons, predicate='within')
# ディゾルブ:属性ごとに図形を統合
dissolved = gdf.dissolve(by='pref', aggfunc='sum')
# さまざまな形式へ書き出し
gdf.to_parquet('output.parquet') # GeoParquet(推奨)
gdf.to_file('output.geojson', driver='GeoJSON') # GeoParquet 非対応ツール向け
地図の可視化(.explore()、lonboard、Datawrapper)
.explore()
最適な用途: 分析中のすばやい探索・試作。
長所: GeoPandas に組み込み(任意の GeoDataFrame で使える)。データが正しく見えるかの確認、空間パターンの探索、地図デザインの試行錯誤に向く。
短所: 大規模データ(10万件超)で遅くなる。専用ライブラリより細かい調整がしにくい。追加の依存パッケージが必要。
使うとき: 分析の途中で、ツールを切り替えずにGeoDataFrameをさっと見たいとき。
必要な依存パッケージ:
pip install folium mapclassify matplotlib
folium-.explore()の動作に必須(インタラクティブ地図を描画)mapclassify-scheme=(分類方式、'naturalbreaks'・'quantiles'・'equalinterval' 等)を使うとき必要matplotlib- カラーマップ(cmap=)に必要
import geopandas as gpd
# folium・mapclassify・matplotlib はインストールが必要だが import は不要
# .explore() 呼び出し時に geopandas が自動で読み込む
# 基本のインタラクティブ地図(内部で folium を使う)
gdf.explore()
# 調整したコロプレス(階級区分図)
# (scheme パラメータには mapclassify が必要)
gdf.explore(
column='population', # 色分けに使う列
cmap='YlOrRd', # matplotlib のカラーマップ
scheme='naturalbreaks', # 分類方式(mapclassify が必要)
k=5, # 階級の数
legend=True,
tooltip=['name', 'population'], # ホバーで表示する列
popup=True, # クリックで全列表示
tiles='CartoDB positron', # 背景タイル
style_kwds={'color': 'black', 'weight': 0.5} # 境界線のスタイル
)
lonboard
最適な用途: 大規模データと、Jupyter での高性能な可視化。
長所: deck.gl による GPU 描画で数百万点も滑らかに扱える。パン・ズーム・ホバーが大規模でも快適。GeoArrow 形式に対応し効率的にデータ転送。
短所: 別途インストールが必要(pip install lonboard)。スタイル指定がやや技術的(RGBA配列、deck.gl の流儀)。
使うとき: 大規模な点データ(事故地点、センサー計測、事業所立地など)や、10万件超で滑らかな操作が必要なとき。
import geopandas as gpd
from lonboard import viz, Map, ScatterplotLayer, PolygonLayer
# 手早い可視化(ジオメトリ種別を自動判定)
viz(gdf)
# 点用の ScatterplotLayer をカスタム
layer = ScatterplotLayer.from_geopandas(
gdf,
get_radius=100,
get_fill_color=[255, 0, 0, 200], # RGBA
pickable=True
)
m = Map(layer)
m
# 列の値で色付けする PolygonLayer
from lonboard.colormap import apply_continuous_cmap
import matplotlib.pyplot as plt
colors = apply_continuous_cmap(gdf['value'], plt.cm.viridis)
layer = PolygonLayer.from_geopandas(
gdf,
get_fill_color=colors,
get_line_color=[0, 0, 0, 100],
pickable=True
)
Map(layer)
Datawrapper
最適な用途: 記事用の公開品質のコロプレス・比例シンボル地図。
長所: 美しく専門的な既定値。どのCMSでも使える、埋め込み可能でレスポンシブなiframeを生成。読者はコード不要で操作(ホバー・クリック)できる。アクセシブルでモバイル対応。データの更新が容易。
短所: Datawrapper のアカウントが必要(無料枠あり)。Datawrapper が用意した境界ファイルに限られ、任意の図形は持ち込めない。カスタム可視化の自由度は低い。
使うとき: 公開用の洗練された地図が必要なとき。地域別の統計(都道府県別の失業率、市区町村別の患者数、選挙区別の結果など)を示すコロプレスに最適。読者がノートブックではなくブラウザで見る場合。
.explore() や lonboard と違い、生の図形は渡さない。自分のデータを Datawrapper の組み込み境界ファイルに、標準コード(全国地方公共団体コード、ISO 等)で対応づける。
import datawrapper as dw
import pandas as pd
# APIキーを読み込む
with open('datawrapper_api_key.txt', 'r') as f:
api_key = f.read().strip()
# Datawrapper の境界に対応するコードを付けたデータを用意
# 日本の都道府県:Datawrapper の都道府県ベースマップが使う識別子に合わせる
# (都道府県名・コード等。basemap_key で対応列を指定)
df = pd.DataFrame({
'pref': ['北海道', '青森県', '岩手県', '東京都', '大阪府'],
'rate': [4.9, 3.2, 7.1, 4.2, 5.8]
})
# コロプレス(階級区分図)を作成
chart = dw.ChoroplethMap(
title='都道府県別の○○率',
intro='○○率、2024年',
data=df,
# 地図の設定
basemap='japan-prefectures', # 日本の都道府県の組み込み境界
basemap_key='pref', # 位置コードを持つ列名
value_column='rate',
# スタイル
color_palette='YlOrRd', # 配色
legend_title='○○率(%)',
# 出典
source_name='○○省 ××統計',
source_url='https://www.example.go.jp/',
byline='作成者名'
)
# 作成して公開
chart.create(access_token=api_key)
chart.publish()
# 記事用の埋め込みコードを取得
iframe = chart.get_iframe_code(responsive=True)
print(f"Chart URL: https://datawrapper.dwcdn.net/{chart.chart_id}")
# 新データで更新(自動更新地図向け)
new_df = pd.DataFrame({...}) # 更新後のデータ
existing_chart = dw.get_chart('YOUR_CHART_ID')
existing_chart.data = new_df
existing_chart.update()
existing_chart.publish()
Datawrapper の主なベースマップ(例):
japan-prefectures(都道府県)、world、asia、europeほか- 各国別の地図。利用できる basemap 名や市区町村粒度の対応は Datawrapper 側で随時変わるため、最新の対応表を確認すること。
- 市区町村単位など細かい境界が必要な場合は、国土数値情報の行政区域や国勢調査小地域の境界(シェープファイル/GeoJSON)を GeoPandas で扱い、
.explore()/lonboard等で描く方法も検討する。
プライバシーと小集計
データから個人が特定されうる扱いに注意する。
- 小集計(少人数セル)の秘匿:1〜数件のセルは個人が割れる。公的統計でも秘匿(「-」「***」「x」)されている。自分で集計するときも、少人数の区分を公表する際は丸め・統合・非表示を検討する。
- 再識別のリスク:地域・年齢・性別・職業などを掛け合わせると、匿名化したつもりでも個人が特定されうる。
- 公開と取材の区別:開示文書に含まれる個人情報の扱いは、個人情報保護法と各社の取材倫理・編集方針に従う。
- 健康・前科・属性などセンシティブ情報は特に慎重に。掲載の必要性と公益性を編集と確認する。
再現性・出典・手法の明記
- 出典を必ず明記:統計名・公表機関・対象期間・取得日・URL を記事と図表に書く。
- 取得時点を保存:データはダウンロードして保管し、改定があっても元を示せるようにする。
- コードと手法を残す:クリーニング・分析のコード、加工前後のデータ、判断(除外・補完)の記録を残す。可能なら手法の注記欄や公開リポジトリで共有する。
- 数値は再計算:原稿に載せる数値は独立に計算し直して突き合わせる。
学習リソース
- データジャーナリズム・ハンドブック2(Data Journalism Handbook 2) — 現在の定番(datajournalism.com/read/handbook/two、英語)。
- Flowing Data(flowingdata.com)— Nathan Yau による作例とチュートリアル。
- The Pudding(pudding.cool)— 編集的なデータエッセイの作例。
- GIJN(Global Investigative Journalism Network) — 調査報道・データ報道の手法やツールの解説(日本語コンテンツもある)。
- 国内:日本新聞協会や各社のデータ報道事例、大学・研究機関の統計講座、e-Stat / RESAS の利用ガイド。
- 関連スキル — 計算した数値の裏取りは fact-check-workflow、データの情報公開請求は joho-kokai-seikyu(情報公開請求)、SNS由来データの検証は source-verification、OSINT・プラットフォームAPIは social-media-intelligence を参照。
注:米国固有の学習リソース(NICAR/IRE、Knight Center、Sigma Awards 等)は、日本から利用しにくい/制度が異なるため一部を割愛・置換した。
スキルのメタ情報
| 項目 | 値 |
|---|---|
| version | 1.1.0(日本語ローカライズ版) |
| 原典 version | 1.1.0 |
| domain | ジャーナリズム、データ |
| complexity | 中級 |
出典・ライセンス(帰属表示)
本スキルは claude-skills-journalism(by Joe Amditis, MIT License, © 2025 Joe Amditis,
https://github.com/jamditis/claude-skills-journalism )の journalism-core/data-journalism を
原典の構成(記事設計・データ入手・クリーニング・統計分析・可視化・地理空間・手法開示)に沿って日本語化し、
米国のデータ源(Data.gov・Census・BLS・FRED・SEC EDGAR・CDC WONDER 等)やジオコーダ・ベースマップを
日本の制度(e-Stat・RESAS・各府省統計・EDINET・気象庁・国土数値情報 等)に再構成したものです。
日本に相当物のない米国固有の制度別表・参考リンクは置き換えるか割愛しています。
原典の著作物本文(AP Stylebook 等の第三者著作物を含む)は転載していません。
原作者の公認・推奨を受けたものではありません。
MIT ライセンス全文は同梱の LICENSE-MIT.txt を参照(無い場合は原典リポジトリの LICENSE を参照)。
レビュー
まだレビューはありません。使ってみた感想をお寄せください。
コメント
ログインなしで書き込めます。使ってみた感想、質問、動かなかった環境などを気軽にどうぞ。
まだコメントはありません。最初のコメントを書いてみませんか。
同じリポジトリのスキル
取材・調査・データ報道で使える無料データ源(API)のカタログ。政府統計(e-Stat)、地域経済(RESAS)、気象(気象庁)、法令(e-Gov)、企業開示(EDINET)、書誌・国会会議録(国立国会図書館)、地理空間(国土地理院)、汎用の国際API(GDELT・Open-Meteo・Wayback等)を用途別に整理し、登録要否・利用条件・選び方を示す。「無料のデータ源が欲しい」「このテーマのAPIは?」「公的データをプログラムで取りたい」といった依頼で使う。
取材で集めた資料(開示文書・記事・SNS・反訳など)をデジタルアーカイブ化し、AIで要約・分類・エンティティ抽出・関係づけ(知識グラフ化)して横断検索できる調査基盤を作るための手引き。「取材資料を整理したい」「大量の開示文書をデータベース化したい」「人物・組織・出来事の関係を可視化したい」「調査報道のアーカイブを作りたい」などの依頼で使う。
発言・記事・SNS投稿などの主張を体系的にファクトチェックする。主張の抽出、検証可能な命題への分解、根拠の収集、当事者への確認、判定(レーティング)、記録、訂正対応までを支援する。掲載前の裏取り、ファクトチェック記事の作成、編集部の検証手順づくりに使う。「ファクトチェックして」「この発言は本当か」「裏取りの手順を」などの依頼で使う。
テキスト・記事・ウェブページの内容を、厳密な検証方法論にもとづいてファクトチェックする。検証可能な主張を抽出し、信頼できる一次情報・公的データ・検証機関と突き合わせ、各主張の真偽と確からしさ(確信度)、情報源の信頼度スコアを評価し、根拠つきの検証レポートにまとめる。「この主張を検証して」「記事をファクトチェックして」「情報源の信頼性を見て」「真偽を判定して」「誤情報か確かめて」などの依頼で使う。付属の references(信頼できる情報源/検証方法論)を参照する検証重視版。
学術的な文章執筆・研究設計・学術コミュニケーションの作業を支援する。論文の執筆、文献レビュー、IMRaD構成、研究費(科研費・JST・AMED等)申請、引用管理、査読対応、オープンアクセス(OA)の選択、プレプリント投稿、ORCID/DOI等の識別子、CRediT役割分担、事前登録、生成AI(LLM)利用の開示などを扱う。研究者・大学院生のほか、調査報道で学術論文や研究手法を読み解き引用する記者にも使う。「論文を書く」「研究計画を立てる」「文献レビュー」「査読コメントへの返信」「科研費の申請書」などの依頼で使う。