1
2

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】Power Queryだけで「縦に伸びない」ガントチャートを作る(日/週/月表示・マクロなし)

Last updated at Posted at 2025-12-08

はじめに

Excel関数とピボットを駆使して、ExcelでWBS(タスク一覧)からガントチャートを作ってみたものの、通常は「1タスク=1行」になるため、タスクが増えると縦に長い表になってしまうなぁと。

同一行に圧縮したいが、単純にピボットすると縦に長くなる:
image.png

「チームごと」や「担当者ごと」にまとめて、スケジュールが空いている期間は同じ行に詰め込みたい(行を圧縮したい) けれど、VBA(マクロ)を使いたくない。。。

ということで、Power Queryだけで「行を圧縮するガントチャート」を作る汎用的なM言語コードを作成しました。

実現できること

  • 行の圧縮: チームや担当者単位などを行見出しにして、集計セルに開発フェーズなどの文言を集約するには、/区切りで1セルに詰め込みます(★ここが今回のポイント)
  • 期間の可変: 列見出しは「日次」「週次(月曜始まり)」「月次」を引数で切り替えられます。
  • マクロ不要: .xlsx のまま保存でき、更新ボタン一つで反映されます。
  • 関数化: 汎用的な関数として定義して、ピボットの行見出しを引数で与えて、集計するコードをいじらなくてもすむようにしています。

完成イメージ

これは条件付き書式でフェーズの変化を示しています。

image.png

素のPowerQueryだとこんな感じ。

image.png


作り方

今回は処理を2つの関数(クエリ)に分けました。

  1. fxPrepWBS: 日付を展開し、カレンダー情報(日・週・月)を付与する(ETL層)
  2. fxPivotGantt: 指定した粒度で行列を入れ替えて表示する(Presentation層)

手順1. WBSテーブルの準備

ExcelシートにWBSを用意し、テーブル化(Ctrl+T)しておきます。
列名は以下を想定していますが、違っていても関数の引数で吸収可能です。

  • チーム
  • 開発フェーズ
  • 開始日付
  • 終了日付
チーム 開発フェーズ 担当者 タスク 開始日付 終了日付
3.フロント 基本設計 鈴木 画面A 2025/11/7 2025/12/1
3.フロント 詳細設計 鈴木 画面A 2025/12/2 2026/1/15
3.フロント 実装 鈴木 画面A 2026/1/20 2026/5/1
3.フロント テスト 鈴木 画面A 2026/5/2 2026/6/30
3.フロント 基本設計 佐藤 画面B 2025/11/13 2025/12/12
3.フロント 詳細設計 佐藤 画面B 2025/12/13 2026/1/20
3.フロント 実装 佐藤 画面B 2026/1/21 2026/6/30
4.バックエンド 基本設計 田中 バックエンドA/B 2025/11/1 2026/1/31
4.バックエンド 詳細設計 田中 バックエンドA/B 2026/1/22 2026/3/15
4.バックエンド 実装 田中 バックエンドA 2026/3/7 2026/6/30
4.バックエンド 実装 伊藤 バックエンドB 2026/3/1 2026/5/15
4.バックエンド テスト 伊藤 バックエンドB 2026/5/16 2026/6/30
5.インフラチーム 基本設計 渡辺 DB/インフラ 2025/11/1 2025/11/30
5.インフラチーム 詳細設計 渡辺 DB/インフラ 2025/12/1 2025/12/31
5.インフラチーム 実装 渡辺 DB/インフラ 2026/1/1 2026/1/31
5.インフラチーム テスト 渡辺 DB/インフラ 2026/2/1 2026/3/1
1.PM マネジメント 山田 マネジメント 2025/11/1 2026/6/30
1.PM マネジメント 加藤 マネジメント 2025/11/1 2025/12/25
2.アーキテクト 技術支援 佐々木 技術支援 2026/1/1 2026/2/28

手順2. 関数 fxPrepWBS の作成

「データの取得」>「その他のデータソースから」>「空のクエリ」を作成し、詳細エディタに以下を貼り付けます。クエリ名は fxPrepWBS としてください。

  • 週次ロジック: 202511-1 のようなラベルを作成する際、月曜日に週が始まる前提で計算しています。そのため、「その週の月曜日が何月か」を基準に計算して、月をまたぐ週を分けています。
  • 2025/10/27(月)202510-42025/11/01(土)202511-12025/12/01(月)202512-1

‐ WBSの列名で「チーム」「開発フェーズ」「開始日付」「終了日付」を想定してハードコーディングしているので、適宜修正してください。

‐ 「開始日付」「終了日付」は日付型を想定していますが、日時型の場合はtype dateのところをtype datetimeに適宜修正してください。

(SourceTable as table) as table =>
let
    // 1. 型変換(★修正箇所)
    Types = Table.TransformColumnTypes(SourceTable,{
        {"チーム", type text}, {"開発フェーズ", type text}, 
        {"開始日付", type date}, {"終了日付", type date}
    }),

    // 2. 日付展開(1行1日)(★修正箇所)
    AddedDates = Table.AddColumn(Types, "DateKey", each 
        List.Dates([開始日付], Duration.Days([終了日付]-[開始日付])+1, #duration(1,0,0,0))
    ),
    Expanded = Table.ExpandListColumn(AddedDates, "DateKey"),
    TypeDate = Table.TransformColumnTypes(Expanded,{{"DateKey", type date}}),

    // --- 各種期間情報の付与 ---

    // 3. 日次情報
    AddDayLabel = Table.AddColumn(TypeDate, "DayLabel", each Date.ToText([DateKey], "yy/MM/dd"), type text),

    // 4. 月次情報
    AddMonthKey = Table.AddColumn(AddDayLabel, "MonthKey", each Date.StartOfMonth([DateKey]), type date),
    AddMonthLabel = Table.AddColumn(AddMonthKey, "MonthLabel", each Date.ToText([DateKey], "yyyy/MM"), type text),

    // 5. 週次情報(月曜始まり・月曜基準の月度)
    // 例: 11/1(土)が含まれる週の月曜が10月なら、それは10月度第N週とする
    AddWeekKey = Table.AddColumn(AddMonthLabel, "WeekKey", each Date.StartOfWeek([DateKey], Day.Monday), type date),
    AddWeekLabel = Table.AddColumn(AddWeekKey, "WeekLabel", each 
        let
            Monday = Date.StartOfWeek([DateKey], Day.Monday),
            YYYYMM = Date.ToText(Monday, "yyyyMM"),
            WeekNum = Number.IntegerDivide(Date.Day(Monday) - 1, 7) + 1
        in
            YYYYMM & "-" & Text.From(WeekNum),
        type text
    )
in
    AddWeekLabel

手順3. 関数 fxPivotGantt の作成

同様に「空のクエリ」を作成し、詳細エディタに以下を貼り付けます。クエリ名は fxPivotGantt としてください。

(PreparedTable as table, RowHeaders as list, TimeScale as text, ValueColumn as text) as table =>
let
    // 1. モードに応じて使用する列名(キーとラベル)を決定
    TargetColumns = 
        if TimeScale = "Day" then [Key="DateKey", Label="DayLabel"]
        else if TimeScale = "Week" then [Key="WeekKey", Label="WeekLabel"]
        else if TimeScale = "Month" then [Key="MonthKey", Label="MonthLabel"]
        else error "TimeScaleは 'Day', 'Week', 'Month' のいずれかを指定してください",
    
    KeyCol = TargetColumns[Key],
    LabelCol = TargetColumns[Label],

    // 2. 必要な列だけを選択(行見出し + 時間キー + 時間ラベル + 集計対象列)
    SelectCols = Table.SelectColumns(PreparedTable, RowHeaders & {KeyCol, LabelCol, ValueColumn}),

    // 3. 集計対象列を固定名(TempTarget)に変更(動的対応のため)
    RenamedTable = Table.RenameColumns(SelectCols, {{ValueColumn, "TempTarget"}}),

    // 4. 重複削除
    DistinctRows = Table.Distinct(RenamedTable),

    // 5. グループ化(行見出し + 時間単位 で値を結合)(★今回のポイント)
    GroupKeys = RowHeaders & {KeyCol, LabelCol},
    Grouped = Table.Group(DistinctRows, GroupKeys, {
        {"Val", each Text.Combine(List.Distinct([TempTarget]), "/"), type text}
    }),

    // 6. ソート(時系列順を保証)
    Sorted = Table.Sort(Grouped, {{KeyCol, Order.Ascending}}),

    // 7. ピボット用のカラムリスト作成
    PivotHeaders = List.Distinct(Table.Column(Sorted, LabelCol)),

    // 8. ピボット実行前の列整理(キー列を削除して行圧縮させる)
    PrePivotTable = Table.SelectColumns(Sorted, RowHeaders & {LabelCol, "Val"}),

    // 9. ピボット実行
    Pivoted = Table.Pivot(
        PrePivotTable,
        PivotHeaders,
        LabelCol,
        "Val"
    )
in
    Pivoted

使い方

準備ができたら、新しいクエリを作成してこんな感じで関数を呼び出すだけです。

let
    PreparedWBS = fxPrepWBS(Excel.CurrentWorkbook(){[Name="T_WBS"]}[Content]),
    Result = fxPivotGantt(PreparedWBS, {"チーム", "担当者"}, "Month", "開発フェーズ")
in
    Result

1. 準備(データ加工)

下表をT_WBSテーブルとします。
image.png

fxPrepWBS に通します。

= fxPrepWBS(Excel.CurrentWorkbook(){[Name="T_WBS"]}[Content])

結果:
image.png

2. ガントチャート生成

fxPivotGantt を使い、好きな切り口で出力します。

例A:チーム単位 × 月次(セルには「開発フェーズ」を表示)

= fxPivotGantt(PreparedWBS, {"チーム"}, "Month", "開発フェーズ")

結果:
image.png

例B:担当者単位 × 日次(セルには「タスク」を表示)

= fxPivotGantt(PreparedWBS, {"担当者"}, "Day", "タスク")

結果:
image.png

例C:チーム・担当者 × 週次

= fxPivotGantt(PreparedWBS, {"チーム", "担当者"}, "Week", "開発フェーズ")

結果:
image.png

例D:チーム・担当者 × 月次を条件付き書式で表現
何も書式を指定しないとPower Queryのデフォルトの書式になりますが、条件付き書式を使うとこんな感じにできます。
image.png

条件付き書式:
image.png

技術的な特徴・工夫した点

今回のPower Query(M言語)の実装において、技術的に重要ないくつかのポイントを説明します。

1. ETL層とPresentation層の分離

処理を2つの関数に分割したアーキテクチャを採用しています。

  • fxPrepWBS (ETL層):
    • List.Dates を用いて、期間(開始~終了)を1日1行のレコードに増幅する処理を行います。
    • 計算コストが高い処理ですが、最初に1回だけ実行して中間テーブルとして保持します。
  • fxPivotGantt (Presentation層):
    • 展開済みのテーブルに対して、フィルタリングとピボットのみを行います。
    • これにより、同じデータソースから「日次」「週次」「月次」など複数のビューを作成する際、重い展開処理を再計算せずに済み、パフォーマンスを確保しています。

2. 動的なグルーピング(リストによる列指定)

M言語の Table.Group は通常、固定された列名を指定する必要がありますが、汎用性を高めるためにリスト操作を活用してます。

    // 5. グループ化(行見出し + 時間単位 で値を結合)(★今回のポイント)
    GroupKeys = RowHeaders & {KeyCol, LabelCol},
    Grouped = Table.Group(DistinctRows, GroupKeys, {
        {"Val", each Text.Combine(List.Distinct([TempTarget]), "/"), type text}
    }),
1
2
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
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?