It looks like what you want. I presented the data in the form you proposed, since the ideal form depends entirely on what you want to do with the result
It works by calculating a list of 0s and 1s from each column, adding zero barriers at each end (one in $prev and one in the for list), and then looking at the list for changes between 1 and 0
Each time a change occurs, the beginning or end of the track is recorded. If $start undefined, then the current index is written as the beginning of the segment, otherwise the current segment ends one less than the current index. The hash is built using the start and end keys and is placed in the @segments array.
The final set of nested loops unloads the calculated data in the form displayed in the question
use strict; use warnings; use constant THRESHOLD => 0.5; my @data = ( [ qw/ 0.2 0.7 0.2 / ], [ qw/ 0.6 0.8 0.7 / ], [ qw/ 0.6 0.1 0.8 / ], [ qw/ 0.1 0.2 0.9 / ], [ qw/ 0.6 0.3 0.0 / ], [ qw/ 0.6 0.9 0.2 / ], ); my @tracks; for my $colno (0 .. $#{$data[0]}) { my @segments; my $start; my $prev = 0; my $i = 0; for my $val ( (map { $_->[$colno] > THRESHOLD ? 1 : 0 } @data), 0 ) { next if $val == $prev; if (defined $start) { push @segments, { start => $start, end=> $i-1 }; undef $start; } else { $start = $i; } } continue { $prev = $val; $i++; } push @tracks, \@segments; } # Dump the derived @tracks data # for my $colno (0 .. $#tracks) { my $col = $tracks[$colno]; for my $track (0 .. $#$col) { my $data = $col->[$track]; printf "\$tracks[%d][%d]{start} = %d\n", $colno, $track, $data->{start}; printf "\$tracks[%d][%d]{end} = %d\n", $colno, $track, $data->{end}; } print "\n"; }
Output
$tracks[0][0]{start} = 1 $tracks[0][0]{end} = 2 $tracks[0][1]{start} = 4 $tracks[0][1]{end} = 5 $tracks[1][0]{start} = 0 $tracks[1][0]{end} = 1 $tracks[1][1]{start} = 5 $tracks[1][1]{end} = 5 $tracks[2][0]{start} = 1 $tracks[2][0]{end} = 3
source share