代码之家  ›  专栏  ›  技术社区  ›  tenticon

kdbq-在flatfiles中有效地计数表

kdb
  •  3
  • tenticon  · 技术社区  · 6 年前

    我有很多表存储在平面文件中(在一个名为 basepath )我要检查他们的行数。我现在能做的最好的事情是:

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

    谢谢你的帮助

    4 回复  |  直到 6 年前
        1
  •  3
  •   Mark Kelly    6 年前

    从二进制文件的实验来看,当您保存一个平面文件时,表计数似乎被保存为二进制文件的一部分,在初始对象类型和列标题之后占用4个字节,这将因表而异。

    `:test set ([]a:1 2 3;b:4 5 6;c:7 8 9;aa:10 11 12;bb:13 14 15)
    q)read1 `:test
    0xff016200630b000500000061006200630061610062620000000500000009000300000
      0             7       11                                      31          
    
    bytes             | example                  | meaning
    ---------------------------------------------------------------------------------------
    0 - 5             | 0xff016200630b0          | object is a flat table
    7 - 11            | 0x05000000               | number of columns (5)
    12- 22            | 0x6100620063006161006262 | one byte for the ascii values of column "a" and "b" in hex followed by the one byte separator
    23 - 30           | 0x0000050000000900       | 8 bytes that can be skipped
    31 - 34           | 0x0300000                | 4 bytes for row count of first column (3)
    

    这将有助于你理解菲奥娜发布的功能。

    你可以用 1: 读入二进制文件的内容,列表中的附加参数指定偏移量—从何处开始读取,以及读取多少字节。在我们的例子中,我们希望从字节31开始,读入4个字节,指定输出应该是一个整数,并将输入分割成单独的4字节块。

    q)first first (enlist "i";enlist 4)1:(`:test;31;4)
    3i
    

    将小的endian字节转换成long,我们就得到了行数。因为它只需要读取4个字节而不是整个文件,所以速度要快得多。

    q)\t 0x0 sv reverse first (enlist "x";enlist 1)1:(`:test10000;31;4)
    0
    q)\t count get `:test10000
    0
    

    对于具有100m行和2列的表:

    q)\t 0x0 sv reverse first (enlist "x";enlist 1)1:(`:test10m;31;4)
    0
    q)\t count get `:test10m
    2023
    

    如果您有一个八字表,您可以像这样从字节9-13读取其中一列中的元素数,假设该列是一个简单列表:

    q)first first (enlist "i";enlist 4)1:(`:a;8;4)
    3i
    

    您可以在这里阅读有关从二进制文件读入的更多信息 https://code.kx.com/q/ref/filenumbers/#1-binary-files

        2
  •  4
  •   Fiona Morgan    6 年前

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

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

    system 函数 cnt 获取每个平面表的路径(作为符号)并返回行数,而不将它们保存到内存中。

    但是,请注意,为了避免将它们拉入内存,您必须使用 read1 再看看二进制数据的头。正如您所说的,最好保存为一个八字表并在一列中读取。

    更新:我不建议这样做,并强烈建议做以上,但好奇后,研究使用 阅读1 下面是一个黑客解决方案的示例:

    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. 将列列表中的符号计数读取为一个小的endian 4 byte int
    4. 在以null结尾的字符串中扫描这么多的零字节
    5. 如果列太多或太冗长以至于我们没有它们
    6. 把字符串变成符号
    7. 使用从列列表末尾得到的偏移量,跳过一点 列混合列表的更多标题
    8. 然后从第一列的标题读取计数

    希望这能回答你的问题!

        3
  •  3
  •   Josef Baines    6 年前

    您可以使用以下方法使您当前拥有的更高效

    counttables:{count each get each hsym`$basepath}

    这将通过不包括额外的数据读入和当前正在执行的连接来提高计数速度。你是正确的,但是如果保存的表是八字展开的,那么你只需要读入一列就可以了,这样效率就更高了。

        4
  •  0
  •   user20349    6 年前

    但是哈奇:-(