9
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【PowerShell】Windows標準機能でグラフ描画 〜基本編〜

Posted at

この記事では以下のようなグラフをPowerShellで描画する方法について解説しています。

やりたいこと

グラフ作成の自動化です。
Pythonで作ったコードを配布できれば楽なのですが、自職場ではオープンソースなどを自由に導入できないので困難。

そこで、自職場でも使えるPowerShellで、グラフを作成する方法について調べてみました。

PowerShellでのグラフ描画

PowerShellについて

PowerShellはWindowsに標準搭載されているスクリプト言語です。Windowsであれば追加インストールなしですぐに利用を開始できます。なので、配布も容易です。

PowerShellが強力なのは、Windowsの基盤である「.NET Framework」の機能を直接呼び出せる点にあります。これにより、GUIを持つウィンドウアプリケーションやグラフの描画も可能になります。

描画できるグラフの例

今回は例として散布図を作成しますが、System.Windows.Forms.DataVisualizationでは以下のような様々な種類のグラフを描画できます。

  • 棒グラフ
    • Column: 一般的な縦棒グラフ
    • Bar: 横棒グラフ

34_1_棒グラフ系_サンプル.png

 

  • 折れ線グラフ
    • Line: データ点を直線で結んだ、一般的な折れ線グラフ
    • Spline: データ点を滑らかな曲線で結んだ折れ線グラフ

34_2_折れ線グラフ系_サンプル.png

 

  • 散布図
    • Point: 点でデータをプロットする散布図
    • Bubble: 散布図の拡張版。第三の要素をバブルの大きさで表現

34_3_散布図系_サンプル.png

 

  • 円グラフ
    • Pie: 一般的な円グラフ
    • Doughnut: 中央に穴の開いたドーナツ型の円グラフ

34_4_円ドーナツ系_サンプル.png
 

  • 積み上げグラフ
    • StackedColumn: 縦棒グラフを積み上げたもの
    • StackedBar: 積み上げグラフの横棒バージョン

34_5_積み上げ系_サンプル.png

  • レーダー/ポーラーチャート
    • Radar: 円周方向にカテゴリ(離散値)をセットし、データをプロット
    • Polar: 円周方向に軸(連続値)をセットし、データをプロット

34_7_レーダー_ポーラー_同一チャート_サンプル.png

 
その他にも様々なグラフを描くことができます。詳細はMicroSoftの公式ドキュメントを参照ください。

上記サンプルの描画スクリプト(自分のメモ用) (全てAIに書いてもらったスクリプトになります)
棒グラフ系.ps1
# 棒グラフ系サンプル(Column / Bar)
# 目的: 同じデータを縦棒・横棒で比較表示。横に並べて固定サイズで図示

# 事前準備 ================================================

# 必要なアセンブリの読み込み
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Windows.Forms.DataVisualization

# データ準備 ================================================

# グラフにプロットするデータ(カテゴリと値)を定義
$categories = @("A", "B", "C", "D", "E")
$values     = @(12, 25, 18, 30, 20)

# フォームの作成(表示用ウィンドウ) ================================================
$form = [System.Windows.Forms.Form] @{
    Text   = "棒グラフ系"
    Width  = 900
    Height = 400
}

# チャートの作成 ================================================

# チャート全体の作成とフォームへの配置
$chart = [System.Windows.Forms.DataVisualization.Charting.Chart] @{
    Width  = 860
    Height = 320
    Left   = 20
    Top    = 20
}
$form.Controls.Add($chart)

# チャート全体のタイトル設定
$mainTitle = [System.Windows.Forms.DataVisualization.Charting.Title] @{
    Text = "棒グラフ系"
    Font = [System.Drawing.Font]::new("Meiryo", 18, [System.Drawing.FontStyle]::Bold)
}
$chart.Titles.Add($mainTitle)

# チャートエリアの作成 ================================================

# グラフを2つ描画するため、チャートエリアを左右に分割して作成
$areaLeftName  = "Area_Left"
$areaRightName = "Area_Right"
$areaLeft = [System.Windows.Forms.DataVisualization.Charting.ChartArea] @{
    Name    = $areaLeftName
    Position = [System.Windows.Forms.DataVisualization.Charting.ElementPosition]::new(5, 24, 42, 72)
}
$areaRight = [System.Windows.Forms.DataVisualization.Charting.ChartArea] @{
    Name    = $areaRightName
    Position = [System.Windows.Forms.DataVisualization.Charting.ElementPosition]::new(53, 24, 42, 72)
}
$chart.ChartAreas.Add($areaLeft)
$chart.ChartAreas.Add($areaRight)

# 各チャートエリアの上に、個別のタイトル(副題)を設定
$subTitleLeft = [System.Windows.Forms.DataVisualization.Charting.Title] @{
    Text                   = "縦棒グラフ(Column)"
    Font                   = [System.Drawing.Font]::new("Meiryo", 12, [System.Drawing.FontStyle]::Bold)
    DockedToChartArea      = $areaLeftName
    IsDockedInsideChartArea = $false
    Docking                = [System.Windows.Forms.DataVisualization.Charting.Docking]::Top
    DockingOffset          = 2
}
$subTitleRight = [System.Windows.Forms.DataVisualization.Charting.Title] @{
    Text                   = "横棒グラフ(Bar)"
    Font                   = [System.Drawing.Font]::new("Meiryo", 12, [System.Drawing.FontStyle]::Bold)
    DockedToChartArea      = $areaRightName
    IsDockedInsideChartArea = $false
    Docking                = [System.Windows.Forms.DataVisualization.Charting.Docking]::Top
    DockingOffset          = 2
}
$chart.Titles.Add($subTitleLeft)
$chart.Titles.Add($subTitleRight)

# 軸ラベルの設定
$areaLeft.AxisX.Title  = "カテゴリ"
$areaLeft.AxisY.Title  = "値"
$areaRight.AxisX.Title = "カテゴリ"
$areaRight.AxisY.Title = "値"

# シリーズの作成とデータ投入 ================================================

# シリーズ1: 縦棒グラフ (Column)
$seriesColumn = [System.Windows.Forms.DataVisualization.Charting.Series] @{
    Name      = "ColumnSeries"
    ChartType = [System.Windows.Forms.DataVisualization.Charting.SeriesChartType]::Column
    ChartArea = $areaLeftName
}
$chart.Series.Add($seriesColumn)

# シリーズ2: 横棒グラフ (Bar)
$seriesBar = [System.Windows.Forms.DataVisualization.Charting.Series] @{
    Name      = "BarSeries"
    ChartType = [System.Windows.Forms.DataVisualization.Charting.SeriesChartType]::Bar
    ChartArea = $areaRightName
}
$chart.Series.Add($seriesBar)

# データの追加(カテゴリと値を1点ずつプロット)
for ($i = 0; $i -lt $categories.Count; $i++) {
    $category = $categories[$i]
    $value    = $values[$i]
    
    # 両方のシリーズに同じデータを追加(出力抑制)
    $null = $seriesColumn.Points.AddXY($category, $value)
    $null = $seriesBar.Points.AddXY($category, $value)
}

# 出力 ================================================

# フォームの表示(ShowDialogでウィンドウをモーダル表示)
$null = $form.ShowDialog()

# 画像保存
# $chart.SaveImage(".\棒グラフ系_サンプル.png", [System.Windows.Forms.DataVisualization.Charting.ChartImageFormat]::Png)
折れ線グラフ系.ps1
# 折れ線グラフ系サンプル(Line / Spline)
# 目的: 同じデータを折れ線・スプラインで比較。横に並べ固定サイズで図示

# 事前準備 ================================================

# 必要なアセンブリの読み込み
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Windows.Forms.DataVisualization

# データ準備 ================================================

# グラフにプロットするデータ(X軸用の連番と、Y軸用の乱数)を生成
$rand = [System.Random]::new()
$xValues = 1..10
$yValues = @(1..10 | ForEach-Object { 5 + $rand.Next(0, 20) })

# フォームの作成(表示用ウィンドウ) ================================================
$form = [System.Windows.Forms.Form] @{
    Text   = "折れ線グラフ系"
    Width  = 900
    Height = 400
}

# チャートの作成 ================================================

# チャート全体の作成とフォームへの配置
$chart = [System.Windows.Forms.DataVisualization.Charting.Chart] @{
    Width  = 860
    Height = 320
    Left   = 20
    Top    = 20
}
$form.Controls.Add($chart)

# チャート全体のタイトル設定
$mainTitle = [System.Windows.Forms.DataVisualization.Charting.Title] @{
    Text = "折れ線グラフ系"
    Font = [System.Drawing.Font]::new("Meiryo", 18, [System.Drawing.FontStyle]::Bold)
}
$chart.Titles.Add($mainTitle)

# チャートエリアの作成 ================================================

# グラフを2つ描画するため、チャートエリアを左右に分割して作成
$areaLeftName  = "Area_Left"
$areaRightName = "Area_Right"
$areaLeft = [System.Windows.Forms.DataVisualization.Charting.ChartArea] @{
    Name     = $areaLeftName
    Position = [System.Windows.Forms.DataVisualization.Charting.ElementPosition]::new(5, 24, 42, 72)
}
$areaRight = [System.Windows.Forms.DataVisualization.Charting.ChartArea] @{
    Name     = $areaRightName
    Position = [System.Windows.Forms.DataVisualization.Charting.ElementPosition]::new(53, 24, 42, 72)
}
$chart.ChartAreas.Add($areaLeft)
$chart.ChartAreas.Add($areaRight)

# 各チャートエリアの上に、個別のタイトル(副題)を設定
$subTitleLeft = [System.Windows.Forms.DataVisualization.Charting.Title] @{
    Text                   = "折れ線(Line)"
    Font                   = [System.Drawing.Font]::new("Meiryo", 12, [System.Drawing.FontStyle]::Bold)
    DockedToChartArea      = $areaLeftName
    IsDockedInsideChartArea = $false
    Docking                = [System.Windows.Forms.DataVisualization.Charting.Docking]::Top
    DockingOffset          = 2
}
$subTitleRight = [System.Windows.Forms.DataVisualization.Charting.Title] @{
    Text                   = "スプライン(Spline)"
    Font                   = [System.Drawing.Font]::new("Meiryo", 12, [System.Drawing.FontStyle]::Bold)
    DockedToChartArea      = $areaRightName
    IsDockedInsideChartArea = $false
    Docking                = [System.Windows.Forms.DataVisualization.Charting.Docking]::Top
    DockingOffset          = 2
}
$chart.Titles.Add($subTitleLeft)
$chart.Titles.Add($subTitleRight)

# 軸ラベルの設定
$areaLeft.AxisX.Title  = "X"
$areaLeft.AxisY.Title  = "Y"
$areaRight.AxisX.Title = "X"
$areaRight.AxisY.Title = "Y"

# シリーズの作成とデータ投入 ================================================

# シリーズ1: 折れ線グラフ (Line)
$seriesLine = [System.Windows.Forms.DataVisualization.Charting.Series] @{
    Name        = "LineSeries"
    ChartType   = [System.Windows.Forms.DataVisualization.Charting.SeriesChartType]::Line
    ChartArea   = $areaLeftName
    BorderWidth = 2
    MarkerStyle = [System.Windows.Forms.DataVisualization.Charting.MarkerStyle]::Circle
    MarkerSize  = 7
}
$chart.Series.Add($seriesLine)

# シリーズ2: スプライングラフ (Spline)
$seriesSpline = [System.Windows.Forms.DataVisualization.Charting.Series] @{
    Name        = "SplineSeries"
    ChartType   = [System.Windows.Forms.DataVisualization.Charting.SeriesChartType]::Spline
    ChartArea   = $areaRightName
    BorderWidth = 2
    MarkerStyle = [System.Windows.Forms.DataVisualization.Charting.MarkerStyle]::Diamond
    MarkerSize  = 7
}
$chart.Series.Add($seriesSpline)

# データの追加(事前に生成したX/Y値の配列をバインド)
# DataBindXYを使うとループ処理が不要になり、コードが簡潔になる
$seriesLine.Points.DataBindXY($xValues, $yValues)
$seriesSpline.Points.DataBindXY($xValues, $yValues)

# 出力 ================================================

# フォームの表示
$null = $form.ShowDialog()

# 画像保存
# $chart.SaveImage(".\折れ線グラフ系_サンプル.png", [System.Windows.Forms.DataVisualization.Charting.ChartImageFormat]::Png)
散布図系.ps1
# 散布図系サンプル(Point / Bubble)
# 目的: 同じ分布データを散布図・バブルで比較表示。横に並べて固定サイズで図示

# 事前準備 ================================================

# 必要なアセンブリの読み込み
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Windows.Forms.DataVisualization

# データ準備 ================================================

# グラフにプロットするデータ(X, Y, バブルサイズ)を生成
$rand = [System.Random]::new()
$points = 1..40 | ForEach-Object {
    [PSCustomObject]@{
        X    = $rand.Next(0, 100)
        Y    = $rand.Next(0, 100)
        Size = $rand.Next(5, 25)
    }
}

# フォームの作成(表示用ウィンドウ) ================================================
$form = [System.Windows.Forms.Form] @{
    Text   = "散布図系"
    Width  = 900
    Height = 400
}

# チャートの作成 ================================================

# チャート全体の作成とフォームへの配置
$chart = [System.Windows.Forms.DataVisualization.Charting.Chart] @{
    Width  = 860
    Height = 320
    Left   = 20
    Top    = 20
}
$form.Controls.Add($chart)

# チャート全体のタイトル設定
$mainTitle = [System.Windows.Forms.DataVisualization.Charting.Title] @{
    Text = "散布図系"
    Font = [System.Drawing.Font]::new("Meiryo", 18, [System.Drawing.FontStyle]::Bold)
}
$chart.Titles.Add($mainTitle)

# チャートエリアの作成 ================================================

# グラフを2つ描画するため、チャートエリアを左右に分割して作成
$areaLeftName  = "Area_Left"
$areaRightName = "Area_Right"
$areaLeft = [System.Windows.Forms.DataVisualization.Charting.ChartArea] @{
    Name     = $areaLeftName
    Position = [System.Windows.Forms.DataVisualization.Charting.ElementPosition]::new(5, 24, 42, 72)
}
$areaRight = [System.Windows.Forms.DataVisualization.Charting.ChartArea] @{
    Name     = $areaRightName
    Position = [System.Windows.Forms.DataVisualization.Charting.ElementPosition]::new(53, 24, 42, 72)

}
$chart.ChartAreas.Add($areaLeft)
$chart.ChartAreas.Add($areaRight)


# 各チャートエリアの上に、個別のタイトル(副題)を設定
$subTitleLeft = [System.Windows.Forms.DataVisualization.Charting.Title] @{
    Text                   = "散布図(Point)"
    Font                   = [System.Drawing.Font]::new("Meiryo", 12, [System.Drawing.FontStyle]::Bold)
    DockedToChartArea      = $areaLeftName
    IsDockedInsideChartArea = $false
    Docking                = [System.Windows.Forms.DataVisualization.Charting.Docking]::Top
    DockingOffset          = 2
}
$subTitleRight = [System.Windows.Forms.DataVisualization.Charting.Title] @{
    Text                   = "バブル(Bubble)"
    Font                   = [System.Drawing.Font]::new("Meiryo", 12, [System.Drawing.FontStyle]::Bold)
    DockedToChartArea      = $areaRightName
    IsDockedInsideChartArea = $false
    Docking                = [System.Windows.Forms.DataVisualization.Charting.Docking]::Top
    DockingOffset          = 2
}
$chart.Titles.Add($subTitleLeft)
$chart.Titles.Add($subTitleRight)

# 軸ラベルの設定
$areaLeft.AxisX.Title  = "X"
$areaLeft.AxisY.Title  = "Y"
$areaRight.AxisX.Title = "X"
$areaRight.AxisY.Title = "Y"

# バブルチャートのみ、軸の最小値を0、最大値を100に設定
$areaRight.AxisX.Minimum = 0
$areaRight.AxisY.Minimum = 0
$areaRight.AxisX.Maximum = 100
$areaRight.AxisY.Maximum = 100

# シリーズの作成とデータ投入 ================================================

# シリーズ1: 散布図 (Point)
$seriesPoint = [System.Windows.Forms.DataVisualization.Charting.Series] @{
    Name      = "PointSeries"
    ChartType = [System.Windows.Forms.DataVisualization.Charting.SeriesChartType]::Point
    ChartArea = $areaLeftName
}
$chart.Series.Add($seriesPoint)

# シリーズ2: バブルチャート (Bubble)
$seriesBubble = [System.Windows.Forms.DataVisualization.Charting.Series] @{
    Name      = "BubbleSeries"
    ChartType = [System.Windows.Forms.DataVisualization.Charting.SeriesChartType]::Bubble
    ChartArea = $areaRightName
}
$chart.Series.Add($seriesBubble)

# データの追加
# 散布図はX, Yの2値、バブルはX, Y, Sizeの3値が必要なため、ループで個別に投入
foreach ($p in $points) {
    # 散布図 (X, Y)
    $null = $seriesPoint.Points.AddXY($p.X, $p.Y)
    
    # バブル (X, Y, バブルサイズ)
    # バブルのY値は配列で [Y座標, サイズ] の2つを指定する
    $null = $seriesBubble.Points.AddXY($p.X, @($p.Y, $p.Size))
}

# 出力 ================================================

# フォームの表示
$null = $form.ShowDialog()

# 画像保存
# $chart.SaveImage(".\散布図系_サンプル.png", [System.Windows.Forms.DataVisualization.Charting.ChartImageFormat]::Png)
円グラフ系.ps1
# 円/ドーナツ系サンプル(Pie / Doughnut)
# 目的: 構成比の2表現を並べて比較。横に並べて固定サイズで図示

# 事前準備 ================================================

# 必要なアセンブリの読み込み
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Windows.Forms.DataVisualization

# データ準備 ================================================

# グラフにプロットするデータ(ラベルと値)を定義
$labels = @("A", "B", "C", "D")
$values = @(40, 25, 20, 15)

# フォームの作成(表示用ウィンドウ) ================================================
$form = [System.Windows.Forms.Form] @{
    Text   = "円/ドーナツ系"
    Width  = 900
    Height = 400
}

# チャートの作成 ================================================

# チャート全体の作成とフォームへの配置
$chart = [System.Windows.Forms.DataVisualization.Charting.Chart] @{
    Width  = 860
    Height = 320
    Left   = 20
    Top    = 20
}
$form.Controls.Add($chart)

# チャート全体のタイトル設定
$mainTitle = [System.Windows.Forms.DataVisualization.Charting.Title] @{
    Text = "円/ドーナツ系"
    Font = [System.Drawing.Font]::new("Meiryo", 18, [System.Drawing.FontStyle]::Bold)
}
$chart.Titles.Add($mainTitle)

# チャートエリアの作成 ================================================

# グラフを2つ描画するため、チャートエリアを左右に分割して作成
$areaLeftName  = "Area_Left"
$areaRightName = "Area_Right"
$areaLeft = [System.Windows.Forms.DataVisualization.Charting.ChartArea] @{
    Name     = $areaLeftName
    Position = [System.Windows.Forms.DataVisualization.Charting.ElementPosition]::new(5, 24, 42, 72)
}
$areaRight = [System.Windows.Forms.DataVisualization.Charting.ChartArea] @{
    Name     = $areaRightName
    Position = [System.Windows.Forms.DataVisualization.Charting.ElementPosition]::new(53, 24, 42, 72)
}
$chart.ChartAreas.Add($areaLeft)
$chart.ChartAreas.Add($areaRight)

# 各チャートエリアの上に、個別のタイトル(副題)を設定
$subTitleLeft = [System.Windows.Forms.DataVisualization.Charting.Title] @{
    Text                   = "円グラフ(Pie)"
    Font                   = [System.Drawing.Font]::new("Meiryo", 12, [System.Drawing.FontStyle]::Bold)
    DockedToChartArea      = $areaLeftName
    IsDockedInsideChartArea = $false
    Docking                = [System.Windows.Forms.DataVisualization.Charting.Docking]::Top
    DockingOffset          = 2
}
$subTitleRight = [System.Windows.Forms.DataVisualization.Charting.Title] @{
    Text                   = "ドーナツ(Doughnut)"
    Font                   = [System.Drawing.Font]::new("Meiryo", 12, [System.Drawing.FontStyle]::Bold)
    DockedToChartArea      = $areaRightName
    IsDockedInsideChartArea = $false
    Docking                = [System.Windows.Forms.DataVisualization.Charting.Docking]::Top
    DockingOffset          = 2
}
$chart.Titles.Add($subTitleLeft)
$chart.Titles.Add($subTitleRight)

# シリーズの作成とデータ投入 ================================================

# シリーズ1: 円グラフ (Pie)
$seriesPie = [System.Windows.Forms.DataVisualization.Charting.Series] @{
    Name      = "PieSeries"
    ChartType = [System.Windows.Forms.DataVisualization.Charting.SeriesChartType]::Pie
    ChartArea = $areaLeftName
}
$chart.Series.Add($seriesPie)

# シリーズ2: ドーナツグラフ (Doughnut)
$seriesDoughnut = [System.Windows.Forms.DataVisualization.Charting.Series] @{
    Name      = "DoughnutSeries"
    ChartType = [System.Windows.Forms.DataVisualization.Charting.SeriesChartType]::Doughnut
    ChartArea = $areaRightName
}
$chart.Series.Add($seriesDoughnut)

# データの追加(X値がラベル、Y値が実際の値となる)
# DataBindXYで一括投入が可能
$seriesPie.Points.DataBindXY($labels, $values)
$seriesDoughnut.Points.DataBindXY($labels, $values)

# 出力 ================================================

# フォームの表示
$null = $form.ShowDialog()

# 画像保存
# $chart.SaveImage(".\円ドーナツ系_サンプル.png", [System.Windows.Forms.DataVisualization.Charting.ChartImageFormat]::Png)
積み上げ系.ps1
# 積み上げ系サンプル(StackedColumn / StackedBar)
# 目的: 総量と内訳を縦横で比較。横に並べて固定サイズで図示

# 事前準備 ================================================

# 必要なアセンブリの読み込み
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Windows.Forms.DataVisualization

# データ準備 ================================================

# グラフにプロットするデータ(カテゴリ、系列名、値)を定義
$categories = @("A", "B", "C", "D")
$seriesNames = @("S1", "S2", "S3")
# 値は (系列 x カテゴリ) の2次元配列で定義
$values = @(
    @(10, 15, 20, 12),  # Series S1 の値 (A, B, C, D)
    @(8,  12, 18, 10),  # Series S2 の値 (A, B, C, D)
    @(5,  7,  9,  6)    # Series S3 の値 (A, B, C, D)
)

# フォームの作成(表示用ウィンドウ) ================================================
$form = [System.Windows.Forms.Form] @{
    Text   = "積み上げ系"
    Width  = 900
    Height = 400
}

# チャートの作成 ================================================

# チャート全体の作成とフォームへの配置
$chart = [System.Windows.Forms.DataVisualization.Charting.Chart] @{
    Width  = 860
    Height = 320
    Left   = 20
    Top    = 20
}
$form.Controls.Add($chart)

# チャート全体のタイトル設定
$mainTitle = [System.Windows.Forms.DataVisualization.Charting.Title] @{
    Text = "積み上げ系"
    Font = [System.Drawing.Font]::new("Meiryo", 18, [System.Drawing.FontStyle]::Bold)
}
$chart.Titles.Add($mainTitle)

# チャートエリアの作成 ================================================

# グラフを2つ描画するため、チャートエリアを左右に分割して作成
$areaLeftName  = "Area_Left"
$areaRightName = "Area_Right"
$areaLeft = [System.Windows.Forms.DataVisualization.Charting.ChartArea] @{
    Name     = $areaLeftName
    Position = [System.Windows.Forms.DataVisualization.Charting.ElementPosition]::new(5, 24, 42, 72)
}
$areaRight = [System.Windows.Forms.DataVisualization.Charting.ChartArea] @{
    Name     = $areaRightName
    Position = [System.Windows.Forms.DataVisualization.Charting.ElementPosition]::new(53, 24, 42, 72)
}
$chart.ChartAreas.Add($areaLeft)
$chart.ChartAreas.Add($areaRight)

# 各チャートエリアの上に、個別のタイトル(副題)を設定
$subTitleLeft = [System.Windows.Forms.DataVisualization.Charting.Title] @{
    Text                   = "縦積み上げ(StackedColumn)"
    Font                   = [System.Drawing.Font]::new("Meiryo", 12, [System.Drawing.FontStyle]::Bold)
    DockedToChartArea      = $areaLeftName
    IsDockedInsideChartArea = $false
    Docking                = [System.Windows.Forms.DataVisualization.Charting.Docking]::Top
    DockingOffset          = 2
}
$subTitleRight = [System.Windows.Forms.DataVisualization.Charting.Title] @{
    Text                   = "横積み上げ(StackedBar)"
    Font                   = [System.Drawing.Font]::new("Meiryo", 12, [System.Drawing.FontStyle]::Bold)
    DockedToChartArea      = $areaRightName
    IsDockedInsideChartArea = $false
    Docking                = [System.Windows.Forms.DataVisualization.Charting.Docking]::Top
    DockingOffset          = 2
}
$chart.Titles.Add($subTitleLeft)
$chart.Titles.Add($subTitleRight)

# シリーズの作成とデータ投入 ================================================

# --- 縦積み上げグラフのシリーズをすべて作成 ---
foreach ($s in 0..($seriesNames.Count - 1)) {
    $seriesColumn = [System.Windows.Forms.DataVisualization.Charting.Series] @{
        Name      = "Col_" + $seriesNames[$s]
        ChartType = [System.Windows.Forms.DataVisualization.Charting.SeriesChartType]::StackedColumn
        ChartArea = $areaLeftName
    }
    $chart.Series.Add($seriesColumn)
    # カテゴリの配列と、現在の系列に対応する値の配列をバインド
    $seriesColumn.Points.DataBindXY($categories, $values[$s])
}

# --- 横積み上げグラフのシリーズをすべて作成 ---
foreach ($s in 0..($seriesNames.Count - 1)) {
    $seriesBar = [System.Windows.Forms.DataVisualization.Charting.Series] @{
        Name      = "Bar_" + $seriesNames[$s]
        ChartType = [System.Windows.Forms.DataVisualization.Charting.SeriesChartType]::StackedBar
        ChartArea = $areaRightName
    }
    $chart.Series.Add($seriesBar)
    # カテゴリの配列と、現在の系列に対応する値の配列をバインド
    $seriesBar.Points.DataBindXY($categories, $values[$s])
}

# 出力 ================================================

# フォームの表示
$null = $form.ShowDialog()

# 画像保存
# $chart.SaveImage(".\積み上げ系_サンプル.png", [System.Windows.Forms.DataVisualization.Charting.ChartImageFormat]::Png)
レーダー/ポーラー.ps1
# レーダー/ポーラー 同一チャート サンプル
# 目的: 1つのチャート内にレーダー(左)とポーラー(右)を横並びで表示

# 事前準備 ================================================

# 必要なアセンブリの読み込み
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Windows.Forms.DataVisualization

# データ準備 ================================================

# レーダーチャート用のデータ(カテゴリと値)を定義
$radarLabels = @("Speed", "Power", "Skill", "Stamina", "Luck")
$radarValues = @(70, 85, 65, 75, 60)

# ポーラーチャート用のデータ(角度と半径)を生成
$polarData = 0..36 | ForEach-Object {
    $deg = $_ * 10
    $rad = [math]::PI * $deg / 180
    [PSCustomObject]@{
        Degree = $deg
        Radius = 10 + [math]::Sin($rad * 2) * 5
    }
}

# フォームの作成(表示用ウィンドウ) ================================================
$form = [System.Windows.Forms.Form] @{
    Text   = "レーダー/ポーラー"
    Width  = 900
    Height = 400
}

# チャートの作成 ================================================

# チャート全体の作成とフォームへの配置
$chart = [System.Windows.Forms.DataVisualization.Charting.Chart] @{
    Width  = 860
    Height = 320
    Left   = 20
    Top    = 20
}
$form.Controls.Add($chart)

# チャート全体のタイトル設定
$mainTitle = [System.Windows.Forms.DataVisualization.Charting.Title] @{
    Text = "レーダー / ポーラー"
    Font = [System.Drawing.Font]::new("Meiryo", 18, [System.Drawing.FontStyle]::Bold)
}
$chart.Titles.Add($mainTitle)

# チャートエリアの作成 ================================================

# グラフを2つ描画するため、チャートエリアを左右に分割して作成
$areaLeftName  = "Area_Left"
$areaRightName = "Area_Right"
$areaLeft = [System.Windows.Forms.DataVisualization.Charting.ChartArea] @{
    Name     = $areaLeftName
    Position = [System.Windows.Forms.DataVisualization.Charting.ElementPosition]::new(5, 24, 42, 72)
}
$areaRight = [System.Windows.Forms.DataVisualization.Charting.ChartArea] @{
    Name     = $areaRightName
    Position = [System.Windows.Forms.DataVisualization.Charting.ElementPosition]::new(53, 24, 42, 72)
}
$chart.ChartAreas.Add($areaLeft)
$chart.ChartAreas.Add($areaRight)

# 各チャートエリアの上に、個別のタイトル(副題)を設定
$subTitleLeft = [System.Windows.Forms.DataVisualization.Charting.Title] @{
    Text                   = "レーダー(面塗り)"
    Font                   = [System.Drawing.Font]::new("Meiryo", 12, [System.Drawing.FontStyle]::Bold)
    DockedToChartArea      = $areaLeftName
    IsDockedInsideChartArea = $false
    Docking                = [System.Windows.Forms.DataVisualization.Charting.Docking]::Top
    DockingOffset          = 2
}
$subTitleRight = [System.Windows.Forms.DataVisualization.Charting.Title] @{
    Text                   = "ポーラー(マーカー)"
    Font                   = [System.Drawing.Font]::new("Meiryo", 12, [System.Drawing.FontStyle]::Bold)
    DockedToChartArea      = $areaRightName
    IsDockedInsideChartArea = $false
    Docking                = [System.Windows.Forms.DataVisualization.Charting.Docking]::Top
    DockingOffset          = 2
}
$chart.Titles.Add($subTitleLeft)
$chart.Titles.Add($subTitleRight)

# シリーズの作成とデータ投入 ================================================

# シリーズ1: レーダーチャート (Radar)
$seriesRadar = [System.Windows.Forms.DataVisualization.Charting.Series] @{
    Name        = "RadarArea"
    ChartType   = [System.Windows.Forms.DataVisualization.Charting.SeriesChartType]::Radar
    ChartArea   = $areaLeftName
    BorderWidth = 2
    Color       = [System.Drawing.Color]::FromArgb(120, [System.Drawing.Color]::RoyalBlue)
}
$chart.Series.Add($seriesRadar)
$seriesRadar.Points.DataBindXY($radarLabels, $radarValues)

# シリーズ2: ポーラーチャート (Polar)
$seriesPolar = [System.Windows.Forms.DataVisualization.Charting.Series] @{
    Name        = "PolarMarker"
    ChartType   = [System.Windows.Forms.DataVisualization.Charting.SeriesChartType]::Polar
    ChartArea   = $areaRightName
    BorderWidth = 2
    MarkerStyle = [System.Windows.Forms.DataVisualization.Charting.MarkerStyle]::Circle
    MarkerSize  = 6
}
$chart.Series.Add($seriesPolar)
# ポーラーチャートはDataSourceバインドを使うとより宣言的に記述できる
$seriesPolar.Points.DataBind($polarData, "Degree", "Radius", $null)


# 出力 ================================================

# フォームの表示
$null = $form.ShowDialog()

# 画像保存
# $chart.SaveImage(".\レーダー_ポーラー_サンプル.png", [System.Windows.Forms.DataVisualization.Charting.ChartImageFormat]::Png)

つくってみよう

それでは、実際にグラフを作成していきましょう。

使用するサンプルデータは、複数のゲームタイトルのスコアや売上をまとめたデータです。
(AIに適当に集めてもらったデータを加工。正確性は未検証)

    :

このデータからメタスコアとユーザースコアを取り出し、散布図にプロットしていきます。

最終的に完成したスクリプトはこちら。

完成スクリプト
# 事前準備 ================================================

# 必要なアセンブリの読み込み
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Windows.Forms.DataVisualization

# CSVファイルのパス設定
$csvPath = ".\game_sales_dataset.csv"
# CSVファイルからデータを読み込む(UTF-8想定)
$gameData = Import-Csv -Path $csvPath -Encoding UTF8

# フォームの作成(表示用ウィンドウ)
$form = [System.Windows.Forms.Form] @{
    Text = "メタスコアとユーザースコアの散布図"
    Width = 700
    Height = 600
}


# チャートの作成 ================================================

# チャートの作成(固定配置)
$chart = [System.Windows.Forms.DataVisualization.Charting.Chart] @{
    Width = $form.ClientSize.Width - 100
    Height = $form.ClientSize.Height - 100
    Left = 50
    Top = 50
}

# タイトルの設定
$title = [System.Windows.Forms.DataVisualization.Charting.Title] @{
    Text = "メタスコア vs ユーザースコア"
    Font = [System.Drawing.Font]::new("Meiryo UI", 14)
}
# チャートにタイトルを追加
$chart.Titles.Add($title)

# フォームにチャートを追加
$form.Controls.Add($chart)

# チャートエリアの作成 ================================================

# チャートエリアの作成
$chartArea = [System.Windows.Forms.DataVisualization.Charting.ChartArea]::new()

# チャートにチャートエリアを追加
$chart.ChartAreas.Add($chartArea)

# 軸ラベル
$chartArea.AxisX.Title = "メタスコア"
$chartArea.AxisY.Title = "ユーザースコア"


# # シリーズの作成 ================================================

# シリーズの作成
$series = $chart.Series.Add("ゲームデータ")

# シリーズの設定
$series.ChartType = [System.Windows.Forms.DataVisualization.Charting.SeriesChartType]::Point
$series.MarkerStyle = [System.Windows.Forms.DataVisualization.Charting.MarkerStyle]::Circle
$series.MarkerSize = 8
$series.Color = [System.Drawing.Color]::Blue


# データバインドするためのXY配列を生成
# !読み込んだimport-csvで読み込んだデータは文字列であるため、数値に変換する必要がある
$xValues = $gameData.メタスコア | ForEach-Object { [double]$_ }
$yValues = $gameData.ユーザースコア | ForEach-Object { [double]$_ }

# ★データ追加(データバインド)
$series.Points.DataBindXY($xValues, $yValues)

# 出力 ================================================

# フォームの表示(出力抑制)
$null = $form.ShowDialog()

# 画像保存(必要に応じてコメントを外して使用)
# $chart.SaveImage(".\scatter.png", [System.Windows.Forms.DataVisualization.Charting.ChartImageFormat]::Png)

では、このスクリプトを実際の作成の流れに沿って解説していきます。

1.グラフ作成前の事前準備

アセンブリ読み込み

はじめに、グラフ描画に必要となる.NETのライブラリ(アセンブリ)を2つ読み込みます。
今回はフォーム表示に必要なSystem.Windows.Formsと、グラフ作成に必要なSystem.Windows.Forms.DataVisualizationを読み込みます。

# 必要なアセンブリの読み込み
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Windows.Forms.DataVisualization

CSVデータの読み込み

次に、グラフ化したいCSVファイルをImport-Csvコマンドレットで読み込みます。
今回はあらかじめ指定したパスのCSVファイルを読み込んでいきます。

# CSVファイルからデータを読み込む
$csvPath = ".\game_sales_dataset.csv"
$gameData = Import-Csv -Path $csvPath -Encoding UTF8
エンコードの指定

CSVファイルに日本語が含まれる場合は、-Encodingエンコードを指定する必要があります。
UTF-8なら-Encoding UTF8、Shift-JIS(cp932)なら、-Encoding Default (PowerShell5.1) のように指定します。

データ整形について

可視化においてデータ整形は重要な要素ですが、本稿では割愛しています。

  • 欠損値の除去
  • 異常値の除去
  • 非数値データの数値化
  • 列名を一意にする(列名が重複しないようにする)

など、事前に修正したCSVを使用します。
(PowerShellではこの辺りの処理が複雑になる。。。)

フォームの作成

今回はフォーム上にウィンドウ(フォーム)を表示するため、まず土台となるフォームを作成します。ここではウィンドウのタイトルやサイズを指定しています。

# フォームの作成
$form = [System.Windows.Forms.Form] @{
    Text = "メタスコアとユーザースコアの散布図"
    Width = 700
    Height = 600
}

フォームについては前回の記事でも紹介しているので、参考にしてください。

2.グラフの骨格を作る(Chart/ChartArea/Series)

続いて、グラフを構成する主要な3つの要素を作成していきます。
3つの要素は以下の通りです。

  • Chart: グラフ全体(タイトルや凡例なども含んだコントロール)
  • ChartArea: 軸に囲まれた、データが実際に描画される領域
  • Series: グラフとして描画される、ひとまとまりのデータ系列(1グループの点群など)

image.png


Chartの作成・設定

タイトルなどを含むグラフ領域を作成していきます。

Chartオブジェクトの作成

System.Windows.Forms.DataVisualization.Charting.Chartクラスにleft(横方向の位置)やtop(高さ方向の位置)、width(幅)やheight(高さ)などのパラメータを渡して、インスタンスを生成します。

# チャートの作成
$chart = [System.Windows.Forms.DataVisualization.Charting.Chart] @{
    Width = $form.ClientSize.Width - 100
    Height = $form.ClientSize.Height - 100
    Left = 50
    Top = 50
}

タイトルの追加

System.Windows.Forms.DataVisualization.Charting.Titleクラスにテキストやフォントを指定して新規オブジェクトを作成し、チャートのタイトルに追加します。
Fontには[System.Drawing.Font]クラスのオブジェクトを指定します。
追加先はChart自体ではなく、Chartの子要素のTitlesになります。

# タイトルの設定
$title = [System.Windows.Forms.DataVisualization.Charting.Title] @{
    Text = "メタスコア vs ユーザースコア"
    Font = [System.Drawing.Font]::new("Meiryo UI", 14)
}
# タイトルをチャートに追加
$chart.Titles.Add($title)

親要素への紐づけ

あとは親要素となるフォームへ紐づけるだけです。
他のコントロールと同様、Formの子要素ControlsAddメソッドを使用して追加します。

# フォームにチャートを追加
$form.Controls.Add($chart)

今回はタイトルの追加を行った後に紐づけていますが、順番が前後しても問題ありません。

ここまでの結果

とりあえず、まっさらな領域とタイトルだけが追加されました。


ChartAreaの作成・設定

次に実際にデータがプロットされる領域を定義していきます。

ChartAreaオブジェクトの作成

System.Windows.Forms.DataVisualization.Charting.ChartAreaクラスの新規オブジェクトを作成するだけです。
今回のケースでは引数に何も指定する必要はありません。

# チャートエリアの定義
$chartArea = [System.Windows.Forms.DataVisualization.Charting.ChartArea]::new()

親要素への紐づけ

作成した親要素(Chart)へ紐づけます。Chartの持つChartAreasAddでオブジェクトを追加するだけです。

# チャートにチャートエリアを追加
$chart.ChartAreas.Add($chartArea)

軸タイトルの設定

今度は親要素に紐づけた後に設定をいじってみます。
ChartAreaの持つ様々な子要素を設定することで、グラフの見た目などをカスタマイズすることができます。
今回はシンプルに軸ラベルのみ指定します。

# 軸ラベル
$chartArea.AxisX.Title = "メタスコア"
$chartArea.AxisY.Title = "ユーザースコア"

ここまでの結果

…先ほどと何も変わりませんね。
理由は、ChartAreaはプロットする何かしらのデータが設定されないと描画されないためです。
なので、データプロットに関する設定を行っていきます。


Seriesの作成・設定

では、データプロットに直接関わるSeriesの作成を行っていきます。
エクセルのグラフで言うところの「系列」ですね。

Seriesの作成

SeriesはこれまでのChartChartAreaと作成の仕方が異なります。

# シリーズの作成
$series = $chart.Series.Add("ゲームデータ")

ChartChartArea[クラス名]::new()で新規オブジェクトを作成していたのに対し、SeriesChartのもつ子要素SeriesにいきなりAddメソッドで追加しています。
これはSeriesがインスタンス生成→設定に加え、→データ追加という流れを持つためです。
(あくまでも一般的な書き方なだけであって、先にインスタンスを生成して後からAddすることも可能です)

また、SeriesChartAreaではなくChartの子要素になる点も注意が必要です。

Seriesの設定

先ほどAddメソッドを呼び出した際に戻り値を変数に格納しておき、その変数の子要素をいじることでグラフの設定を変えられます。

# シリーズの設定
$series.ChartType = [System.Windows.Forms.DataVisualization.Charting.SeriesChartType]::Point # グラフの種類を散布図に
$series.MarkerStyle = [System.Windows.Forms.DataVisualization.Charting.MarkerStyle]::Circle # マーカーの形を円に
$series.MarkerSize = 8 # マーカーのサイズ
$series.Color = [System.Drawing.Color]::Blue # マーカーの色

ChartType
特に重要な設定が ChartType です。何を指定したかでグラフの種類が変わってきます。System.Windows.Forms.DataVisualization.Charting.SeriesChartTypeクラスの列挙子(::の後ろに指定する文字)で指定します。
主なグラフの種類と列挙子の対応は以下の通り。
その他のグラフは公式のSeriesChartTypeのページを参照してください。

グラフの種類 列挙子
散布図 Point
縦棒グラフ Column
横棒グラフ Bar
折れ線グラフ Line
バブルチャート Bubble
円グラフ Pie

マーカースタイルなど
MarkerStyleでマーカーの形、MarkerSizeでマーカーのサイズ、Colorでマーカーの色を指定しています。
後日グラフをカスタマイズするための記事を作成予定なので、本稿では解説を割愛します。

クラス名が長すぎる問題

コード中の[System.Windows.Forms.DataVisualization.Charting.SeriesChartType]等を見て、「うわ…長っ...」と思った方もいるかも知れません。実はこの手のパラメータは以下のように直接文字列で指定することも可能です。

# シリーズの設定
$series.ChartType = "Point"     # グラフの種類を散布図に
$series.MarkerStyle = "Circle"  # マーカーの形を円に
$series.Color = "Blue"          # マーカーの色

直接文字列を指定すると記述が簡潔になる一方、タイプミスに気付きにくい入力補完が使えないコードの意図が読めない場合がある、などのデメリットも存在します。


using namespace も使えるよ

上記のデメリットを解消しつつ簡潔に記述する方法として、using namespace があります(PowerShell 5.0以降)

# 必要なアセンブリの読み込み
:

# よく使う名前空間の短縮を宣言
using namespace System.Windows.Forms.DataVisualization.Charting
:
# シリーズの設定
$series.ChartType = [SeriesChartType]::Point  # グラフの種類を散布図に
$series.MarkerStyle = [MarkerStyle]::Circle   # マーカーの形を円に

using namespaceの後ろにクラスの名前空間(.で記される住所みたいなもの)を指定することで、指定した名前空間を省いて記述可能になります。
入力補完なども使用でき、安全性と簡潔さを両立させることができます。


Seriesへのデータ投入

ようやくデータをSeriesに投入していきます。
Seriesへのデータ投入の仕方はいくつか選択肢があります。

AddXYで1点ずつ追加

  • 1行ずつデータを取り出し、AddXY1点ずつ追加していく方法
    • シンプルだが、Forなどの制御構文が必須
    • 1点ずつ追加するため、データ数が重いと処理が重くなる

DataBindXYで列ごと追加

  • X/Y要素となる列を配列として取り出し、DataBindXY列ごと追加する
    • シンプルで直感的、処理も軽い
    • 列を取り出す処理がワンクッションはいる

DataBindにデータ全体を指定

  • DataBindの際、第一引数でデータ全体を指定し、第二・第三引数でどの列をX/Yにするか設定する
    • 引数の順番を覚える必要がある
    • 第四引数でラベルなどに指定する列を規定することができる
      (ツールチップは何故かうまく動作しない。。)

今回はシンプルかつ処理も軽い「DataBindXYで列ごと指定」を採用します。

投入前処理(列の取り出しと変換)

まず最初にデータ全体からXとYに指定する列を取り出します。

数値型への型変換が必須

Import-CSVで読み込まれたデータはすべて文字列型になります。
そのままではグラフ化出来ないので、数値型への型変換が必須となります。
この変換処理は「1点ずつ追加」「データ全体を指定」でも同様に必須です。

# データバインドするためのXY配列を生成
# !読み込んだimport-csvで読み込んだデータは文字列であるため、数値に変換する必要がある
$xValues = $gameData.メタスコア | ForEach-Object { [double]$_ }
$yValues = $gameData.ユーザースコア | ForEach-Object { [double]$_ }

いかにもPowerShellな書き方なので、細かめに解説しておきます。

  • $gameData.メタスコア
    $gameDataに格納されているのはPSCustomObjectです。PSCustomObjectは.<列名>列データを配列として取り出すことができます。
  • |(パイプライン):
    パイプライン前のコマンドで取り出したデータ列を分解し、中の要素をバケツリレーのように一つずつ後ろのコマンドに渡しています。
  • ForEach-Object { }
    PowerShellでおなじみ「〇〇-Object」シリーズの一つ。渡された要素すべてに{}内で指定した処理を実行します。
  • [double]$_
    $_はパイプラインで渡された一つひとつの要素を指します。変数の前に[double]を置くことで double型にキャスト(型変換) しています。

以上の処理でDouble型にキャストされた配列が戻り値として取得できるので、それを変数に格納しています。

Seriesへのデータ投入

あとは変換した列データをSeriesに投入するだけです。

# データ追加(データバインド)
$series.Points.DataBindXY($xValues, $yValues)

Seriesの子要素であるPointsのメソッドでデータを投入します。
X列とY列は個別に投入も可能ですが、今回はDataBindXYでまとめて投入しています。

今回サンプルとして散布図(Point)を使用していますが、Seriesのデータ群を示すPointsコレクションとは無関係です。グラフの種類を変えてもPointsコレクションにデータを投入するのは共通です。

これでグラフを表示する準備が整いました。


グラフ表示

Chartが格納されたFormをShowDialog()メソッドで画面に表示します。

# フォームの表示
$form.ShowDialog()


出来ました!

画像としても出力可能

今回はフォームに出力するサンプルを作成してきましたが、SaveImageメソッドでChartオブジェクトを画像として出力することも可能です。

# 画像ファイルに出力
$chart.SaveImage(".\scatter.png", [System.Windows.Forms.DataVisualization.Charting.ChartImageFormat]::Png)

以上が基本的なグラフの作り方となります。お疲れ様でした。

3. まとめ

今回は基本的なグラフの書き方を紹介しました。ポイントは

  • Chart(グラフ全体)
  • ChartArea(データが実際に描画される領域)
  • Series(グラフとして描画されるデータ系列)

それぞれの役割と使い方を理解することだと思います。


次回あたりの記事では、見た目の整え方等を紹介したいと考えています。
(多分時間かかるので気長にお待ち下さい。。)

9
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?