按指定基准对齐的分组运算

【摘要】
    在分组时经常会要求结果集必须按基准集合的次序出现,这种对齐分组在日常统计中是很常见的。我们还能把对齐分组推广成更一般的枚举分组。如何简便快捷的处理对齐分组,这里为你全程解析,并提供 esProc 示例代码。按指定基准对齐的分组运算

对照一个基准集合,将待分组集合成员的某个字段或表达式与基准集合成员比较,相同者则分到同一个组中,最后拆分出来的组数和基准集合成员数是相同的。这种分组我们称为对齐分组。对齐分组可能会有空组,也可能有成员未分配到任何一个组中。

1.     普通对齐分组

1.1    每组保留最多一个匹配成员

按某字段的指定顺序,将表中所有记录分组并汇总求和。

【例 1】 根据相互关联的课程表和选课表,按课程表顺序查询有哪些课程无人选择:

..

【SPL 脚本】


A

B

1

=connect("db")

/连接数据库

2

=A1.query("select * from   SelectCouse")

/查询选课表

3

=A1.query("select * from Course")

/查询课程表

4

=A2.align(A3:ID,CourseID)

/使用函数 A.align(),将选课表按照课程表的 ID 对齐,每组选择一个匹配成员

5

=A3(A4.pos@a(null))

/在课程表中选出没有选择(值为 null)的课程信息

    A5的执行结果如下:

ID

NAME

TeacherID

1

Environmental   protection and sustainable development

5

10

Music   appreciation

18

 

1.2    每组保留所有匹配成员

按某字段的指定顺序,将表中所有记录分组并汇总求和。

【例 2】 根据相互关联的员工表和部门表,按部门表中的部门顺序统计各部门人数:

..

【SPL 脚本】


A

B

1

=connect("db")

/连接数据库

2

=A1.query("select * from   EMPLOYEE")

/查询员工表

3

=A1.query("select * from   DEPARTMENT")

/查询部门表

4

=A2.align@a(A3:ID, DEPARTMENT)

/使用函数 A.align@a(),将员工表按部门对齐分组,@a 选项每组返回所有匹配成员

5

=A4.new(DEPT, ~.count():COUNT)

/统计各部门的人数

    A5的执行结果如下:

DEPT

COUNT

Admin

4

R&D

29

Sales

187

 

1.3    不匹配记录放到新组

按某字段的指定顺序,将表中所有记录分组,不匹配记录放到新组。

【例 3】 根据员工薪资表,统计 [California, Texas, New York, Florida] 的平均工资,未指定的州作为“Other”统计。员工薪资表部分数据如下:

ID

NAME

STATE

SALARY

1

Rebecca

California

7000

2

Ashley

New   York

11000

3

Rachel

New   Mexico

9000

4

Emily

Texas

7000

5

Ashley

Texas

16000

【SPL 脚本】


A

B

1

=connect("db")

/连接数据库

2

=A1.query("select * from   EMPLOYEE")

/查询雇员表

3

[California,Texas,New York,Florida]

/创建地区序列

4

=A2.align@an(A3,STATE)

/使用函数 A.align@an(),将雇员表按地区对位分组,@a 选项每组返回所有匹配成员,@n 选项不匹配成员存放到新组。

5

=A4.new(if (#>A3.len(),"Other",STATE):STATE,~.avg(SALARY):AvgSalary)

/统计每组的平均工资,产生新序表。最后一组的地区更名为 Other,否则为当前组第一条记录的地区。

    A5的执行结果如下:

STATE

SALARY

California

7700.0

Texas

7592.59

New   York

7677.77

Florida

7145.16

Other

7308.1

 

2.     序号对齐分组

序号对齐分组,是指按照指定的序号进行分组,序号相同的成员分到同一组。

2.1    每组保留最多一个匹配成员

在相互关联的两个表中,查找未被引用的记录。

【例 4】 根据相互关联的销售表和客户表,顺序列出 2014 年没有销售记录的客户:

..

【SPL 脚本】


A

B

1

=connect("db")

/连接数据库

2

=A1.query("select * from Sales")

/查询销售表

3

=A1.query("select * from   Customer")

/查询客户表

4

=A3.(ID)

/从客户表中选出客户序号

5

=A2.align(A4.len(), A4.pos(CustomerID))

/使用函数 A.align(n,y),将销售表按照客户序号对位分组

6

=A3(A5.pos@a(null))

/在客户表中选出没有销售记录(值为 null)的客户信息

A6的执行结果如下:

ID

Name

State

ALFKI

CMA-CGM

Texas

CENTC

Nedlloyd

Florida

 

2.2    每组保留所有匹配成员

按序号将表中所有记录分组并汇总求和。

【例 5】 根据订单表,顺序列出 2013 年每月的订单总数。订单表部分数据如下:

 

ID

CustomerID

OrderDate

Amount

10248

VINET

2012/07/04

428.0

10249

TOMSP

2012/07/05

1842.0

10250

HANAR

2012/07/08

1523.5

10251

VICTE

2012/07/08

624.95

10252

SUPRD

2012/07/09

3559.5

【SPL 脚本】


A

B

1

=connect("db")

/连接数据库

2

=A1.query("select * from Orders where   year(OrderDate)=2013")

/查询 2013 年的订单

3

=A2.align@a(12,month(OrderDate))

/使用函数 A.align@a(),将订单表按月份对位分为 12 组,@a 选项每组选择所有匹配成员

4

=A3.new(#:Month,~.count():OrderCount)

/列出每月的订单总数

A4的执行结果如下:

Month

OrderCount

1

33

2

29

3

30

4

31

5

32

6

30

7

33

8

33

9

37

10

38

11

34

12

48

 

2.3    按序号重复性分组

按计算出的序号数列重复性分组并计算。

【例 6】 根据发帖记录表,按标签将帖子分组,并统计各个标签出现频数。发帖记录表部分数据如下:

 

ID

Title

Author

Label

1

Easy   analysis of Excel

2

Excel,ETL,Import,Export

2

Early   commute: Easy to pivot excel

3

Excel,Pivot,Python

3

Initial   experience of SPL

1

Basics,Introduction

4

Talking   about set and reference

4

Set,Reference,Dispersed,SQL

5

Early   commute: Better weapon than Python

4

Python,Contrast,Install

【SPL 脚本】


A

B

1

=connect("db")

/连接数据库

2

=A1.query("select * from   PostRecord")

/查询发帖记录表

3

=A2.conj(Label.split(",")).id()

/将标签按逗号分隔后合并到一个序列,获得没有重复值的全部标签。

4

=A2.align@ar(A3.len(),A3.pos(Label.split(",")))

/使用 align 函数的 @r 选项,按照每个帖子的标签在全部标签中的定位分组。

5

=A4.new(A3(#):Label,~.count():Count).sort@z(Count)

/统计每个标签的帖子数量,按降序排列

A5的执行结果如下:

Label

Count

SPL

7

SQL

6

Basics

5

 

2.4    分段分组

根据指定字段的值,分段分组并汇总计数。

【例 7】 根据员工薪资表,按工资 8000 以下、8000~12000 和 12000 以上分组,并统计各组的人数。员工薪资表部分数据如下:

 

ID

NAME

BIRTHDAY

SALARY

1

Rebecca

1974-11-20

7000

2

Ashley

1980-07-19

11000

3

Rachel

1970-12-17

9000

4

Emily

1985-03-07

7000

5

Ashley

1975-05-13

16000

【SPL 脚本】


A

B

1

=connect("db")

/连接数据库

2

=A1.query("select * from   EMPLOYEE")

/查询员工表

3

[0,8000,12000]

/定义工资区间

4

=A2.align@a(A3.len(),A3.pseg(SALARY))

/使用函数 A.pseg(x) 获取工资所在区间

5

=A4.new(A3 (#):SALARY,~.count():COUNT)

/统计每组的人数

A5的执行结果如下:

SALARY

COUNT

0

308

8000

153

12000

39

 

根据表达式的计算结果,将记录分段分组并汇总计算平均值。

【例 8】根据员工表,按入职时间 10 年以下,10~20 年和 20 年以上分组,并统计每组的平均工资。员工表部分数据如下:

ID

NAME

BIRTHDAY

SALARY

1

Rebecca

1974-11-20

7000

2

Ashley

1980-07-19

11000

3

Rachel

1970-12-17

9000

4

Emily

1985-03-07

7000

5

Ashley

1975-05-13

16000

【SPL 脚本】


A

B

1

=connect("db")

/连接数据库

2

=A1.query("select * from   EMPLOYEE")

/查询员工表

3

[0,10,20]

/定义入职年限区间

4

=now()

/获取当前日期时间

5

=A2.align@a(A3.len(),A3.pseg(elapse@y(A4,-~),   HIREDATE))

/使用函数 A.pseg(x,y) 获取入职时间所在区间

6

=A5.new(A3(#):EntryYears,~.avg(SALARY):AvgSalary)

/统计每组的平均工资

A6的执行结果如下:

EntryYears

AvgSalary

0

6777.78

10

7445.53

20

6928.57

 

3.     枚举分组

枚举分组是指,事先指定一组枚举条件,将待分组集合的成员作为参数计算这批条件,条件成立者都被划分到与该条件对应的一个子集中,结果集的子集和事先指定的条件一一对应。

3.1    每个成员只存放到第一个匹配组

根据枚举条件表达式,将记录分组,分组时记录只置于第一个匹配组。

【例 9】 根据中国主要城市人口表,按人口将城市分类。中国主要城市人口表部分数据如下:

 

ID

City

Population

Province

1

Shanghai

12286274

Shanghai

2

Beijing

9931140

Beijing

3

Chongqing

7421420

Chongqing

4

Guangzhou

7240465

Guangdong

5

Hong   Kong

7010000

Hong   Kong Special Administrative Region

【SPL 脚本】


A

B

1

=connect("db")

/连接数据库

2

=A1.query("select * from   UrbanPopulation")

/查询城市人口表

3

[?>2000000,?>1000000,?>500000,?<=500000]

/超大城市 200 万人口以上,特大城市 100-200 万人口,大城市 50-100 万,其他中小城市。

4

=A2.enum(A3,Population)

/使用函数 A.enum(),将人口按 A3 定义的条件进行枚举分组

A4的执行结果如下:

..

3.2    不匹配成员存放到新组

根据枚举条件表达式,将记录分组,不匹配记录放到新组。

【例 10】 根据员工薪资表,按年龄条件 [小于 35 岁, 小于 45 岁] 将员工分组,统计平均工资,不满足条件的分到新组。员工薪资表部分数据如下:

ID

NAME

BIRTHDAY

SALARY

1

Rebecca

1974-11-20

7000

2

Ashley

1980-07-19

11000

3

Rachel

1970-12-17

9000

4

Emily

1985-03-07

7000

5

Ashley

1975-05-13

16000

【SPL 脚本】


A

B

1

=connect("db")

/连接数据库

2

=A1.query("select * from   EMPLOYEE")

/查询员工表

3

[?<35,?<45]

/将年龄段划分为 35 岁以下,45 岁以下

4

=A2.enum@n(A3, age(BIRTHDAY))

/使用函数 A.enum@n(),按年龄枚举分组,选项 @n 不匹配成员存放到新组。

5

=A4.new(if (#>A3.len(),   "Other",A3(#)):AGE,~.avg(SALARY):AvgSalary)

/统计每组的平均工资,最后一组名称设置为 Other

A5的执行结果如下:

AGE

AvgSalary

?<35

7118.18

?<45

7448.16

Other

7395.06

 

3.3    按照枚举条件可重复分组

根据不同的指定序列,将记录分组并计算,分组时记录可重复。

【例 11】 根据城市 GDP 表,分别统计直辖市、一线城市、二线城市的人均 GDP,分组时可能重复。城市 GDP 表部分数据如下:

 

ID

City

GDP

Population

1

Shanghai

32679

2418

2

Beijing

30320

2171

3

Shenzhen

24691

1253

4

Guangzhou

23000

1450

5

Chongqing

20363

3372

【SPL 脚本】


A

B

1

=connect("db")

/连接数据库

2

=A1.query("select * from GDP")

/查询城市 GDP 表

3

[["Beijing","Shanghai","Tianjing","Chongqing"].pos(?)>0,["Beijing","Shanghai","Guangzhou","Shenzhen"].pos(?)>0,["Chengdu","Hangzhou","Chongqing","Wuhan","Xian","Suzhou","Tianjing","Nanjing","Changsha","Zhengzhou","Dongguan","Qingdao","Shenyang","Ningbo","Kunming"].pos(?)>0]

/枚举直辖市、一线城市和二线城市

4

=A2.enum@r(A3,City)

/使用函数 A.enum@r(),按城市枚举分组。选项 @r 表示可重复分组。

5

=A4.new(A3(#):Area,~.sum(GDP)/~.sum(Population)*10000:CapitaGDP)

/统计每组的人均 GDP

A5的执行结果如下:

Area

CapitaGDP

["Beijing","Shanghai","Tianjing","Chongqing"].pos(?)>0

107345.03

["Beijing","Shanghai","Guangzhou","Shenzhen"].pos(?)>0

151796.49

["Chengdu","Hangzhou","Chongqing","Wuhan","Xian","Suzhou","Tianjing","Nanjing","Changsha","Zhengzhou","Dongguan","Qingdao","Shenyang","Ningbo","Kunming"].pos(?)>0

106040.57

 

SPL CookBook》中还有更多相关计算示例。