Question
Row number based on groups of data
Calculate row number for groups
有时候我们需要基于分组来对数据进行内部排序,例如一个id+date,把不同的属性的记录标记为123,又或者把重复记录标记出来
Solved: Calculate row number for groups - Qlik Community - 54864
Solution: Group by and Get Rowno
source data like below , we want to group by pool_id and balance_date then give it the RowNo
PoolDataTmp1:
LOAD * inline [
Pool_ID|Account_ID|Balance_Date
P0001|ACC0011|2024-04-11
P0001|ACC0012|2024-04-11
P0001|ACC0013|2024-04-11
P0001|ACC0011|2024-04-10
P0001|ACC0012|2024-04-10
P0002|ACC0021|2024-05-01
P0002|ACC0022|2024-05-01
P0002|ACC0023|2024-05-01
P0002|ACC0023|2024-05-01
] (delimiter is '|');
PoolDataTmp2:
Load Hash128(Pool_ID,Balance_Date) as MyKey,*
Resident
PoolDataTmp1
;
drop table PoolDataTmp1;
left join(PoolDataTmp2)
Load
IF(MyKey <> Previous(MyKey), 1, Peek('GroupRow')+1) AS GroupRow,
MyKey,Account_ID
Resident
PoolDataTmp2
Order By MyKey;
Exit Script;
Result: group by pool_id and balance_date , give the every record a (Group)RowNo
extend : use it to mark duplicate record by key
if you modify the key to Load Hash128(Pool_ID,Balance_Date,Account_ID) as MyKey,*
then you could use it to check if any duplicate result
Peek - 脚本函数
Peek() 用于在表格中返回已经加载行的字段值。可以将行号指定为表格。如果未指定行号,将使用上次加载的记录。
peek() 函数最常用于查找以前加载的表中的相关边界,即特定字段的第一个值或最后一个值。在大多数情况下,该值存储在一个变量中供以后使用,例如,作为 do-while 循环中的一个条件
语法:
Peek( field_name [, row_no[, table_name ] ])
返回数据类型: 双
参数:
参数 | 说明 |
---|---|
field_name | 需要返回值的字段的名称。输入值必须为字符串(例如引用的文字)。 |
row_no | 表格中的行用于指定所需的字段。可以是表达式,但解算结果必须为整数。0 表示第一个记录,1 表示第二个记录,以此类推。负数表示从表格末端开始计算的顺序。-1 表示最后读取的记录。 如果未指定row_no,则假定为 -1。 |
table_name | 表格标签不能以冒号结束。如果未指定table_name,则假定为当前表格。如果用于 LOAD 语句之外或指向另外一个表格,则必须包括 table_name。 |
限制:
该函数只能从已加载的记录中返回值。这意味着在表的第一条记录中,使用 -1 作为 row_no 的调用将返回 NULL。
示例和结果:
示例 1
将示例脚本添加到应用程序并运行。要查看结果,将结果列中列出的字段添加到应用程序中的工作表。
EmployeeDates:
Load * Inline [
EmployeeCode|StartDate|EndDate
101|02/11/2010|23/06/2012
102|01/11/2011|30/11/2013
103|02/01/2012|
104|02/01/2012|31/03/2012
105|01/04/2012|31/01/2013
106|02/11/2013|
] (delimiter is '|');
First_last_Employee:
Load
EmployeeCode,
Peek('EmployeeCode',0,'EmployeeDates') As FirstCode,
Peek('EmployeeCode',-1,'EmployeeDates') As LastCode
Resident EmployeeDates;
员工代码 | StartDate | EndDate | FirstCode | LastCode |
---|---|---|---|---|
101 | 02/11/2010 | 23/06/2012 | 101 | 106 |
102 | 01/11/2011 | 30/11/2013 | 101 | 106 |
103 | 02/01/2012 | 101 | 106 | |
104 | 02/01/2012 | 31/03/2012 | 101 | 106 |
105 | 01/04/2012 | 31/01/2013 | 101 | 106 |
106 | 02/11/2013 | 101 | 106 |
FirstCode = 101,因为 Peek('EmployeeCode',0, 'EmployeeDates') 返回表格 EmployeeDates 的 EmployeeCode 中的第一个值。
LastCode = 106,因为 Peek('EmployeeCode',-1, 'EmployeeDates') 返回表格 EmployeeDates 的 EmployeeCode 中的最后一个值。
替代参数 row_no 返回表格中其他行的值,如下所示:
Peek('EmployeeCode',2, 'EmployeeDates') 用于返回表格中的第三个值 103(作为 FirstCode)。
但是,请注意,如果在这些示例中没有将表格指定为第三个参数 table_name,此函数引用当前表格(在此例中,为内部表格)。
示例 2
如果要访问表中更深层的数据,需要分两步进行:首先,将整个表加载到临时表中,然后在使用 Peek() 时对其重新排序。
将示例脚本添加到应用程序并运行。要查看结果,将结果列中列出的字段添加到应用程序中的工作表。
T1:
LOAD * inline [
ID|Value
1|3
1|4
1|6
3|7
3|8
2|1
2|11
5|2
5|78
5|13
] (delimiter is '|');
T2:
LOAD *,
IF(ID=Peek('ID'), Peek('List')&','&Value,Value) AS List
RESIDENT T1
ORDER BY ID ASC;
DROP TABLE T1;
ID | 列表 | 值 |
---|---|---|
1 | 3,4 | 4 |
1 | 3,4,6 | 6 |
1 | 3 | 3 |
2 | 1,11 | 11 |
2 | 1 | 1 |
3 | 7,8 | 8 |
3 | 7 | 7 |
5 | 2,78 | 78 |
5 | 2,78,13 | 13 |
5 | 2 | 2 |
IF() 语句是根据临时表格 T1 构建。
Peek('ID') 引用当前表格 T2 的上一行中的字段 ID。
Peek('List') 引用当前表格 T2 的上一行中的字段 List,目前正在构建要解算的表达式。
如下运算语句:
如果 ID 的当前值与 ID 的上一个值相同,则写入 Peek('List') 的值串联 Value 的当前值。否则,只写入 Value 的当前值。
如果 Peek('List') 已经包含串联结果,则会将 Peek('List') 的新结果串联至其当前值。
信息注释注意,Order by 子句。该子句用于指定表格的排序方式(按 ID 进行升序排序)。如果没有使用此子句,Peek() 函数将使用内部表格拥有的任意排序方式,这可能会导致产生不可预测的结果。
示例 3
将示例脚本添加到应用程序并运行。要查看结果,将结果列中列出的字段添加到应用程序中的工作表。
Amounts:
Load
Date#(Month,'YYYY-MM') as Month,
Amount,
Peek(Amount) as AmountMonthBefore
Inline
[Month,Amount
2022-01,2
2022-02,3
2022-03,7
2022-04,9
2022-05,4
2022-06,1];
金额 | AmountMonthBefore | 月 |
---|---|---|
1 | 4 | 2022-06 |
2 | - | 2022-01 |
3 | 2 | 2022-02 |
4 | 9 | 2022-05 |
7 | 3 | 2022-03 |
9 | 7 | 2022-04 |
字段 AmountMonthBefore 将保存上个月的金额。
这里省略了 row_no 和 table_name 参数,因此使用默认值。在本例中,以下三个函数调用是等效的:
- Peek(Amount)
- Peek(Amount,-1)
- Peek(Amount,-1,'Amounts')
将 -1 用作 row_no 并不意味着将使用前一行中的值。通过替换该值,可以获取表中其他行的值:
Peek(Amount,2) 用于返回表格中的第三个值:7。
示例 4:
数据需要正确排序才能得到正确的结果,但遗憾的是,情况并非总是如此。此外,Peek() 函数不能用于引用尚未加载的数据。通过使用临时表并对数据进行多次传递,可以避免此类问题。
将示例脚本添加到应用程序并运行。要查看结果,将结果列中列出的字段添加到应用程序中的工作表。
tmp1Amounts:
Load * Inline
[Month,Product,Amount
2022-01,B,3
2022-01,A,8
2022-02,B,4
2022-02,A,6
2022-03,B,1
2022-03,A,6
2022-04,A,5
2022-04,B,5
2022-05,B,6
2022-05,A,7
2022-06,A,4
2022-06,B,8];
tmp2Amounts:
Load *,
If(Product=Peek(Product),Peek(Amount)) as AmountMonthBefore
Resident tmp1Amounts
Order By Product, Month Asc;
Drop Table tmp1Amounts;
Amounts:
Load *,
If(Product=Peek(Product),Peek(Amount)) as AmountMonthAfter
Resident tmp2Amounts
Order By Product, Month Desc;
Drop Table tmp2Amounts;
解释
初始表是按月份排序的,这意味着 peek() 函数在很多情况下会返回错误产品的金额。因此,该表需要重新排序。这是通过运行第二次数据传递并创建一个新表来完成的。注意,Order by 子句。它先按产品将记录排序,然后按月份升序排序。
需要 If() 函数,因为如果前一行包含同一产品但属于上一个月的数据,则只应计算 AmountMonthBefore。通过将当前行的产品与前一行的产品进行比较,可以验证此条件。
创建第二个表时,使用 Drop 创建第二个表时,使用Drop Table 语句删除第一个表。
最后,对数据进行第三次遍历,但现在月份的排序是相反的。这样,也可以计算 AmountMonthAfter。
信息注释Order by子句指定表格的排序方式;如果没有使用这些子句,Peek() 函数将使用内部表格拥有的任意排序方式,这可能会导致产生不可预测的结果。
结果
月 | 产品 | 金额 | AmountMonthBefore | AmountMonthAfter |
---|---|---|---|---|
2022-01 | A | 8 | - | 6 |
2022-02 | B | 3 | - | 4 |
2022-03 | A | 6 | 8 | 6 |
2022-04 | B | 4 | 3 | 1 |
2022-05 | A | 6 | 6 | 5 |
2022-06 | B | 1 | 4 | 5 |
2022-01 | A | 5 | 6 | 7 |
2022-02 | B | 5 | 1 | 6 |
2022-03 | A | 7 | 5 | 4 |
2022-04 | B | 6 | 5 | 8 |
2022-05 | A | 4 | 7 | - |
2022-06 | B | 8 | 6 | - |
示例 5
将示例脚本添加到应用程序并运行。要查看结果,将结果列中列出的字段添加到应用程序中的工作表。
T1:
Load * inline [
Quarter, Value
2003q1, 10000
2003q1, 25000
2003q1, 30000
2003q2, 1250
2003q2, 55000
2003q2, 76200
2003q3, 9240
2003q3, 33150
2003q3, 89450
2003q4, 1000
2003q4, 3000
2003q4, 5000
2004q1, 1000
2004q1, 1250
2004q1, 3000
2004q2, 5000
2004q2, 9240
2004q2, 10000
2004q3, 25000
2004q3, 30000
2004q3, 33150
2004q4, 55000
2004q4, 76200
2004q4, 89450 ];
T2:
Load *, rangesum(SumVal,peek('AccSumVal')) as AccSumVal;
Load Quarter, sum(Value) as SumVal resident T1 group by Quarter;
结果
季度 | SumVal | AccSumVal |
---|---|---|
2003q1 | 65000 | 65000 |
2003q2 | 132450 | 197450 |
2003q3 | 131840 | 329290 |
2003q4 | 9000 | 338290 |
2004q1 | 5250 | 343540 |
2004q2 | 24240 | 367780 |
2004q3 | 88150 | 455930 |
2004q4 | 220650 | 676580 |
解释
Load 语句 Load *, rangesum(SumVal,peek('AccSumVal')) as AccSumVal 包括一个递归调用,其中以前的值被添加到当前值。此操作用于计算脚本中值的累积。