1

I have two data sets: 'Data 1' and 'Data 2'. Could you please help me to find, for each value of posi in 'Data 1', the ranges in 'Data 2' where posi lies between Star_posi and end_posi.

Data 1

  Num     posi 
   1        2 
   2        14
   3        18
   4        19
  ...      ...

Data 2

 Num      Star_posi    End_posi
  1          1            10
  2          3            15
  3          17           21
  4          23           34
 ...       ...           ...

Output

  1. Data 1 at posi 2 contained in Data 2 between star_posi 1 and end_posi 10.
  2. Data 1 at posi 14 contained in Data 2 between star_posi 3 and end_posi 15.

I want to identify the rows in Data 2 where the value in Data 1 is contained in the range of the row in Data 2. I made the script below, but I did not get far.

   #!/usr/bin/perl -w
   use strict;
   use warnings;
   use Data:ump qw(dump);

   #Sort the position**************

   my (@posi1, $Num2, @Num2, @Num1);
   open(POS1,"<posi.txt");
   @posi1=<POS1>;
   @Num1=@posi1;
   open(LIST,">list.txt"); {
   @Num2= sort {$a <=> $b} @Num1;
   $Num2 = join( '', @Num2);
   print $Num2;
   print LIST $Num2."\n";
   }
   close(LIST); 

I would appreciate if you could give some pointers.`

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Phan
  • 47
  • 1
  • 6

2 Answers2

3

Your code is a mess. Also, it does not address your problem in any way.

What you want to do is split the lines from the file in a while loop, storing them in a hash. Once you have the values, you can easily compare them with the < and > operators to see in what ranges they fall.

use strict;
use warnings;
use autodie;

my (%data1,%data2);


open my $in, '<', 'data1.txt';
while (<$in>) {
    next unless /^\s*\d/;
    my ($num, $posi) = split;
    $data1{$num} = $posi;
}

open $in, '<', 'data2.txt';
while (<$in>) {
    next unless /^\s*\d/;
    my ($num, $star, $end) = split;
    $data2{$num}{'star'} = $star;
    $data2{$num}{'end'}  = $end;
}
close $in;

Note that I am skipping (next) any lines which do not start with numbers, e.g. headers and empty lines and other stuff we don't want in the data.

Now you will have the values in the hashes, and can perform what tests you need. For example:

for my $num (keys %data1) {
    my $val = $data1{$num};
    for my $num2 (keys %data2) {
        my $min = $data2{$num2}{'star'};
        my $max = $data2{$num2}{'end'};
        if ( ($val > $min) and ($val < $max) ) {
            print "Data 1 at posi $val contained in Data 2 between star_posi $min and end_posi $max.\n";
            last;
        }
    }
}

Good luck!

TLP
  • 66,756
  • 10
  • 92
  • 149
  • thank you so much. I edited with your code but It had some erro. EX "my" valible $ masks earlier declaration in some scope. could you please show me how to solve this erro. – Phan Aug 24 '11 at 02:12
  • Yes, a copy/paste error for me. `my $in` is declared in two places in the same scope, which gives a warning. Just remove the `my` from the second one. – TLP Aug 24 '11 at 02:15
  • I inserted if ( ($data1{$num} > $data2{$num}{'star'}) and ($data1{$num} < $data2{$num}{'end'}) ) and print $data1{$num} but it has erro: global symbol "$num" require explicit pakage name. COuld you please show me how to solve this erro. – Phan Aug 24 '11 at 02:31
  • Well, `$num` is a variable.. you need to declare it and puts some values into it. E.g. `for my $num (keys %data1) { ...`. – TLP Aug 24 '11 at 02:56
  • sorry TLP, I am new comer with perl so I can not understand what do you think about that and what I have to write then. could you please show me. – Phan Aug 24 '11 at 03:06
  • I did not write that last part because I was not sure what you wanted to check. Do you want to check only values in each "Num" row against each other? Or all values against all ranges? – TLP Aug 24 '11 at 03:10
  • I want to find that: if Star – Phan Aug 24 '11 at 03:26
  • I insert the line: else {print"no data\n";} and with my data at the high value (ex: posi 14185 star 16202 end 16543) all of them "print no data". how can i solve that? – Phan Aug 24 '11 at 04:17
  • EX: num 1 star (17)fale and at num 2 star (19)fale. But how about star(17) – Phan Aug 24 '11 at 04:42
  • If you want to check all the ranges, you need to create another loop. Perhaps you should try and do that yourself. If you do not learn at least rudimentary perl, you won't be able to maintain this script. – TLP Aug 24 '11 at 04:48
2

You should have a look at the CPAN module called Tie::RangeHash, which is for exactly this sort of problem.

use Tie::RangeHash;
my $hour_name = new Tie::RangeHash Type => Tie::RangeHash::TYPE_NUMBER;

$hour_name->add(' 0, 5', 'EARLY');
$hour_name->add(' 6,11', 'MORNING');
$hour_name->add('12,17', 'AFTERNOON');
$hour_name->add('18,23', 'EVENING');

# and in a loop elsewhere...
my $name = $hour_name->fetch($hour) || "UNKNOWN";
RET
  • 9,100
  • 1
  • 28
  • 33