TXR:如何旋转串联表?

TXR: how to pivot concatenated tables?

我有这样的数据:

i,a,b,c
1,0.2,3.2,4.5
2,0.8,4.1,3.5
3,0.5,3.1,4.1
i,a,b,c,d
4,3.2,5.2,7.5,1.1
5,2.8,5.1,8.5,0.9
6,2.5,5.1,8.1,1.0
i,a,d
7,3.2,5.2
8,2.8,5.1
9,2.5,5.1

我想用TXR处理成这样:

i,key,val
1,a,0.2
1,b,3.2
1,c,4.5
2,a,0.8
2,b,4.1
2,c,3.5
3,a,0.5
3,b,3.1
3,c,4.1
4,a,3.2
4,b,5.2
4,c,7.1
4,d,1.1
5,a,2.8
5,b,5.1
5,c,8.5
5,d,0.9
6,a,2.5
6,b,5.1
6,c,8.1
6,d,1.0
7,a,3.2
7,b,5.2
8,a,2.8
8,b,5.1
9,a,2.5
9,b,5.1

我当前不正确的 TXR 脚本是:

@(output)
i,key,val
@(end)
@(repeat)
i,@(coll)@{key /[^,]+/}@(end)
@  (collect :gap 0)
@{i /[0-9]+/},@(coll)@{value /[^,]+/}@(end)
@  (end)
@  (output)
@    (repeat)
@      (repeat)
@i,@key,@value
@      (end)
@    (end)
@  (end)
@(end)

它会生成:

i,key,val
1,a,0.2
1,a,3.2
1,a,4.5
2,b,0.8
2,b,4.1
2,b,3.5
3,c,0.5
3,c,3.1
3,c,4.1
4,a,3.2
4,a,5.2
4,a,7.5
4,a,1.1
5,b,2.8
5,b,5.1
5,b,8.5
5,b,0.9
6,c,2.5
6,c,5.1
6,c,8.1
6,c,1.0
7,a,3.2
7,a,5.2
8,d,2.8
8,d,5.1
9,,2.5
9,,5.1

我怎样才能达到预期的输出?我能以某种方式使用 @(merge) 吗,还是我需要下降到口齿不清?我看到有一个 transpose 函数可能对此有用。

代码快写完了。问题是值必须与键相关联。为此,我们可以利用 2016 年 6 月 29 日发布的 TXR 144 中添加的功能:collectcoll 中支持的 :counter 关键字:

@(output)
i,key,val
@(end)
@(repeat)
i,@(coll)@{keylist /[^,]+/}@(end)
@  (collect :gap 0)
@{i /[0-9]+/},@(coll :counter c)@{value /[^,]+/}@(bind key @[keylist c])@(end)
@  (end)
@  (output)
@    (repeat)
@      (repeat)
@i,@key,@value
@      (end)
@    (end)
@  (end)
@(end)

键被收集到 keylist 变量而不是 key 中,并且 key 通过索引到 keylistvalue 绑定在一起 c。默认情况下,计数器 c 从 0 步进;可以使用 :counter (c expr).

指定不同的起始值

但是我们不要用这种笨拙的方式来做。我的意思是,看看我们在做什么:我们正在通过 keys 重复这个计数器,并且只是收集与值并行的键列表。我们可以在内部 coll 之外使用单个 bind 实现完全相同的效果,如下所示:

@(output)
i,key,val
@(end)
@(repeat)
i,@(coll)@{keylist /[^,]+/}@(end)
@  (collect :gap 0)
@{i /[0-9]+/},@(coll)@{value /[^,]+/}@(end)
@  (bind key keylist)
@  (end)
@  (output)
@    (repeat)
@      (repeat)
@i,@key,@value
@      (end)
@    (end)
@  (end)
@(end)

看到了吗?为了获得每一行的 value 列表,我们收集值。键不会逐行更改,因此要获取每个 key 列表以与第 value 配对,我们只需将其绑定到 keylist.

如果我们想用这个逻辑做的只是获得打印输出,那么我们可以不收集行和每行后的输出。换句话说,这只是一个收集键名的练习,然后将它们与给定部分中每一行的值一起转储:

@(output)
i,key,val
@(end)
@(repeat)
i,@(coll)@{key /[^,]+/}@(end)
@  (repeat :gap 0)
@{i /[0-9]+/},@(coll)@{value /[^,]+/}@(end)
@    (output)
@      (repeat)
@i,@key,@value
@      (end)
@    (end)
@  (end)
@(end)

可以说,这对 Awk 来说是一个很好的任务。请注意,不一定是 Unix 版本:

(awk (:set fs "," ofs ",")
     (:let keys)
     (:begin (prn 'i 'key 'val))
     ((equal [f 0] "i") (set keys (rest f))
                        (next))
     (f (each ((k keys)
               (v (rest f)))
          (prn [f 0] k v))))