配当利回り(株価に対する 1 株あたり配当の比率)は、ファンダメンタルズ分析でも最初に触れることの多い指標です。本記事は、銘柄一覧・財務情報・直近株価を組み合わせて配当利回りトップ 30 を抽出するまでの流れを、生成AI へのプロンプト → コード → 結果 → 落とし穴の順で公開します。

過去のデータでの集計結果は、将来の配当や株価を保証しません。本記事はあくまで「データの集計手順」を学ぶための教材です。

目次

  1. 配当利回りの定義をそろえる
  2. 必要なライブラリ
  3. サンプルデータの生成
  4. コード(コピペで動く)
  5. 実行結果(イメージ)
  6. 業種別の分布を可視化する
  7. 落とし穴
  8. 集計結果の追加検証

配当利回りの定義をそろえる

「配当利回り」と一口に言っても、数字の作り方は何通りかあります。本記事では次の式に固定します。

配当利回り=1 株あたり年間配当直近終値\text{配当利回り} = \frac{\text{1 株あたり年間配当}}{\text{直近終値}}
  • 1 株あたり年間配当: 直近の本決算で開示された通期予想 DPS(Dividends Per Share)
  • 直近終値: 集計実行日の前営業日の終値
  • 単位: 小数(0.04 = 4.0%)

実務では「実績配当」「予想配当」「特別配当を含むか」で値が変わります。どの数字を使ったかをデータと一緒に記録する のが集計の基本です。

必要なライブラリ

Terminal window
pip install pandas numpy matplotlib

検証バージョン: Python 3.12.5 / pandas 2.2.3

サンプルデータの生成

本記事では仮想の銘柄データを使います。実際の分析では、このデータ生成部分を API やCSV 読み込みに差し替えます。データの入手方法は株価データの入手方法を参照してください。

"""sample_market_data.py
仮想の銘柄・財務・株価データを生成するユーティリティ。
"""
import numpy as np
import pandas as pd
def generate_market_data(n_companies: int = 80, seed: int = 42) -> dict[str, pd.DataFrame]:
"""仮想の上場企業データ一式を生成する。列名は J-Quants API に準拠。"""
rng = np.random.default_rng(seed=seed)
sectors = [
"情報・通信業", "電気機器", "医薬品", "銀行業", "輸送用機器",
"化学", "機械", "食料品", "小売業", "建設業",
"鉄鋼", "海運業", "証券、商品先物取引業", "不動産業", "サービス業",
]
# --- 銘柄一覧(/equities/master 相当) ---
codes = [f"{1301 + i}" for i in range(n_companies)]
sector_assign = rng.choice(sectors, size=n_companies)
names = [f"サンプル商事{i+1:03d}" for i in range(n_companies)]
listed = pd.DataFrame({
"Code": codes,
"CoName": names,
"S33Nm": sector_assign,
})
# --- 財務情報(/fins/summary 相当。銘柄コード列は Code) ---
base_dps = rng.exponential(scale=40, size=n_companies)
# 約 15% の企業は無配
base_dps[rng.random(n_companies) < 0.15] = 0.0
base_dps = np.round(base_dps, 1)
statements = pd.DataFrame({
"Code": codes,
"DiscDate": pd.Timestamp("2026-05-15"),
"FDivAnn": base_dps,
})
# --- 株価(/equities/bars/daily 相当) ---
base_price = rng.lognormal(mean=7.0, sigma=0.8, size=n_companies)
close = np.round(base_price, 1)
prices = pd.DataFrame({
"Code": codes,
"Date": pd.Timestamp("2026-05-27"),
"C": close,
})
return {"listed": listed, "statements": statements, "prices": prices}

generate_market_data() を呼ぶと、銘柄一覧・財務情報・株価の 3 つの DataFrame が返ります。列名は J-Quants API の項目名に準拠しているため、データ取得部分を差し替えるだけで実データにも適用できます。

data = generate_market_data()
print(data["listed"].head())
print(data["statements"].head())
print(data["prices"].head())

生成AI へのプロンプト例

最初の叩き台はプロンプトから始めます。「目的・入力・出力・制約」を順に書くと、修正が少ないコードが返ってきます。

目的:
上場銘柄一覧と財務情報、直近株価を組み合わせて
配当利回りトップ 30 の表を作る。学習用なので売買推奨は含めない。
入力(列名は J-Quants API に準拠):
- listed(上場銘柄一覧)DataFrame: Code, CoName, S33Nm
- statements(財務情報)DataFrame: Code, DiscDate, FDivAnn
- prices(日次株価)DataFrame: Code, Date, C
出力:
- 配当利回り上位 30 銘柄のデータフレーム
列: Code, CoName, S33Nm, C, dps, dividend_yield
- dividend_yield 降順、index は 0 から振り直し
制約:
- pandas 2.2 系
- 銘柄コードはどの表も Code 列。Code で結合する
- 各銘柄について「最新の DiscDate を 1 行だけ」採用する
- prices は最新営業日の C を使う
- FDivAnn が NaN や 0 の行は除外
- 100% 超の利回りは外れ値として除外

コード(コピペで動く)

"""high_dividend.py
銘柄一覧・財務情報・株価を結合し、配当利回りトップ 30 を抽出する。
"""
from __future__ import annotations
import pandas as pd
TOP_N = 30
YIELD_UPPER_LIMIT = 1.0 # 100% を超える値は外れ値として除外
def load_latest_dps(statements: pd.DataFrame) -> pd.DataFrame:
"""銘柄ごとに最新開示分の予想 DPS を 1 行に絞る。"""
df = statements.dropna(subset=["FDivAnn"])
df = df[df["FDivAnn"] > 0]
# 銘柄ごとに最新の開示日を 1 行だけ残す
df = df.sort_values(["Code", "DiscDate"])
df = df.groupby("Code", as_index=False).tail(1)
return df.rename(columns={"FDivAnn": "dps"})[["Code", "dps"]]
def load_latest_close(prices: pd.DataFrame) -> pd.DataFrame:
"""銘柄ごとに最新営業日の終値を取り出す。"""
df = prices.sort_values(["Code", "Date"])
df = df.groupby("Code", as_index=False).tail(1)
return df[["Code", "C"]]
def extract_top_dividend(
listed: pd.DataFrame,
statements: pd.DataFrame,
prices: pd.DataFrame,
) -> pd.DataFrame:
dps = load_latest_dps(statements)
close = load_latest_close(prices)
# どの表も銘柄コードは Code。Code で結合する
merged = (
listed.merge(dps, on="Code")
.merge(close, on="Code")
)
merged["dividend_yield"] = merged["dps"] / merged["C"]
# 100% 超は明らかに入力ミスや特殊要因。外れ値として除外
merged = merged[merged["dividend_yield"] < YIELD_UPPER_LIMIT]
top = (
merged.sort_values("dividend_yield", ascending=False)
.head(TOP_N)
.reset_index(drop=True)
)
top["dividend_yield_pct"] = (top["dividend_yield"] * 100).round(2)
return top
# --- 実行 ---
data = generate_market_data()
top30 = extract_top_dividend(data["listed"], data["statements"], data["prices"])
print(top30[["Code", "CoName", "S33Nm", "C", "dps", "dividend_yield_pct"]])

実行結果(イメージ)

Code CoName S33Nm C dps dividend_yield_pct
0 1324 サンプル商事024 海運業 820.3 62.0 7.56
1 1348 サンプル商事048 鉄鋼 1180.5 85.0 7.20
2 1306 サンプル商事006 証券、商品先物取引業 1452.1 100.0 6.89
3 1362 サンプル商事062 銀行業 825.0 55.0 6.67
...

サンプルデータでも、利回りの高い銘柄が特定の業種に偏る傾向が見えます。これは実データでも同様で、業績変動が大きい業種(海運・鉄鋼・証券など)ほど、株価下落や一時的な高配当で利回りが高くなるパターンがあります。

業種別の分布を可視化する

import matplotlib.pyplot as plt
import matplotlib
matplotlib.rcParams["font.family"] = "sans-serif"
sector_counts = top30["S33Nm"].value_counts()
fig, ax = plt.subplots(figsize=(8, 5))
sector_counts.plot.barh(ax=ax, color="tab:blue", edgecolor="white")
ax.set_xlabel("Number of companies in Top 30")
ax.set_title("Dividend Yield Top 30 — Sector Distribution (Sample Data)")
ax.invert_yaxis()
ax.grid(axis="x", alpha=0.3)
plt.tight_layout()
plt.savefig("top30_sector.png", dpi=120)
plt.close(fig)

1 業種に集中している場合は、「利回りの高さ」が業種固有のリスクの裏返しである可能性を考えます。

落とし穴

集計を信じる前に確認しておきたい点をまとめます。

  • 記念配当・特別配当: 一時的な配当が DPS に含まれていると、翌期から大きく下がる可能性があります。
  • 無配転落リスク: 利回りが極端に高い銘柄は、株価が業績悪化を織り込んで下がっている場合があります。市場が「次回は減配・無配」を予想している可能性。
  • 権利落ち日: 配当取得後に株価が落ちる日です。直前後で集計すると数値がブレます。
  • REIT・優先株: 計算式が同じでも、性質が普通株とは異なります。集計対象を S33Nm で絞るかどうかを意思決定する必要があります。
  • データ取得日のズレ: 財務情報の開示日と株価の最新日が大きく離れていないか確認します。

集計結果の追加検証

トップ 30 を眺めて終わりにせず、次の追加チェックを通すと信頼度が上がります。

  • 業種ごとの分布: 表から S33Nm の構成比を出す。1 業種に偏っていないか。
  • 市場区分: プライム / スタンダード / グロースのどこに集中しているか。
  • 過去の配当推移: 過去 3 期の実績 DPS の平均で利回りを再計算して比較。

集計コードは 1 つに固定せず、定義を変えた版を 2 〜 3 種類作って結果が大きく変わらないかを見るのが、定量分析の作法です。

まとめ

  • 配当利回りは「DPS / 株価」で計算するが、DPS の取り方で結果が変わる
  • 高利回りの上位は業績変動の大きい業種が並びやすい
  • 100% 超は外れ値として除外。1 度のクリーニングで終わらせず、複数視点で再計算する
  • 集計結果は売買判断に直結させず、「データの傾向」として読む

過去の集計結果は将来の配当・株価を保証しません。本記事のコードは学習用です。