PER(株価収益率、Price Earnings Ratio)は、ファンダメンタルズ分析でもっともよく登場する指標の 1 つです。本記事では、PER の意味・計算式・読み方の落とし穴を整理し、pandas で複数銘柄を一括計算するコード例を示します。

目次

  1. PER とは
  2. 何のために使うか
  3. PER の限界(ここを読まないと事故ります)
  4. EPS の取り方
  5. 単一銘柄を計算する
  6. 複数銘柄を pandas で一括計算する
  7. 業種別の平均 PER を出す
  8. 外れ値の扱い

PER とは

PER は「ある銘柄の利益に対して、市場がいま何倍の値段を付けているか」を表す指標です。

PER=株価1 株あたり純利益 (EPS)\text{PER} = \frac{\text{株価}}{\text{1 株あたり純利益 (EPS)}}

たとえば、株価 3000 円、EPS 200 円なら、PER = 3000 / 200 = 15(倍)です。

「投資した金額を何年分の利益で回収できるか」と読み替えることもできます。利益が今後も同じペースで稼ぎ続けられるなら、PER 15 倍の銘柄は 15 年で投資金額が回収できる計算です(配当や再投資・成長率を無視した単純な目安)。

何のために使うか

  • 同じ業種の銘柄同士で「割高 / 割安」の感覚をつかむ
  • 同じ銘柄でも、過去の自社の PER 水準と比べて現状を評価する
  • 業種ごとの PER 平均を出して、相対的な傾向を見る

PER の限界(ここを読まないと事故ります)

PER は便利ですが、単独で投資判断に使えるほど万能ではありません。次のような注意点があります。

  • 業種で平均値が大きく違う: 成長性が高い業種ほど PER は高くなる傾向(IT・サービス業 vs 銀行業)
  • 赤字企業ではそもそも計算できない: EPS がマイナスのとき PER は意味を失います
  • 特別損益で利益が歪む年がある: 1 度きりの売却益・減損で EPS が一時的に大きく動くと PER も歪む
  • 会計方針による差: 同業界でも会計方針(減価償却・在庫評価)で利益額が変わる
  • 将来の利益は反映されていない: PER は基本的に「直近の実績」または「予想」から計算するもの

「PER が低い = 割安」と単純に決めつけず、上記を踏まえて使います。

EPS の取り方

PER を計算するには EPS(1 株あたり純利益)が必要です。J-Quants API では財務データから取得できます(詳細は#6-6「財務情報を取得する (/fins/summary)」)。EPS は次のいずれかの形で得られます。

  • 実績 EPS: 直近の決算で確定した値(過去 1 年合計 = LTM や、前期実績)
  • 予想 EPS: 会社・アナリストの今期予想値

両者で計算した PER は別物です。本記事では「実績 EPS を使った PER」を扱います。

単一銘柄を計算する

サンプル値で 1 銘柄を計算する例です。

def per(price: float, eps: float) -> float | None:
"""株価と EPS から PER を返す。EPS が 0 / 負の場合は None。"""
if eps is None or eps <= 0:
return None
return price / eps
print(per(3000, 200)) # 15.0
print(per(3000, -50)) # None (赤字)
print(per(3000, 0)) # None (利益ゼロ)

eps <= 0 を弾くことで、赤字・利益ゼロ企業を「PER 計算不能」として扱います。これは強く推奨される実装です。

複数銘柄を pandas で一括計算する

実用的な場面では、複数の銘柄について一気に計算したくなります。次のような DataFrame を入力に取る前提です。

import numpy as np
import pandas as pd
df = pd.DataFrame({
"Code": ["1301", "1302", "1303", "1304", "1305"],
"CoName": ["サンプル商事001", "サンプル商事002", "サンプル商事003",
"サンプル商事004", "サンプル商事005"],
"C": [2900, 9800, 1500, 13000, 4900],
"EPS": [310, -120, 130, 700, 380],
})
# PER を一気に計算: NaN を許容するため pd.NA を使う
df["per"] = np.where(df["EPS"] > 0,
df["C"] / df["EPS"], np.nan)
df = df.assign(per=df["per"].round(2))
print(df)
Code CoName C EPS per
0 1301 サンプル商事001 2900 310 9.35
1 1302 サンプル商事002 9800 -120 NaN
2 1303 サンプル商事003 1500 130 11.54
3 1304 サンプル商事004 13000 700 18.57
4 1305 サンプル商事005 4900 380 12.89

EPS が負の銘柄(サンプル商事002)は PER が NaN になり、後続の処理で集計から自動的に除外されます。

業種別の平均 PER を出す

複数銘柄に業種(S33Nm)が付いている場合、業種ごとの平均が出せます。

# サンプル: 業種付きの DataFrame
df["S33Nm"] = ["輸送用機器", "情報・通信業", "銀行業", "電気機器", "化学"]
sector_avg = (
df.dropna(subset=["per"])
.groupby("S33Nm")["per"]
.agg(["mean", "median", "count"])
.round(2)
.sort_values("mean")
)
print(sector_avg)

中央値(median)も併記すると、外れ値に強い見方ができます。1 銘柄だけ極端な PER の銘柄が混じっても、median であれば代表値として使えます。

外れ値の扱い

業種比較で平均を出すと、PER 100 倍を超えるような極端な銘柄が混じることがあります。次のような前処理が定番です。

# 上下 1% を切り捨ててから平均を取る(winsorize)
q_low = df["per"].quantile(0.01)
q_high = df["per"].quantile(0.99)
filtered = df[(df["per"] >= q_low) & (df["per"] <= q_high)]
print(filtered["per"].mean())

または 中央値で代表値を扱う のが一番楽です。

生成AI へのプロンプト例

財務データから PER のスクリーニングを書きたい場合の例です。

入力 DataFrame に次の列があります(列名は J-Quants API に準拠)。
- Code (str)
- CoName (str)
- S33Nm (str)
- C (float)
- EPS (float, 赤字なら負)
次の条件を満たす銘柄を抽出する関数 screen_per(df) を書いてください。
条件:
- PER が計算可能(EPS > 0)
- PER が業種中央値の 1/2 以下(その業種の中で割安と見なせる水準)
- 流動性のため C > 100
要件:
- pandas 2.2 系
- 戻り値は元の列に per 列を加えた DataFrame
- 業種の中央値は同じ DataFrame 内で計算
- docstring は日本語

まとめ

  • PER は「利益に対して株価が何倍か」を測る指標
  • 赤字企業では計算できない。EPS > 0 のフィルタを必ず入れる
  • 業種で水準が大きく違うため、業種を揃えるか、中央値を使う
  • 単独で投資判断には使わない。他指標との組み合わせが前提
  • 1 度きりの特別損益でも歪むため、複数年で見る