> On Sep 12, 2016, at 6:24 AM, Nathalie Conte <nco...@ebi.ac.uk> wrote:
> 
> Dear all,
> 
> Thanks a lot for the codes various people which all work perfectly!! I have 
> also discover some useful functions (eval and state) which can also be very 
> helpful for this kind of data.
> 
> In the light of this, my dataset got more complicated, now there are 3 layers 
> of hashes and I need now to calculate across those hashes for each name, 
> I have tried to apply some of the code structure which were kindly provided 
> and worked well for my first question, although as the data structure is now 
> more complicated and I need to assess the data accross hashes makes it more 
> complex and I din’t manage to get  a solution for this. Again, any help would 
> be greatly appreciated.
> ##############
> 
> 
> ########################
> For each $name (100 and 101)- there are 3 levels, x, y and z -Times are 1,3,5 
> for all datasets, across x, y and z .

You call the second level keys “levels” here, but in your code the variable you 
use to hold these key values is $subject. What you call “times” here are held 
in a variable called $values. That is confusing. It is better if your variable 
names describe what values they hold, e.g. $level, $time.

> What needs to be calculated now with this more complex structure,  is  for 
> each name, a calculation across the 3 hashes, for the 3 times.
> 
> for 100:
> first line : no action
> second line : should reflect the difference between time 3 and time 1 across 
> x,y and z (x3-x1)+(y3-y1)+(z3-z1)
> time :3 - 1 =2, 
> value: calculation:  x (value 3(117)-value 1(97) + y (value 3 (89)) - value 1 
> (88)) + z (value 3 (97) - value 1 (95))
> total should be : 117-97 + 89-88 +97-95 =13 

I think that should be 23, because (117-97) is 20.

> third line: should reflect difference between time 5 and time 3 AND time 5 
> and time 1
> time : 5-3, and 5-1
> value: calculation: difference between time 5 and time 3 : x (-value5 (107) - 
> value 3(117)) + y (value5 (99)- value 3 (89)) + z (value 5 (94) - value 3 
> (97) ) = -3 total
>                   difference between time 5 and time 1 : x (-value5 (107) - 
> value 1(97)) + y (value5 (99)- value 1 (88)) + z (value 5 (94) - value 1 (95) 
> ) = 20 total

So it looks like you have a three-level hash of values (grades?) indexed by 
name (‘100’, ‘101’), level (‘x’, ‘y’, ‘z’), and time (1, 3, 5). For each name 
you want to iterate over pairs of times and perform calculations done by 
grouping the grades according to levels.

The first thing to notice is that you need to reorganize your hash so you can 
iterate over names and times.

> 
> #######################
> my partial code,
> #!/usr/local/bin/perl
> use strict;
> use warnings;
> use feature 'state';
>  
> use Data::Dumper qw(Dumper);
> my %hall;
> 
> $hall{"100 "}{'x'}{1}   = 97;
> $hall{"100 "}{'x'}{3}    = 117;
> $hall{"100 "}{'x'}{5}    = 107;
> $hall{"100 "}{'y'}{1}   = 88;
> $hall{"100 "}{'y'}{3}  = 89;
> $hall{"100 "}{'y'}{5}  = 99;
> $hall{"100 "}{'z'}{1}  = 95;
>  $hall{"100 "}{'z'}{3}  = 97;
>  $hall{"100 "}{'z'}{5}  = 94;
> $hall{"101 "}{'x'}{1}   = 197;
> $hall{"101 "}{'x'}{3}    = 1117;
> $hall{"101 "}{'x'}{5}    = 1107;
> $hall{"101 "}{'y'}{1}   = 188;
> $hall{"101 "}{'y'}{3}  = 189;
> $hall{"101 "}{'y'}{5}  = 199;
> $hall{"101 "}{'z'}{1}  = 195;
>  $hall{"101 "}{'z'}{3}  = 197;
>  $hall{"101 "}{'z'}{5}  = 194;
>  
> print Dumper \%hall;
> print "----------------\n";
> 
> foreach  my $name ( sort keys %hall) {
> print "name.$name\n";
>     foreach  my $subject (sort keys %{ $hall{$name} }) {
> print "suject.$subject\n";
> 
>       foreach  my $values  ( sort  keys %{ $hall{$name}{$subject}}) {
>         state ($lgrade,$lsubject,$lname,$ltimes);
> 
>        my $grade = $hall{$name}{$subject}; 
> print "grade is .$grade\n"; # this is not working and gives a reference!

%hall has three levels of hashes. So any value of %hall with only two indices 
will be a reference to a hash. if you want the scalar grade values, you must 
supply three indices.

>       my $times = $hall{$name}{$subject}{$values};
> print "time is .$times\n";
>       
>         if (eval { $lgrade and $lname eq $name }) { 

Why the eval? You can evaluate logical expressions without doing the eval.

> 
>             my ( $grade_diff, $times_diff) = ( $grade - $lgrade, 
> $times-$ltimes);
>             print "$name,  
> $grade-$lgrade=$grade_diff:$name,$times-$ltimes=$times_diff \n";
>         }
>         ($ltimes, $lgrade,$lname) = ($times, $grade,$name);
>     }
> }
> }

Try this:

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

my %hall;

$hall{"100 "}{'x'}{1} =   97;
$hall{"100 "}{'x'}{3} =  117;
$hall{"100 "}{'x'}{5} =  107;
$hall{"100 "}{'y'}{1} =   88;
$hall{"100 "}{'y'}{3} =   89;
$hall{"100 "}{'y'}{5} =   99;
$hall{"100 "}{'z'}{1} =   95;
$hall{"100 "}{'z'}{3} =   97;
$hall{"100 "}{'z'}{5} =   94;
$hall{"101 "}{'x'}{1} =  197;
$hall{"101 "}{'x'}{3} = 1117;
$hall{"101 "}{'x'}{5} = 1107;
$hall{"101 "}{'y'}{1} =  188;
$hall{"101 "}{'y'}{3} =  189;
$hall{"101 "}{'y'}{5} =  199;
$hall{"101 "}{'z'}{1} =  195;
$hall{"101 "}{'z'}{3} =  197;
$hall{"101 "}{'z'}{5} =  194;
 
# reorder hash from ( name, level, time ) to ( name, time, level )
my %rehash;
for my $name ( keys %hall) {
        for my $level ( keys %{$hall{$name}} ) {
                for my $time ( keys %{$hall{$name}{$level}} ) {
                        $rehash{$name}{$time}{$level} = 
$hall{$name}{$level}{$time};
                }
        }
}

# iterate over names
for my $name ( sort keys %rehash ) {
        print "\nName: $name\n";

        # calculate sums over levels for each time
        my $name_ref = $rehash{$name};
        my @pg;
        for my $time ( sort keys %{$name_ref} ) {
                my $time_ref = $name_ref->{$time};
                my $sum;

                # sum over all levels
                for my $level ( sort keys %{$time_ref} ) {
                        my $grade = $time_ref->{$level};
                        $sum += $grade; 
                }
                # save results in array
                push( @pg, [ $time, $sum ]);
                
                # if more than one saved sum, calculate difference with all 
previous sums
                if( $#pg > 0 ) {
                        for my $i ( 0..$#pg-1 ) {
                                my( $t, $s ) = @{$pg[$i]};
                                my $val = $sum - $s;
                                print "Difference between time $time and time 
$t:  $val\n";
                        }
                }
        }
}
__END__

Which produces this:

Name: 100 
Difference between time 3 and time 1:  23
Difference between time 5 and time 1:  20
Difference between time 5 and time 3:  -3

Name: 101 
Difference between time 3 and time 1:  923
Difference between time 5 and time 1:  920
Difference between time 5 and time 3:  -3




--
To unsubscribe, e-mail: beginners-unsubscr...@perl.org
For additional commands, e-mail: beginners-h...@perl.org
http://learn.perl.org/


Reply via email to