---
title: DFG2_データビジュアル_ハンズオンガイド
tags: 
author: [Eiki Sui](https://docswell.com/user/marshal_dabao)
site: [Docswell](https://www.docswell.com/)
thumbnail: https://bcdn.docswell.com/page/5EGLNN81JL.jpg?width=480
description: FabCon EU 2026で発表されたDataflow Gen2を一から作るハンズオンです。
published: September 30, 26
canonical: https://docswell.com/s/marshal_dabao/ZR878E-2026-09-30-213604
---
# Page. 1

![Page Image](https://bcdn.docswell.com/page/5EGLNN81JL.jpg)

Dataflow Gen2 データビジュアル（プレビュー）ハンズオン ガイド
Dataflow Gen2 で「ダッシュボード体験」をつくる
データビジュアル（プレビュー）を、サンプルデータで試すステップバイステップ ガイド
2026 年 9 月 30 日
｜
対象：Microsoft Fabric Dataflow Gen2
｜
所要時間の目安：基本編 約 35 分＋応用編 約 35 分
本書について
• 「データビジュアル」は Dataflow Gen2 のプレビュー機能です。本書は Microsoft Learn「Create data visuals in
Dataflow Gen2 (Preview)」（2026 年 9 月 18 日更新）と Fabric September 2026 Feature Summary などの公開
情報をもとに、日本語のサンプルデータで手順を組み立て直したものです。
• 掲載したすべての M コードは、Excel の Power Query エンジンで評価し、公式ドキュメントに記載された可視化ドキュメント
のルール（ルートは 1 行、Card の子は 1 つ、KpiCard の値は text など）を満たすことを自動点検しています（16 種類の
違反パターンでチェッカー自体もテスト済み）。ただし Dataflow Gen2 上での描画（色・並び順・細かなレイアウト）は、
本書の図と異なる場合があります。
• 本書の図は Word の図形とグラフで作成しており、そのまま編集できます（グラフは右クリック →［データの編集］）。画
面のスクリーンショットは Microsoft Learn（CC BY 4.0）から引用しています。
• コードは付属の M_code フォルダーにテキスト ファイル（.pq）として収録しています。コピー＆ペーストにはファイルの利用をお
勧めします。
目次
1. データビジュアルとは ................................................................................................................................................................. 3
1.1 何ができるようになったのか ......................................................................................................................................................................... 3
1.2 仕組み：クエリが「可視化ドキュメント」を返す ...................................................................................................................................... 3
1.3 使える部品（PartType）とグラフ（ChartType） ............................................................................................................................ 4
1.4 Power BI レポートとの使い分け ................................................................................................................................................................ 5
1.5 「ダッシュボード体験」を DFG2 で実現する方法 .................................................................................................................................... 6
2. ハンズオンの準備 .................................................................................................................................................................... 6
2.1 必要なもの .................................................................................................................................................................................................... 6
2.2 本書で作るもの ............................................................................................................................................................................................ 7
2.3 サンプルデータ「Contoso Coffee」 ........................................................................................................................................................... 7
2.4 Dataflow Gen2 を作成する ..................................................................................................................................................................... 8
2.5 共通の操作 .................................................................................................................................................................................................. 9
3. 基本編：ダッシュボードを組み立てる .................................................................................................................................... 9
Step 1
サンプルデータ（SalesData）を作る
［5 分］ ...................................................................................................................... 9
Step 2
はじめてのビジュアル（Visual_Hello）
［5 分］ ............................................................................................................... 11
1


# Page. 2

![Page Image](https://bcdn.docswell.com/page/4JQYQQ9N7P.jpg)

Dataflow Gen2 データビジュアル（プレビュー）ハンズオン ガイド
Step 3
骨組みを作る：ヘッダーと KPI カード
Step 4
グラフを追加する
Step 5
明細テーブルを追加して完成
［10 分］ ................................................................................................................. 13
［10 分］ .................................................................................................................................................... 15
［5 分］ ................................................................................................................................. 17
4. 応用編：ダッシュボードらしさを高める .................................................................................................................................. 18
Step 6
パラメーターで「スライサー風」に切り替える
［10 分］ ......................................................................................................... 18
Step 7
データから自動生成する「地域別スコアカード」
［10 分］ ................................................................................................ 20
Step 8
再利用できる「データプロファイル」ダッシュボード
［10 分］ ............................................................................................... 22
Step 9
自己診断ツールで点検する
［5 分］.................................................................................................................................... 24
5. 共有と再利用....................................................................................................................................................................... 25
6. トラブルシューティング ............................................................................................................................................................ 26
7. 制限事項と設計のコツ ......................................................................................................................................................... 27
7.1 制限事項（プレビュー時点） ................................................................................................................................................................ 27
7.2 設計のコツ .................................................................................................................................................................................................. 27
7.3 自分のデータに置き換えるには ............................................................................................................................................................... 28
8. 参考リンク ............................................................................................................................................................................. 28
付録 A
可視化ドキュメントの仕様早見表 ........................................................................................................................... 29
付録 B
付属ファイル一覧 ...................................................................................................................................................... 29
付録 C
完成版コード ............................................................................................................................................................. 30
C-1
Dashboard（Step 5 完成版） ..................................................................................................................................................... 30
C-2
Dashboard_Regions（Step 7） .................................................................................................................................................. 32
C-3
fnProfileDashboard（Step 8） ................................................................................................................................................... 34
C-4
fnCheckVisualDocument（Step 9）......................................................................................................................................... 36
2


# Page. 3

![Page Image](https://bcdn.docswell.com/page/K74WNNX3E1.jpg)

Dataflow Gen2 データビジュアル（プレビュー）ハンズオン ガイド
1. データビジュアルとは
1.1 何ができるようになったのか
これまで Dataflow Gen2（以下 DFG2）のクエリ結果は、データ プレビューに「行と列の表」として表示されるだけでした。デー
タビジュアル（プレビュー）では、クエリが表の代わりに可視化ドキュメントを返すと、見出し・KPI カード・グラフ・表を組み合わ
せたダッシュボードが、同じ編集キャンバス上に描画されます。
• ダッシュボードも「ただのクエリ」：Power Query M だけで記述します。別のデザイナーやレポート ファイルは不要で、データ
フローと一緒に保存され、詳細エディターでレビューできます。
• データから動的に生成できる：KPI の値だけでなく、グラフの系列やカードの枚数までデータから計算できます（Step 7 で
実践）。
• 有効化の操作は不要：Feature Summary によると、データビジュアルは DFG2 で自動的に描画されます。
• 主な用途は 2 つ：① データの探索（NULL・分布・外れ値などのプロファイル）と、② 結果の要約（KPI とグラフで要
点を示す）。
図 1 DFG2 のキャンバスに描画されたデータビジュアルの例（公式ドキュメントのサンプル。出典：Microsoft Learn、CC BY 4.0）
1.2 仕組み：クエリが「可視化ドキュメント」を返す
ダッシュボードの正体は、次の 5 列を持つ小さくフラットな表です。1 行が 1 つのビジュアルを表し、Parent 列で親子関係（入
れ子）を作ります。表そのものに入れ子はありません。
3


# Page. 4

![Page Image](https://bcdn.docswell.com/page/LJ1Y55NZEG.jpg)

Dataflow Gen2 データビジュアル（プレビュー）ハンズオン ガイド
表 1 可視化ドキュメントの 5 列
列
型
意味
Name
nullable text
行を識別する一意な ID（例：kpi-revenue）
Parent
nullable text
親の Name。最上位（ルート）の 1 行だけ null
PartType
nullable text
ビジュアルの種類：Container／Card／Header／KpiCard／Table／Chart
Properties
nullable record
種類ごとの設定（タイトル、表示値、グラフの種類と列の割り当てなど）
Data
any
グラフや表が読むテーブル。使わない行は null
③ 可視化ドキュメント（5 列の表）
① 元データ
② 集計・整形
SalesData
Table.Group
（576 行の表）
Number.ToText
ふつうのクエリ
Power Query M
Name｜Parent｜PartType
④ DFG2 の
dashboard｜null｜Container
キャンバスに
header｜dashboard｜Header
ダッシュボード
kpi-row｜dashboard｜Container
として描画
kpi-revenue｜kpi-row｜KpiCard
…（右に Properties・Data 列）
5 列（Name・Parent・PartType・Properties・Data）の表を返すと、表の代わりにビジュアルとして描画される
図2
クエリから描画までの流れ
押さえておきたい 2 つのポイント
• 描画されるかどうかは、返した表の列名で決まります。5 列のどれかが欠けたり名前が違ったりすると、通常の表として表示
されます（5 列以外の列は無視されます）。
• 5 列がそろっていても中身が不正だとエラーになります。たとえば Properties に数値を入れると、プレビュー全体が表示され
なくなります。一方、KpiCard の Value に数値を入れた場合などは、その 1 つのビジュアルだけがエラー表示になります。
1.3 使える部品（PartType）とグラフ（ChartType）
プレビュー時点で使える PartType は 6 種類です。グラフはすべて PartType = &quot;Chart&quot; とし、種類は Properties の
ChartType で指定します。旧来の LineChart や BarChart などの PartType は認識されません。
表2
PartType
Container
Card
表示するもの
子を横（row）または縦
（column）に並べる
タイトル付きの枠で子を囲む
PartType の一覧
子
必須の入力
1 つ以上
なし
ちょうど 1 つ
Title（text）
4
任意の入力
Direction：&quot;row&quot;（既
定）／&quot;column&quot;
―


# Page. 5

![Page Image](https://bcdn.docswell.com/page/GJWG554672.jpg)

Dataflow Gen2 データビジュアル（プレビュー）ハンズオン ガイド
PartType
表示するもの
子
必須の入力
任意の入力
Header
見出しと右寄せのテキスト
なし
Header（text）
FarText（text か null）
KpiCard
1 つの指標を大きく表示
なし
Value・Label（text）
Sub（text か null）
Table
テーブルの行と列
なし
Data 列にテーブル
―
Chart
グラフ
なし
表3
ChartType・DataSeries、Data
列にテーブル
ChartTitle
ChartType の一覧
ChartType
向いている用途
ValueColumns（値の列）
Line
推移（例：月別売上）
1列
Area
推移（線の下を塗りつぶす）
1列
Bar
カテゴリ間の比較
1列
StackedBar
カテゴリ間の比較＋内訳（色分けした積み上げ）
1 列以上（ワイド形式）
Doughnut
構成比（中央が空洞）
1列
Pie
構成比
1列
グラフの Properties には DataSeries レコードを渡し、AxisColumns（軸の列）と ValueColumns（値の列）に、Data
列に渡したテーブルの列名を指定します。列名は 1 つなら text、複数ならリストで書けます（&quot;地域&quot; と {&quot;地域&quot;} は同じ意
味）。
1.4 Power BI レポートとの使い分け
表 4 DFG2 のデータビジュアルと Power BI レポートの比較
観点
主な利用者
DFG2 のデータビジュアル
Power BI レポート
データを準備する作成者（データ エンジニア、アナリス
ト）
業務ユーザー、意思決定者
目的
準備中のデータの確認、結果の要約、品質チェック
分析、共有、意思決定
作り方
M コードで可視化ドキュメントを記述
キャンバス上でドラッグ＆ドロップ
操作性
表示される場所
更新と出力
フィルター・スライサー・クロスフィルターなし（評価時点
のスナップショット）
DFG2 の編集キャンバス（共有クエリの閲覧画面を
含む）
更新の出力、データの宛先、DFG2 コネクタには含ま
れない
5
対話的な絞り込み、ドリルダウン
Power BI サービス、アプリ、Teams など
スケジュール更新、Direct Lake など


# Page. 6

![Page Image](https://bcdn.docswell.com/page/4EZLNN2R73.jpg)

Dataflow Gen2 データビジュアル（プレビュー）ハンズオン ガイド
観点
DFG2 のデータビジュアル
Power BI レポート
提供状況
プレビュー
一般提供
つまりデータビジュアルは Power BI の代わりではなく、データを準備しているその場所で、すぐに確かめ、要点を伝えるための機
能です。本書では、この機能で「ダッシュボードのような体験」をどこまで作れるかを、手を動かしながら確認します。
1.5 「ダッシュボード体験」を DFG2 で実現する方法
ダッシュボードに求められる要素と、DFG2 のデータビジュアルでの実現方法を対応させると次のとおりです。フィルターやスライサ
ーのように標準では用意されていない要素も、Power Query M とパラメーターの組み合わせで近い体験を作れます。
表5
ダッシュボードの要素と実現方法
ダッシュボードの要素
DFG2 での実現方法
本書
レイアウト（行・列・グリッド）
Container（row／column）の入れ子。タイトルは Card で付ける
Step 3〜5
見出し・対象期間の表示
Header（Header と右寄せの FarText）
Step 3
KPI（大きな数字＋比較）
KpiCard（Value／Label／Sub）。数値は Number.ToText で text に整形
Step 3
推移・比較・構成のグラフ
Chart（Line／Area／Bar／StackedBar／Doughnut／Pie）に集計済みの小
さな表を渡す
Step 2・4
明細
Table に必要な列だけの表を渡す
Step 5
スライサー（絞り込み）
パラメーターの値を切り替えて、クエリ全体を再評価する
Step 6
条件付き書式・アラート
条件で Sub や表のテキストを変える（◎ △ × など）
Step 7
項目ごとに同じグラフを並べる
List.Transform／List.Split で可視化ドキュメントの行を生成する
Step 7
データ品質の監視
プロファイル用の関数（Table.Profile＋分布＋外れ値）を任意の表に適用する
Step 8
品質保証（壊れていないか）
ルールを点検する関数で可視化ドキュメントを検査する
Step 9
共有・再利用
My queries、Shared queries、共有リンク（閲覧画面）
5章
最新の状態にする
［プレビューの更新］で再評価する（評価時点のスナップショット）
2.5
2. ハンズオンの準備
2.1 必要なもの
• Microsoft Fabric の容量（試用版を含む）が割り当てられたワークスペース
• そのワークスペースで Dataflow Gen2 を作成・編集できる権限（共同作成者以上）
• 外部のデータ ソースや資格情報は不要です（Step 8 の Northwind は任意）
6


# Page. 7

![Page Image](https://bcdn.docswell.com/page/Y76W88V17V.jpg)

Dataflow Gen2 データビジュアル（プレビュー）ハンズオン ガイド
2.2 本書で作るもの
1 つのデータフローに、次のクエリを順に追加していきます。Step 3〜6 では同じ Dashboard クエリを育てていきます。
表6
本書で作成するクエリ
Step
クエリ名
種類
内容
ファイル（M_code）
1
SalesData
表
サンプルデータ（576 行）
01_SalesData.pq
2
Visual_Hello
ビジュアル
カード＋折れ線の最小例
02_Visual_Hello.pq
3〜5
Dashboard
ビジュアル
ヘッダー、KPI、6 種類のグラフ、明細
03〜05_Dashboard_*.pq
6
pRegion
パラメーター
地域の切り替え（Dashboard も更
06a_pRegion_parameter.pq
新）
／06b_*.pq
7
Dashboard_Regions
ビジュアル
地域別スコアカード（自動生成）
07_Dashboard_Regions.pq
8
fnProfileDashboard ほか
関数＋ビジュアル
再利用できるデータプロファイル
08a〜08d_*.pq
関数＋表
可視化ドキュメントの自己診断
09a〜09b_*.pq
9
fnCheckVisualDocument／
Check_All
dashboard（Container：column）
header（Header） Contoso Coffee 販売ダッシュボード
………… 2025 年度（前年度比較）
kpi-row（Container：row）
売上高
目標達成率
粗利率
受注件数
49.93 億円
101.7%
46.8%
2,015,397 件
前年比 +11.4%
目標 49.09 億円
前年差 +0.3pt
平均単価 2,477 円
trend-row（Container：row）
Card：月別売上の推移（百万円）
Card：月別受注件数
Chart：Line 月 → 売上(百万円)
Chart：Area 月 → 受注件数
region-row（Container：row）
Card：地域別売上（百万円）
Card：地域 × カテゴリ別売上（百万円）
Chart：Bar 地域 → 売上(百万円)
Chart：StackedBar 地域 → 4 カテゴリ
mix-row（Container：row）
Card：カテゴリ別の売上構成
Chart：Doughnut
Card：カテゴリ別の受注件数構成
カテゴリ → 売上
Chart：Pie
カテゴリ → 受注件数
detail-card（Card）
Card：地域 × カテゴリ別の明細（2025 年度） ｜ Table（24 行 × 6 列）
図3
Step 5 で完成する Dashboard の構成（ワイヤーフレーム）
2.3 サンプルデータ「Contoso Coffee」
架空のコーヒー会社 Contoso Coffee の販売実績です。外部ファイルは使わず、M の計算式で生成します。乱数を使わない
7


# Page. 8

![Page Image](https://bcdn.docswell.com/page/G75M99DL74.jpg)

Dataflow Gen2 データビジュアル（プレビュー）ハンズオン ガイド
ので誰が実行しても同じ値になり、各 Step の「確認ポイント」の数値と照合できます。
表 7 SalesData の列
列
型
内容
例（先頭行）
MonthStart
date
月初日
2024-04-01
Month
text
年月
2024-04
FiscalYear
整数
年度（4 月始まり）
2024
Region
text
地域（6 地域）
北海道・東北
Category
text
カテゴリ（4 種類）
コーヒー豆
Revenue
数値
売上（円）
13,582,000
Target
数値
売上目標（円）
14,440,000
GrossProfit
数値
粗利（円）
6,292,000
Orders
整数
受注件数
5,509
期間は 2024 年度と 2025 年度（2024 年 4 月〜2026 年 3 月）の 24 か月で、6 地域 × 4 カテゴリの 576 行です。冬
（特に 12 月のお歳暮シーズン）に売上が伸び、ギフトは 7 月（お中元）にも山があります。2025 年度の売上は 49.9 億
円で前年度比 +11.4% ですが、地域によって目標の達成度に差が出るように作ってあります。
700
売上（百万円）
600
500
400
300
200
100
0
4月
5月
6月
7月
8月
9月
2024年度
図4
10月
11月
12月
1月
2月
2025年度
参考：サンプルデータの月別売上（百万円）。Word のグラフで再現
2.4 Dataflow Gen2 を作成する
1. Fabric ポータル（app.fabric.microsoft.com）で、使用するワークスペースを開きます。
2. ［+ 新しい項目］（New item）を選び、一覧から［Dataflow Gen2］を選びます。
3. 名前に DFG2_DataVisuals_Handson などと入力して作成します。Power Query エディターが開きます。
8
3月


# Page. 9

![Page Image](https://bcdn.docswell.com/page/9J292283ER.jpg)

Dataflow Gen2 データビジュアル（プレビュー）ハンズオン ガイド
2.5 共通の操作
以降の Step で繰り返し使う操作です。画面の表示言語によってボタン名が英語の場合があるため、英語名も併記します。
表8
操作
共通の操作
手順
空のクエリを追加する
クエリの名前を変える
コードを置き換える
表示を更新する
［ホーム］→［データの取得］（Get data）→［空のクエリ］（Blank query）を選ぶ。表示され
たエディターの内容をすべて削除してコードを貼り付け、［次へ］（Next）を選ぶ。
右側の［クエリの設定］（Query settings）で［名前］を書き換える。または［クエリ］ウィンドウ
でクエリを右クリック →［名前の変更］。
対象のクエリを選び、［ホーム］→［詳細エディター］（Advanced editor）で全体を置き換えて
［OK］を選ぶ。
［ホーム］→［更新］（Refresh）→［プレビューの更新］（Refresh preview）
ビジュアル用クエリの推奨設定
• データの宛先は設定しない：データビジュアルは編集キャンバスでの表示専用です。更新の出力やデータの宛先には含まれ
ません。
• ステージングはオフに：クエリを右クリックして［ステージングを有効にする］（Enable staging）がオンなら、オフにしておく
と安心です（本書の推奨）。
• 保存を忘れずに：作業内容を残すにはデータフローを保存します。ビジュアルを確認するだけなら、更新（実行）は不要で
す。
3. 基本編：ダッシュボードを組み立てる
基本編では、1 つのクエリ Dashboard を少しずつ育てながら、ヘッダー → KPI → グラフ → 明細の順にダッシュボードを完成
させます。
Step 1
サンプルデータ（SalesData）を作る
［5 分］
1. 空のクエリを追加し、次のコード（01_SalesData.pq）を貼り付けて［次へ］を選びます。
2. クエリの名前を SalesData に変更します（以降のクエリはこの名前で参照します）。
クエリ：SalesData
｜ ファイル：M_code\01_SalesData.pq
// SalesData（Step 1）
// Contoso Coffee（架空の会社）の販売サンプルデータ
// 2024 年度～2025 年度（2024-04～2026-03）の 24 か月 × 6 地域 × 4 カテゴリ = 576 行
// 乱数ではなく計算式で値を作っているため、誰が実行しても同じ結果になります。
let
// 地域：月あたりの基準売上（百万円）と、2025 年度の成長率
Regions = #table(
type table [Region = text, Base = number, Growth = number],
9


# Page. 10

![Page Image](https://bcdn.docswell.com/page/DEY4YY68JM.jpg)

Dataflow Gen2 データビジュアル（プレビュー）ハンズオン ガイド
{
{&quot;北海道・東北&quot;, 38, -0.02},
{&quot;関東&quot;, 120, 0.08},
{&quot;中部&quot;, 55, 0.05},
{&quot;近畿&quot;, 70, 0.06},
{&quot;中国・四国&quot;, 30, 0.03},
{&quot;九州・沖縄&quot;, 42, 0.15}
}
),
// カテゴリ：売上構成比・粗利率・平均単価（円）・2025 年度の成長率
Categories = #table(
type table [
Category = text, Share = number, Margin = number,
Ticket = number, Growth = number, IsGift = logical
],
{
{&quot;コーヒー豆&quot;, 0.40, 0.45, 2400, 0.03, false},
{&quot;ドリップバッグ&quot;, 0.25, 0.55, 1500, 0.14, false},
{&quot;カフェ用品&quot;, 0.20, 0.35, 4800, -0.04, false},
{&quot;ギフト&quot;, 0.15, 0.50, 5500, 0.06, true}
}
),
// 季節係数（4 月～翌 3 月）。ギフトはお中元（7 月）とお歳暮（12 月）に山がある
Season
= {0.95, 0.90, 0.88, 0.85, 0.85, 0.95, 1.05, 1.12, 1.25, 1.18, 1.10, 1.00},
GiftPeak = {1.0, 1.0, 1.0, 2.2, 1.0, 1.0, 1.0, 1.0, 2.8, 1.0, 1.0, 1.0},
// 同じ入力なら必ず同じ値を返す「疑似乱数」（0 以上 1 未満）
Hash = (seed as number) as number =&gt;
Number.Mod((seed + 1) * (seed + 7) * 7919 + 104729, 10007) / 10007,
RegionList
= Table.ToRecords(Regions),
CategoryList = Table.ToRecords(Categories),
// 行の一覧（List.Buffer で一度だけ計算してメモリに保持）
RowList = List.Buffer(List.Combine(
List.Transform({0..23}, (m) =&gt;
List.Combine(
List.Transform({0..5}, (r) =&gt;
List.Transform({0..3}, (c) =&gt;
let
reg
= RegionList{r},
cat
= CategoryList{c},
i
= m * 24 + r * 4 + c,
fm
= Number.Mod(m, 12),
fy
= if m &lt; 12 then 2024 else 2025,
monthStart = Date.AddMonths(#date(2024, 4, 1), m),
plan
= reg[Base] * 1000000 * cat[Share] * Season{fm}
* (if cat[IsGift] then GiftPeak{fm} else 1),
growth
= if fy = 2025
then (1 + reg[Growth]) * (1 + cat[Growth]) else 1,
noise
= 0.94 + 0.12 * Hash(i),
revenue
= Number.Round(plan * growth * noise / 1000) * 1000,
targetRate = if fy = 2025 then 1.10 else 1,
target
= Number.Round(plan * targetRate / 1000) * 1000,
margin
= cat[Margin] + (Hash(i + 1000) - 0.5) * 0.04,
gross
= Number.Round(revenue * margin / 1000) * 1000,
ticket
= cat[Ticket] * (0.95 + 0.10 * Hash(i + 2000)),
orders
= Number.Round(revenue / ticket)
in
{monthStart, Date.ToText(monthStart, &quot;yyyy-MM&quot;), fy,
reg[Region], cat[Category], revenue, target, gross, orders}
)
)
)
)
)),
SalesData = #table(
type table [
MonthStart = date, Month = text, FiscalYear = Int64.Type,
Region = text, Category = text,
Revenue = number, Target = number, GrossProfit = number, Orders = Int64.Type
],
RowList
)
10


# Page. 11

![Page Image](https://bcdn.docswell.com/page/VJNYPPZ978.jpg)

Dataflow Gen2 データビジュアル（プレビュー）ハンズオン ガイド
in
SalesData
コードのポイント
• Regions／Categories：地域とカテゴリごとの前提（基準売上、構成比、粗利率、平均単価、成長率）を
#table で定義しています。
• Season／GiftPeak：月ごとの季節係数です。リストの 1 番目が 4 月にあたります。
• Hash：同じ入力に必ず同じ値を返す「疑似乱数」で、売上に ±6% のゆらぎを付けています。
• List.Buffer：生成した行のリストをメモリに保持し、ほかのクエリから何度参照されても再計算しないようにしていま
す。
確認ポイント
• 576 行 × 9 列の表が表示される（FiscalYear が 2024 と 2025 の行が 288 行ずつ）。
• 先頭行は「2024-04／北海道・東北／コーヒー豆／Revenue 13,582,000」。
• まだビジュアルではなく、通常の表として表示される（5 列の可視化ドキュメントではないため）。
Step 2
はじめてのビジュアル（Visual_Hello）
［5 分］
1. 空のクエリを追加し、次のコード（02_Visual_Hello.pq）を貼り付けます。
2. クエリの名前を Visual_Hello に変更します。
11


# Page. 12

![Page Image](https://bcdn.docswell.com/page/YE9P33Z3J3.jpg)

Dataflow Gen2 データビジュアル（プレビュー）ハンズオン ガイド
クエリ：Visual_Hello ｜
ファイル：M_code\02_Visual_Hello.pq
// Visual_Hello（Step 2）
// いちばん小さなデータビジュアル：カード（タイトル）の中に折れ線グラフを 1 つ置く
let
// 2025 年度の月別売上（百万円）を集計した小さな表
MonthlyRevenue = Table.Sort(
Table.Group(
Table.SelectRows(SalesData, each [FiscalYear] = 2025),
{&quot;Month&quot;},
{{&quot;売上(百万円)&quot;, each Number.Round(List.Sum([Revenue]) / 1000000, 1), type number}}
),
{{&quot;Month&quot;, Order.Ascending}}
),
// 可視化ドキュメントの型：この 5 列があると、クエリの結果がビジュアルとして描画される
VisualDocumentType = type table [
Name = nullable text, Parent = nullable text, PartType = nullable text,
Properties = nullable record, Data = any
],
// 1 行 = 1 ビジュアル。2 行目の Parent に 1 行目の Name を入れて親子にする
VisualDocument = #table(
VisualDocumentType,
{
{&quot;sales-card&quot;, null, &quot;Card&quot;, [Title = &quot;月別売上（2025 年度・百万円）&quot;], null},
{&quot;sales-trend&quot;, &quot;sales-card&quot;, &quot;Chart&quot;,
[ChartType = &quot;Line&quot;,
DataSeries = [AxisColumns = &quot;Month&quot;, ValueColumns = &quot;売上(百万円)&quot;]],
MonthlyRevenue}
}
)
in
VisualDocument
コードのポイント
• MonthlyRevenue は、2025 年度の月別売上（百万円）を集計した 12 行 × 2 列の小さな表です。グラフには、この
ように集計済みの小さな表を渡します。
• 可視化ドキュメントの 1 行目は Card（Parent = null なのでルート）、2 行目は Chart で、Parent に &quot;sales-card&quot;
を指定してカードの中に入れています。
• Chart の Properties では ChartType = &quot;Line&quot; を指定し、DataSeries で軸に Month 列、値に 売上(百万円) 列
を割り当てています。列名は Data 列に渡した MonthlyRevenue の列名と一致させます。
12


# Page. 13

![Page Image](https://bcdn.docswell.com/page/GE8DMM1LED.jpg)

Dataflow Gen2 データビジュアル（プレビュー）ハンズオン ガイド
図5
参考：同じ構造（Card の中に Line）を描画した例（公式ドキュメントのサンプル。出典：Microsoft Learn、CC BY 4.0）
確認ポイント
• カード「月別売上（2025 年度・百万円）」の中に、12 か月分の折れ線グラフが表示される。
• 値は 4 月の 375.3 から始まり、12 月に 636.0 でピークになる。
試してみよう
• ChartType を &quot;Area&quot; や &quot;Bar&quot; に変えると、同じデータのまま別のグラフになります。
• PartType を &quot;LineChart&quot; に変えると「Visual not recognized」と表示されます（旧来の名前は使えません）。確認し
たら元に戻しましょう。
Step 3
骨組みを作る：ヘッダーと KPI カード ［10 分］
1. 空のクエリを追加し、次のコード（03_Dashboard_step3_skeleton.pq）を貼り付けます。
2. クエリの名前を Dashboard に変更します。Step 4〜6 では、このクエリのコードを置き換えていきます。
クエリ：Dashboard（Step 3）
｜ ファイル：M_code\03_Dashboard_step3_skeleton.pq
// Dashboard（Step 3）：ヘッダーと KPI カードだけの骨組み
let
// ---- 1) 対象データ：最新年度（当年度）と前年度 ---CurFY = List.Max(SalesData[FiscalYear]),
PrevFY = CurFY - 1,
Cur
= Table.SelectRows(SalesData, each [FiscalYear] = CurFY),
Prev
= Table.SelectRows(SalesData, each [FiscalYear] = PrevFY),
// ---- 2) KPI の計算（数値）---Revenue
= List.Sum(Cur[Revenue]),
RevenuePrev
= List.Sum(Prev[Revenue]),
Target
= List.Sum(Cur[Target]),
GrossMargin
= List.Sum(Cur[GrossProfit]) / Revenue,
GrossMarginPrev = List.Sum(Prev[GrossProfit]) / RevenuePrev,
Orders
= List.Sum(Cur[Orders]),
13


# Page. 14

![Page Image](https://bcdn.docswell.com/page/LELM33QQ7R.jpg)

Dataflow Gen2 データビジュアル（プレビュー）ハンズオン ガイド
// ---- 3) KPI の表示テキスト（KpiCard の値は text で渡す）---Fmt
= (n as number, format as text) as text =&gt; Number.ToText(n, format, &quot;ja-JP&quot;),
Signed = (n as number, format as text) as text =&gt;
(if n &gt; 0 then &quot;+&quot; else &quot;&quot;) &amp; Fmt(n, format),
RevenueText
= Fmt(Revenue / 100000000, &quot;N2&quot;) &amp; &quot;億円&quot;,
RevenueSub
= &quot;前年比 &quot; &amp; Signed(Revenue / RevenuePrev - 1, &quot;P1&quot;),
AchievementText = Fmt(Revenue / Target, &quot;P1&quot;),
AchievementSub = &quot;目標 &quot; &amp; Fmt(Target / 100000000, &quot;N2&quot;) &amp; &quot;億円&quot;,
MarginText
= Fmt(GrossMargin, &quot;P1&quot;),
MarginSub
= &quot;前年差 &quot; &amp; Signed((GrossMargin - GrossMarginPrev) * 100, &quot;N1&quot;) &amp; &quot;pt&quot;,
OrdersText
= Fmt(Orders, &quot;N0&quot;) &amp; &quot;件&quot;,
OrdersSub
= &quot;平均単価 &quot; &amp; Fmt(Revenue / Orders, &quot;N0&quot;) &amp; &quot;円&quot;,
// ---- 4) 可視化ドキュメント（1 行 = 1 ビジュアル。Parent で親子関係を表す）---VisualDocumentType = type table [
Name = nullable text, Parent = nullable text, PartType = nullable text,
Properties = nullable record, Data = any
],
VisualDocument = #table(
VisualDocumentType,
{
// ルート（Parent = null はこの 1 行だけ）。column = 子を縦に並べる
{&quot;dashboard&quot;, null, &quot;Container&quot;, [Direction = &quot;column&quot;], null},
{&quot;header&quot;, &quot;dashboard&quot;, &quot;Header&quot;,
[Header = &quot;Contoso Coffee 販売ダッシュボード&quot;,
FarText = Text.From(CurFY) &amp; &quot;年度（前年度比較）&quot;], null},
// KPI 行。row = 子を横に並べる
{&quot;kpi-row&quot;, &quot;dashboard&quot;, &quot;Container&quot;, [Direction = &quot;row&quot;], null},
{&quot;kpi-revenue&quot;, &quot;kpi-row&quot;, &quot;KpiCard&quot;,
[Label = &quot;売上高&quot;, Value = RevenueText, Sub = RevenueSub], null},
{&quot;kpi-achievement&quot;, &quot;kpi-row&quot;, &quot;KpiCard&quot;,
[Label = &quot;目標達成率&quot;, Value = AchievementText, Sub = AchievementSub], null},
{&quot;kpi-margin&quot;, &quot;kpi-row&quot;, &quot;KpiCard&quot;,
[Label = &quot;粗利率&quot;, Value = MarginText, Sub = MarginSub], null},
{&quot;kpi-orders&quot;, &quot;kpi-row&quot;, &quot;KpiCard&quot;,
[Label = &quot;受注件数&quot;, Value = OrdersText, Sub = OrdersSub], null}
}
)
in
VisualDocument
レイアウトは Parent の入れ子で決まる
dashboard
Container（column）＝ルート
header
kpi-row
Header
Container（row）
Parent に親の Name を書くと、
その下に入れ子になる。
column＝縦に並ぶ
row＝横に並ぶ
kpi-revenue
kpi-achievement
kpi-margin
kpi-orders
KpiCard
KpiCard
KpiCard
KpiCard
図6
Step 3 の親子関係（Name と Parent）
• ルートの dashboard は Direction = &quot;column&quot; の Container なので、子のヘッダーと kpi-row が縦に並びます。
• kpi-row は Direction = &quot;row&quot; の Container なので、4 枚の KpiCard が横に並びます。
14


# Page. 15

![Page Image](https://bcdn.docswell.com/page/4JMYDMKKJW.jpg)

Dataflow Gen2 データビジュアル（プレビュー）ハンズオン ガイド
• KpiCard の Value と Label は text でなければなりません。数値は Number.ToText で書式を付けてから渡します
（&quot;P1&quot;＝小数 1 桁のパーセント、&quot;N0&quot;＝桁区切りの整数）。第 3 引数に &quot;ja-JP&quot; を指定すると、環境によらず日
本語の書式になります。
• Sub（小さな補足行）に前年比や目標を入れると、数字の意味がひと目で伝わり、ダッシュボードらしくなります。
表9
確認ポイント：KPI カードの表示（全国・2025 年度）
Label
Value
Sub
売上高
49.93 億円
前年比 +11.4%
目標達成率
101.7%
目標 49.09 億円
粗利率
46.8%
前年差 +0.3pt
受注件数
2,015,397 件
平均単価 2,477 円
ステータス バーには 5 列・7 行と表示されます（可視化ドキュメントの行数）。
Step 4
グラフを追加する
［10 分］
1. Dashboard クエリを選び、［詳細エディター］でコード全体を 04_Dashboard_step4_charts.pq の内容に置き換えま
す。
2. ［OK］を選ぶと、KPI の下にグラフが 3 行（推移、地域の比較、構成比）追加されます。
追加したのは、①グラフに渡す集計テーブルと、②カードとグラフの行です。主要部分を抜粋します（★ の付いたコメントが追
加箇所）。
抜粋①：グラフ用の集計テーブル（04_Dashboard_step4_charts.pq）
// ---- 4) グラフに渡す集計テーブル（★Step 4 で追加）---// 列名がそのまま軸タイトルや表の見出しになるので、日本語の表示名に変える
ToJapanese = (t as table) as table =&gt;
Table.RenameColumns(t,
{{&quot;Month&quot;, &quot;月&quot;}, {&quot;Region&quot;, &quot;地域&quot;}, {&quot;Category&quot;, &quot;カテゴリ&quot;}}, MissingField.Ignore),
ToMillions = (n as number) as number =&gt; Number.Round(n / 1000000, 1),
RevenueByMonth = ToJapanese(Table.Sort(
Table.Group(Cur, {&quot;Month&quot;},
{{&quot;売上(百万円)&quot;, each ToMillions(List.Sum([Revenue])), type number}}),
{{&quot;Month&quot;, Order.Ascending}})),
OrdersByMonth = ToJapanese(Table.Sort(
Table.Group(Cur, {&quot;Month&quot;},
{{&quot;受注件数&quot;, each List.Sum([Orders]), type number}}),
{{&quot;Month&quot;, Order.Ascending}})),
RevenueByRegion = ToJapanese(Table.Sort(
Table.Group(Cur, {&quot;Region&quot;},
{{&quot;売上(百万円)&quot;, each ToMillions(List.Sum([Revenue])), type number}}),
{{&quot;売上(百万円)&quot;, Order.Descending}})),
// 積み上げ横棒はワイド形式（カテゴリごとに 1 列）が必要 → Table.Pivot で列に展開
CategoryNames = List.Sort(List.Distinct(Cur[Category])),
RevenueByRegionCategory = ToJapanese(Table.Pivot(
Table.Group(Cur, {&quot;Region&quot;, &quot;Category&quot;},
{{&quot;Revenue&quot;, each ToMillions(List.Sum([Revenue])), type number}}),
CategoryNames, &quot;Category&quot;, &quot;Revenue&quot;, List.Sum)),
// （RevenueByCategory／OrdersByCategory も同じ書き方で集計）
15


# Page. 16

![Page Image](https://bcdn.docswell.com/page/PJR94VX679.jpg)

Dataflow Gen2 データビジュアル（プレビュー）ハンズオン ガイド
抜粋②：可視化ドキュメントに追加した行（04_Dashboard_step4_charts.pq）
// ★Step 4 で追加：推移（折れ線・面）
{&quot;trend-row&quot;, &quot;dashboard&quot;, &quot;Container&quot;, [Direction = &quot;row&quot;], null},
{&quot;revenue-trend-card&quot;, &quot;trend-row&quot;, &quot;Card&quot;, [Title = &quot;月別売上の推移（百万円）&quot;], null},
{&quot;revenue-trend-chart&quot;, &quot;revenue-trend-card&quot;, &quot;Chart&quot;,
[ChartType = &quot;Line&quot;,
DataSeries = [AxisColumns = &quot;月&quot;, ValueColumns = &quot;売上(百万円)&quot;]],
RevenueByMonth},
{&quot;orders-trend-card&quot;, &quot;trend-row&quot;, &quot;Card&quot;, [Title = &quot;月別受注件数&quot;], null},
{&quot;orders-trend-chart&quot;, &quot;orders-trend-card&quot;, &quot;Chart&quot;,
[ChartType = &quot;Area&quot;,
DataSeries = [AxisColumns = &quot;月&quot;, ValueColumns = &quot;受注件数&quot;]],
OrdersByMonth},
…
{&quot;region-mix-chart&quot;, &quot;region-mix-card&quot;, &quot;Chart&quot;,
[ChartType = &quot;StackedBar&quot;,
DataSeries = [AxisColumns = &quot;地域&quot;, ValueColumns = CategoryNames]],
RevenueByRegionCategory},
コードのポイント
• 軸タイトル＝列名：グラフの軸タイトルには Data のテーブルの列名が表示されます。そこで ToJapanese 関数で
Month→月、Region→地域 のように表示名へ変えています。
• 数値の書式はデータ側で：グラフに書式設定のプロパティはないため、ToMillions で百万円単位に丸め、列名「売上
(百万円)」で単位を示しています。
• 積み上げ横棒はワイド形式：StackedBar は「カテゴリごとに 1 列」の表が必要です。Table.Pivot で Category の値
を列に展開し、ValueColumns には列名のリスト CategoryNames をそのまま渡しています。カテゴリが増えても自動で
追従します。
• タイトルは Card で：各グラフは Card の子にしてタイトルを付けています（Card の子はちょうど 1 つ）。
表 10
Step 4 で追加したグラフ
Name
ChartType
Data（行 × 列）
軸→値
revenue-trend-chart
Line
12 × 2
月 → 売上(百万円)
orders-trend-chart
Area
12 × 2
月 → 受注件数
region-chart
Bar
6×2
地域 → 売上(百万円)
region-mix-chart
StackedBar
6×5
地域 → カフェ用品・ギフト・コーヒー豆・ドリップバッグ
category-chart
Doughnut
4×2
カテゴリ → 売上(百万円)
orders-mix-chart
Pie
4×2
カテゴリ → 受注件数
16


# Page. 17

![Page Image](https://bcdn.docswell.com/page/PEXQ2Z6DJX.jpg)

Dataflow Gen2 データビジュアル（プレビュー）ハンズオン ガイド
関東
1,719.0
近畿
973.7
中部
761.3
九州・沖縄
637.5
北海道・東北
492.0
中国・四国
409.6
0
500
1,000
1,500
2,000
売上（百万円）
図 7 確認ポイント：地域別売上（2025 年度・百万円）の値。Word のグラフで再現
並び順についての注意
公式ドキュメントのスクリーンショットでは、売上の降順に並べたデータを渡した地域別の横棒が、名前順（アルファベット順）で
表示されています。表示順を確実に固定したい場合は、ラベルの先頭に番号を付ける方法が有効です（Step 8 のヒストグラム
では「①」「②」…を付けています）。
Step 5
明細テーブルを追加して完成
［5 分］
1. Dashboard クエリのコード全体を 05_Dashboard_step5_complete.pq（付録 C-1）の内容に置き換えます。
抜粋：明細テーブルの追加（05_Dashboard_step5_complete.pq）
// ★Step 5 で追加：明細テーブル（地域 × カテゴリ、売上の大きい順）
DetailTable = ToJapanese(Table.Sort(
Table.Group(Cur, {&quot;Region&quot;, &quot;Category&quot;}, {
{&quot;売上(百万円)&quot;, each ToMillions(List.Sum([Revenue])), type number},
{&quot;目標達成率(%)&quot;,
each Number.Round(List.Sum([Revenue]) / List.Sum([Target]) * 100, 1), type number},
{&quot;粗利率(%)&quot;,
each Number.Round(List.Sum([GrossProfit]) / List.Sum([Revenue]) * 100, 1), type number},
{&quot;受注件数&quot;, each List.Sum([Orders]), type number}}),
{{&quot;売上(百万円)&quot;, Order.Descending}})),
…
// ★Step 5 で追加：明細（Card の子に Table を 1 つ）
{&quot;detail-card&quot;, &quot;dashboard&quot;, &quot;Card&quot;,
[Title = &quot;地域 × カテゴリ別の明細（&quot; &amp; Text.From(CurFY) &amp; &quot;年度）&quot;], null},
{&quot;detail-table&quot;, &quot;detail-card&quot;, &quot;Table&quot;, [], DetailTable}
Table パーツは、Data に渡したテーブルをそのまま行と列で表示します（集計はしません）。明細は大きくなりがちなので、必
要な列に絞り、並べ替えてから渡します。
17


# Page. 18

![Page Image](https://bcdn.docswell.com/page/3EK9M81DED.jpg)

Dataflow Gen2 データビジュアル（プレビュー）ハンズオン ガイド
図8
参考：構成比のグラフと明細テーブル（公式ドキュメントのサンプル。出典：Microsoft Learn、CC BY 4.0）
確認ポイント
• 画面下部のステータス バーに 5 列・ 行と表示される（可視化ドキュメントの行数。コンテナーとカードを含む）。
• 明細は 24 行 × 6 列。先頭は「関東・コーヒー豆 売上 659.1 百万円、目標達成率 103.3%、粗利率 44.8%」。
基本編のまとめ：3 段階で書く
• 集計する：グラフや表に渡す小さなテーブルを Table.Group などで作る（列名＝表示名）。
• 整形する：KPI の数値を Number.ToText で text にする。
• 配置する：Container（row／column）と Card を入れ子にして、1 行 1 ビジュアルで並べる。
4. 応用編：ダッシュボードらしさを高める
データビジュアルにはフィルターやスライサー、クロスフィルターがありません。応用編では、DFG2 と Power Query M の機能を組
み合わせて、「切り替える」「自動で増える」「どのデータにも使い回す」「壊れていないか確かめる」という、ダッシュボードらしい体
験を補います。
Step 6
パラメーターで「スライサー風」に切り替える
［10 分］
DFG2 のパラメーターの値を切り替えると、そのパラメーターを参照するクエリが再評価されます。これを利用して、地域を選ぶと
ダッシュボード全体が切り替わる仕組みを作ります。
18


# Page. 19

![Page Image](https://bcdn.docswell.com/page/L73WY2QP75.jpg)

Dataflow Gen2 データビジュアル（プレビュー）ハンズオン ガイド
① pRegion の
② クエリを再評価
③ KPI・グラフ・明細を
現在の値を選ぶ
Source で SalesData を
再計算
ヘッダー右側に
全国／関東／近畿 …
Region で絞り込む
（評価時点のスナップショット）
「関東｜2025 年度…」
④ 表示が切り替わる
スライサーの代わりに、パラメーターの値を変えてクエリ全体を再評価する（値の一覧から選べるようにしておく）
図9
パラメーターによる切り替えの流れ
1. ［ホーム］→［パラメーターの管理］（Manage parameters）→［新しいパラメーター］（New parameter）を選
びます。
2. 名前を pRegion、種類を［テキスト］、推奨値を［値の一覧］にして「全国／北海道・東北／関東／中部／近畿
／中国・四国／九州・沖縄」を入力し、既定値と現在の値を「全国」にして保存します。UI を使わずに、次の
06a_pRegion_parameter.pq を空のクエリに貼り付けて名前を pRegion にしても作成できます。
3. Dashboard クエリのコード全体を 06b_Dashboard_step6_parameter.pq の内容に置き換えます。
4. ［クエリ］ウィンドウで pRegion を選び、現在の値を「関東」に変えてから Dashboard を選びます。表示が変わらない
場合は［プレビューの更新］を選びます。
クエリ：pRegion（パラメーター） ｜
ファイル：M_code\06a_pRegion_parameter.pq
// pRegion（Step 6）：地域を切り替えるためのパラメーター
// ※通常は［ホーム］→［パラメーターの管理］で作成します。UI を使わない場合は、
//
空のクエリにこのコードを貼り付けて名前を pRegion にするとパラメーターになります。
&quot;全国&quot; meta [
IsParameterQuery = true,
IsParameterQueryRequired = true,
Type = &quot;Text&quot;,
List = {&quot;全国&quot;, &quot;北海道・東北&quot;, &quot;関東&quot;, &quot;中部&quot;, &quot;近畿&quot;, &quot;中国・四国&quot;, &quot;九州・沖縄&quot;},
DefaultValue = &quot;全国&quot;
]
抜粋：Dashboard の変更点（06b_Dashboard_step6_parameter.pq）。ほかにヘッダーの FarText と明細のタイトルに pRegion を表示
// ---- 0) パラメーター pRegion で対象データを絞り込む（★Step 6 で追加）---Source = if pRegion = &quot;全国&quot; then SalesData
else Table.SelectRows(SalesData, each [Region] = pRegion),
Checked = if Table.IsEmpty(Source)
then error Error.Record(&quot;NoData&quot;, &quot;pRegion の値に一致するデータがありません&quot;, pRegion)
else Source,
// ---- 1) 対象データ：最新年度と前年度（★SalesData を Checked に変更）---CurFY = List.Max(Checked[FiscalYear]),
PrevFY = CurFY - 1,
Cur
= Table.SelectRows(Checked, each [FiscalYear] = CurFY),
Prev
= Table.SelectRows(Checked, each [FiscalYear] = PrevFY),
表 11 確認ポイント：pRegion を切り替えたときの KPI
KPI
全国
関東
売上高
49.93 億円（前年比 +11.4%）
17.19 億円（前年比 +13.6%）
19


# Page. 20

![Page Image](https://bcdn.docswell.com/page/87DK5Z93JG.jpg)

Dataflow Gen2 データビジュアル（プレビュー）ハンズオン ガイド
KPI
全国
関東
目標達成率
101.7%（目標 49.09 億円）
103.6%（目標 16.59 億円）
粗利率
46.8%（前年差 +0.3pt）
46.7%（前年差 -0.1pt）
受注件数
2,015,397 件（平均単価 2,477 円）
692,663 件（平均単価 2,482 円）
ポイント
• ヘッダー右側（FarText）に選択中の地域が表示されるので、今どの条件で見ているかが分かります。
• 存在しない値（例：「東京」）を指定すると、Checked のステップで「pRegion の値に一致するデータがありません」という
エラーになります。空のデータで分かりにくい表示になるのを防ぐための工夫です。
• 同じ方法で、年度やカテゴリのパラメーターを追加することもできます。
Step 7
データから自動生成する「地域別スコアカード」
［10 分］
地域ごとに KPI カードとグラフを手で書くと、地域が増えるたびにコードの修正が必要です。可視化ドキュメントは「ただの表」な
ので、行そのものを M で生成できます。
RegionStats
地域ごとのレコード × 6
[Region, Achievement,
YoY, Status, Monthly]
行を生成
List.Split(…, 3)
kpi-row-1／kpi-row-2
3 地域ずつの
固定の行 &amp; 生成した行
（Container：row）
グループ × 2
kpi-1 … kpi-6（KpiCard）
→ 可視化ドキュメント
（31 行）
地域が増えても、コードを変えずに KPI カード・グラフ・行コンテナーが増える
Name は番号から作るので一意になる（kpi-1、trend-card-1、trend-chart-1 …）
図 10
スコアカードの行を自動生成する流れ
1. 空のクエリを追加し、07_Dashboard_Regions.pq（付録 C-2）を貼り付けて、名前を Dashboard_Regions にしま
す。
抜粋：KPI カードの行を生成する部分（07_Dashboard_Regions.pq）
// ---- 2) 3 件ずつ横に並べる：行コンテナーと、その子の行をリストで生成 ---Groups = List.Split(RegionStats, 3),
KpiRows = List.Combine(List.Transform(List.Positions(Groups), (g) =&gt;
{ {&quot;kpi-row-&quot; &amp; Text.From(g + 1), &quot;scorecard&quot;, &quot;Container&quot;, [Direction = &quot;row&quot;], null} }
&amp; List.Transform(Groups{g}, (r) =&gt;
{&quot;kpi-&quot; &amp; Text.From(r[No]), &quot;kpi-row-&quot; &amp; Text.From(g + 1), &quot;KpiCard&quot;,
[Label = r[Region] &amp; &quot;：目標達成率&quot;,
Value = Fmt(r[Achievement], &quot;P1&quot;),
Sub
= r[Status]], null})
)),
20


# Page. 21

![Page Image](https://bcdn.docswell.com/page/VJPKGDZPE8.jpg)

Dataflow Gen2 データビジュアル（プレビュー）ハンズオン ガイド
コードのポイント
• RegionStats：地域ごとの売上・目標達成率・前年比・判定と、月別推移の小さな表を 1 件のレコードにまとめていま
す。
• List.Split(RegionStats, 3)：3 地域ずつのグループに分け、グループごとに行コンテナー（kpi-row-1、kpirow-2）と子の KpiCard の行を生成します。Name は番号から作るので必ず一意になります。
• 固定の行（ルート、ヘッダーなど）と生成した行のリストを &amp; でつなげると、1 つの可視化ドキュメントになります。
• 判定（◎ 目標達成／△ あと少し／× 要対策）は条件付き書式の代わりです。色は変えられませんが、記号と言葉
で状態を伝えられます。
115%
109.8%
110%
103.6%
目標達成率
105%
100.1%
100.6%
中部
近畿
98.7%
100%
95%
93.6%
90%
85%
80%
北海道・東北
関東
目標達成率（%）
目標（100%）
中国・四国
九州・沖縄
注意ライン（95%）
図 11 確認ポイント：地域別の目標達成率（2025 年度）。Word のグラフで再現
表 12
地域
確認ポイント：「目標達成率の低い順」の表
売上(百万円)
目標達成率(%)
前年比(%)
判定
北海道・東北
492.0
93.6
2.8 × 要対策（95%未満）
中国・四国
409.6
98.7
8.5 △ あと少し（95%以上）
中部
761.3
100.1
9.4 ◎ 目標達成
近畿
973.7
100.6
9.7 ◎ 目標達成
関東
1,719.0
103.6
13.6 ◎ 目標達成
637.5
109.8
20.7 ◎ 目標達成
九州・沖縄
北海道・東北は前年比プラス（+2.8%）でも、前年度の計画値比 +10% で設定した目標には届かず「× 要対策」です。
KPI と推移を並べると、「伸びているのに未達」のような読み違いに気づきやすくなります。可視化ドキュメントは 31 行になりま
すが、コードは地域の数に依存しません。
21


# Page. 22

![Page Image](https://bcdn.docswell.com/page/2EVV61WVEQ.jpg)

Dataflow Gen2 データビジュアル（プレビュー）ハンズオン ガイド
Step 8
再利用できる「データプロファイル」ダッシュボード
［10 分］
データの準備中は「NULL はないか」「値の分布はおかしくないか」を確かめたくなります。任意のテーブルを受け取り、プロファイ
ル用の可視化ドキュメントを返す関数を 1 つ作っておけば、どのクエリにも同じダッシュボードを付けられます。
1. 空のクエリを追加し、08a_fnProfileDashboard.pq（付録 C-3）を貼り付けて、名前を fnProfileDashboard にし
ます（関数として認識されます）。
2. 空のクエリを追加して次の 1 行を貼り付け、名前を Profile_SalesData にします。
3. （任意）公開サンプルの Northwind でも試します。08c_NorthwindOrders.pq を NorthwindOrders として追加し
（資格情報を求められたら認証の種類に［匿名］）、08d のコードで Profile_Northwind を作ります。
クエリ：Profile_SalesData／Profile_Northwind ｜
ファイル：08b／08d
// Profile_SalesData（Step 8）：関数 fnProfileDashboard を SalesData に適用する
fnProfileDashboard(SalesData, &quot;SalesData のデータプロファイル&quot;)
// Profile_Northwind（Step 8・任意）：同じ関数を別のテーブルにそのまま再利用する
fnProfileDashboard(NorthwindOrders, &quot;Northwind Orders のデータプロファイル&quot;)
プロファイル ダッシュボードの中身
• ヘッダーと KPI：行数 × 列数、数値列の数、NULL の割合、重複行の数。
• 品質のグラフ：列ごとの NULL 件数と、一意な値の数（横棒）。
• 分布：数値列（最大 6 列）のヒストグラム。等幅 8 区間で数え、区間名に「①」「②」…を付けて並び順を固定してい
ます。年度のように整数で種類が 8 以下の列は、値ごとに数えます。
• 列プロファイル表：Table.Profile の結果（件数・NULL・一意な値・最小／最大・平均・標準偏差）に、四分位
（Excel の PERCENTILE.INC と同じ線形補間）と IQR 法の外れ値（Q1−1.5×IQR 未満または Q3＋1.5×IQR
超）の件数を加えています。
• Table.Buffer：ソースを一度だけ読み込み、何度集計しても再取得しないようにしています。OData のような外部ソース
では特に重要です。
22


# Page. 23

![Page Image](https://bcdn.docswell.com/page/57GLN2D1EL.jpg)

Dataflow Gen2 データビジュアル（プレビュー）ハンズオン ガイド
図 12
参考：公式ドキュメントのプロファイル例（Northwind の Orders。出典：Microsoft Learn、CC BY 4.0）
800
677
700
600
件数
500
400
300
200
106
100
24
10
4
4
3
2
0
① 0.02〜126 ② 126〜252 ③ 252〜378 ④ 378〜504 ⑤ 504〜630 ⑥ 630〜756 ⑦ 756〜882 ⑧ 882〜1,008
Freight の区間（等幅 8 区間）
図 13
確認ポイント：Northwind の Freight の分布（件数）。Word のグラフで再現
表 13
確認ポイント：プロファイルの主な値
項目
Profile_SalesData
Profile_Northwind
ヘッダー（FarText）
576 行 × 9 列
830 行 × 9 列
NULL の割合
0.0%（0 セルが NULL）
7.1%（ShipRegion 507 件、ShippedDate 21 件）
重複行
0
0
数値列の分布
Freight の四分位
うち数値列 5（FiscalYear は「① 2024」「②
2025」）
―
うち数値列 3（OrderID、EmployeeID、Freight）
Q1 13.38／中央値 41.36／Q3 91.43、外れ値 67 件
Northwind の四分位と外れ値の件数は、図のスクリーンショット（公式ドキュメント）の値と一致します。自分のクエリでも
fnProfileDashboard(クエリ名, &quot;タイトル&quot;) の 1 行で同じダッシュボードを付けられます。
23


# Page. 24

![Page Image](https://bcdn.docswell.com/page/4EQYQP1NJP.jpg)

Dataflow Gen2 データビジュアル（プレビュー）ハンズオン ガイド
Step 9
自己診断ツールで点検する
［5 分］
可視化ドキュメントの誤りには、エラーにならず「表示されないだけ」のものがあります（Parent の綴り違いなど）。そこで、公式
ドキュメントのルールを M で点検する関数を用意しました。
1. 09a_fnCheckVisualDocument.pq（付録 C-4）を貼り付けた空のクエリを、fnCheckVisualDocument という名前
で追加します。
2. 次の 09b_Check_All.pq を Check_All として追加します。作成した可視化クエリをまとめて点検し、結果を通常の表
で表示します。
クエリ：Check_All
｜ ファイル：M_code\09b_Check_All.pq
// Check_All（Step 9）：作成した可視化クエリをまとめて点検する（通常の表として表示されます）
let
Targets = {
{&quot;Visual_Hello&quot;, Visual_Hello},
{&quot;Dashboard&quot;, Dashboard},
{&quot;Dashboard_Regions&quot;, Dashboard_Regions},
{&quot;Profile_SalesData&quot;, Profile_SalesData}
},
Results = List.Transform(Targets, (t) =&gt;
Table.AddColumn(fnCheckVisualDocument(t{1}), &quot;クエリ&quot;, each t{0}, type text)),
Combined = Table.ReorderColumns(Table.Combine(Results),
{&quot;クエリ&quot;, &quot;重要度&quot;, &quot;Name&quot;, &quot;ルール&quot;, &quot;内容&quot;})
in
Combined
表 14 fnCheckVisualDocument が点検する主なルール
区分
全体
ルール
重要度
5 列（Name・Parent・PartType・Properties・Data）がある／ルート（Parent = null）はちょうど
1行
エラー
全体
Name が空でない・一意である／ルートからたどれない行（Parent の循環など）がない
エラー／警告
構造
Parent が既存の Name を指す／PartType が 6 種類のいずれか／Properties がレコード
エラー
構造
部品
グラフ
グラフ
Container の子は 1 つ以上、Card の子はちょうど 1 つ、Header・KpiCard・Table・Chart は子を
持たない
Card の Title、Header の Header、KpiCard の Value・Label が text。FarText・Sub は text か
null
ChartType が 6 種類のいずれか／AxisColumns は 1 列／StackedBar 以外は ValueColumns
が1列
指定した列が Data にある／値の列が数値／列名にバックスラッシュがない／Data が大きすぎない
24
エラー
エラー
エラー
警告・情報


# Page. 25

![Page Image](https://bcdn.docswell.com/page/KJ4WNYR371.jpg)

Dataflow Gen2 データビジュアル（プレビュー）ハンズオン ガイド
確認ポイント
• Check_All の 4 行（Visual_Hello、Dashboard、Dashboard_Regions、Profile_SalesData）がすべて「OK」になる。
• 試しに Dashboard のどこかの Parent をわざと書き換えると、「Parent は既存の Name」のエラーが表示される（確認した
ら元に戻す）。
5. 共有と再利用
作ったダッシュボードや関数は、DFG2 の My queries と Shared queries で再利用・共有できます。データビジュアルと組み
合わせると、「チーム共通のプロファイル画面」や「状況を要約したビュー」を配れます。
自分の別のデータフローで
My queries（一般提供）
［データの取得］から取り込む
［Add to My Queries］
M コードのみ保存・最新 50 件
作成者の
データフロー
Shared queries（プレビュー）
fnProfileDashboard
［Enable sharing (Preview)］
同僚が［データの取得］→
［Shared queries］からコピーを取り込む
独立したコピー（元の変更は反映されない）
Dashboard など
同僚が閲覧画面で結果を見る・更新
共有リンク（プレビュー）
（エディターは開かない）
［Get share link (Preview)］
ワークスペースの共同作成者以上が必要
図 14 My queries と Shared queries による再利用と共有
表 15
機能
My queries
Shared queries（作成
時の再利用）
共有リンク（閲覧）
状態
一般提供
プレビュー
プレビュー
再利用と共有の方法
用途
操作
自分用のライブラリ。関数やテンプレートを
別のデータフローで再利用
同僚がクエリのコピーを自分のデータフローに
取り込む
クエリを右クリック →［Add to My Queries］。取
り込みは［データの取得］→［Recents &amp; My
Queries］→［My Queries］フィルター
右クリック →［Enable sharing (Preview)］。同
僚は［データの取得］→［Shared queries］から
選ぶ
エディターを開かずに、クエリの結果を閲
右クリック →［Get share link (Preview)］→ リン
覧・更新してもらう
クをコピーして送る
• 共有リンクで閲覧するには、ソースのデータフローがあるワークスペースで共同作成者（Contributor）以上の権限が必要
です（プレビュー時点）。リンク自体はアクセス権を付与しません。
• 共有リンクの閲覧画面では、データビジュアルで作った出力も表示できます（公式ドキュメントでは、データセットの統計プ
ロファイルを表示する例が紹介されています）。［Refresh］で結果を更新できます。
25


# Page. 26

![Page Image](https://bcdn.docswell.com/page/LE1Y56QZ7G.jpg)

Dataflow Gen2 データビジュアル（プレビュー）ハンズオン ガイド
• My queries に保存されるのは M コードだけです（資格情報、データの宛先、クエリの属性は含まれません）。一覧は最
新 50 件まで表示され、削除はできません。
• Shared queries から取り込んだクエリは独立したコピーです。元のクエリの変更は反映されません。
おすすめの使い方
fnProfileDashboard と fnCheckVisualDocument を My Queries に保存しておくと、どのデータフローでも数秒で「プロファ
イル」と「点検」を追加できます。チームで使うなら、共有を有効にしたデータフローにまとめておくと便利です。
なお、ビジュアル自体は更新時に出力されませんが、同じように PartType と Data を持つナビゲーション テーブルで、更新のた
びにグラフ付きの Excel ブックを出力する機能（Excel の出力先の Advanced 形式、一般提供）もあります。定期的な配
布が目的なら、あわせて検討できます。
6. トラブルシューティング
公式ドキュメントに記載されたエラー メッセージと、本書の検証で確認した注意点をまとめます。迷ったら Step 9 の
fnCheckVisualDocument で点検してください。
表 16
よくある症状と対処
症状・メッセージ
主な原因
ビジュアルにならず、通常の表が表示される
5 列のいずれかがない、または列名が違う
Preview.Error: The navigation table must
contain exactly one root row.
対処
Parent = null の行が 0 行、または 2 行以上
Visual not recognized: &quot;&lt;値&gt;&quot;
PartType の誤り（例：LineChart）
Missing required visual property &quot;cells&quot;
子のない Container または Card
Unexpected number of cells. Expected: 1.
Actual: n
Unexpected result type. Expected: &quot;Text&quot;.
Actual: &quot;number&quot;
一部のビジュアルがエラーなしで表示されない
Card の子が 2 つ以上
KpiCard の Value などに数値を渡した
Parent が存在しない Name を指している（綴
り違い）
同じビジュアルが 2 か所に表示される
Name の重複
グラフが空、または &quot;undefined&quot; の項目が 1 本
AxisColumns／ValueColumns の列名が
だけ出る
Data にない（列名の変更後など）
プレビュー全体が表示されない
積み上げ横棒の積み上がり方がおかしい
列名（Name・Parent・PartType・
Properties・Data）の綴りを確認する
ルートを 1 行にし、ほかの行は既存の Name を
Parent に指定する
PartType は 6 種類。グラフは &quot;Chart&quot; ＋
ChartType で指定する
子を追加する。条件で行を生成する場合は、空
のときにコンテナーごと作らない
複数を入れたいときは、Card の中に Container
を 1 つ置く
Number.ToText で text にしてから渡す
Parent と Name の綴りを合わせる
Name を一意にする（番号を付けるなど）
Properties にレコード以外（数値など）を入れ
た
列名にバックスラッシュ（\）が含まれている
26
Data の列名と一致させる
Properties は必ず [...] のレコードにする
列名を変える


# Page. 27

![Page Image](https://bcdn.docswell.com/page/GEWG5WM6J2.jpg)

Dataflow Gen2 データビジュアル（プレビュー）ハンズオン ガイド
症状・メッセージ
表示や編集が重い
pRegion の値に一致するデータがありません
主な原因
対処
ビジュアルの数や Data の行数が多い、ソースの
集計してから渡す、Table.Buffer を使う、少数
再評価
から始めて広げる
パラメーター値の綴り違い（Step 6）
値の一覧から選ぶ
7. 制限事項と設計のコツ
7.1 制限事項（プレビュー時点）
• プレビュー機能であり、仕様は変更される可能性があります。
• フィルター、スライサー、日付ピッカー、ビジュアル間のクロスフィルターはありません。各ビジュアルは評価時点の Data のスナ
ップショットです。
• ビジュアルの数が多い場合や、Data のテーブルが大きい場合は、編集が遅くなることがあります。少数のビジュアルと少な
い行数から始め、動作を確認してから広げます。
• ビジュアルは DFG2 の編集キャンバスに描画されるもので、データフローの更新の出力、データの宛先、Dataflow Gen2 コ
ネクタには含まれません。
• Line／Area で日付の軸を使っても、連続した時系列としては扱われません。欠けている月は補完されず、点の間隔も経
過時間を表しません。
• StackedBar はワイド形式が必要です。系列の並び（アルファベット順）、数値軸のタイトルなし、下部の凡例など、描
画上の特徴があります。
• グラフは 6 種類だけです。Excel のグラフ スキーマ由来の配置プロパティ（Bounds など）は受け付けられますが、無視さ
れます。
7.2 設計のコツ
• 集計 → 整形 → 配置の順にコードを分ける（本書のコードの 1)〜5) の構成）。
• グラフには集計済みの小さな表を渡し、列名は単位付きの表示名にする。
• KPI は Value（大きな数字）・Label（何の数字か）・Sub（比較・補足）の 3 点セットで考える。
• 全体は Container（column）、各行は Container（row）、タイトルは Card、という入れ子でグリッドを作る。
• Name は英数字の一意な ID にし、命名規則（例：kpi-…、…-card、…-chart）を決めておくと保守しやすい。
• 地域やカテゴリのように増減する要素は、List.Transform／List.Split で行を生成してコードの修正を不要にする。
• 並び順が大切なラベルには番号を付ける。
• 大きなソースは先に集計して小さくし（可能ならソース側に折りたたむ）、必要に応じて Table.Buffer する。
• 仕上げに fnCheckVisualDocument で点検する。
27


# Page. 28

![Page Image](https://bcdn.docswell.com/page/47ZLN5QRJ3.jpg)

Dataflow Gen2 データビジュアル（プレビュー）ハンズオン ガイド
7.3 自分のデータに置き換えるには
1. SalesData クエリの中身を自分のデータ ソースに置き換えるか、Dashboard 内の SalesData を自分のクエリ名に置き
換えます。
2. 列名と型（Month、Region、Category、Revenue、Target、GrossProfit、Orders など）を合わせるか、集計のステ
ップとグラフの列の割り当てを書き換えます。
3. 積み上げ横棒の系列は CategoryNames で自動的に決まります（手書きのリストにしている場合は、ピボット後の列名
に合わせて更新します）。
4. まず fnProfileDashboard でデータを確認し、最後に fnCheckVisualDocument で点検します。
8. 参考リンク
• Create data visuals in Dataflow Gen2 (Preview)（Microsoft Learn）
https://learn.microsoft.com/en-us/fabric/data-factory/dataflow-gen2-data-visuals
• Fabric September 2026 Feature Summary（Fabric Updates Blog）
https://community.fabric.microsoft.com/t5/Fabric-Updates-Blog/Fabric-September-2026-Feature-Summary/ba-p/5325825
• My queries and shared queries in Dataflow Gen2（Microsoft Learn）
https://learn.microsoft.com/en-us/fabric/data-factory/dataflow-gen2-my-queries-shared-queries
• Create your first Dataflow Gen2（Microsoft Learn）
https://learn.microsoft.com/en-us/fabric/data-factory/create-first-dataflow-gen2
• Using parameters（Power Query）
https://learn.microsoft.com/en-us/power-query/power-query-query-parameters
• Use public parameters in Dataflow Gen2（Microsoft Learn）
https://learn.microsoft.com/en-us/fabric/data-factory/dataflow-parameters
• Excel Advanced data destination（Microsoft Learn）
https://learn.microsoft.com/en-us/fabric/data-factory/dataflow-gen2-data-destinations-excel-advanced
• Table.Profile（Power Query M リファレンス）
https://learn.microsoft.com/en-us/powerquery-m/table-profile
• Number.ToText（Power Query M リファレンス）
https://learn.microsoft.com/en-us/powerquery-m/number-totext
• Table.Buffer（Power Query M リファレンス）
https://learn.microsoft.com/en-us/powerquery-m/table-buffer
28


# Page. 29

![Page Image](https://bcdn.docswell.com/page/YJ6W8961JV.jpg)

Dataflow Gen2 データビジュアル（プレビュー）ハンズオン ガイド
付録 A
可視化ドキュメントの仕様早見表
表 17 PartType ごとの子と入力（公式ドキュメントの Schema summary を和訳）
PartType
子
必須の入力
任意の入力
Container
1 つ以上
なし
Card
ちょうど 1 つ
Title：text
なし
Header
なし（リーフ）
Header：text
FarText：text または null
KpiCard
なし（リーフ）
Value：text、Label：text
Sub：text または null
Table
なし（リーフ）
Data 列：テーブル
なし
ChartType（Line／Area／Bar／StackedBar／
ChartTitle：text、
Chart
なし（リーフ）
Doughnut／Pie）、DataSeries（AxisColumns と
DataSeries.PrimaryAxisColumn：
ValueColumns のレコード）、Data 列：テーブル
text（効果なし）
Direction：&quot;row&quot; または &quot;column&quot;
（既定は &quot;row&quot;）
表 18
ドキュメント全体のルール
ルール
違反したとき
Parent = null の行（ルート）はちょうど 1 行。ルートの PartType は何でもよ
い
クエリ全体が Preview.Error で失敗する
Name は一意にする
エラーにならず、ビジュアルが重複して表示されることがある
Parent は null か、既存の Name と完全一致
その行と子孫がエラーなしで表示されない
その行の位置に「Visual not recognized」が表示される（ほ
PartType は 6 種類のいずれか
かは表示される）
「Missing required visual property &quot;cells&quot;」などが表示され
Container は子 1 つ以上、Card は子ちょうど 1 つ、ほかはリーフ
る
レコード以外はプレビュー全体が失敗。型違いはその行だけエラ
Properties はレコード。KpiCard の Value などは text
付録 B
ー表示
付属ファイル一覧
すべて DFG2_DataVisuals_Guide\M_code フォルダーにあります（UTF-8 のテキスト ファイル）。メモ帳などで開き、すべて
選択してコピーし、DFG2 に貼り付けます。
表 19
M_code フォルダーのファイル
ファイル
クエリ名
用途
01_SalesData.pq
SalesData
Step 1：サンプルデータ
29


# Page. 30

![Page Image](https://bcdn.docswell.com/page/GJ5M9N6LJ4.jpg)

Dataflow Gen2 データビジュアル（プレビュー）ハンズオン ガイド
ファイル
クエリ名
用途
02_Visual_Hello.pq
Visual_Hello
Step 2：最小のビジュアル
03_Dashboard_step3_skeleton.pq
Dashboard
Step 3：ヘッダーと KPI
04_Dashboard_step4_charts.pq
Dashboard
Step 4：グラフを追加
05_Dashboard_step5_complete.pq
Dashboard
Step 5：完成版（付録 C-1）
06a_pRegion_parameter.pq
pRegion
Step 6：パラメーター（UI で作る場合は不要）
06b_Dashboard_step6_parameter.pq
Dashboard
Step 6：パラメーター対応版
07_Dashboard_Regions.pq
Dashboard_Regions
Step 7：地域別スコアカード（付録 C-2）
08a_fnProfileDashboard.pq
fnProfileDashboard
Step 8：プロファイル関数（付録 C-3）
08b_Profile_SalesData.pq
Profile_SalesData
Step 8：関数の呼び出し
08c_NorthwindOrders.pq
NorthwindOrders
Step 8（任意）：公開 OData の取得
08d_Profile_Northwind.pq
Profile_Northwind
Step 8（任意）：関数の呼び出し
09a_fnCheckVisualDocument.pq
fnCheckVisualDocument
Step 9：点検関数（付録 C-4）
09b_Check_All.pq
Check_All
Step 9：まとめて点検
付録 C
完成版コード
本文で抜粋したクエリの全文です。付属ファイルと同じ内容です。
C-1
Dashboard（Step 5 完成版）
クエリ：Dashboard ｜
ファイル：M_code\05_Dashboard_step5_complete.pq
// Dashboard（Step 5 完成版）：ヘッダー＋KPI＋6 種類のグラフ＋明細テーブル
let
// ---- 1) 対象データ：最新年度（当年度）と前年度 ---CurFY = List.Max(SalesData[FiscalYear]),
PrevFY = CurFY - 1,
Cur
= Table.SelectRows(SalesData, each [FiscalYear] = CurFY),
Prev
= Table.SelectRows(SalesData, each [FiscalYear] = PrevFY),
// ---- 2) KPI の計算（数値）---Revenue
= List.Sum(Cur[Revenue]),
RevenuePrev
= List.Sum(Prev[Revenue]),
Target
= List.Sum(Cur[Target]),
GrossMargin
= List.Sum(Cur[GrossProfit]) / Revenue,
GrossMarginPrev = List.Sum(Prev[GrossProfit]) / RevenuePrev,
Orders
= List.Sum(Cur[Orders]),
// ---- 3) KPI の表示テキスト（KpiCard の値は text で渡す）---Fmt
= (n as number, format as text) as text =&gt; Number.ToText(n, format, &quot;ja-JP&quot;),
Signed = (n as number, format as text) as text =&gt;
(if n &gt; 0 then &quot;+&quot; else &quot;&quot;) &amp; Fmt(n, format),
RevenueText
= Fmt(Revenue / 100000000, &quot;N2&quot;) &amp; &quot;億円&quot;,
RevenueSub
= &quot;前年比 &quot; &amp; Signed(Revenue / RevenuePrev - 1, &quot;P1&quot;),
AchievementText = Fmt(Revenue / Target, &quot;P1&quot;),
AchievementSub = &quot;目標 &quot; &amp; Fmt(Target / 100000000, &quot;N2&quot;) &amp; &quot;億円&quot;,
MarginText
= Fmt(GrossMargin, &quot;P1&quot;),
30


# Page. 31

![Page Image](https://bcdn.docswell.com/page/LE3WY2QDE5.jpg)

Dataflow Gen2 データビジュアル（プレビュー）ハンズオン ガイド
MarginSub
OrdersText
OrdersSub
= &quot;前年差 &quot; &amp; Signed((GrossMargin - GrossMarginPrev) * 100, &quot;N1&quot;) &amp; &quot;pt&quot;,
= Fmt(Orders, &quot;N0&quot;) &amp; &quot;件&quot;,
= &quot;平均単価 &quot; &amp; Fmt(Revenue / Orders, &quot;N0&quot;) &amp; &quot;円&quot;,
// ---- 4) グラフ・表に渡す集計テーブル ---// 列名がそのまま軸タイトルや表の見出しになるので、日本語の表示名に変える
ToJapanese = (t as table) as table =&gt;
Table.RenameColumns(t,
{{&quot;Month&quot;, &quot;月&quot;}, {&quot;Region&quot;, &quot;地域&quot;}, {&quot;Category&quot;, &quot;カテゴリ&quot;}}, MissingField.Ignore),
ToMillions = (n as number) as number =&gt; Number.Round(n / 1000000, 1),
RevenueByMonth = ToJapanese(Table.Sort(
Table.Group(Cur, {&quot;Month&quot;},
{{&quot;売上(百万円)&quot;, each ToMillions(List.Sum([Revenue])), type number}}),
{{&quot;Month&quot;, Order.Ascending}})),
OrdersByMonth = ToJapanese(Table.Sort(
Table.Group(Cur, {&quot;Month&quot;},
{{&quot;受注件数&quot;, each List.Sum([Orders]), type number}}),
{{&quot;Month&quot;, Order.Ascending}})),
RevenueByRegion = ToJapanese(Table.Sort(
Table.Group(Cur, {&quot;Region&quot;},
{{&quot;売上(百万円)&quot;, each ToMillions(List.Sum([Revenue])), type number}}),
{{&quot;売上(百万円)&quot;, Order.Descending}})),
// 積み上げ横棒はワイド形式（カテゴリごとに 1 列）が必要 → Table.Pivot で列に展開
CategoryNames = List.Sort(List.Distinct(Cur[Category])),
RevenueByRegionCategory = ToJapanese(Table.Pivot(
Table.Group(Cur, {&quot;Region&quot;, &quot;Category&quot;},
{{&quot;Revenue&quot;, each ToMillions(List.Sum([Revenue])), type number}}),
CategoryNames, &quot;Category&quot;, &quot;Revenue&quot;, List.Sum)),
RevenueByCategory = ToJapanese(Table.Sort(
Table.Group(Cur, {&quot;Category&quot;},
{{&quot;売上(百万円)&quot;, each ToMillions(List.Sum([Revenue])), type number}}),
{{&quot;売上(百万円)&quot;, Order.Descending}})),
OrdersByCategory = ToJapanese(Table.Sort(
Table.Group(Cur, {&quot;Category&quot;},
{{&quot;受注件数&quot;, each List.Sum([Orders]), type number}}),
{{&quot;受注件数&quot;, Order.Descending}})),
// ★Step 5 で追加：明細テーブル（地域 × カテゴリ、売上の大きい順）
DetailTable = ToJapanese(Table.Sort(
Table.Group(Cur, {&quot;Region&quot;, &quot;Category&quot;}, {
{&quot;売上(百万円)&quot;, each ToMillions(List.Sum([Revenue])), type number},
{&quot;目標達成率(%)&quot;,
each Number.Round(List.Sum([Revenue]) / List.Sum([Target]) * 100, 1), type number},
{&quot;粗利率(%)&quot;,
each Number.Round(List.Sum([GrossProfit]) / List.Sum([Revenue]) * 100, 1), type number},
{&quot;受注件数&quot;, each List.Sum([Orders]), type number}}),
{{&quot;売上(百万円)&quot;, Order.Descending}})),
// ---- 5) 可視化ドキュメント（1 行 = 1 ビジュアル。Parent で親子関係を表す）---VisualDocumentType = type table [
Name = nullable text, Parent = nullable text, PartType = nullable text,
Properties = nullable record, Data = any
],
VisualDocument = #table(
VisualDocumentType,
{
// ルート（Parent = null はこの 1 行だけ）。column = 子を縦に並べる
{&quot;dashboard&quot;, null, &quot;Container&quot;, [Direction = &quot;column&quot;], null},
{&quot;header&quot;, &quot;dashboard&quot;, &quot;Header&quot;,
[Header = &quot;Contoso Coffee 販売ダッシュボード&quot;,
FarText = Text.From(CurFY) &amp; &quot;年度（前年度比較）&quot;], null},
// KPI 行。row = 子を横に並べる
{&quot;kpi-row&quot;, &quot;dashboard&quot;, &quot;Container&quot;, [Direction = &quot;row&quot;], null},
{&quot;kpi-revenue&quot;, &quot;kpi-row&quot;, &quot;KpiCard&quot;,
[Label = &quot;売上高&quot;, Value = RevenueText, Sub = RevenueSub], null},
{&quot;kpi-achievement&quot;, &quot;kpi-row&quot;, &quot;KpiCard&quot;,
[Label = &quot;目標達成率&quot;, Value = AchievementText, Sub = AchievementSub], null},
{&quot;kpi-margin&quot;, &quot;kpi-row&quot;, &quot;KpiCard&quot;,
[Label = &quot;粗利率&quot;, Value = MarginText, Sub = MarginSub], null},
{&quot;kpi-orders&quot;, &quot;kpi-row&quot;, &quot;KpiCard&quot;,
[Label = &quot;受注件数&quot;, Value = OrdersText, Sub = OrdersSub], null},
// 推移（折れ線・面）
{&quot;trend-row&quot;, &quot;dashboard&quot;, &quot;Container&quot;, [Direction = &quot;row&quot;], null},
{&quot;revenue-trend-card&quot;, &quot;trend-row&quot;, &quot;Card&quot;, [Title = &quot;月別売上の推移（百万円）&quot;], null},
{&quot;revenue-trend-chart&quot;, &quot;revenue-trend-card&quot;, &quot;Chart&quot;,
31


# Page. 32

![Page Image](https://bcdn.docswell.com/page/8EDK5Z927G.jpg)

Dataflow Gen2 データビジュアル（プレビュー）ハンズオン ガイド
[ChartType = &quot;Line&quot;,
DataSeries = [AxisColumns = &quot;月&quot;, ValueColumns = &quot;売上(百万円)&quot;]],
RevenueByMonth},
{&quot;orders-trend-card&quot;, &quot;trend-row&quot;, &quot;Card&quot;, [Title = &quot;月別受注件数&quot;], null},
{&quot;orders-trend-chart&quot;, &quot;orders-trend-card&quot;, &quot;Chart&quot;,
[ChartType = &quot;Area&quot;,
DataSeries = [AxisColumns = &quot;月&quot;, ValueColumns = &quot;受注件数&quot;]],
OrdersByMonth},
// 地域の比較（横棒・積み上げ横棒）
{&quot;region-row&quot;, &quot;dashboard&quot;, &quot;Container&quot;, [Direction = &quot;row&quot;], null},
{&quot;region-card&quot;, &quot;region-row&quot;, &quot;Card&quot;, [Title = &quot;地域別売上（百万円）&quot;], null},
{&quot;region-chart&quot;, &quot;region-card&quot;, &quot;Chart&quot;,
[ChartType = &quot;Bar&quot;,
DataSeries = [AxisColumns = &quot;地域&quot;, ValueColumns = &quot;売上(百万円)&quot;]],
RevenueByRegion},
{&quot;region-mix-card&quot;, &quot;region-row&quot;, &quot;Card&quot;, [Title = &quot;地域 × カテゴリ別売上（百万円）&quot;], null},
{&quot;region-mix-chart&quot;, &quot;region-mix-card&quot;, &quot;Chart&quot;,
[ChartType = &quot;StackedBar&quot;,
DataSeries = [AxisColumns = &quot;地域&quot;, ValueColumns = CategoryNames]],
RevenueByRegionCategory},
// 構成比（ドーナツ・円）
{&quot;mix-row&quot;, &quot;dashboard&quot;, &quot;Container&quot;, [Direction = &quot;row&quot;], null},
{&quot;category-card&quot;, &quot;mix-row&quot;, &quot;Card&quot;, [Title = &quot;カテゴリ別の売上構成&quot;], null},
{&quot;category-chart&quot;, &quot;category-card&quot;, &quot;Chart&quot;,
[ChartType = &quot;Doughnut&quot;,
DataSeries = [AxisColumns = &quot;カテゴリ&quot;, ValueColumns = &quot;売上(百万円)&quot;]],
RevenueByCategory},
{&quot;orders-mix-card&quot;, &quot;mix-row&quot;, &quot;Card&quot;, [Title = &quot;カテゴリ別の受注件数構成&quot;], null},
{&quot;orders-mix-chart&quot;, &quot;orders-mix-card&quot;, &quot;Chart&quot;,
[ChartType = &quot;Pie&quot;,
DataSeries = [AxisColumns = &quot;カテゴリ&quot;, ValueColumns = &quot;受注件数&quot;]],
OrdersByCategory},
// ★Step 5 で追加：明細（Card の子に Table を 1 つ）
{&quot;detail-card&quot;, &quot;dashboard&quot;, &quot;Card&quot;,
[Title = &quot;地域 × カテゴリ別の明細（&quot; &amp; Text.From(CurFY) &amp; &quot;年度）&quot;], null},
{&quot;detail-table&quot;, &quot;detail-card&quot;, &quot;Table&quot;, [], DetailTable}
}
)
in
VisualDocument
C-2
Dashboard_Regions（Step 7）
クエリ：Dashboard_Regions ｜ ファイル：M_code\07_Dashboard_Regions.pq
// Dashboard_Regions（Step 7）：地域の数だけ KPI カードとグラフを「データから自動生成」する
let
CurFY
= List.Max(SalesData[FiscalYear]),
PrevFY
= CurFY - 1,
Fmt
= (n as number, format as text) as text =&gt; Number.ToText(n, format, &quot;ja-JP&quot;),
Signed
= (n as number, format as text) as text =&gt;
(if n &gt; 0 then &quot;+&quot; else &quot;&quot;) &amp; Fmt(n, format),
ToMillions = (n as number) as number =&gt; Number.Round(n / 1000000, 1),
// ---- 1) 地域ごとの指標をレコード 1 件ずつ計算（地域が増減しても自動で追従）---Regions = List.Distinct(SalesData[Region]),
RegionStats = List.Transform(List.Positions(Regions), (i) =&gt;
let
region
= Regions{i},
rows
= Table.SelectRows(SalesData, each [Region] = region),
cur
= Table.SelectRows(rows, each [FiscalYear] = CurFY),
prev
= Table.SelectRows(rows, each [FiscalYear] = PrevFY),
revenue
= List.Sum(cur[Revenue]),
achievement = revenue / List.Sum(cur[Target]),
yoy
= revenue / List.Sum(prev[Revenue]) - 1,
// 条件によって表示テキストを変える（条件付き書式の代わり）
status
= if achievement &gt;= 1 then &quot;◎ 目標達成&quot;
else if achievement &gt;= 0.95 then &quot;△ あと少し（95%以上）&quot;
else &quot;× 要対策（95%未満）&quot;,
monthly
= Table.RenameColumns(
32


# Page. 33

![Page Image](https://bcdn.docswell.com/page/V7PKGDZLJ8.jpg)

Dataflow Gen2 データビジュアル（プレビュー）ハンズオン ガイド
Table.Sort(
Table.Group(cur, {&quot;Month&quot;},
{{&quot;売上(百万円)&quot;, each ToMillions(List.Sum([Revenue])),
type number}}),
{{&quot;Month&quot;, Order.Ascending}}),
{{&quot;Month&quot;, &quot;月&quot;}})
in
[No = i + 1, Region = region, Revenue = revenue, Achievement = achievement,
YoY = yoy, Status = status, Monthly = monthly]
),
// ---- 2) 3 件ずつ横に並べる：行コンテナーと、その子の行をリストで生成 ---Groups = List.Split(RegionStats, 3),
KpiRows = List.Combine(List.Transform(List.Positions(Groups), (g) =&gt;
{ {&quot;kpi-row-&quot; &amp; Text.From(g + 1), &quot;scorecard&quot;, &quot;Container&quot;, [Direction = &quot;row&quot;], null} }
&amp; List.Transform(Groups{g}, (r) =&gt;
{&quot;kpi-&quot; &amp; Text.From(r[No]), &quot;kpi-row-&quot; &amp; Text.From(g + 1), &quot;KpiCard&quot;,
[Label = r[Region] &amp; &quot;：目標達成率&quot;,
Value = Fmt(r[Achievement], &quot;P1&quot;),
Sub
= r[Status]], null})
)),
TrendRows = List.Combine(List.Transform(List.Positions(Groups), (g) =&gt;
{ {&quot;trend-row-&quot; &amp; Text.From(g + 1), &quot;trends&quot;, &quot;Container&quot;, [Direction = &quot;row&quot;], null} }
&amp; List.Combine(List.Transform(Groups{g}, (r) =&gt;
{
{&quot;trend-card-&quot; &amp; Text.From(r[No]), &quot;trend-row-&quot; &amp; Text.From(g + 1), &quot;Card&quot;,
[Title = r[Region] &amp; &quot;（前年比 &quot; &amp; Signed(r[YoY], &quot;P1&quot;) &amp; &quot;）&quot;], null},
{&quot;trend-chart-&quot; &amp; Text.From(r[No]), &quot;trend-card-&quot; &amp; Text.From(r[No]), &quot;Chart&quot;,
[ChartType = &quot;Line&quot;,
DataSeries = [AxisColumns = &quot;月&quot;, ValueColumns = &quot;売上(百万円)&quot;]],
r[Monthly]}
}
))
)),
// ---- 3) 下段：前年比の横棒と、達成率の低い順の一覧 ---GrowthTable = #table(
type table [地域 = text, #&quot;前年比(%)&quot; = number],
List.Transform(RegionStats, (r) =&gt; {r[Region], Number.Round(r[YoY] * 100, 1)})
),
StatusTable = Table.Sort(
#table(
type table [
地域 = text, #&quot;売上(百万円)&quot; = number, #&quot;目標達成率(%)&quot; = number,
#&quot;前年比(%)&quot; = number, 判定 = text
],
List.Transform(RegionStats, (r) =&gt;
{r[Region], ToMillions(r[Revenue]), Number.Round(r[Achievement] * 100, 1),
Number.Round(r[YoY] * 100, 1), r[Status]})
),
{{&quot;目標達成率(%)&quot;, Order.Ascending}}
),
// ---- 4) 可視化ドキュメント：固定の行 ＋ 生成した行を &amp; で連結 ---VisualDocumentType = type table [
Name = nullable text, Parent = nullable text, PartType = nullable text,
Properties = nullable record, Data = any
],
VisualDocument = #table(
VisualDocumentType,
{
{&quot;root&quot;, null, &quot;Container&quot;, [Direction = &quot;column&quot;], null},
{&quot;header&quot;, &quot;root&quot;, &quot;Header&quot;,
[Header = &quot;地域別スコアカード&quot;,
FarText = Text.From(CurFY) &amp; &quot;年度｜&quot; &amp; Text.From(List.Count(Regions)) &amp; &quot; 地域&quot;],
null},
{&quot;scorecard&quot;, &quot;root&quot;, &quot;Container&quot;, [Direction = &quot;column&quot;], null}
}
&amp; KpiRows
&amp; { {&quot;trends&quot;, &quot;root&quot;, &quot;Container&quot;, [Direction = &quot;column&quot;], null} }
&amp; TrendRows
&amp; {
{&quot;bottom-row&quot;, &quot;root&quot;, &quot;Container&quot;, [Direction = &quot;row&quot;], null},
{&quot;growth-card&quot;, &quot;bottom-row&quot;, &quot;Card&quot;, [Title = &quot;地域別の前年比（%）&quot;], null},
{&quot;growth-chart&quot;, &quot;growth-card&quot;, &quot;Chart&quot;,
[ChartType = &quot;Bar&quot;,
DataSeries = [AxisColumns = &quot;地域&quot;, ValueColumns = &quot;前年比(%)&quot;]],
33


# Page. 34

![Page Image](https://bcdn.docswell.com/page/2JVV61W6JQ.jpg)

Dataflow Gen2 データビジュアル（プレビュー）ハンズオン ガイド
GrowthTable},
{&quot;status-card&quot;, &quot;bottom-row&quot;, &quot;Card&quot;, [Title = &quot;目標達成率の低い順&quot;], null},
{&quot;status-table&quot;, &quot;status-card&quot;, &quot;Table&quot;, [], StatusTable}
}
)
in
VisualDocument
C-3
fnProfileDashboard（Step 8）
クエリ：fnProfileDashboard ｜ ファイル：M_code\08a_fnProfileDashboard.pq
// fnProfileDashboard（Step 8）：テーブルを渡すと「データプロファイル」ダッシュボードを返す関数
// 使い方： fnProfileDashboard(SalesData, &quot;SalesData のデータプロファイル&quot;)
(Source as table, optional Title as nullable text) as table =&gt;
let
Title_ = if Title = null then &quot;データプロファイル&quot; else Title,
Fmt
= (n as number, format as text) as text =&gt; Number.ToText(n, format, &quot;ja-JP&quot;),
// ---- 0) 表示できない型（テーブル・レコード・リストなど）の列は対象外にする ---ExcludedKinds = {&quot;table&quot;, &quot;record&quot;, &quot;list&quot;, &quot;function&quot;, &quot;binary&quot;, &quot;type&quot;, &quot;action&quot;},
ScalarColumns = Table.SelectRows(Table.Schema(Source),
each not List.Contains(ExcludedKinds, [Kind]))[Name],
ExcludedCount = Table.ColumnCount(Source) - List.Count(ScalarColumns),
// Table.Buffer：一度だけ読み込んでメモリに保持し、ソースの再評価を防ぐ
Src
= Table.Buffer(Table.SelectColumns(Source, ScalarColumns)),
Schema
= Table.Buffer(Table.SelectColumns(Table.Schema(Src), {&quot;Name&quot;, &quot;Kind&quot;})),
KindOf
= (col as text) as text =&gt; Table.SelectRows(Schema, each [Name] = col){0}[Kind],
RowCount
= Table.RowCount(Src),
ColumnCount = List.Count(ScalarColumns),
// ---- 1) 列ごとの基本統計（件数・NULL・一意な値・最小/最大・平均・標準偏差）---Profile
= Table.Buffer(Table.Profile(Src)),
NullCells
= if Table.IsEmpty(Profile) then 0 else List.Sum(Profile[NullCount]),
TotalCells
= RowCount * ColumnCount,
NullRate
= if TotalCells = 0 then null else NullCells / TotalCells,
DuplicateRows = RowCount - Table.RowCount(Table.Distinct(Src)),
// ---- 2) 数値列：四分位（Excel の PERCENTILE.INC と同じ線形補間）と IQR 法の外れ値 ---IsNumeric = (col as text) as logical =&gt;
let
kind
= KindOf(col),
nonNull = List.RemoveNulls(Table.Column(Src, col))
in
List.Count(nonNull) &gt; 0
and (kind = &quot;number&quot;
or (kind = &quot;any&quot;
and List.AllTrue(List.Transform(nonNull, each Value.Is(_, type number))))),
NumericColumns = List.Buffer(List.Select(ScalarColumns, IsNumeric)),
Quantile = (sorted as list, p as number) as number =&gt;
let
h = (List.Count(sorted) - 1) * p,
lo = Number.RoundDown(h),
hi = Number.RoundUp(h)
in
sorted{lo} + (h - lo) * (sorted{hi} - sorted{lo}),
NumericStats = List.Buffer(List.Transform(NumericColumns, (col) =&gt;
let
sorted = List.Buffer(List.Sort(List.RemoveNulls(Table.Column(Src, col)))),
q1
= Quantile(sorted, 0.25),
median = Quantile(sorted, 0.5),
q3
= Quantile(sorted, 0.75),
iqr
= q3 - q1,
lower = q1 - 1.5 * iqr,
upper = q3 + 1.5 * iqr
in
[Column = col, Q1 = q1, Median = median, Q3 = q3, IQR = iqr,
Outliers = List.Count(List.Select(sorted, each _ &lt; lower or _ &gt; upper))]
)),
NoStats = [Q1 = null, Median = null, Q3 = null, IQR = null, Outliers = null],
StatsFor = (col as text) as record =&gt;
let found = List.Select(NumericStats, each [Column] = col)
in if List.IsEmpty(found) then NoStats else found{0},
34


# Page. 35

![Page Image](https://bcdn.docswell.com/page/5EGLN2D2JL.jpg)

Dataflow Gen2 データビジュアル（プレビュー）ハンズオン ガイド
// ---- 3) 表示用のテーブル（列名がそのまま見出しになる）---Round2 = (v as any) as any =&gt;
if v &lt;&gt; null and Value.Is(v, type number) then Number.Round(v, 2) else null,
ToDisplay = (v as any) as nullable text =&gt;
if v = null then null
else if Value.Is(v, type number) then Fmt(v, if Number.Round(v) = v then &quot;N0&quot; else &quot;N2&quot;)
else Text.From(v, &quot;ja-JP&quot;),
ProfileTable = Table.FromRecords(
List.Transform(Table.ToRecords(Profile), (r) =&gt;
let s = StatsFor(r[Column]) in
[列 = r[Column], 型 = KindOf(r[Column]), 件数 = r[Count], NULL 件数 = r[NullCount],
一意な値の数 = r[DistinctCount],
最小値 = ToDisplay(r[Min]), 最大値 = ToDisplay(r[Max]),
平均 = Round2(r[Average]), 標準偏差 = Round2(r[StandardDeviation]),
Q1 = Round2(s[Q1]), 中央値 = Round2(s[Median]), Q3 = Round2(s[Q3]),
IQR = Round2(s[IQR]), 外れ値の数 = s[Outliers]]),
type table [
列 = text, 型 = text, 件数 = number, NULL 件数 = number, 一意な値の数 = number,
最小値 = nullable text, 最大値 = nullable text,
平均 = nullable number, 標準偏差 = nullable number,
Q1 = nullable number, 中央値 = nullable number, Q3 = nullable number,
IQR = nullable number, 外れ値の数 = nullable number
]
),
NullChart = Table.FromRecords(
List.Transform(Table.ToRecords(Profile), (r) =&gt; [列 = r[Column], NULL 件数 = r[NullCount]]),
type table [列 = text, NULL 件数 = number]),
DistinctChart = Table.FromRecords(
List.Transform(Table.ToRecords(Profile),
(r) =&gt; [列 = r[Column], 一意な値の数 = r[DistinctCount]]),
type table [列 = text, 一意な値の数 = number]),
// ---- 4) 数値列のヒストグラム（等幅 8 区間）。区間名に番号を付けて並び順を固定 ---Circled = {&quot;①&quot;, &quot;②&quot;, &quot;③&quot;, &quot;④&quot;, &quot;⑤&quot;, &quot;⑥&quot;, &quot;⑦&quot;, &quot;⑧&quot;},
Compact = (x as number) as text =&gt;
let a = Number.Abs(x) in
if a &gt;= 100000000 then Fmt(x / 100000000, &quot;N1&quot;) &amp; &quot;億&quot;
else if a &gt;= 10000 then Fmt(x / 10000, &quot;N0&quot;) &amp; &quot;万&quot;
else if a &gt;= 100 or Number.Round(x) = x then Fmt(x, &quot;N0&quot;)
else Fmt(x, &quot;N2&quot;),
Histogram = (col as text) as table =&gt;
let
values
= List.Buffer(List.RemoveNulls(Table.Column(Src, col))),
distinct = List.Buffer(List.Sort(List.Distinct(values))),
bins
= 8,
// 整数で種類が 8 以下の列（年度・コードなど）は、区間ではなく値ごとに数える
discrete = List.Count(distinct) &lt;= bins
and List.AllTrue(List.Transform(distinct, each Number.Round(_) = _)),
mn
= List.Min(values),
mx
= List.Max(values),
width
= (mx - mn) / bins,
binOf
= (v as number) as number =&gt;
List.Min({bins - 1, Number.RoundDown((v - mn) / width)}),
idx
= List.Buffer(List.Transform(values, binOf)),
rows =
if discrete then
List.Transform(List.Positions(distinct), (i) =&gt;
{Circled{i} &amp; &quot; &quot; &amp; Text.From(distinct{i}),
List.Count(List.Select(values, each _ = distinct{i}))})
else if width = 0 then
{{Circled{0} &amp; &quot; &quot; &amp; Compact(mn), List.Count(values)}}
else
List.Transform({0 .. bins - 1}, (b) =&gt;
{Circled{b} &amp; &quot; &quot; &amp; Compact(mn + b * width) &amp; &quot;～&quot;
&amp; Compact(mn + (b + 1) * width),
List.Count(List.Select(idx, each _ = b))})
in
#table(type table [区間 = text, 件数 = number], rows),
DistColumns = List.FirstN(NumericColumns, 6),
// 分布グラフは最大 6 列まで
DistGroups = List.Split(DistColumns, 3),
// 3 枚ずつ横に並べる
DistRows =
if List.IsEmpty(DistColumns) then {}
// 空の Container を作らない
else
{ {&quot;dist&quot;, &quot;root&quot;, &quot;Container&quot;, [Direction = &quot;column&quot;], null} }
&amp; List.Combine(List.Transform(List.Positions(DistGroups), (g) =&gt;
{ {&quot;dist-row-&quot; &amp; Text.From(g + 1), &quot;dist&quot;, &quot;Container&quot;, [Direction = &quot;row&quot;], null} }
&amp; List.Combine(List.Transform(List.Positions(DistGroups{g}), (k) =&gt;
let
col = DistGroups{g}{k},
id = Text.From(g * 3 + k + 1)
in
{
35


# Page. 36

![Page Image](https://bcdn.docswell.com/page/4JQYQP197P.jpg)

Dataflow Gen2 データビジュアル（プレビュー）ハンズオン ガイド
{&quot;dist-card-&quot; &amp; id, &quot;dist-row-&quot; &amp; Text.From(g + 1), &quot;Card&quot;,
[Title = col &amp; &quot; の分布&quot;], null},
{&quot;dist-chart-&quot; &amp; id, &quot;dist-card-&quot; &amp; id, &quot;Chart&quot;,
[ChartType = &quot;Bar&quot;,
DataSeries = [AxisColumns = &quot;区間&quot;, ValueColumns = &quot;件数&quot;]],
Histogram(col)}
}
))
)),
// ---- 5) 可視化ドキュメント ---FarText = Fmt(RowCount, &quot;N0&quot;) &amp; &quot; 行 × &quot; &amp; Text.From(ColumnCount) &amp; &quot; 列&quot;
&amp; (if ExcludedCount &gt; 0 then &quot;（対象外 &quot; &amp; Text.From(ExcludedCount) &amp; &quot; 列）&quot; else &quot;&quot;),
VisualDocumentType = type table [
Name = nullable text, Parent = nullable text, PartType = nullable text,
Properties = nullable record, Data = any
],
VisualDocument = #table(
VisualDocumentType,
{
{&quot;root&quot;, null, &quot;Container&quot;, [Direction = &quot;column&quot;], null},
{&quot;header&quot;, &quot;root&quot;, &quot;Header&quot;, [Header = Title_, FarText = FarText], null},
{&quot;kpi-row&quot;, &quot;root&quot;, &quot;Container&quot;, [Direction = &quot;row&quot;], null},
{&quot;kpi-rows&quot;, &quot;kpi-row&quot;, &quot;KpiCard&quot;,
[Label = &quot;行数&quot;, Value = Fmt(RowCount, &quot;N0&quot;), Sub = null], null},
{&quot;kpi-cols&quot;, &quot;kpi-row&quot;, &quot;KpiCard&quot;,
[Label = &quot;列数&quot;, Value = Fmt(ColumnCount, &quot;N0&quot;),
Sub = &quot;うち数値列 &quot; &amp; Text.From(List.Count(NumericColumns))], null},
{&quot;kpi-null&quot;, &quot;kpi-row&quot;, &quot;KpiCard&quot;,
[Label = &quot;NULL の割合&quot;, Value = if NullRate = null then &quot;―&quot; else Fmt(NullRate, &quot;P1&quot;),
Sub = Fmt(NullCells, &quot;N0&quot;) &amp; &quot; セルが NULL&quot;], null},
{&quot;kpi-dup&quot;, &quot;kpi-row&quot;, &quot;KpiCard&quot;,
[Label = &quot;重複行&quot;, Value = Fmt(DuplicateRows, &quot;N0&quot;), Sub = &quot;すべての列が同じ行&quot;], null},
{&quot;quality-row&quot;, &quot;root&quot;, &quot;Container&quot;, [Direction = &quot;row&quot;], null},
{&quot;null-card&quot;, &quot;quality-row&quot;, &quot;Card&quot;, [Title = &quot;列ごとの NULL 件数&quot;], null},
{&quot;null-chart&quot;, &quot;null-card&quot;, &quot;Chart&quot;,
[ChartType = &quot;Bar&quot;, DataSeries = [AxisColumns = &quot;列&quot;, ValueColumns = &quot;NULL 件数&quot;]],
NullChart},
{&quot;distinct-card&quot;, &quot;quality-row&quot;, &quot;Card&quot;, [Title = &quot;列ごとの一意な値の数&quot;], null},
{&quot;distinct-chart&quot;, &quot;distinct-card&quot;, &quot;Chart&quot;,
[ChartType = &quot;Bar&quot;, DataSeries = [AxisColumns = &quot;列&quot;, ValueColumns = &quot;一意な値の数&quot;]],
DistinctChart}
}
&amp; DistRows
&amp; {
{&quot;profile-card&quot;, &quot;root&quot;, &quot;Card&quot;,
[Title = &quot;列プロファイル（件数・NULL・最小/最大・四分位・外れ値）&quot;], null},
{&quot;profile-table&quot;, &quot;profile-card&quot;, &quot;Table&quot;, [], ProfileTable}
}
)
in
VisualDocument
C-4
fnCheckVisualDocument（Step 9）
クエリ：fnCheckVisualDocument ｜
ファイル：M_code\09a_fnCheckVisualDocument.pq
// fnCheckVisualDocument（Step 9）：可視化ドキュメントが公式ドキュメントのルールを満たすかを点検
// 使い方： fnCheckVisualDocument(Dashboard)
//
→ 問題の一覧を通常の表で返します（問題がなければ「OK」の 1 行）
(Doc as table) as table =&gt;
let
Required
= {&quot;Name&quot;, &quot;Parent&quot;, &quot;PartType&quot;, &quot;Properties&quot;, &quot;Data&quot;},
PartTypes = {&quot;Container&quot;, &quot;Card&quot;, &quot;Header&quot;, &quot;KpiCard&quot;, &quot;Table&quot;, &quot;Chart&quot;},
LeafTypes = {&quot;Header&quot;, &quot;KpiCard&quot;, &quot;Table&quot;, &quot;Chart&quot;},
ChartTypes = {&quot;Line&quot;, &quot;Area&quot;, &quot;Bar&quot;, &quot;StackedBar&quot;, &quot;Doughnut&quot;, &quot;Pie&quot;},
ResultType = type table [重要度 = text, Name = nullable text, ルール = text, 内容 = text],
Issue = (severity as text, name as nullable text, rule as text, message as text) as list =&gt;
{severity, name, rule, message},
MissingColumns = List.Difference(Required, Table.ColumnNames(Doc)),
36


# Page. 37

![Page Image](https://bcdn.docswell.com/page/K74WNY6RE1.jpg)

Dataflow Gen2 データビジュアル（プレビュー）ハンズオン ガイド
// List.Buffer：一度だけ評価してメモリに保持する
Rows = List.Buffer(
if List.IsEmpty(MissingColumns)
then Table.ToRecords(Table.SelectColumns(Doc, Required)) else {}),
Names = List.Buffer(List.Transform(Rows, each [Name])),
IsText
= (v as any) as logical =&gt; Value.Is(v, type text),
ChildCount = (name as any) as number =&gt;
List.Count(List.Select(Rows, each [Parent] &lt;&gt; null and [Parent] = name)),
AsList
= (v as any) as any =&gt;
if Value.Is(v, type text) then {v} else if Value.Is(v, type list) then v else null,
Txt = (v as any) as text =&gt;
if v = null then &quot;null&quot;
else if Value.Is(v, type text) then v
else try Text.From(v) otherwise
&quot;（&quot; &amp; (if Value.Is(v, type record) then &quot;レコード&quot;
else if Value.Is(v, type list) then &quot;リスト&quot;
else if Value.Is(v, type table) then &quot;テーブル&quot; else &quot;値&quot;) &amp; &quot;）&quot;,
// ---- ドキュメント全体のルール ---RootRows
= List.Select(Rows, each [Parent] = null),
Duplicates = List.Select(List.Distinct(List.RemoveNulls(Names)),
(n) =&gt; List.Count(List.Select(Names, each _ = n)) &gt; 1),
DocIssues =
(if List.Count(RootRows) &lt;&gt; 1
then {Issue(&quot;エラー&quot;, null, &quot;ルートはちょうど 1 行&quot;,
&quot;Parent が null の行が &quot; &amp; Text.From(List.Count(RootRows))
&amp; &quot; 行あります（プレビュー全体がエラーになります）&quot;)}
else {})
&amp; List.Transform(List.Select(Rows, each [Name] = null), (r) =&gt;
Issue(&quot;エラー&quot;, null, &quot;Name は必須&quot;,
&quot;Name が null の行があります（PartType: &quot; &amp; Txt(r[PartType]) &amp; &quot;）&quot;))
&amp; List.Transform(Duplicates, (n) =&gt;
Issue(&quot;警告&quot;, n, &quot;Name は一意&quot;,
&quot;同じ Name の行が複数あります（ビジュアルが重複して表示されることがあります）&quot;)),
// ---- ルートからたどれない行（Parent の循環など）---RootName = if List.Count(RootRows) = 1 then RootRows{0}[Name] else null,
Reachable =
if RootName = null then {}
else List.Last(List.Generate(
() =&gt; [seen = {RootName}, frontier = {RootName}],
each not List.IsEmpty([frontier]),
(s) =&gt;
let
children = List.Transform(
List.Select(Rows, (r) =&gt; List.Contains(s[frontier], r[Parent])),
each [Name]),
next = List.Difference(List.Distinct(children), s[seen])
in
[seen = s[seen] &amp; next, frontier = next],
each [seen])),
UnreachableIssues =
if RootName = null then {}
else List.Transform(
List.Select(Rows, (r) =&gt;
r[Parent] &lt;&gt; null and List.Contains(Names, r[Parent])
and not List.Contains(Reachable, r[Name])),
(r) =&gt; Issue(&quot;警告&quot;, r[Name], &quot;ルートから到達できる&quot;,
&quot;ルートからたどれないため表示されません（Parent の循環など）&quot;)),
// ---- グラフのルール ---CheckChart = (name as any, p as record, data0 as any) as list =&gt;
let
data
= if Value.Is(data0, type table) then Table.Buffer(data0) else data0,
chartType = Record.FieldOrDefault(p, &quot;ChartType&quot;, null),
series
= Record.FieldOrDefault(p, &quot;DataSeries&quot;, null),
seriesOk = Value.Is(series, type record),
axis
= if seriesOk then AsList(Record.FieldOrDefault(series, &quot;AxisColumns&quot;, null))
else null,
values
= if seriesOk then AsList(Record.FieldOrDefault(series, &quot;ValueColumns&quot;, null))
else null,
axisOk
= axis &lt;&gt; null and List.Count(axis) = 1
and List.AllTrue(List.Transform(axis, IsText)),
valuesOk = values &lt;&gt; null and not List.IsEmpty(values)
and List.AllTrue(List.Transform(values, IsText)),
isTable
= Value.Is(data, type table),
columns
= if isTable then Table.ColumnNames(data) else {},
named
= (if axisOk then axis else {}) &amp; (if valuesOk then values else {}),
missing
= if isTable
37


# Page. 38

![Page Image](https://bcdn.docswell.com/page/LJ1Y56VVEG.jpg)

Dataflow Gen2 データビジュアル（プレビュー）ハンズオン ガイド
then List.Distinct(List.Select(named, (c) =&gt; not List.Contains(columns, c)))
else {},
nonNumeric =
if isTable and valuesOk
then List.Select(List.Intersect({values, columns}), (c) =&gt;
not List.AllTrue(List.Transform(List.RemoveNulls(Table.Column(data, c)),
each Value.Is(_, type number))))
else {}
in
(if not List.Contains(ChartTypes, chartType)
then {Issue(&quot;エラー&quot;, name, &quot;ChartType は 6 種類のいずれか&quot;,
&quot;未対応の ChartType: &quot; &amp; Txt(chartType)
&amp; &quot;（Line / Area / Bar / StackedBar / Doughnut / Pie）&quot;)} else {})
&amp; (if not seriesOk
then {Issue(&quot;エラー&quot;, name, &quot;DataSeries が必要&quot;,
&quot;Properties に DataSeries（レコード）がありません&quot;)} else {})
&amp; (if seriesOk and not axisOk
then {Issue(&quot;エラー&quot;, name, &quot;AxisColumns は 1 列&quot;,
&quot;AxisColumns には列名（text）を 1 つだけ指定します&quot;)} else {})
&amp; (if seriesOk and not valuesOk
then {Issue(&quot;エラー&quot;, name, &quot;ValueColumns は列名&quot;,
&quot;ValueColumns には列名（text または text のリスト）を指定します&quot;)} else {})
&amp; (if valuesOk and chartType &lt;&gt; &quot;StackedBar&quot; and List.Count(values) &gt; 1
then {Issue(&quot;エラー&quot;, name, &quot;値の列は 1 つ（StackedBar 以外）&quot;,
Txt(chartType) &amp; &quot; の ValueColumns は 1 列だけです&quot;)} else {})
&amp; List.Transform(missing, (c) =&gt;
Issue(&quot;警告&quot;, name, &quot;指定した列が Data にある&quot;,
&quot;列「&quot; &amp; c &amp; &quot;」が Data のテーブルにありません（空のグラフの原因になります）&quot;))
&amp; List.Transform(nonNumeric, (c) =&gt;
Issue(&quot;警告&quot;, name, &quot;値の列は数値&quot;, &quot;列「&quot; &amp; c &amp; &quot;」に数値以外の値があります&quot;))
&amp; (if chartType = &quot;StackedBar&quot;
and List.AnyTrue(List.Transform(named, each Text.Contains(_, &quot;\&quot;)))
then {Issue(&quot;警告&quot;, name, &quot;列名にバックスラッシュを使わない&quot;,
&quot;積み上げ横棒の列名に \ が含まれています&quot;)} else {})
&amp; (if isTable and Table.RowCount(data) &gt; 1000
then {Issue(&quot;情報&quot;, name, &quot;Data は小さく&quot;,
&quot;Data が &quot; &amp; Text.From(Table.RowCount(data))
&amp; &quot; 行あります。集計して行数を減らすと編集が快適になります&quot;)} else {}),
// ---- 行ごとのルール ---CheckRow = (r as record) as list =&gt;
let
name
= r[Name],
partType = r[PartType],
props
= r[Properties],
data
= r[Data],
propsBad = props &lt;&gt; null and not Value.Is(props, type record),
p
= if Value.Is(props, type record) then props else [],
get
= (field as text) as any =&gt; Record.FieldOrDefault(p, field, null),
kids
= ChildCount(name),
// 構造のルール（親子関係・PartType・Data）
Structure =
(if r[Parent] &lt;&gt; null and not List.Contains(Names, r[Parent])
then {Issue(&quot;エラー&quot;, name, &quot;Parent は既存の Name&quot;,
&quot;Parent「&quot; &amp; Txt(r[Parent])
&amp; &quot;」が見つかりません（この行と子孫は表示されません）&quot;)} else {})
&amp; (if not List.Contains(PartTypes, partType)
then {Issue(&quot;エラー&quot;, name, &quot;PartType は 6 種類のいずれか&quot;,
&quot;Visual not recognized: &quot; &amp; Txt(partType))} else {})
&amp; (if propsBad
then {Issue(&quot;エラー&quot;, name, &quot;Properties はレコード&quot;,
&quot;Properties がレコードではありません（プレビュー全体が表示されません）&quot;)}
else {})
&amp; (if partType = &quot;Container&quot; and kids = 0
then {Issue(&quot;エラー&quot;, name, &quot;Container の子は 1 つ以上&quot;,
&quot;Missing required visual property &quot;&quot;cells&quot;&quot;（子がありません）&quot;)} else {})
&amp; (if partType = &quot;Card&quot; and kids &lt;&gt; 1
then {Issue(&quot;エラー&quot;, name, &quot;Card の子はちょうど 1 つ&quot;,
&quot;子の数: &quot; &amp; Text.From(kids))} else {})
&amp; (if List.Contains(LeafTypes, partType) and kids &gt; 0
then {Issue(&quot;エラー&quot;, name, &quot;リーフは子を持てない&quot;,
Txt(partType) &amp; &quot; の下に子が &quot; &amp; Text.From(kids) &amp; &quot; 行あります&quot;)}
else {})
&amp; (if List.Contains({&quot;Table&quot;, &quot;Chart&quot;}, partType) and not Value.Is(data, type table)
then {Issue(&quot;エラー&quot;, name, &quot;Data にテーブルが必要&quot;,
Txt(partType) &amp; &quot; の Data がテーブルではありません&quot;)} else {}),
// Properties の中身のルール（Properties がレコードのときだけ点検）
38


# Page. 39

![Page Image](https://bcdn.docswell.com/page/GJWG5WL372.jpg)

Dataflow Gen2 データビジュアル（プレビュー）ハンズオン ガイド
PropertyRules =
if propsBad then {}
else
(if partType = &quot;Container&quot; and get(&quot;Direction&quot;) &lt;&gt; null
and not List.Contains({&quot;row&quot;, &quot;column&quot;}, get(&quot;Direction&quot;))
then {Issue(&quot;警告&quot;, name, &quot;Direction は row / column&quot;,
&quot;Direction の値: &quot; &amp; Txt(get(&quot;Direction&quot;)))} else {})
&amp; (if partType = &quot;Card&quot; and not IsText(get(&quot;Title&quot;))
then {Issue(&quot;エラー&quot;, name, &quot;Card には Title（text）&quot;,
&quot;Title が text ではありません&quot;)} else {})
&amp; (if partType = &quot;Header&quot; and not IsText(get(&quot;Header&quot;))
then {Issue(&quot;エラー&quot;, name, &quot;Header には Header（text）&quot;,
&quot;Header が text ではありません&quot;)} else {})
&amp; (if partType = &quot;Header&quot; and get(&quot;FarText&quot;) &lt;&gt; null and not IsText(get(&quot;FarText&quot;))
then {Issue(&quot;エラー&quot;, name, &quot;FarText は text か null&quot;,
&quot;FarText の型が不正です&quot;)} else {})
&amp; (if partType = &quot;KpiCard&quot; and not (IsText(get(&quot;Value&quot;)) and IsText(get(&quot;Label&quot;)))
then {Issue(&quot;エラー&quot;, name, &quot;KpiCard の Value と Label は text&quot;,
&quot;Unexpected result type（数値は Number.ToText で文字列に）&quot;)}
else {})
&amp; (if partType = &quot;KpiCard&quot; and get(&quot;Sub&quot;) &lt;&gt; null and not IsText(get(&quot;Sub&quot;))
then {Issue(&quot;エラー&quot;, name, &quot;Sub は text か null&quot;, &quot;Sub の型が不正です&quot;)}
else {})
&amp; (if partType = &quot;Chart&quot; then CheckChart(name, p, data) else {})
in
Structure &amp; PropertyRules,
AllIssues =
if not List.IsEmpty(MissingColumns)
then {Issue(&quot;エラー&quot;, null, &quot;必須の 5 列&quot;,
&quot;列がありません: &quot; &amp; Text.Combine(MissingColumns, &quot;, &quot;)
&amp; &quot;（ビジュアルではなく通常の表として表示されます）&quot;)}
else DocIssues &amp; UnreachableIssues &amp; List.Combine(List.Transform(Rows, CheckRow)),
Result =
if List.IsEmpty(AllIssues)
then #table(ResultType, {{&quot;OK&quot;, null, &quot;すべてのルール&quot;,
&quot;問題は見つかりませんでした（&quot; &amp; Text.From(List.Count(Rows)) &amp; &quot; 行）&quot;}})
else #table(ResultType, AllIssues)
in
Result
39


