PER(株価収益率、Price Earnings Ratio)は、ファンダメンタルズ分析でもっともよく登場する指標の 1 つです。本記事では、PER の意味・計算式・読み方の落とし穴を整理し、pandas で複数銘柄を一括計算するコード例を示します。
目次
- PER とは
- 何のために使うか
- PER の限界(ここを読まないと事故ります)
- EPS の取り方
- 単一銘柄を計算する
- 複数銘柄を pandas で一括計算する
- 業種別の平均 PER を出す
- 外れ値の扱い
PER とは
PER は「ある銘柄の利益に対して、市場がいま何倍の値段を付けているか」を表す指標です。
たとえば、株価 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.0print(per(3000, -50)) # None (赤字)print(per(3000, 0)) # None (利益ゼロ)eps <= 0 を弾くことで、赤字・利益ゼロ企業を「PER 計算不能」として扱います。これは強く推奨される実装です。
複数銘柄を pandas で一括計算する
実用的な場面では、複数の銘柄について一気に計算したくなります。次のような DataFrame を入力に取る前提です。
import numpy as npimport 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 per0 1301 サンプル商事001 2900 310 9.351 1302 サンプル商事002 9800 -120 NaN2 1303 サンプル商事003 1500 130 11.543 1304 サンプル商事004 13000 700 18.574 1305 サンプル商事005 4900 380 12.89EPS が負の銘柄(サンプル商事002)は PER が NaN になり、後続の処理で集計から自動的に除外されます。
業種別の平均 PER を出す
複数銘柄に業種(S33Nm)が付いている場合、業種ごとの平均が出せます。
# サンプル: 業種付きの DataFramedf["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 度きりの特別損益でも歪むため、複数年で見る