Excel, Python, gnuplopt 等の追加ソフトは一切無しで、Windows 標準のスクリプト機能である PowerShell のみをもちいてグラフをプロットする方法を紹介します。
目次
- まえがき
- 最初の例 - 関数を計算してプロット
- 【プログラミングが苦手な方向け】CSV ファイルを読み込んでプロット
- 複雑な例 - 複数種類のグラフ、複数の系列をまとめてプロット
- 補足 - PowerShell 用の開発環境 (色分けエディター、実行環境)
- おわりに
- 主な更新履歴
まえがき
棒グラフや折れ線グラフ等の各種グラフは、Excel 等の各種専用ソフトが無いと作成できないと思っている方も多いと思います。
しかし、実は Windows には、Excel も、その他フリーソフトも一切無しで、標準機能だけで複雑なグラフの作成にも耐えられる機能が存在します。
それが PowerShell というスクリプティング機能 (プログラミング環境) です。
PowerShell は汎用性が高いため、グラフ以外にも様々なことに活用できますが、本記事ではグラフ作成のみに焦点をあて、実際にコピペで試せるスクリプト付きで紹介します。
最初の例 - 関数を計算してプロット
まずは、以下のような Sin 関数を計算してプロットする例からです。
ウィンドウとして表示する方法と、PNG などの画像ファイルとして出力する方法の両方を紹介します。
必要な準備
上記スクリーンショットにも出ているとおり、2 つのテキストファイル (bat 形式、ps1 形式) を用意する必要があります。
エクスプローラー上で右クリック → 新規作成 → テキスト ドキュメント で、空のテキストファイルを 2 つ作成します。
- plot.bat (バッチファイル)
- PowerShell スクリプト実行用 (セキュリティ設定込み) です。
- plot.ps1 (PowerShell スクリプト)
- グラフのプロットを行うスクリプト本体です。
それぞれ txt から bat, ps1 に拡張子の変更も必要なので注意して下さい。
各ファイルに貼り付ける内容
それぞれのファイルを、「メモ帳」などのテキストエディタで (ドラッグ & ドロップ等で) 開き、以下の内容を貼り付けて上書き保存します。
plot.bat
1 行だけです。セキュリティ設定を一時的に迂回 (今回に限り実行可能) するための設定と、実行するスクリプトのファイル名 (plot.ps1) を指定しています。
PowerShell -NoProfile -ExecutionPolicy Unrestricted .\plot.ps1
plot.ps1
スクリプト本体です。ps1 ファイルを直接実行した場合、Windows 側のセキュリティ設定で弾かれて実行できないことが多いため、バッチファイル (bat) 経由で実行するのが確実です。
# ■ オブジェクト名を省略形で使用するための宣言 # (Windows 10 から標準搭載の PowerShell v5.0 以降で使用可能) using namespace System.Drawing using namespace System.Drawing.Imaging using namespace System.Windows.Forms using namespace System.Windows.Forms.DataVisualization.Charting # ■ グラフ関係で使用する .NET の機能を読み込む Add-Type -AssemblyName System.Windows.Forms Add-Type -AssemblyName System.Windows.Forms.DataVisualization # ■ Sin 関数を系列 (Series) オブジェクトにプロット $series = New-Object Series for ($x = 0; $x -lt 32; $x++) { $y = [Math]::Sin(2 * [Math]::PI * $x / 32) [void]$series.Points.AddXY($x, $y) } # ■ 系列 (Series) をグラフ本体 (Chart) に紐づける $chart = New-Object Chart $chart.ChartAreas.Add((New-Object ChartArea)) $chart.Series.Add($series) # ■ グラフの中身を画像ファイル (PNG) に保存 $bitmap = New-Object Bitmap( $chart.Size.Width, $chart.Size.Height) $chart.DrawToBitmap($bitmap, (New-Object Rectangle( 0, 0, $chart.Size.Width, $chart.Size.Height))) $bitmap.Save($PSScriptRoot + "\plot.png", [ImageFormat]::Png) # ■ グラフの中身をウィンドウとして表示 $form = New-Object Form $form.ClientSize = $chart.Size $form.Controls.Add($chart) [void]$form.ShowDialog()
実行方法
plot.bat をダブルクリック等で実行します。
ウィンドウが表示され、かつグラフの画像ファイル (plot.png) も出力されれば成功です。
終了するには、グラフ側のウィンドウの「×」ボタンで閉じます。
【プログラミングが苦手な方向け】CSV ファイルを読み込んでプロット
先ほどのスクリプトにはコメントも入れておきましたので、Excel マクロや Python、gnuplot 等を使用したことのある方なら、理解するのはさほど難しくはないと思います。
しかし、プログラミング経験が無い方には難しいと思われますので、CSV ファイルから読み込んでプロットする例も紹介します。
CSV ファイルの一例
以下のように、x 軸と y 軸の値を半角カンマで区切ったデータを plot.csv として保存します。
(Excel を持っている方は、上記スクリーンショットのように A 列と B 列に x, y の値を書いたペアを作って、CSV 形式として保存する方法でも作成可能です)
plot.csv
-3,6 -2,-1 -1,4 0,5 1,-6 2,2 3,-3
この例では 7 行のみですが、行数 (x, y のペア数) は不問です。
スクリプトの内容
plot.bat
変更箇所はありません。
PowerShell -NoProfile -ExecutionPolicy Unrestricted .\plot.ps1
plot.ps1
変更箇所は、以下の 2 箇所です。
# ■ オブジェクト名を省略形で使用するための宣言 # (Windows 10 から標準搭載の PowerShell v5.0 以降で使用可能) using namespace System.Drawing using namespace System.Drawing.Imaging using namespace System.IO using namespace System.Windows.Forms using namespace System.Windows.Forms.DataVisualization.Charting # ■ グラフ関係で使用する .NET の機能を読み込む Add-Type -AssemblyName System.Windows.Forms Add-Type -AssemblyName System.Windows.Forms.DataVisualization # ■ CSV ファイルの中身を系列 (Series) オブジェクトにプロット $series = New-Object Series $file = New-Object StreamReader($PSScriptRoot + "\plot.csv") while (($csvLine = $file.ReadLine()) -ne $null) { $csvXY = $csvLine.Split(',') [void]$series.Points.AddXY( [Double]$csvXY[0], [Double]$csvXY[1]) } $file.Dispose() # ■ 系列 (Series) をグラフ本体 (Chart) に紐づける $chart = New-Object Chart $chart.ChartAreas.Add((New-Object ChartArea)) $chart.Series.Add($series) # ■ グラフの中身を画像ファイル (PNG) に保存 $bitmap = New-Object Bitmap( $chart.Size.Width, $chart.Size.Height) $chart.DrawToBitmap($bitmap, (New-Object Rectangle( 0, 0, $chart.Size.Width, $chart.Size.Height))) $bitmap.Save($PSScriptRoot + "\plot.png", [ImageFormat]::Png) # ■ グラフの中身をウィンドウとして表示 $form = New-Object Form $form.ClientSize = $chart.Size $form.Controls.Add($chart) [void]$form.ShowDialog()
実行方法
最初の例と同じく、plot.bat をダブルクリック等で実行します。
プログラミングが必要であることには変わりはありませんが、CSV ファイルの中身さえ差し替えれば別のグラフに変更できるため、多少は敷居が下がると思います。
プログラミングが苦手な方は本スクリプトをベースに、少しずつスクリプトの中身の理解や、改造などを試みていくと良いでしょう。
複雑な例 - 複数種類のグラフ、複数の系列をまとめてプロット
最後に、比較的使用頻度が高いと思われるカスタマイズ例を詰め込んだ複雑な例を紹介します。
本節のコードを参考にグラフのプロットの仕方をカスタマイズすれば、大抵のケースには対応できるのではないかと思います。
スクリプトの内容
plot.bat
変更箇所はありません。
PowerShell -NoProfile -ExecutionPolicy Unrestricted .\plot.ps1
plot.ps1
# ■■ オブジェクト名を省略形で使用するための宣言 # (Windows 10 から標準搭載の PowerShell v5.0 以降で使用可能) using namespace System.Drawing using namespace System.Drawing.Imaging using namespace System.Windows.Forms using namespace System.Windows.Forms.DataVisualization.Charting # ■■ グラフ関係で使用する .NET の機能を読み込む Add-Type -AssemblyName System.Windows.Forms Add-Type -AssemblyName System.Windows.Forms.DataVisualization # ■■ 各系列のプロットデータを作成 # ■ 系列 1-1 (サイン関数 (8pt)) $series11 = New-Object Series $series11.ChartArea = "Area-1" # 領域 Area-1 に表示 $series11.ChartType = [SeriesChartType]::Line # 折れ線グラフ $series11.BorderWidth = 4 # 線の太さ 4 $series11.MarkerSize = 12 # マーカーの大きさ 12 $series11.MarkerStyle = [MarkerStyle]::Square # 四角形 $series11.LegendText = "サイン関数 (8pt)" for ($x = 0; $x -lt 8; $x++) { [void]$series11.Points.AddXY($x / 8, [Math]::Sin(2 * [Math]::PI * $x / 8)) } # ■ 系列 1-2 (コサイン関数 (32pt)) $series12 = New-Object Series $series12.ChartArea = "Area-1" # 領域 Area-1 に表示 $series12.ChartType = [SeriesChartType]::Line # 折れ線グラフ $series12.BorderWidth = 3 # 線の太さ 3 $series12.MarkerSize = 10 # マーカーの大きさ 10 $series12.MarkerStyle = [MarkerStyle]::Diamond # 菱形 $series12.LegendText = "コサイン関数 (32pt)" for ($x = 0; $x -lt 32; $x++) { [void]$series12.Points.AddXY($x / 32, [Math]::Cos(2 * [Math]::PI * $x / 32)) } # ■ 系列 2-1 (コンスタレーション (256QAM)) $series21 = New-Object Series $series21.ChartArea = "Area-2" # 領域 Area-2 に表示 $series21.ChartType = [SeriesChartType]::Point # 散布図 $series21.MarkerSize = 6 # マーカーの大きさ 6 $series21.MarkerStyle = [MarkerStyle]::Circle # 円 $series21.LegendText = "256QAM" for ($x = 0; $x -lt 16; $x++) { for ($y = 0; $y -lt 16; $y++) { [void]$series21.Points.AddXY( ($x * 2 - 15) / [Math]::Sqrt(170), ($y * 2 - 15) / [Math]::Sqrt(170)) } } # ■ 系列 2-2 (コンスタレーション (QPSK)) $series22 = New-Object Series $series22.ChartArea = "Area-2" # 領域 Area-2 に表示 $series22.ChartType = [SeriesChartType]::Point # 散布図 $series22.MarkerSize = 12 # マーカーの大きさ 12 $series22.MarkerStyle = [MarkerStyle]::Triangle # 三角形 $series22.LegendText = "QPSK" [void]$series22.Points.AddXY( -1 / [Math]::Sqrt(2), -1 / [Math]::Sqrt(2)) [void]$series22.Points.AddXY( +1 / [Math]::Sqrt(2), -1 / [Math]::Sqrt(2)) [void]$series22.Points.AddXY( -1 / [Math]::Sqrt(2), +1 / [Math]::Sqrt(2)) [void]$series22.Points.AddXY( +1 / [Math]::Sqrt(2), +1 / [Math]::Sqrt(2)) # ■■ 各グラフ領域 (Area-1, 2) の作成 # ■ Area-1 $chartArea1 = New-Object ChartArea("Area-1") $chartArea1.AxisX.Title = "時刻 [t]" $chartArea1.AxisX.LabelStyle.Format = "{0:0.00}" # 2桁まで $chartArea1.AxisY.Title = "振幅 [a]" $chartArea1.AxisY.LabelStyle.Format = "{0:0.00}" # 2桁まで $chartArea1.Position = New-Object ElementPosition( 0, 10, 50, 80) # 横開始=0%, 縦開始=10%, 幅=50%, 高さ=80% # ■ Area-2 $chartArea2 = New-Object ChartArea("Area-2") $chartArea2.AxisX.Title = "I 相 [Re]" $chartArea2.AxisX.LabelStyle.Format = "{0:0.000}" # 3桁まで $chartArea2.AxisY.Title = "Q 相 [Im]" $chartArea2.AxisY.LabelStyle.Format = "{0:0.000}" # 3桁まで $chartArea2.Position = New-Object ElementPosition( 50, 10, 50, 80) # 横開始=50%, 縦開始=10%, 幅=50%, 高さ=80% # ■■ 凡例の作成 $legend = New-Object Legend $legend.Position = New-Object ElementPosition( 0, 95, 100, 5) # 横開始=0%, 縦開始=95%, 幅=100%, 高さ=5% # ■■ グラフタイトルの作成 $titleMain = New-Object Title("複雑なグラフを 2 種類プロット") $titleArea1 = New-Object Title("正弦波 (Sin, Cos)") $titleArea1.DockedToChartArea = "Area-1" # 領域 Area-1 に紐付け $titleArea2 = New-Object Title("コンスタレーション") $titleArea2.DockedToChartArea = "Area-2" # 領域 Area-2 に紐付け # ■■ グラフ本体を作成 $chart = New-Object Chart $chart.Series.Add($series11) $chart.Series.Add($series12) $chart.Series.Add($series21) $chart.Series.Add($series22) $chart.ChartAreas.Add($chartArea1) $chart.ChartAreas.Add($chartArea2) $chart.Legends.Add($legend) $chart.Titles.Add($titleMain) $chart.Titles.Add($titleArea1) $chart.Titles.Add($titleArea2) $chart.Size = New-Object Size(640, 360) # ■■ グラフの中身を画像ファイル (PNG) に保存 $bitmap = New-Object Bitmap( $chart.Size.Width, $chart.Size.Height) $chart.DrawToBitmap($bitmap, (New-Object Rectangle( 0, 0, $chart.Size.Width, $chart.Size.Height))) $bitmap.Save($PSScriptRoot + "\plot.png", [ImageFormat]::Png) # ■■ グラフの中身をウィンドウとして表示 $form = New-Object Form $form.ClientSize = $chart.Size $form.Controls.Add($chart) [void]$form.ShowDialog()
文字化けが起こった場合の対策
上記のように日本語文字 (スクリプト中のコメントを除く) を含むスクリプトを実行した場合に、以下のように文字化けが発生してしまう場合があります。
その場合は、文字コードを BOM (Byte Order Mark) 付きの UTF-8 として保存すると改善するはずです。(下記は Windows 標準のメモ帳の場合)
補足 - PowerShell 用の開発環境 (色分けエディター、実行環境)
Windows には、PowerShell スクリプトの「色分け」や「実行」が簡単にできる環境である PowerShell ISE という開発環境も標準でインストールされています。
プログラミング経験がある方で、(本記事のグラフの件も含めて) 今後も PowerShell スクリプトの活用を考えている方は、PowerShell ISE に慣れておくのもおすすめです。
- 起動方法 (Windows 11)
- スタートメニュー → すべてのアプリ → Windows ツール → Windows PowerShell ISE
- 起動方法 (Windows 10)
- スタートメニュー → Windows PowerShell → Windows PowerShell ISE
PowerShell ISE でスクリプトを実行する場合の注意
拡張子 ps1 の PowerShell スクリプトを直接実行できますが、バッチファイル (bat) 経由で実行する場合と同様、セキュリティ設定が必要になります。
原則、PowerShell ISE の起動時に「毎回」、プロンプト (画面左下の紺色の画面) に以下のコマンドを打ち込んで Enter を押す必要があります。
Set-ExecutionPolicy -Scope Process Unrestricted
毎回設定するのが面倒な場合 (※ セキュリティ上「非推奨」)
上記のコマンドを、以下のように置き換えることで、以降は (現在 Windows で使用中のユーザーであれば) コマンド入力でセキュリティを解除する必要は無くなります。
(マイクロソフト公式情報によると、レジストリに保存されるとのことです)
Set-ExecutionPolicy -Scope CurrentUser Unrestricted
ただし、この状態では他の PowerShell スクリプトも常時実行できる状態になってしまうため、セキュリティのことを考えるとおすすめできません。
なお、元の状態 (スクリプト実行を制限) に戻すには、以下のようにします。
Set-ExecutionPolicy -Scope CurrentUser Restricted
おわりに
本記事では、Windows の標準機能のみをもちいてグラフをプロットする方法を紹介しました。
これ以上複雑な例や、本記事には出てこないカスタマイズの仕方などは、マイクロソフトの (.NET の) Chart クラスの説明や、他サイトの情報などを参考にすると見つかると思います。
また、本記事で使用した機能は .NET の API (Windows のさまざまな機能が集約されているライブラリ) であり、PowerShell だけでなく C# や VB.NET などの他のプログラミング言語からも呼び出すことができる、という特徴があります。
.NET の Chart クラスについて検索して出てくるのは、大半が C# 言語による情報ですが、基本的な設定の仕方は同じ (文法が違うだけ) なので、たとえネット上で見つけた情報が C# などの異なるプログラミング言語の情報であっても、参考にしてみることをおすすめします。
主な更新履歴
- 2023-05-06
- 文字化けが起こった場合の対策を追記。
- 2023-04-02
- 初版