本篇介绍 Power Query M 语言的三种结构化类型(或称为容器类型):List、Record 和 Table,它们是 Power Query 数据处理的核心。了解本篇三种容器类型后,应该能大体看懂查询编辑器和高级编辑器中的代码逻辑。
List
List 中文翻译为列表,跟 Python 语言的 list 很类似,由一序列有顺序的元素构成,这些元素可以是不同的数据类型。M 语言的 List 由一对大括号来,每个元素之间用逗号分隔。
A List is a zero-based ordered sequence of values enclosed in curly brace characters { }. The curly brace characters { } are also used to retrieve an item from a List by index position.
定义一个由 1 到 5 的整数组成的 List:
{1, 2, 3, 4, 5} // 由 1 到 5 的整数构成的 List
我们在查询编辑器中来看一下。创建一个空白查询(请参考上一篇如何创建空查询),然后在公式栏中输入 {1, 2, 3, 4, 5}
,点击"对号"确认。
确认后,查询编辑器的显示区显示了列表的值,左边是索引:
切换到功能区【主页】,点击【关闭并上载】,看看 Excel Sheet 中列表的显示效果。
M 语言提供
..
符号表示表示连续的元素,比如从 1 到 100:
{1..100}
连续符号不仅仅被用作数字,也可以用于单个字符,比如我们想列出所有的字母,包括大写字母和小写字母:
{"a".."z", "A".."Z"}
Power Query (PQ) 为什么能够理解从 a 到 z 呢,背后的原理就是这些字母的 Unicode 的编码,中文的 Unicode 编码大致范围在 19968-40891 之间,所以,我们也可以构造出所有中文(包括汉字和字符)字的列表:
List.Transform({19968..40891}, Character.FromNumber)
List 的元素是有顺序的,使用从 0 开始的索引获取 List 中的元素:
numbers = {1..10}
first = numbers{0} // first element
last = numbers{9} // last element
如果有更复杂条件的存取,需要用到标准库的函数,比如:
- List.First
- List.Last
- List.FirstN
- List.LastN
假设有一个从 1 到 100 的列表,要取出列表最后的 5 个元素:
let
source = {1..100},
lastFive = List.LastN(source, 5)
in
lastFive
Record (记录)
Record 类似于 Python 中的 dict,用于表达有键值对 (key-value pair) 的数据。Record 用一对中括号 []
来包含,不同键值对用逗号分隔,键 (key)没有引号:
A Record is a set of fields. A field is a name/value pair where the name is a text value that is unique within the field’s record.
let
Product = [Product="TV", Price=1800]
in
Product
将列表上载到 Excel Sheet 中的效果:
Record 是键值对,使用 key 来读取 Record 中的值,比如:
let
source = [Product="TV", Price=1800],
product = source[Product]
in
product
通常情况下,我们的数据都是由行和列构成的表格形式,对于这种格式,可以用 List 和 Record 组合来表示,每一行是一个 Record,多个 Record 组成一个 List:
let
products = {
[product="TV", price=1800],
[product="Computer", price=6800]
}
in
products
在查询编辑器中的显示如下:
如果我们读取 Computer 的价格:
products{1}[price]
let
products = {
[product="TV", price=1800],
[product="Computer", price=6800]
},
computerPrice = products{1}[price]
in
computerPrice
Table (查询表)
Table 是由行和列构成的数据集合,一般 Table 都是从外部数据源导入到 PQ 中,但也可以用 M 脚本手工创建。
A Table is a set of values organized into named columns and rows. The column type can be implicit or explicit.
手工创建 table 的基本方法是按行构建:
products = #table(
// columns
{
"Product", "Price"
},
// lines
{
{"TV", 1800},
{"Computer", 5600}
}
查询编辑器界面的显示:
上面创建查询表的方法,没有指定数据类型,用下面的语法创建 Table 的同时,指定 Column 的数据类型:
let
products = #table( type table
// columns
[
Product = text, Priece = number
],
// lines
{
{"TV", 1800},
{"Computer", 5600}
}
)
in
products
标准库中提供了 Table.FromXXX
函数来创建 Table,可以根据需要来学习,这里提供其中的两种:
Table.FromRecords()
语法:
Table.FromRecords(records as list,
optional columns as any,
optional missingField as nullable number) as table
将 records 或者 record 构成的 list 转换成 table,可以理解为按行构建 table。举一个例子:
let
scores = {
[Name="张三", Subject="语文", Score= 98],
[Name="李四", Subject="语文", Score= 90],
[Name="张三", Subject="数学", Score= 100],
[Name="李四", Subject="数学", Score= 87],
[Name="张三", Subject="英语", Score= 60],
[Name="李四", Subject="英语", Score= 72]
},
source = Table.FromRecords(scores)
in
source
Table.FromColumns()
按列构建一个 table,需要一个 list 作为参数,语法如下:
Table.FromColumns(lists as list, optional columns as any) as table
上面的示例数据,使用 Table.FromColumns()
函数构建:
let
source = Table.FromColumns(
{
{"张三","李四", "张三","李四","张三","李四"},
{"语文","语文","数学","数学", "英语","英语"},
{98,90,100,87,60, 72}
},
{"Name", "Subject", "Score"}
)
in
source
获取行的值
Table 每一行是一个 record 类型的数据,所有行可以看成由 record 构成的 list:
products = {
[product="TV", price=1800],
[product="Computer", price=6800]
},
Table 每一列是 list 类型的数据,所有列可以看成由 list 构成的 record:
products = [
Product={"TV", "Computer"},
Price={1800, 5600}
]
上面两句如何看待行和列的方式是理解读取 table 中行、列和单元格值的基础。怎么读取行的数据呢?比如我们要读取第一行所有列,可以这样:
navigation = products{0} // 第一行所有列
获取列的值
比如上面的表,想获取 Product 整列的数据
prodName = products[Product]
获取单元格的值
在上面方法,能获取到行或列之后,获取单元格的值就比较简单了:
productName = products{0}[Product] // 先读取行 (record),再根据 record 的 key 读取
productName = products[Product]{0} // 先读取列 (list),再根据 list 的索引读取
还有一种更直观的方式,这种方式比较直观,尽量应该采用这种方法:
productName = products{[Product="TV"]}