如何在 q 函数中使用 functional select 按动态列名分组?
可以使用 functional select 实现动态分组。先对 groupCol 求值,得到传入的列名,再用它构造分组字典;labels 则作为 symbol 常量数据嵌入筛选表达式。
对于单个 symbol 分组列,写法如下:
c:enlist (in;`label;enlist labels);
b:(enlist groupCol)!enlist groupCol;
以下说明假定 groupCol 是数据表中某列的 symbol 名称,label、priced 和 status 为 symbol 列,qty 为数值列。
为什么原来的写法没有按指定列分组?
原始代码中的两处写法混淆了参数值和查询表达式中的名称:
enlist (in;`label;`labels)
(enlist `groupCol)!enlist `groupCol
在 functional select 的表达式树中,裸 symbol 通常表示需要解析的列名或名称。因此,反引号后的 groupCol 是固定名称,不会先读取函数参数 groupCol,再把参数值当作列名。
同样,反引号后的 labels 也没有把参数中的筛选值直接嵌入表达式。如果这个名称能被解析到其他变量或列,查询可能执行,但含义仍容易偏离预期。
需要区分三种情况:
groupCol是普通 q 表达式中的局部参数,其值可以是某个实际列名。- 表达式树中的裸 symbol 引用对应的列或名称。
- 表达式树中经过
enlist包装的 symbol 或 symbol 列表作为常量数据使用。
KX 的 Functional qSQL 文档说明了这种区分和分组字典的构造方式。
普通 qSQL 中的 by groupCol 也不会自动把参数值再次解释为列名。如果它解析得到的是标量参数,就可能让所有记录落入同一个分组;如果表中存在同名列,行为又会受到该列影响。因此,动态列名适合在 functional select 中显式构造。
正确构造 where 和 by
筛选条件写成:
c:enlist (in;`label;enlist labels);
两层 enlist 的作用不同:外层把一个条件表达式包装成条件列表,内层把 labels 中的 symbol 值标记为常量数据。
单列分组字典写成:
b:(enlist groupCol)!enlist groupCol;
例如传入:
groupCol:`desk;
得到的分组字典等价于:
(enlist `desk)!enlist `desk
字典的键是结果中的分组列名,值是引用原表 desk 列的表达式。
原先尝试的写法:
(groupCol)!(groupCol)
只有括号,没有把 symbol atom 转换成列表。这里需要用 enlist 构造两侧各含一项的列表。
完整函数示例
可以先保存重复使用的表达式树,再组合聚合字典,避免维护一整行嵌套表达式。
原代码的 volDealt 使用了 DEAlT,其他成交指标使用 DEALT,二者大小写不同。下面假定这些指标都应匹配 DEALT,因此统一使用它;如果它们代表不同业务状态,应保留各自条件。
agg:{[data;groupCol;labels]
c:enlist (in;`label;enlist labels);
b:(enlist groupCol)!enlist groupCol;
p:(=;`priced;enlist `Y);
d:(in;`status;enlist `DEALT);
r:(count;(`i;(where;p)));
v:(sum;(`qty;(where;p)));
rd:(count;(`i;(where;d)));
vd:(sum;(`qty;(where;d)));
a:`req`vol`reqDealt`volDealt`reqPcnt`volPcnt!(
r;
(%;v;1e6);
rd;
(%;vd;1e6);
(%;rd;r);
(%;vd;v)
);
?[data;c;b;a]
};
调用时,分组列使用 symbol,筛选值使用 symbol 或 symbol 列表。例如,假设表中存在 desk 列,且 label 包含相应值:
agg[data;`desk;`L1`L2]
查询会先筛选 label,再按 desk 的不同取值计算六个指标,返回分组后的 keyed table。
如何缩小排查范围?
如果修改后仍有错误,可以先只计算每组记录数,确认动态分组和筛选条件正常:
checkGroups:{[data;groupCol;labels]
?[data;
enlist (in;`label;enlist labels);
(enlist groupCol)!enlist groupCol;
(enlist `rows)!enlist (count;`i)]
};
确认这一步正常后,再加入原有聚合表达式。也可以检查表结构和入参类型:
meta data
cols data
type groupCol
type labels
这里的 groupCol 应为单个 symbol,其类型为 -11h。字符串列名需要先转换为 symbol。上面的完整函数按单列分组设计,多列分组时应将列名组织成 symbol 列表,再构造相应字典。
聚合口径还需要注意什么?
reqPcnt 和 volPcnt 当前计算的是比例,没有乘以 100。分子只筛选 status,分母只筛选 priced,所以比例不一定小于或等于 1。修改动态分组不会改变这一统计口径。
如果某组没有 priced=Y 的记录,或这些记录的 qty 合计为零,还需要按业务规则处理零分母。
备注:内容仅供参考。
