我有这样的数据(真实数据有超过 50,000 位数字和 8000 行):
输入:
1 11122
1 21121
2 22221
2 11122
3 21121
3 11122
我想将第二行的值放在同名的第一行的值旁边。此外,每对值之间应该有两个空格作为分隔符,并且不同对值之间应该有一个制表符作为分隔符。输出应如下所示:
输出:
1 1 2 1 1 1 1 2 2 2 1
2 2 1 2 1 2 1 2 2 1 2
3 2 1 1 1 1 1 2 2 1 2
有什么建议吗?
答案1
我会使用 perl,并将其作为 oneliner 运行,如下所示:
perl -wne 'sub parseline { ($id,$v) = split; return split //,$v };
@a = parseline();
print "$id\t";
$_ = <>;
@b = parseline();
for ($i=0; $i<@a; $i++) {
print "$a[$i] $b[$i]\t"
};
print "\n"' < input > output
解释:
perl -wne
为每行输入运行其余命令sub parseline { .... }
将解析输入,并将行中的第一个数字设置为$id
,并将其余部分作为字符数组返回。@a=parseline()
将在数组 @a 中存储第一行字符- 接下来,我们打印
$id
,然后按 TAB (\t
) $_=<>; @b=parseline();
将读取下一个(偶数)行并将其数据放入数组中@b
for ($i=0; $i<@a; $i++) { print "$a[$i] $b[$i]\t" }
对于数组的每个元素@a
,我们将打印该元素、两个空格、数组中的相应元素@b
,然后打印 tabprint "\n"
将在末尾打印换行符- 由于启动时
-n
的参数 to ,整个过程将从第 3 行开始,然后是第 5 行,然后是第 7 行,依此类推。perl
< input > output
指示我们从哪个文件读取输入,以及将输出写入哪个文件。
注意:代码将在每行末尾打印额外的选项卡。删除它作为读者的练习,以防止众包作业并保持代码简单一些。此外,代码还假设要配对的行始终是两行且一个接一个(如示例中所示)
当它逐行处理输入文件时,它可以轻松地线性缩放数千行......
答案2
这是 Matija Nalis 脚本的一个版本,它使用模块pairwise
中的函数List:MoreUtils
来连接两个数组,并且也不要求具有匹配 ID 的行(第一个字段)位于连续行上。即它们可以由任意数量的行分隔。
#! /usr/bin/perl
use strict;
use warnings;
use List::MoreUtils qw(pairwise);
sub parseline { my ($id,$v) = split; return $id, split //,$v };
my %ID=();
while (<>) {
my ($id, @line) = parseline();
if ( !defined($ID{$id}) ) {
push @{ $ID{$id} }, @line ;
} else {
my @paired = pairwise { "$a $b" } @{ $ID{$id} }, @line;
print join("\t", $id, @paired), "\n";
delete $ID{$id};
};
};
$id
MN 的parseline
子例程已修改为返回每行中的 id 和元素数组,而不是使用全局变量。
$id
%ID
用作存储每个已解析行的哈希的键。当我们第一次看到给定的 时$id
,我们只是将解析后的行数组 ( @line
) 存储在哈希中,然后继续处理下一行。下次看到它时,我们将存储的数组与当前@line
数组成对连接,用 TAB 字段分隔符打印出来,然后$id
从%ID
哈希中删除它。
man List::MoreUtils
有关该pairwise
功能如何工作的详细信息,请参阅 参考资料。顺便说一句,List::Util
和List::MoreUtils
是两个出色的模块,用于执行各种列表(也称为数组)操作。
输出:
$ ./zara.pl zara.txt
1 1 2 1 1 1 1 2 2 2 1
2 2 1 2 1 2 1 2 2 1 2
3 2 1 1 1 1 1 2 2 1 2