PHPで対数グラフ(ムーアの法則)を描く

(1/1)
インフルエンザ患者数のグラフを作る」では単純な折れ線グラフを扱ったが、PHPと JpGraph を用いることでY軸が対数になっている折れ線グラフも描くことができる。今回は、ムーアの法則を題材に、インテルマイクロプロセッサのトランジスタ数の増え方を対数グラフに描いてみることにする。

(2026年3月15日)PHP8.5対応, pahooInputData.php導入

サンプル・プログラムの実行例

ムーアの法則
サンプル・プログラムを実行すると、インテルCPUのトランジスタ数の推移と、ムーアの法則(18ヶ月ごとに2倍、24ヶ月ごとに2倍の2通り)の3つの折れ線グラフが描かれる。

目次

サンプル・プログラム

圧縮ファイルの内容
Moore.phpサンプル・プログラム本体。
pahooInputData.phpデータ入力に関わる関数群。
使い方は「数値入力とバリデーション」「文字入力とバリデーション」などを参照。include_path が通ったディレクトリに配置すること。
Moore.php 更新履歴
バージョン 更新日 内容
1.5.0 2023/06/06 PHP8.5対応, pahooInputData.php導入
1.4.1 2023/06/06 Apple M2 Ultra追加
1.4.0 2023/05/05 グラフにフッタを追加
1.3.1 2023/01/20 Z80, MOS 6502, MC68000 など追加
1.3.0 2023/01/20 同じ年のCPUがあったら,トランジスタ数が多い方を採用する.
pahooInputData.php 更新履歴
バージョン 更新日 内容
2.0.1 2025/08/11 getParam() bug-fix
2.0.0 2025/08/11 pahooLoadEnv() 追加
1.9.0 2025/07/26 getParam() 引数に$trim追加
1.8.1 2025/03/15 validRegexPattern() debug
1.8.0 2024/11/12 validRegexPattern() 追加

準備:pahooInputData 関数群

PHPのバージョンや入力データのバリデーションなど、汎用的に使う関数群を収めたファイル "pahooInputData.php" が同梱されているが、include_path が通ったディレクトリに配置してほしい。他のプログラムでも "pahooInputData.php" を利用するが、常に最新のファイルを1つ配置すればよい。

また、各種クラウドサービスに登録したときに取得するアカウント情報、アプリケーションパスワードなどを登録した .pahooEnv ファイルから読み込む関数 pahooLoadEnv を備えている。こちらについては、「各種クラウド連携サービス(WebAPI)の登録方法」をご覧いただきたい。

準備:JpGraphライブラリ

Moore.php

  53: // JpGraph:include_pathが通ったディレクトリに配置すること
  54: require_once('jpgraph/jpgraph.php');
  55: require_once('jpgraph/jpgraph_line.php');
  56: require_once('jpgraph/jpgraph_log.php');
  57: 

国内ではアシアル株式会社がサポートしている PHPで様々なグラフを生成するライブラリ「JpGraph」を準備する。JpGraph コミュニティ版は、QPL (Qt Free License) 1.0 ライセンスの下で、非営利目的で使用する場合に無償利用できる。
JpGraph は、サーバ側(PHP側)でグラフを画像データとして生成し、クライアントへ配信する。このため、GDライブラリが有効化されていることが前提である。
また、使用する機能によってインクルードする PHPファイルの種類が異なることに留意してほしい。

公式サイトからダウンロードしたファイルを解凍したら、include_path が通ったディレクトリに配置してほしい。

凡例やラベルに日本語を使う場合は、日本語フォント(TrueTypeファイル)を用意しておく。これについても、アシアル社のサイトに詳しい説明がある。
具体的には、フォント・ファイルを格納したフォルダを "jpg-config.inc.php" の定数 TTF_DIR および MBTTF_DIR に設定してやる。
実際に使うフォントは "jpgraph_ttf.inc.php" の定数 MINCHO_TTF_FONTPMINCHO_TTF_FONTGOTHIC_TTF_FONTPGOTHIC_TTF_FONT に設定されている。デフォルトで設定されているのは IPA の無償フォントで、文字情報技術促進協議会の IPAフォントから入手できる。

準備:各種定数など

Moore.php

  58: // 各種定数(START) ===========================================================
  59: 
  60: // 表示幅(ピクセル)
  61: define('WIDTH', 500);
  62: 
  63: // エラー表示用フォント・ファイル名;各自の環境に合わせて
  64: define('FONT', '../../../../common/font/ipagp.ttf');
  65: 
  66: // 各種定数(END) ===============================================================

各種パラメータは定数を defineしている。とくに記載のないものは、適宜変更してかまわない。

解説:マイクロプロセッサ情報

Moore.php

  68: // マイクロプロセッサの情報
  69: //    マイクロプロセッサ  クロック(Hz)    プロセス(mトランジスタ数
  70: //      *印のマイクロプロセッサはグラフ上に名称表示
  71: $DataCPU =<<< EOT
  72: 1971    *4004           7.40E+05        1.00E-05        2.30E+03
  73: 1972    8008            5.00E+05        1.00E-05        3.50E+03
  74: 1974    *8080           2.00E+06        6.00E-06        4.50E+03
  75: 1975    MOS 6502        3.00E+06        8.00E-06        3.51E+03
  76: 1976    *Z80            6.00E+06        4.00E-06        8.50E+03
  77: 1979    *8086           5.00E+06        3.00E-06        2.90E+04
  78: 1980    MC68000         2.00E+07        3.50E-06        6.80E+04
  79: 1982    *80286          1.20E+07        1.50E-06        1.34E+05
  80: 1985    *80386          1.60E+07        1.50E-06        2.75E+05
  81: 1986    R2000           1.50E+07        2.00E-06        1.10E+05
  82: 1987    MC68030         5.00E+07        8.00E-07        3.00E+05
  83: 1989    *80486          1.60E+08        1.00E-06        1.19E+06
  84: 1991    R4000           1.50E+08        1.00E-06        1.20E+06
  85: 1993    *Pentium        6.00E+07        8.00E-07        3.10E+06
  86: 1994    PowerPC 603     3.00E+08        2.90E-07        2.60E+06
  87: 1995    PentiumPro      1.50E+08        6.00E-07        5.50E+06
  88: 1997    *PentiumII      2.33E+08        3.50E-07        7.50E+06
  89: 1999    PentiumIII      4.50E+08        2.50E-07        9.50E+06
  90: 2000    *Pentium4       1.30E+09        1.80E-07        4.20E+07
  91: 2005    *PentiumM       9.00E+08        1.30E-07        7.70E+07
  92: 2006    CoreDuo         1.07E+09        6.50E-08        1.51E+08
  93: 2006    Core2 Duo       1.60E+09        6.50E-08        1.67E+08
  94: 2009    *Core i7-860    2.80E+09        4.50E-08        7.74E+08
  95: 2011    Core i7-2700K   3.50E+09        3.20E-08        1.16E+09
  96: 2012    Core i7-3770K   3.50E+09        2.20E-08        1.40E+09
  97: 2014    Core i7-5960X   3.00E+09        2.20E-08        2.60E+09
  98: 2016    *Core i7-6950X  3.00E+09        1.40E-08        3.20E+09
  99: 2017    Ryzen 7 1800X   4.00E+09        1.40E-08        4.80E+09
 100: 2018    A12 Bionic      2.49E+09        7.00E-09        6.90E+09
 101: 2020    *Apple M1       3.20E+09        5.00E-09        1.60E+10
 102: 2021    Apple M1 Max    3.20E+09        5.00E-09        5.70E+10
 103: 2022    Ryzen 9 7950X   5.70E+09        5.00E-09        1.31E+10
 104: 2022    Apple M1 Ultra  3.20E+09        5.00E-09        1.14E+11
 105: 2022    Apple M2        3.49E+09        5.00E-09        2.00E+10
 106: 2023    Apple M2 Max    3.49E+09        5.00E-09        6.70E+10
 107: 2023    *Apple M2 Ultra 3.49E+09        5.00E-09        13.40E+10
 108: 
 109: EOT;

Moore.php

 139: /**
 140:  * 指定したCPUデータ文字列を分解し,配列に格納する.
 141:  * 文字列の行区切りは改行,列区切りはタブ(複数可).
 142:  * @param   string $sour  入力文字列
 143:  * @param   array  $dest  結果を格納する2次元配列
 144:  * @return  int 分解した行数
 145: */
 146: function str2matrix($sour, &$dest) {
 147:     $arr1 = preg_split("/\n/ui", $sour);
 148:     $cnt1 = 0;
 149:     foreach ($arr1 as $val1) {
 150:         $str = preg_replace("/\n/ui", '', $val1);
 151:         if (mb_strlen($str< 1continue;
 152:         $arr2 = preg_split("/[\t]+/ui", $str);
 153:         $cnt2 = 0;
 154:         foreach ($arr2 as $val2) {
 155:             $dest[$cnt1][$cnt2] = $val2;
 156:             $cnt2++;
 157:         }
 158:         $cnt1++;
 159:     }
 160: 
 161:     return $cnt1;
 162: }

マイクロプロセッサの情報は、Excelで入力してあったのだが、ソースプログラムに展開することにした。行区切りを改行に、列区切りをタブ(複数可能)と定義し、まず文字列として変数 $DataCPU に代入しておく。

グラフ描画の前に、これを行×列の2次元配列に展開する目的で、ユーザー関数 str2matrix を用意した。他の用途でも使えるだろう。

解説:描画用配列に変換

Moore.php

 164: /**
 165:  * CPUデータ配列をJpGraph描画用配列に変換する.
 166:  * 同じ年のCPUがあったら,トランジスタ数が多い方を採用する.
 167:  * @param   array  $matrix CPUデータ配列
 168:  * @param   array  $items  描画用配列
 169:  * @return  array(最小年,最大年) 描画用配列
 170: */
 171: function matrix2items($matrix, &$items) {
 172:     // データの分解
 173:     $year_min = 99999;
 174:     $year_max = 0;
 175:     foreach ($matrix as $val) {
 176:         $year = (int)$val[0];
 177:         if ($year < $year_min)   $year_min = $year;
 178:         if ($year > $year_max)  $year_max = $year;
 179:         // 同じ年のCPUがあったら,トランジスタ数が多い方を採用する.
 180:         if (isset($items[$year])) {
 181:             if ((float)$val[4> $items[$year]['transistors']) {
 182:                 $items[$year]['CPU']         = (string)$val[1];
 183:                 $items[$year]['clock']       = (float)$val[2];
 184:                 $items[$year]['process']     = (float)$val[3];
 185:                 $items[$year]['transistors'] = (float)$val[4];
 186:             }
 187:         // 同じ年のCPUがなければ,そのまま採用する.
 188:         } else {
 189:             $items[$year]['CPU']         = (string)$val[1];
 190:             $items[$year]['clock']       = (float)$val[2];
 191:             $items[$year]['process']     = (float)$val[3];
 192:             $items[$year]['transistors'] = (float)$val[4];
 193:         }
 194:     }
 195: 
 196:     return array($year_min, $year_max);
 197: }

目的のグラフは、X軸が西暦年で、Y軸がトランジスタ数(対数)という形である。
そこで、ユーザー関数 str2matrix で変換したデータを、さらに、西暦年×列名という2次元配列に変換するためのユーザー関数 matrix2items を用意した。

解説:グラフを描く

Moore.php

 219: /**
 220:  * 指定した描画用データ配列によりJpGraphに対数グラフを描画する.
 221:  * @param   array  $items 描画用データ配列
 222:  * @param   int    $year_min 最小年
 223:  * @param   int    $year_max 最大年
 224:  * @return  object graphオブジェクト
 225: */
 226: function drawGraph($items, $year_min, $year_max) {
 227:     $plotdata = array();
 228: 
 229:     // 横軸(西暦年)の作成
 230:     $x0 = floor($year_min / 5* 5;
 231:     $x1 = ceil($year_max  / 5* 5;
 232:     $i = 1;
 233:     for ($year = $x0$year <$x1$year++) {
 234:         $data_x[$year - $x0] = $year;
 235:     }
 236: 
 237:     // グラフ描画処理開始
 238:     $graph = new Graph(600, 500, 'auto');
 239:     $graph->SetScale('linlog');     // X軸:整数,Y軸:対数
 240:     $graph->SetBackgroundGradient('#FFFFCC','#FFFFCC', GRAD_HOR, BGRAD_MARGIN);
 241: 
 242:     // タイトル、凡例などの設定
 243:     $graph->img->SetImgFormat('png');
 244:     $graph->img->SetMargin(60, 60, 20, 40);
 245:     $graph->title->SetFont(FF_PGOTHIC, FS_NORMAL, 16);
 246:     $graph->title->Set('ムーアの法則');
 247:     $graph->xaxis->title->SetFont(FF_PGOTHIC);
 248:     $graph->xaxis->SetTitle('年','center');
 249:     $graph->xaxis->SetTickLabels($data_x);
 250:     $graph->xaxis->SetTextTickInterval(5);
 251:     $graph->xaxis->SetTextLabelInterval(2);
 252:     $graph->yaxis->title->SetFont(FF_PGOTHIC);
 253:     $graph->yaxis->title->Set('トランジスタ数');
 254:     $graph->yaxis->SetTitleMargin(40);
 255:     // フッタを設定する.
 256:     $graph->footer->right->SetFont(FF_PGOTHIC, FS_NORMAL, 10);
 257:     $graph->footer->right->Set('produced by JpGraph');
 258: 
 259:     // プロット配列を作成
 260:     for ($year = $x0$year <$x1$year++) {
 261:         $data_y1[$year - $x0] = isset($items[$year]) ? (float)$items[$year]['transistors': '-';
 262:         $data_y2[$year - $x0] = 2.30E+03 * pow(2, ($year - $year_min) / (float)(18 / 12));
 263:         $data_y3[$year - $x0] = 2.30E+03 * pow(2, ($year - $year_min) / (float)(24 / 12));
 264:     }
 265: 
 266:     // ムーアの法則:18ヶ月ごとに2倍
 267:     $plotdata = new LinePlot($data_y2);
 268:     $graph->Add($plotdata);
 269:     $plotdata->SetWeight(2);                    // 太さ
 270:     $plotdata->SetColor('royalblue');               // 
 271:     $plotdata->SetLegend('18ヶ月ごとに2倍');    // 凡例
 272: 
 273:     // ムーアの法則:24ヶ月ごとに2倍
 274:     $plotdata = new LinePlot($data_y3);
 275:     $graph->Add($plotdata);
 276:     $plotdata->SetWeight(2);                    // 太さ
 277:     $plotdata->SetColor('green');               // 
 278:     $plotdata->SetLegend('24ヶ月ごとに2倍');    // 凡例
 279: 
 280:     // マイクロプロセッサ
 281:     $plotdata = new LinePlot($data_y1);
 282:     $graph->Add($plotdata);
 283:     $plotdata->SetColor('deeppink');            // 
 284:     $plotdata->SetWeight(2);                    // 太さ
 285:     $plotdata->mark->SetTYPE(MARK_DIAMOND);     // プロット記号
 286:     $plotdata->value->SetFormatCallback('CPUname');
 287:     $plotdata->value->show();
 288:     $plotdata->SetLegend('マイクロプロセッサ'); // 凡例
 289: 
 290:     // 凡例
 291:     $graph->legend->SetFont(FF_PGOTHIC);                    // フォント
 292:     $graph->legend->SetColumns(1);                          // 表示列数
 293:     $graph->legend->SetPos(0.12, 0.9, 'right', 'bottom');   // 位置
 294: 
 295:     return $graph;
 296: }

グラフの描画はユーザー関数 drawGraph で行う。

まず、X軸を用意する。西暦年を5年刻みで、最小年と最大年を設定する。
次に、グラフ描画処理の開始とタイトル、凡例などの設定を行う。このあたりは「インフルエンザ患者数のグラフを作る」とほぼ同じである。

続けて、プロット用の配列を3つ $data_y1, $data_y2, $data_y3 作る。
配列 $data_y1 には、マイクロプロセッサのトランジスタ数を代入する。最初に与えられたデータにない年は、'-' を代入することで、折れ線グラフを自動的に繋げてくれる。ここで空文字 '' を代入すると、グラフは途切れる。
1971年(昭和46年)に発売された4004のトランジスタ数を基準に、18ヶ月ごとに2倍にした値は配列 $data_y2 に、24ヶ月ごとに2倍にした値は配列 $data_y3 に、それぞれ代入する。2のべき乗を計算するために組み込み関数  pow  を用いている。
ちなみに、当初のムーアの法則では「18ヶ月ごと」だったが、その後のトランジスタ数の増加が減速し、「24ヶ月」に訂正したという経緯がある。

最後に、プロット用の配列を描画していく。
配列 $data_y1 を描く際に、$plotdata->value->SetFormatCallback を使ってデータポイントにマイクロプロセッサ名を表示させている。本来、データポイントにはY値(ここではトランジスタ数)が表示されるのだが、コールバックされるユーザー関数 CPUname において受け取ったトランジスタとデータ表を照合し、マイクロプロセッサ名を返すようにしていることで、グラフ上にマイクロプロセッサ名が表示されるという仕組みである。
すべてのマイクロプロセッサ名が表示されると重なって見にくくなるので、冒頭にアスタリスクが付いているマイクロプロセッサ名だけを表示するようにしている。

解説:画面出力

Moore.php

 308: // JpGraphを使ってグラフ表示する.
 309: $graph = drawGraph($Items, $year_min, $year_max);
 310: // 画面に表示する.
 311: if ($mode == 1) {
 312:     header('Content-type: image/png');
 313:     header('Content-Disposition: filename="Moore.png"');
 314:     $graph->Stroke();
 315: // imgタグ用の文字列を生成する.
 316: else {

画面への出力も JpGraph の機能を利用している。

コマンドライン・オプション img が指定された場合は、出力データをimgタグの中に画像データを埋め込むための文字列を生成する。詳しくは、「PHPでTrueTypeフォントを利用する(その2)」-「解説:画像をHTMLコンテンツに埋め込む」を参照のこと。

参考サイト

(この項おわり)
header