Beefy Boxes and Bandwidth Generously Provided by pair Networks
The stupid question is the question not asked
 
PerlMonks  

Using 2d hash to print a matrix

by angerusso (Novice)
on Oct 28, 2011 at 06:24 UTC ( [id://934314]=perlquestion: print w/replies, xml ) Need Help??

angerusso has asked for the wisdom of the Perl Monks concerning the following question:

Dear Monkers: I am trying to input the following file
AA SET1 0
AA SET2 1
AA SET3 0
BB SET1 0
BB SET3 0
CC SET1 2
CC SET2 3
and print the following
NAME SET1 SET2 SET3
AA 0 1 0
BB 0 0
CC 2 3
My questions is that I am not able to get the right format for the output that I want. I created a 2D hash but got confused when I tried to print using "foreach". I have just started to learn perl and having trouble with printing hashes

%hashname = (); %hashlow = (); %hashhigh = (); open(INPUTL,"<$listfile") || die "Can't open \$listfile for reading.\n +"; while($line=<INPUTL>){ chomp $line; @toks = split(/\t/, $line); $dataset = $toks[0]; $gene = $toks[1]; $hashname{$dataset} = $dataset; $hashlow{$gene}{$dataset} = $toks[2]." ($toks[4])"; $hashhigh{$gene}{$dataset} = $toks[3]." ($toks[4])"; } close(INPUTL); foreach $gene (sort keys %hashlow){ if ($gene =~ /AA/){ foreach $set (sort keys %{$hashlow{$gene}}){ print OUTD2 NAME."\t".$set; } } } print "\n"; foreach $gene (sort keys %hashlow){ print OUTD2 $gene."\t"; foreach $set (sort keys %{$hashlow{$gene}}){ print OUTD2 $hashlow{$gene}{$set}."\t"; } print OUTD2 "\n"; }

Replies are listed 'Best First'.
Re: Using 2d hash to print a matrix
by moritz (Cardinal) on Oct 28, 2011 at 06:48 UTC

    Always start your script with use strict; use warnings;, and declare your variables with my. It will catch some of simple errors you are making, like writing to an unopened file handle.

    It will also warn you that $toks[3] and $toks[4] are undefined, since none of your input lines contains more than three fields. In fact none of your input lines contain more than one field, because you split at a tab character, but the fields in your input file are just separated by spaces.

    Also there's no need to include the word hash in the variable names, because the % sigil at the front tells you already that it's a hash.

    When you have fixed these problems in your code, feel free to post another version, and we'll work on the actual program logic.

Re: Using 2d hash to print a matrix
by GrandFather (Saint) on Oct 28, 2011 at 07:21 UTC

    There are a number of things you need to correct aside from the hash related issues. First off, always use strictures (use strict; use warnings;). Use the three parameter version of open and lexical file handles (see the open in the sample code below). You don't need to initialise hashes and arrays. String interpolation is generally more readable than concatenated string fragments.

    The following sample uses a "trick" that lets me use a string as an input file to save needing an external file. It's easier to see what's going on if you split to a variable list than to an array and much easier to validate the code. Your code is not consistent with your data so I've pretty much ignored you code and simply followed your "specification". Consider:

    use strict; use warnings; my $str = <<STR; AA SET1 0 AA SET2 1 AA SET3 0 BB SET1 0 BB SET3 0 CC SET1 2 CC SET2 3 STR my %rows; my %sets; open my $fIn, '<', \$str or die "Can't open input file: $!\n"; while (defined (my $line = <$fIn>)) { chomp $line; my ($name, $set, $value) = split /\s+/, $line; next if !defined $value; $rows{$name}{$set} = $value; ++$sets{$set}; } my @setNames = sort keys %sets; print join ("\t", 'Name', @setNames), "\n"; for my $rowName (sort keys %rows) { print join ("\t", $rowName, map {defined $_ ? $_ : ''} @{$rows{$rowName}}{@setNa +mes}), "\n"; }

    Prints:

    Name SET1 SET2 SET3 AA 0 1 0 BB 0 0 CC 2 3

    @{$rows{$rowName}}{@setNames} is a hash slice - it returns the list of values for the list if keys provided by @setNames. The map replaces undef values for missing entries with an empty string.

    True laziness is hard work

      This one works beautifully too! Thanks so much for all the help so quickly. I am loving Perl monks!!

Re: Using 2d hash to print a matrix
by atcroft (Abbot) on Oct 28, 2011 at 07:04 UTC

    I know it can be done more efficiently, but this appears to do what you are hoping for (hopefully in an understandable way). It differs that it uses the DATA handle instead of a separate file, and prints a space if there was no data for a particular name/set pair.

    use strict; use warnings; my %hash; my %found; while (my $line = <DATA>) { chomp $line; next if ($line =~ m/^\s*$/); my @part = split /\s+/, $line; $found{$part[1]}++; $hash{$part[0]}{$part[1]} = $part[2]; } my @sets = sort keys %found; print join(q{ }, q{NAME}, @sets), qq{\n}; foreach my $k (sort keys %hash) { my @data; foreach my $l (@sets) { if (defined $hash{$k}{$l}) { push @data, $hash{$k}{$l}; } else { push @data, q{ }; } } print join(q{ }, $k, @data), qq{\n}; } __DATA__ AA SET1 0 AA SET2 1 AA SET3 0 BB SET1 0 BB SET3 0 CC SET1 2 CC SET2 3

    And the output:

    NAME SET1 SET2 SET3 AA 0 1 0 BB 0 0 CC 2 3

      I just finished trying this code for a smaller table. It works perfectly thanks. I am now going to run though it my big file. Thanks again! <\p>

      How do you print the tab de-limited values in this code? I did the following but it puts a tab in first column as well instead of just tab limiting after first columns. I am learning about this join command so i apologize for dumb questions.

      print OUTD2 join("\t", q{ }, $k, @data), qq{\n};

        You don't need the 'q{ }' in the line then. The join() command assembles a single string from the second and following parameters using the first parameter as a separator. So, if you said $str = join(q{asdf}, 1, ( 2, 4, 6 ));, then $str would contain '1asdf2asdf4asdf6', or if you said $str = join(q{|}, qw/a s d f z x c v/);, $str would contain 'a|s|d|f|z|x|c|v'. In the line you mention, there would be a space, followed by a tab character, then the value of $k, then a tab character, then the values in @data, separated by tab characters.

        Hope that helps.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://934314]
Approved by davido
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others wandering the Monastery: (6)
As of 2024-04-18 19:22 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found