0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Excel不要】Windows標準機能だけでCSV/TSVをGUI選択・集計してグラフ描画するPowerShellスクリプト

0
Posted at

はじめに

「手元にあるCSVやTSVの中身をちょっとグラフで可視化したい」という場面はよくあります。
しかし、以下のような理由で少し手間に感じたことはないでしょうか?

  • ExcelがPCに入っていない。
  • 業務端末でPython(matplotlib/pandas)などの外部ライブラリをインストールできない

そこで今回は、Windows標準のPowerShellのみを使い、GUIで直感的にCSV/TSVファイルを選んで集計・グラフ描画まで行えるスクリプトを作成しました。追加ソフトのインストールは一切不要です。

主な機能と特徴

  1. 完全GUI操作
    • ファイル選択(CSV / TSV 自動判別)
    • グラフ種別選択(縦棒・折れ線・散布図・円グラフ)
    • X軸・Y軸の列選択
  2. 自動集計機能(散布図以外)
    • 同じX軸データが複数ある場合、「合計・平均・最大値・最小値・件数」から集計方法を選択可能
  3. 安全なデータ処理
    • [double]::TryParse を用いた堅牢な数値判定(指数表記や空白セルにも対応)
    • X軸が数値の場合は数値順、文字列の場合は文字列順で正しく自動ソート

実行イメージ(処理の流れ)

スクリプトを実行すると、対話形式でダイアログが表示されます。

  1. ファイル選択ダイアログ: 可視化したいCSVまたはTSVファイルを選択
  2. グラフ種類の選択 (Out-GridView): 縦棒 / 折れ線 / 散布図 / 円グラフ から選択(適切なデータ型のヒント付き)
  3. X軸・Y軸の選択 (Out-GridView): CSVヘッダーからプロットしたい列を選択
  4. 集計方法の選択 (Out-GridView): 合計や平均などを選択(※散布図の場合はスキップ)
  5. グラフ描画ウィンドウの表示: System.Windows.Forms.DataVisualization によるグラフが表示される

スクリプト全文

以下のコードを Show-CsvChart.ps1 などの名前で保存してください。


#  ウィンドウやダイアログなどのGUI機能を使うための System.Windows.Forms アセンブリを読み込みます。
Add-Type -AssemblyName System.Windows.Forms
#  グラフを描画するための DataVisualization(データ視覚化)アセンブリを読み込みます。
Add-Type -AssemblyName System.Windows.Forms.DataVisualization

#  ファイルを開くためのダイアログオブジェクトを作成します。
$dialog = New-Object System.Windows.Forms.OpenFileDialog
#  ダイアログのタイトルを設定します。
$dialog.Title = "グラフ化するデータ(CSV/TSV)を選択してください"
#  選択可能なファイルの種類を制限するフィルターを設定します。
$dialog.Filter = "データファイル (*.csv;*.tsv)|*.csv;*.tsv|すべてのファイル (*.*)|*.*"

#  ダイアログを表示し、ユーザーが「OK」ボタンを押したかどうかを判定します。
if ($dialog.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) {
    #  ユーザーが選択したファイルのパスを変数 $path に格納します。
    $path = $dialog.FileName
} else {
    #  「OK」以外(キャンセルなど)が押された場合は警告を表示します。
    Write-Warning "キャンセルされました。"
    #  処理を安全に終了します。
    return
}

#  選択されたファイルの拡張子を取得し、小文字に変換します。
$ext = [System.IO.Path]::GetExtension($path).ToLower()
#  拡張子が ".csv" ならカンマ、それ以外ならタブを区切り文字に設定します。
$delimiter = if ($ext -eq ".csv") { "," } else { "`t" }

#  ファイルの先頭1行を読み込み、各列の名前の配列を取得します。
$headers = (Import-Csv -Path $path -Delimiter $delimiter -Encoding UTF8 | Select-Object -First 1).psobject.Properties.Name

#  列名が取得できなかった場合の処理です。
if (-not $headers) {
    Write-Error "データの列名が取得できませんでした。"
    return
}

#  グラフの種類と推奨データ型のヒントを定義します。
$graphTypes = @(
    [PSCustomObject]@{ "グラフの種類" = "縦棒グラフ";   "X軸の推奨" = "質的変数 (カテゴリ・種類など)";               "Y軸の推奨" = "量的変数";      ChartType = "Column" }
    [PSCustomObject]@{ "グラフの種類" = "折れ線グラフ"; "X軸の推奨" = "質的変数 または 量的変数 (時間・連続データなど)"; "Y軸の推奨" = "量的変数";      ChartType = "Line" }
    [PSCustomObject]@{ "グラフの種類" = "散布図";       "X軸の推奨" = "量的変数";                       "Y軸の推奨" = "量的変数";      ChartType = "Point" }
    [PSCustomObject]@{ "グラフの種類" = "円グラフ";     "X軸の推奨" = "質的変数 (カテゴリ・種類など)";               "Y軸の推奨" = "量的変数 (数値・割合データ)"; ChartType = "Pie" }
)

#  グラフの種類一覧を表示し、ユーザーに一つ選ばせます。
$selectedGraph = $graphTypes | Out-GridView -Title "【手順1】作成するグラフの種類を選択してください" -OutputMode Single
if (-not $selectedGraph) { return }

#  取得した列名一覧を表示し、X軸に使用する列をユーザーに選ばせます。
$xCol = $headers | Out-GridView -Title "【手順2: X軸】列を選択してください (推奨: $($selectedGraph.'X軸の推奨'))" -OutputMode Single
if (-not $xCol) { return }

#  取得した列名一覧を表示し、Y軸に使用する列をユーザーに選ばせます。
$yCol = $headers | Out-GridView -Title "【手順3: Y軸】列を選択してください (推奨: $($selectedGraph.'Y軸の推奨'))" -OutputMode Single
if (-not $yCol) { return }

# =========================================================================
# 同じX軸のデータがあった場合の「集計方法」を選択させる(散布図以外)
# =========================================================================
$selectedAgg = $null
if ($selectedGraph.ChartType -ne "Point") {
    
    #  用意する集計方法のリスト(合計、平均、最大値、最小値、データ数)を定義します。
    $aggregationTypes = @(
        [PSCustomObject]@{ "集計方法" = "合計"; "説明" = "同じ項目の数値を足し合わせます"; Method = "Sum" }
        [PSCustomObject]@{ "集計方法" = "平均"; "説明" = "同じ項目の数値の平均を出します"; Method = "Average" }
        [PSCustomObject]@{ "集計方法" = "最大値"; "説明" = "同じ項目の中で最大の数値を使います"; Method = "Maximum" }
        [PSCustomObject]@{ "集計方法" = "最小値"; "説明" = "同じ項目の中で最小の数値を使います"; Method = "Minimum" }
        [PSCustomObject]@{ "集計方法" = "データ数"; "説明" = "同じ項目の出現回数(有効データ数)を数えます"; Method = "Count" }
    )
    
    #  集計方法の一覧を表示し、ユーザーに選ばせます。
    $selectedAgg = $aggregationTypes | Out-GridView -Title "【手順4: 集計方法】同じX軸($xCol)のデータが複数ある場合の処理を選んでください" -OutputMode Single
    if (-not $selectedAgg) { return }
}
# =========================================================================

#  対象ファイルのデータ全体を読み込みます。
$data = Import-Csv -Path $path -Delimiter $delimiter -Encoding UTF8

#  グラフを表示するためのウィンドウを作成し、幅と高さを設定します。
$form = New-Object System.Windows.Forms.Form
$form.Width = 800; $form.Height = 600

#  ウィンドウのタイトルバーに、選択した内容を分かりやすく表示します。
if ($selectedGraph.ChartType -eq "Point") {
    #  散布図の場合は集計がないため、そのまま X vs Y と表示します。
    $form.Text = "$($selectedGraph.'グラフの種類') ($xCol vs $yCol)"
} else {
    #  それ以外の場合は、選んだ集計方法もタイトルに含めます。
    $form.Text = "$($selectedGraph.'グラフの種類') ($xCol vs $yCol$($selectedAgg.'集計方法'))"
}

#  Chart オブジェクトを作成します。
$chart = New-Object System.Windows.Forms.DataVisualization.Charting.Chart
$chart.Dock = [System.Windows.Forms.DockStyle]::Fill

#  プロットエリアを作成し、軸の自動調整などを行います。
$chartArea = New-Object System.Windows.Forms.DataVisualization.Charting.ChartArea
$chartArea.AxisX.LabelStyle.IsStaggered = $false
if ($selectedGraph.ChartType -ne "Point") {
    $chartArea.AxisX.Interval = 1
}
$chart.ChartAreas.Add($chartArea)

#  データ系列(Series)を作成し、グラフの種類を適用します。
$series = New-Object System.Windows.Forms.DataVisualization.Charting.Series
$series.Name = "Series1"
$series.ChartType = [System.Windows.Forms.DataVisualization.Charting.SeriesChartType]::$($selectedGraph.ChartType)

#  円グラフの場合、値をラベルとして表示します。
if ($selectedGraph.ChartType -eq "Pie") {
    $series.IsValueShownAsLabel = $true
}
$chart.Series.Add($series)
# =========================================================================
# データをグラフに追加する処理
# =========================================================================
if ($selectedGraph.ChartType -eq "Point") {
    # 【散布図の場合】
    foreach ($row in $data) {
        $xVal = 0.0
        $yVal = 0.0
        # 正規表現ではなく TryParse で厳密に数値判定(指数表記等にも対応)
        if ([double]::TryParse($row.$xCol, [ref]$xVal) -and [double]::TryParse($row.$yCol, [ref]$yVal)) {
            [void]$chart.Series["Series1"].Points.AddXY($xVal, $yVal)
        }
    }
} else {
    # 【縦棒・折れ線・円グラフの場合】
    
    # Y軸が数値であり、X軸が空欄でない有効なデータだけを抽出
    $validData = $data | Where-Object { 
        $dummy = 0.0
        [double]::TryParse($_.$yCol, [ref]$dummy) -and (-not [string]::IsNullOrWhiteSpace($_.$xCol))
    }
    
    # X軸の名前ごとにグループ化し、さらにX軸の値でソートする
    $groupedData = $validData | Group-Object -Property $xCol | Sort-Object {
        $parsedX = 0.0
        # 数値に変換できるものは数値としてソート、できないものは文字列としてソート
        if ([double]::TryParse($_.Name, [ref]$parsedX)) { $parsedX } else { $_.Name }
    }
    
    foreach ($group in $groupedData) {
        $aggValue = 0.0
        
        # 集計処理
        switch ($selectedAgg.Method) {
            "Sum"     { $aggValue = ($group.Group | Measure-Object -Property $yCol -Sum).Sum }
            "Average" { $aggValue = ($group.Group | Measure-Object -Property $yCol -Average).Average }
            "Maximum" { $aggValue = ($group.Group | Measure-Object -Property $yCol -Maximum).Maximum }
            "Minimum" { $aggValue = ($group.Group | Measure-Object -Property $yCol -Minimum).Minimum }
            "Count"   { $aggValue = $group.Group.Count } 
        }
        
        $parsedX = 0.0
        # X軸が数値の場合は[double]で渡し、グラフ上の間隔(スケール)を正確に保つ
        if ([double]::TryParse($group.Name, [ref]$parsedX)) {
            [void]$chart.Series["Series1"].Points.AddXY($parsedX, [double]$aggValue)
        } else {
            [void]$chart.Series["Series1"].Points.AddXY([string]$group.Name, [double]$aggValue)
        }
    }
}
# =========================================================================

#  データがセットされた Chart オブジェクトをフォームに追加します。
$form.Controls.Add($chart)

#  ウィンドウを画面に表示し、閉じるまで待機します。
[void]$form.ShowDialog()

#  ウィンドウが閉じられた後、メモリを解放します。
$form.Dispose()

コードの注目ポイント

1. Out-GridView -OutputMode Single による簡易UI

PowerShell標準コマンドレットの Out-GridView-OutputMode Single を指定することで、ユーザーが一覧から1行選択して「OK」を押すまで待機するGUIセレクタとして利用できます。わざわざ複雑なWindows Formsのリストボックスを組む必要がありません。

2. [double]::TryParse を使った堅牢な型判定

CSVデータはすべて文字列として読み込まれます。
正規表現や単純な型キャスト([double]$val)で判定すると、空文字や指数表記(1.2e+3 など)でエラーになるケースがあります。.NET[double]::TryParse(string, [ref]double) を使うことで、例外を発生させずに安全に数値判定を行っています。

3. グループ化と型を意識したソート

棒グラフや折れ線グラフでは、X軸が日付やID(数値)の場合と、カテゴリ名(文字列)の場合があります。
ソート時に数値として解釈できる場合は数値順、そうでない場合は五十音/アルファベット順に切り替えることで、意図しない並び順(例: 1, 10, 2, 3...)になるのを防いでいます。

$groupedData = $validData | Group-Object -Property $xCol | Sort-Object {
    $parsedX = 0.0
    if ([double]::TryParse($_.Name, [ref]$parsedX)) { $parsedX } else { $_.Name }
}

実行環境と注意点

  • 対応環境: Windows PowerShell 5.1(Windows 10/11 に標準搭載)
  • 文字コード: スクリプト内の Import-Csv では -Encoding UTF8 を指定しています。Shift-JIS (CP932) のCSVを読み込む場合は、-Encoding Default に変更してください。

おわりに

PowerShellは単なるタスク自動化シェルにとどまらず、.NETのGUIや描画コンポーネントを即座に呼び出せる点が大きな強みです。
CSVをサクッと確認したいときの社内ツールや簡易分析スクリプトとして、ぜひ活用してみてください。

0
1
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
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?