2013-03-26 60 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

相關問題