首页 > 解决方案 > kdb q - 有效地计算平面文件中的表

问题描述

我有很多表存储在平面文件中(在一个名为 的目录中basepath),我想检查它们的行数。我现在能做到的最好的是:

c:([] filename:system "ls ",basepath; 
      tablesize:count each get each hsym `$basepath,/:system "ls ",basepath)

它将每个表完全加载到内存中,然后执行计数(这很慢)。保存为张开的表格是加快速度的唯一方法(因为我只会加载 1 列并计算它)还是 q 中有一个我可以使用的技巧?

谢谢您的帮助

标签: kdb

解决方案


如果您将 basepath 定义为存储所有平面表的目录的路径字符串,则可以创建行数字典,如下所示:

q)cnt:{count get hsym x}
q)filename:key hsym `$basepath
q)filename!cnt each filename
t| 2
g| 3

这是我在基本路径目录中保存平面表 t 和 g 的地方。这使您不必使用system通常效率较低的命令。该函数cnt获取每个平面表的路径(作为符号)并返回行数而不将它们保存到内存中。

如果您可以控制保存此类文件的过程,最好的解决方案是添加一个额外的步骤,即在保存原始数据的同时将行计数的元信息单独保存在某个地方。这将允许您从该文件快速访问表大小,而不是每次都读取完整的 tbale。

但是,请注意,要完全避免将它们拉入内存,您必须改为使用read1并查看二进制数据的标头。正如您所说,最好保存为张开的表格并在一列中阅读。

更新:我不建议这样做,强烈建议执行上述操作,但出于好奇,在研究使用read1这里的示例后,一个 hacky 解决方案可能是什么样子:

f:{
  b:read1(y;0;x);
  if[not 0x62630b~b[2 4 5];'`$"not a table"];
  cc:first first((),"i";(),4)1:b 7+til 4;
  if[null ce:first where cc=sums 0x0=11 _ b;:.z.s[x*2;y]];
  c:`$"\000" vs "c"$b[11+til ce];
  n:first first((),"i";(),4)1:b[(20+ce)+til 4];
  :`columns`rows!(c;n);
  }[2000]

q 二进制文件格式在任何地方都没有记录,唯一的方法是保存不同的东西并查看字节如何变化。它也可能在版本之间发生变化 - 以上是为 3.5 编写的,可能仅对 3.0-3.5 有效,而不是最新的 3.6 版本或任何 2.X。

给定的代码按以下方式工作:

  1. 从文件的前面读取一个块

  2. 验证它看起来像一个扁平的无键表(使用符号 [11] 键翻转 dict[99] 的 [98])

  3. 将列列表中的符号计数读取为 little endian 4 byte int
  4. 扫描以 null 结尾的字符串以查找那么多零字节
  5. 如果列太多或太冗长以至于我们没有将它们全部放在这个块中,它将使块的大小加倍并重试
  6. 把字符串变成符号
  7. 使用我们从列列表末尾获得的偏移量,跳过更多列的混合列表的标题
  8. 然后从第一列的标题中读取计数

希望这能回答你的问题!


推荐阅读