AIが生成しがちな「いかにもAIっぽい」日本語の定型表現・水増し・空疎な形容を取り除き、読者の信頼を損なわない自然な文章に直す。記事・原稿・プレスリリース・告知・SNS投稿など、AIの支援を受けて書いた文章を公開前に点検するときに使う。「AI臭を消したい」「文章が機械っぽい」「人が書いたように直したい」などの依頼で使う。
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.
データ報道(データジャーナリズム)の手順を支援する。データセットの分析、グラフや地図の作成、雑然としたデータのクリーニング、統計の算出、データに基づく記事の構築に使う。「e-Statのデータを分析したい」「人口あたりで比較したい」「グラフが誤解を招かないか確認したい」「統計から記事を組み立てたい」などの依頼で使う。
記者・編集者・調査担当向け。
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.
실행 점수 미검증
설치 방법 보기설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.
データを見つけ、素性を確かめ、クリーニングし、分析し、可視化し、出典を明記して記事にするまでの体系的な手順。 記者の問いを聞き取り、フェアな比較・誤読防止・再現性を満たす形で支援する。数値の最終確認と公表判断は記者が行う。
注意:統計の更新頻度・定義・公表日程は変わる。重要な数値は公表元の最新資料で必ず確認すること。本スキルは一般的な手引きであり、法的・統計的助言ではない。
データジャーナリズムの土台は、米国の記者フィリップ・マイヤーが著書『The New Precision Journalism(精密ジャーナリズム)』で示した「報道を科学のように扱う」という考え方にある。科学的手法を取り入れる。
この過程は一直線ではなく、行き来しながら繰り返す。
1. つかみ(核心の一段落)
2. 証拠
3. 文脈
4. 人間の要素
5. 含意
6. 手法の注記欄(メソドロジー・ボックス)
## この分析の手法
### データの出典
[すべての出典を、リンクと取得日とともに列挙]
### 対象期間
[正確にどの期間を対象にしたか]
### 定義
[重要な用語と、それをどう操作的に定義したか]
### 分析の手順
1. [分析の最初の手順]
2. [2番目の手順]
3. [以降続く…]
### 限界
- [限界1]
- [限界2]
### 除外したものとその理由
- [除外した区分]:[理由]
### 検証
[発見をどう検証・確認したか]
### コードとデータの公開
[コードやデータを共有する場合はリポジトリ等のリンク]
### 連絡先
[読者が問い合わせる方法]
最初に「これは最新の調査年か」を必ず確認する。 古いデータを使うのはデータ報道の重大な失敗。 e-Stat API は次の手順で最新表を選ぶ(最初の検索ヒットをそのまま使わない)。
SURVEY_DATE 降順に並べるか、surveyYears(例:直近数年)で絞って最新調査年の表を選ぶ。
反面教師:2026年に書店データを2016年(経済センサス)・人口を2010年(国勢調査)で作ってしまった失敗例がある。 実際には2024年(経済センサス基礎調査)・人口推計2024が普通に取得できた。最新確認を省くと古い数字で報じてしまう。
国の統計・データ
総合窓口:
分野別:
企業・金融の開示:
医薬・健康・安全:
裁判・行政:
政治資金・選挙:
国際比較:
学術・ミクロデータ:
削除・改廃されたデータの保全(出典が消えた場合に使う):
注:米国固有のデータ源(Data.gov、Census Bureau、BLS、FRED、SEC EDGAR、CDC WONDER、PACER 等)は日本の相当機関に置換した。日本に相当物がない一部の制度別表は割愛している。
公的に未公開のデータは情報公開請求で取得できることがある。請求書の起草・不開示への対応・審査請求などの手続は joho-kokai-seikyu(情報公開請求)スキルを参照。データ特有のコツ:
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が捏造した存在しない判例引用が法廷で問題になった事例(Mata v. Avianca、2023年)や、チャットボットが述べた誤った払い戻し方針が企業を拘束すると判断された事例(エア・カナダ、2024年)が、「LLMの捏造が公表事実として扱われた」典型例として知られる。
df.dtypes、df.columns.tolist())と突き合わせる。データのクリーニング・分析・可視化・原稿作成のいずれかでAIを使った場合は、手法の注記欄に明示する。どの工程に人間の確認が入り、どの工程が自動だったかを、編集者と読者が知る必要がある。
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))
# 年をまたいで金額を比べるときは必ず実質化する!
フェアな比較の原則:規模の違う対象を総数で比べない。
## 相関を責任を持って報じる
### 言ってよいこと
- 「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分割まで、ドーナツの方がよい)
- **ドーナツグラフ**:全体に対する部分
- **積み上げ棒グラフ**:カテゴリ別の内訳
- **ツリーマップ**:階層的な構成
### 地理
- **コロプレス(階級区分図)**:地域別の値(必ず正規化したデータで!)
- **ドットマップ**:個々の地点
- **比例シンボル**:地点ごとの大きさ
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 に埋め込めばよい。
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軸は必ずゼロ起点にする(途中から始めると差を誇張する)。折れ線は趨勢を示す目的ならゼロ起点でなくてよいが、その場合も目盛りを明示する。面積で量を表すときは半径ではなく面積を値に比例させる。
最適な用途: 日本国内の住所。
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
最適な用途: 海外の住所、高い一致率、雑然とした非標準の住所。
長所: 整っていない住所でも高い一致率。世界中で使える。速くて安定。豊富なメタデータ(場所の種別・住所要素・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/国土地理院)に置き換えた。
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)
最適な用途: 記事用の公開品質のコロプレス・比例シンボル地図。
長所: 美しく専門的な既定値。どの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 ほか.explore()/lonboard 等で描く方法も検討する。データから個人が特定されうる扱いに注意する。
注:米国固有の学習リソース(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 を参照)。
아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.
로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.
아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.
AIが生成しがちな「いかにもAIっぽい」日本語の定型表現・水増し・空疎な形容を取り除き、読者の信頼を損なわない自然な文章に直す。記事・原稿・プレスリリース・告知・SNS投稿など、AIの支援を受けて書いた文章を公開前に点検するときに使う。「AI臭を消したい」「文章が機械っぽい」「人が書いたように直したい」などの依頼で使う。
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.
取材・調査・データ報道で使える無料データ源(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)利用の開示などを扱う。研究者・大学院生のほか、調査報道で学術論文や研究手法を読み解き引用する記者にも使う。「論文を書く」「研究計画を立てる」「文献レビュー」「査読コメントへの返信」「科研費の申請書」などの依頼で使う。
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.