连续排序重复数据但行数不同

我有以下格式的数据:

2
1
A    11.364500   48.395199   40.160500         5
B    35.067699   30.944700   24.155300         4
4
2
A    26.274099   38.533298   32.624298         4
B    36.459000   29.662701   17.806299         5
A    15.850300   28.130899   24.753901         4
A    32.098000   33.665699   20.372499         4
5
3
A    17.501801   44.279202    8.005670         5
B    35.853001   43.095901   17.402901         4
B     1.326100   17.127600   39.600300         4
A     9.837760   41.103199   13.062300         5
B    31.686800   44.997501   16.619499         4
3
4
B    31.274700    8.726580   25.267599         4
A    19.032400   41.384701   19.456301         5
B    19.441900   24.286400    6.961680         4
1
5
B    48.973999   15.508400    5.099710         4
6
6
A    42.586601   21.343800   23.280701         5
B    30.145800   13.256200   30.713800         4
B    29.186001   44.353699    9.057160         4
A    39.311199   27.371201   35.473999         5
B    31.437799   30.415001   37.454399         4
B    48.501999    1.277730   21.900600         4
...

数据由多个重复的数据块组成。每个数据块具有相同的格式如下:

First line = Number of lines of data block
Second line = ID of data block
From third line = Actual data lines (with number of lines designated in the first line)

例如,如果我们看到第一个数据块:

2 (=> This data block has 2 number of lines) 
1 (=> This data block's ID is 1 (very first data block)) 
A    11.364500   48.395199   40.160500         5
B    35.067699   30.944700   24.155300         4
(Third and fourth lines of the data block are actual data. 
 The integer of the first line, the number of lines for this data block, is 2, so the actual data consist of 2 lines. 
 All other data blocks follow the same format. ) 
  1. 前两行是每个重复数据块的标题。第一行指定数据块的行数,第二行是数据块的ID。
  2. 然后真实数据从每个数据块的第三行开始。正如我所解释的,“真实数据”的行数在第一行中指定为整数。
  3. 因此,每个数据块的总行数将为 number_of_lines+2。在这个例子中,数据块 1 的总行数是 4,数据块 2 花费了 6 行......

此格式重复 100,000 次。换句话说,我在单个文本文件中有 100,000 个数据块,就像这个例子一样。我可以提供数据块的总数。

我希望做的是按第四列对每个数据块的“实际数据行”进行排序,并以与原始数据文件相同的格式打印出数据。我希望从上面的示例中实现的目标如下:

2
1
A    11.364500   48.395199   40.160500         5
B    35.067699   30.944700   24.155300         4
4
2
A    26.274099   38.533298   32.624298         4
A    15.850300   28.130899   24.753901         4
A    32.098000   33.665699   20.372499         4
B    36.459000   29.662701   17.806299         5
5
3
B     1.326100   17.127600   39.600300         4
B    35.853001   43.095901   17.402901         4
B    31.686800   44.997501   16.619499         4
A     9.837760   41.103199   13.062300         5
A    17.501801   44.279202    8.005670         5
3
4
B    31.274700    8.726580   25.267599         4
A    19.032400   41.384701   19.456301         5
B    19.441900   24.286400    6.961680         4
1
5
B    48.973999   15.508400    5.099710         4
6
6
B    31.437799   30.415001   37.454399         4
A    39.311199   27.371201   35.473999         5
B    30.145800   13.256200   30.713800         4
A    42.586601   21.343800   23.280701         5
B    48.501999    1.277730   21.900600         4
B    29.186001   44.353699    9.057160         4
...

我知道在 Linux 中使用第 4 列数据进行排序的典型命令,即:

sort -gk4 data.txt > data_4sort.txt

但是,在这种情况下,我需要对每个数据块进行排序。另外,每个数据块没有统一的行数,但每个数据块的数据行数是不同的。

我真的不知道我怎么能做到这一点。我正在考虑使用带有 for 循环和/或 awk、sed、split 等的 shell 脚本和 sort 命令。但是我什至不确定如何使用 for 循环来解决这个问题,或者是否真的需要 for 循环和 shell 脚本来执行这种逐块排序。

回答

使用 awk 准备要排序的文本,然后将其通过管道传递给 1 次调用sort,不会产生子shell,也不会创建临时文件:

$ cat tst.sh
#!/usr/bin/env bash

awk '
BEGIN { OFS="t"; recNr=1 }
NF < pNF { ++recNr }
{
    print recNr, NF, NR, $0
    pNF = NF
}
' "${@:--}" |
sort -k1,1n -k2,2n -k7,7nr -k3,3n |
cut -f4-
$ ./tst.sh file
2
1
A    11.364500   48.395199   40.160500         5
B    35.067699   30.944700   24.155300         4
4
2
A    26.274099   38.533298   32.624298         4
A    15.850300   28.130899   24.753901         4
A    32.098000   33.665699   20.372499         4
B    36.459000   29.662701   17.806299         5
5
3
B     1.326100   17.127600   39.600300         4
B    35.853001   43.095901   17.402901         4
B    31.686800   44.997501   16.619499         4
A     9.837760   41.103199   13.062300         5
A    17.501801   44.279202    8.005670         5
3
4
B    31.274700    8.726580   25.267599         4
A    19.032400   41.384701   19.456301         5
B    19.441900   24.286400    6.961680         4
1
5
B    48.973999   15.508400    5.099710         4
6
6
B    31.437799   30.415001   37.454399         4
A    39.311199   27.371201   35.473999         5
B    30.145800   13.256200   30.713800         4
A    42.586601   21.343800   23.280701         5
B    48.501999    1.277730   21.900600         4
B    29.186001   44.353699    9.057160         4

这是上述 OPs 输入文件执行速度的第三次运行时间差异:

$ time ./tst.sh file >/dev/null

real    0m0.105s
user    0m0.030s
sys     0m0.091s

和当前的 awk 答案为每个要排序的输入块生成一个子shell:

@IceCreamTucan 的回答:

$ cat tst2.sh
#!/usr/bin/env bash

awk '{
  if(NF > 1){
    print | "sort -k4,4nr"
  }
  else{
    close("sort -k4,4nr")
    print
  }
}' "${@:--}"
$ time ./tst2.sh file >/dev/null

real    0m0.405s
user    0m0.120s
sys     0m0.121s

@paxdiablo 的回答:

$ cat tst3.sh
#!/usr/bin/env bash

awk '
    function sort() {
        close(tmp_file)
        system("sort -rnk4 "tmp_file"; rm -rf "tmp_file)
    }
    BEGIN { tmp_file = "/tmp/tmp_file" }
    state == 0 && NF == 1 { print ; next }
    state == 0 && NF != 1 { print >tmp_file ; state = 1 ; next }
    state == 1 && NF != 1 { print >>tmp_file ; next }
    state == 1 && NF == 1 { sort() ; print ; state = 0 ; next }
    END { if (state == 1) { sort() } }
' "${@:--}"
$ time ./tst3.sh file >/dev/null

real    0m0.804s
user    0m0.060s
sys     0m0.396s


以上是连续排序重复数据但行数不同的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>