2013-03-26 55 views
0

我很新的Perl和有,我想通过Perl来完成一项任务:查找多个文件的公共密钥和不同的值存储到一个数组,并计算不同

我有很多的文件看起来像这样的:(空间deliminated,每个人都有行6列,数千;所有文件以* .hgt结束)

example.hgt

ID  NAMES   Test1  Test2  Percentage  Height 
1  abc10A   B   0.21   165 
1  abc40A   B   0.99   162 
1  abc30C   B   0.107   165 
1  abc20A   E   0.31   167 
1  abc50A   B   0.7    165 
.... 

每个名字在每个.hgt文件是唯一的。我想找到所有.hgt文件中常见的NAMES,并提取所有百分比,并找出最高和最低数字之间的最大差异。

例如,如果我有5个.hgt文件,并且它们都包含NAMES = abc300123,并且相应的百分比是:0.107,0.1,0.4,0.9,0.8,那么abc300123的最大差异应该是0.9 - 0.1 = 0.8

然后我想输出NAMES和与从我的所有文件计算出来的NAMES相关的最大差异。输出的顺序按最大差异排序。每行前面有一个整数(0,1,2,3,...)。一个例子是这样的:

输出

0. abc50.1 
1. abc90.3 
2. abc10.7 
3. abc30.8 
4. abc11.9 
.... 

我试图通过每个文件中读取并存储在密钥=名称和值=百分比成阵列。我想对Percentage数组进行排序,并将最大值和最小值存储到新数组中,并进行负数计算。有些时候我陷入了困境,无法把事情放在一起。

这里是我写到目前为止:

open(PIPEFROM, "ls *.hgt |") or die "no \.hgt files founded\!\n"; ## find the files that are ended with hgt 
$i=0; 
@filenames = ""; 

while($temp = <PIPEFROM>){ 

    $temp =~ m/\.hgt/; 
    print out "$temp"; 
    $pre = $`; #gives file name without the dot and the hgt extension 
    $filenames[$i] = $pre; 
    $i++; 
} 


%hash =(); 
$j=0; 
## read in files ended with .hgt 
for ($i = 0; $i<=$filenames; $i++) { 
$temp = $filenames[$i]; 

open(PIPETO, "cat $temp.hgt |") or die "no \.hgt files founded\!\n"; 

<PIPETO>; 
while ($temp2 = <PIPETO>){ 
    chomp $temp2; 
    $temp2 = ~ s/^\s+//; 
    @lst = split(/\s+/, $temp2); 
    $NAMES = $lst[1]; 
    $Percentage = $lst[4]; 
    $hash{$NAMES} .= $Percentage . " "; 
} 
} 
### manipulate the values 
foreach $key (sort keys %hash){ 

    @values = split(/\s+/, $hash{$key}); 
    if ($#values == $#filenames){ 
    print "$j" . "\." . " " . "$key" . "\n"; 
    $j++; 
         ### got stuck 
} 
} 

我想包括到这个问题,但我不知道在哪里把它:

my ($smallest, $largest) = (sort {$a <=> $b} @array)[0,-1]; 

这是如此令人沮丧的。任何形式的帮助将不胜感激!

+0

你说每个文件中有成千上万的独特行,但可能的差异只能是样本中十个值中的一个(0.0 - 0.9)。如果是这样的话,你可能会有数百行的差异。对我来说没有意义。 :-) – 2013-03-26 02:30:21

+0

每一行是由“名称”和可能存在的差异可以是0和1之间的任何东西,如0.1,0.25,区分0.981等,依赖于最大和最小值在第5列中这使得对每个更小线百分比差异。百分比差异首先排序,NAMES排序下一个。 – user1687130 2013-03-26 03:08:27

+0

您的示例显示输出按最小百分比排序,即按升序排序。如果这不是您想要的,则在sort命令中将$ a和$ b更改为$ b和$ a。 – 2013-03-26 18:11:18

回答

1

指定正是这个程序的作用:

# output.pl 
# save this entire script as output.pl 
# obtain output by running this command: 
# 
# cat *.hgt | perl output.pl | more 
# (in order to scroll the results--press "q" in order to quit) 
# 
# cat *.hgt | perl output.pl > results-largest-differences-output-$$.txt 
# in order to create a temporary results file 
# 
# BE CAREFUL because the second command overwrites whatever is in 
# the output file using the ">" operator! 
my %names; 
my $maxcount = `ls *.hgt | wc -l`; 
my %counts; 
while (<>) { 
my @fields = (m/(\S+)/g); 
my $name = $fields[1]; 
my $perc = $fields[4]; 
next if $perc =~ m/[^.\d]/; 
next unless $perc; 
my $t = ($names{$name} ||= [1, 0]); 
# initialize min to as high as possible and max to as low as possible 
$t->[0] = $perc if $perc < $t->[0]; 
$t->[1] = $perc if $perc > $t->[1]; 
$counts{$name}++; # n.b. undef is auto-initialized to 0 before ++ 
} 

for (keys %names) { 
$names{$_} = $names{$_}->[1] - $names{$_}->[0]; 
} 

my $n = 0; 
for (sort { $names{$a} <=> $names{$b} || $a cmp $b } keys %names) { 
next unless $counts{$_} == $maxcount; 
$n++; 
printf("%6s %20s %.2f\n", $n, $_, $names{$_}); 
} 
+0

谢谢。但是,这似乎打印了所有我的测试文件中发现的NAMES和最大百分比差异。有没有办法让我只能打印那些在每个文件中都找到的文件? – user1687130 2013-03-26 03:30:59

+0

当然,只需添加另一个哈希,如我的%计数;以及像我的$ maxcount = 0;的最大数量。在主循环中添加一行记录每个$ name的计数。任何时候都有一个比现有的maxcount更大的计数,然后将maxcount设置为与此相同。然后在最终的输出中,在printf前面放置一个if语句,该语句只在该名称的计数等于maxcount时才运行。 – 2013-03-26 03:38:31

+1

既然你澄清了你想要的,我添加了几行代码。我正在为你写这篇文章,鼓励你学习perl,这是做这种一百万个小工作的最有效方法之一。 – 2013-03-26 17:59:47

2

大厦约瑟夫·迈尔斯的答复,我做了一些改动,以回答有关如何获得只发生在所有文件的数据,如何的问题跳过标题行(输入文件中的行#1),并将输出按最大百分比排序为最小值,并在百分比相等时按名称排序。运行程序的命令行条目如下所示:

perl output.pl *.hgt

my $file_count = @ARGV or die "invoke program as:\nperl $0 *.hgt\n";

这在所有的* .hgt到@ARGV阵列,读取(而不是通过管道猫将其看成自己的程序一样)。然后$file_count将记录读取的文件数量。while循环读取@ARGV中包含的文件,类似于管道猫。

在第一个for循环中,检查是否在每个文件中都读入名称(if ($names{$name}{count} == $file_count))。如果是这样,它计算的百分比之间,如果没有区别,删除从%names散列名称。

最后for循环使用打印自定义排序,by_percent_name结果。

#!/usr/bin/perl 
use strict; 
use warnings; 

my $file_count = @ARGV or die "invoke program as:\nperl $0 *.hgt\n"; 

my %names; 
while (<>) { 
    next if $. == 1; # throw header out 
    my ($name, $perc) = (split ' ')[1,4]; 
    $names{$name}{count}++; 
    my $t = $names{$name}{minmax} ||= [1,0]; 
    $t->[0] = $perc if $perc < $t->[0]; 
    $t->[1] = $perc if $perc > $t->[1]; 
    close ARGV if eof; # reset line counter, '$.', to 1 for next file 
} 

for my $name (keys %names) { 
    if ($names{$name}{count} == $file_count) { 
     $names{$name} = $names{$name}{minmax}[1] - $names{$name}{minmax}[0]; 
    } 
    else { 
     delete $names{$name}; 
    } 
} 

my $i; 
my $total = keys %names; 
for my $name (sort by_percent_name keys %names) { 
    printf "%*d. %s %.6f\n", length($total), ++$i, $name, $names{$name}; 
} 

sub by_percent_name { 
    $names{$b} <=> $names{$a} || $a cmp $b 
} 
+0

在百分比相同的情况下名称比较的巨大贡献。你不需要把头部丢掉。我的程序已经处理了所有这些。此外,匹配非空白字符而不是在单个空格字符处进行分割更加稳健,因为我们无法相信数据文件的确切格式。例如,他在例如给数据文件不是用空格隔开)。事实上,你可以复制并粘贴整个原来的问题到我的程序,它只是提取数据线。但是,谢谢。 – 2013-03-26 18:09:17

+0

@Joseph迈尔斯我看到你是如何处理的头 - -guess我只是用另一种方式,并不是说我认为这是更好的。我只是添加了一些不同的方法。 – 2013-03-26 19:54:06

+0

这一切都很好,谢谢!现在回想起来,我想我会用@ARGV像你这样,因为这样我就不需要再做LS * .hgt。所以保存一行代码就浪费了一个代码。我没有意识到OP只想要发生在所有文件中的数据。实际上,对我来说这仍然没有意义,因为对我来说,一个实验室会随着时间的推移进行测量,可能没有任何变量会存在于所有的文件中。例如,如果他们只需要至少有两个/三个/ N个测量值的数据,那么意义何在。但是,这是一种我喜欢的perl程序,所以它都很好! – 2013-03-26 20:01:41

相关问题