我遇到了一个相当独特的问题。我有2个文件正在阅读。这两个文件的小版本如下所示:
File1中
chr1 9873 12227 11873 2354 + NR_046018 DDX11L1
chr1 760970 763155 762970 2185 + NR_047520 LOC643837
文件2
chr1 9871 0
chr1 9872 1
chr1 9873 1
chr1 9874 2
chr1 9875 1
chr1 9876 3
chr1 9877 3
chr1 760970 1
chr1 760971 1
chr1 760972 1
chr1 760973 2
chr1 760974 3
chr1 760975 3
chr1 760976 4
chr1 760977 5
chr1 760978 6
chr1 760979 7
chr1 760980 6
chr1 760981 7
chr1 760982 8
chr1 760983 9
chr1 760984 10
chr1 760985 11
chr1 760986 12
chr1 760987 10
chr1 760988 9
chr1 760989 6
从第一个文件开始,我必须从每一行中获取第二个元素并将其作为$start
。结束位置由$end = $start + 10
确定。
基于$start
,我现在必须获取第二个文件,并查看每行的第二个元素。找到$start
后,我需要将5个组中第3个元素的下5个对应值相加,直到$end
。
因此,当$end
为$start + 10
并且我以5个为一组进行求和时,将获得2个求和值。
如果第二个文件的第二个元素中不存在$end
的某些值,则代码不应该停止,它应该继续执行求和并将总和显示为0(如果是连续组,则为 5个元素不存在)。
以 File1 中的文件为例,第2个元素= 9873
,分配给$start
。因此$end
将是$start+10
,即9883.
从 File2 ,在行的第2个元素中找到$start
后,接下来的5行的第3个元素必须总计为1个组,接下来的5个值总计为第2组直到$end
。
注意
这里可以在 File2 中看到,$end
,即9883不存在。因此,从9879到9883的值之和必须是zero
。它不能总结760970以后的值...
chr1 9873 12227 11873 2354 + NR_046018 DDX11L1 10 0
chr1 760970 763155 762970 2185 + NR_047520 LOC643837 8 25
0
。 / LI>
我到目前为止编写的代码设法执行以下操作:
$start
和$end
@c_posn
;所有第3个元素都放入数组@peak
。$start
@c_posn
醇>
我无法弄清楚如何进行求和部分。我曾想过创建一个哈希,其中第二个文件的所有第二个元素都进入键,第三个元素进入值。但哈希是无序的。所以我为第二个元素创建了2个数组@c_posn
,为第3个元素创建了@peaks
。但是现在我不知道如何同时比较2个数组(以确保760970的值不能求和)
use 5.012;
use warnings;
use List::Util qw/first/;
my $file1 = 'chr1trialS.out';
my $file2 = 'b1.wig';
open my $fh1,'<',$file1 or die qw /Can't_open_file_$file1/;
open my $fh2,'<',$file2 or die qw /Can't_open_file_$file2/;
my($start, $end);
while(<$fh1>){
my @val1 = split;
$start = $val1[1]; #Assign start value
$end = $start + 10; #Assign end value
say $start,"->",$end; #Can be commented out
}
my @c_posn;
my @peak;
while(<$fh2>){
my @val2 = split;
push @c_posn,$val2[1]; #Push all 2nd elements
push @peak, $val2[2]; #Push all 3rd elements
}
if (first { $_ eq $start} @c_posn) { say "I found it! " } #To check if $start is present in @c_posn
say "@c_posn"; #just to check all 2nd elements are obtained
say "@peak"; #just to check all 3rd elements are obtained
感谢您抽出宝贵时间解决我的问题。如果需要任何澄清,请问我。 我将非常感谢任何评论/答案。
答案 0 :(得分:2)
你对哈希有正确的想法。它是否有序并不是特别相关,因为如果我理解正确,你正在寻找11个特定值(9873,9874,9875 ... 9883),而不是文件中的起始值和下一个10(9873) ,... 9877,760970,... 760975)。
根据您的描述,我会按照以下方式进行操作:
#!/usr/bin/env perl
use strict;
use warnings;
my $sum_interval = 5; # number of lines to group into each sum
my $sum_count = 2; # number of sums to generate
my @sums; # final results of the operation
my %lookup;
open my $fh2, '<', 'file2.txt' or die "Can't open file 2: $!";
while (<$fh2>) {
my @data = split;
$lookup{$data[1]} = $data[2];
}
close $fh2;
open my $fh1, '<', 'file1.txt' or die "Can't open file 1: $!";
while (my $line = <$fh1>) {
my @line_sums;
my $start = (split /\s+/, $line)[1];
for my $interval_num (0 .. $sum_count - 1) {
my $cur_sum = 0;
my $interval_start = $start + ($sum_interval * $interval_num);
for (0 .. $sum_interval - 1) {
# use || instead of // for Perl older than 5.10
$cur_sum += $lookup{$interval_start + $_} // 0;
}
push @line_sums, $cur_sum;
}
push @sums, \@line_sums;
}
use Data::Dumper; print Dumper(\@sums);
可以改进变量名称,但您可以将$sum_interval
和$sum_count
更改为25和400,它应该在您的实际应用程序中完全相同。
如果您提供的样本数据放入file1.txt
和file2.txt
,则会生成输出:
$VAR1 = [
[
10,
0
],
[
8,
25
]
];
如果我手工完成总和,此输出与我提出的结果相符。
请注意,我略微偏离了您的规范,因为它从$start
汇总到$start + 9
而不是$start + 10
,因为您说它应该总和为两组五个{{1} } $start
是11项。
修改:将初始伪代码修改为完整的可运行程序。
答案 1 :(得分:2)
如果b1.wig
足够小,可以读入内存中的哈希,从第2列获取密钥,从第3列中获取值,这很容易。然后,必须完成的只是访问每个键在每个序列中,如果相应的哈希元素不存在则使用零(因此访问它会返回undef
)。
您还没有说过如何将新总数与chr1trialS.out
中的现有数据分开,所以我使用了空格。当然,如果有必要,这很容易改变。
use strict;
use warnings;
use constant SAMPLE_SIZE => 10;
use constant CHUNK_SIZE => 5;
my $file1 = 'chr1trialS.out';
my $file2 = 'b1.wig';
my %data2;
{
open my $fh, '<', $file2 or die $!;
while (<$fh>) {
my ($key, $val) = (split)[1,2];
$data2{$key} = $val;
}
}
open my $fh, '<', $file1 or die $!;
while (<$fh>) {
chomp;
my $key = (split)[1];
my @totals;
my $n = 0;
while ($n < SAMPLE_SIZE) {
push @totals, 0 if $n++ % CHUNK_SIZE == 0;
$totals[-1] += $data2{$key++} // 0;
}
print "$_ @totals\n";
}
<强>输出强>
chr1 9873 12227 11873 2354 + NR_046018 DDX11L1 10 0
chr1 760970 763155 762970 2185 + NR_047520 LOC643837 8 25
答案 2 :(得分:1)
这是我目前的解决方案:
#!/usr/bin/perl
use 5.012; use warnings;
my $file1 = Reader->open("<", "filename1");
my $file2 = Reader->open("<", "filename2");
my $groupsize = 5;
my $step = 10;
my $sum_number = int($step / $groupsize) + ($step % $groupsize ? 1 : 0); # ceil($step/$groupsize)
use constant DEBUG_FLAG => 0;
sub DEBUG (@) { say STDERR "DEBUG: ", @_ if DEBUG_FLAG }
LINE1:
while (my $line1 = $file1->readline) {
my (undef, $start) = split ' ', $line1, 3;
my $end = $start + $step;
my @sums = (0) x $sum_number; # initialize all fields to zero
my $i = 0;
my $last;
LINE2:
while (my $line2 = $file2->readline) {
my (undef, $key, $val) = split ' ', $line2, 4;
if ($start > $key) { # throw away all keys that are too small
DEBUG "key $key too small for start $start";
} elsif ($key >= $end) { # termination condition
DEBUG "key $key too large for end $end";
$file2->pushback($line2);
last LINE2;
} else {
$last = $key unless defined $last;
$i += $key - $last; # get interval. This may be set to "1" as an optimization
DEBUG "counting ($i): $sums[$i/$groupsize] + $val at $key";
$sums[$i/$groupsize] += $val;
$last = $key;
}
}
DEBUG "inner loop broken";
say join "\t", $line1, @sums; # assuming tab-seperated output
}
{
package Reader;
# There is probably a CPAN module for this ... :/
use Carp;
use constant DEBUG_FLAG => 0;
sub open :method {
my ($class, $mode, $filename) = @_;
open my $fh, $mode, $filename or die qq(Can't open "$filename": $!);
bless [$fh, []] => $class;
}
sub readline :method {
my $self = shift;
return shift @{ $self->[1] } if @{ $self->[1] };
my $line = scalar readline $self->[0];
chomp $line if defined $line;
carp "readline: " . ($line // "undef") if DEBUG_FLAG;
return $line;
}
sub pushback {
my ($self, $line) = @_;
carp "pushback: " . ($line // "undef") if DEBUG_FLAG;
unshift @{ $self->[1] }, $line;
return $self;
}
sub eof :method {
my $self = shift;
eof $self->[0];
}
}
输出:
chr1 9873 12227 11873 2354 + NR_046018 DDX11L1 10 0
chr1 760970 763155 762970 2185 + NR_047520 LOC643837 8 25
此解决方案假定两个输入文件按第二个字段按升序排序,并且不会请求重叠序列。如果满足这些条件,它将在恒定的存储器和线性时间内执行。如果没有,它将产生垃圾,你可能有兴趣使用另一个答案(线性记忆,线性时间,没有限制)。事实上,Dave Sherohman的答案一般不那么脆弱,并且在大多数输入上可能表现得更快。
根据您的系统,如果丢弃所有对象方向,可能会提高速度,并且内联缓冲线的代码(或者更确切地说是一条线)。
关于$i = $key - $last
:如果跳过密钥,代码将继续有效,并且仍会将数字添加到右侧存储桶中。如果您可以声明不会跳过任何键,或者正确的总和无关紧要(前五行ID小于$end
,则不应添加下五个ID),然后删除{{1}变量,简单地将$last
递增一个就可以了。