http://qs321.pair.com?node_id=643792

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

I am new to perl. I have to convert the format of data as follows: Existing data format: (Please note that actual data contains more number of rows and colmns.. and not pre-defined)
----------------------- 1 2 3 a b c x y z -----------------------
The data format I need is:
----------------------- 1 a x 2 b y 3 c z -----------------------
I am going to use the new formatted data for an input to create graphs. I have all the necessary modules installed but this logic (I know it is not very complicated, but I am not getting it). Please advice how to take it forward. My final objective is to get the data in variable in somthing similar to following:
my @data = (['1','a','x' ], ['2','b','y' ], ['3','c','z' ]);
Please help.
=============================
Folks, The fact is that I am totally lost now :-) My objective was/is: To read a flat file which has SAR (system activity reporter) data and then to generate the graph using that data. I have module for graph but the problem here is the data needs to be transversed (row-> columns). My data looks like:
17:12:41 0 0 0 100 17:12:42 0 1 0 99 17:12:43 0 0 0 100 : :
And for the graph creation, I need this data in following format:
17:12:41 17:12:42 17:12:43 : : ... ... ... 0 0 0 : : ... ... ... 0 1 0 : : ... ... ... 0 0 0 : : ... ... ... 100 99 100 : : ... ... ...
Finally, to get an array like below:
my @data = ( ['17:12:41','17:12:42','17:12:43'.........], ['0','1','0'.........], ['0','0','0'.........], ['100','99','100'.....]);

Replies are listed 'Best First'.
Re: How to swap rows with columns?
by NetWallah (Canon) on Oct 09, 2007 at 20:18 UTC
    Math::Matrix has a transpose method for this purpose.

         "As you get older three things happen. The first is your memory goes, and I can't remember the other two... " - Sir Norman Wisdom

    A reply falls below the community's threshold of quality. You may see it by logging in.
Re: How to swap rows with columns?
by ikegami (Patriarch) on Oct 09, 2007 at 20:17 UTC

    Assuming the same number of fields on every row of the data file,

    my @data; while (<>) { my @fields = split ' '; for my $row (0..$#fields) { push @{$data[$row]}, $fields[$row]; } }

    Tested.

      Eh; too much pushing! ;-)

      sub transpose { local *_'_; map { $_'_ = $_; [ map $_->[$_'_], @_ ] } 0 .. $#{$_[0]} }

        That doesn't do what my code does at all, which is to avoid the need to fix the array by transposing it by creating it right in the first place. From the point of view, your code is a step backwards. If you wanted to change my code to avoid pushing, then you'd do

        my @data; while (<>) { my @fields = split ' '; my $col = $. - 1; for my $row (0..$#fields) { $data[$row][$col] = $fields[$row]; } }

        Tested.

        Good code, but with that very high sigil-to-text ratio, I was scrambling Google to find out what "*_'_" meant. (FWIW, I did not find the answer in Google, or the docs, but figured it out experimentally). (Side note - the single-quote also threw off my editor's perl-highlighter).

        Eventually, I decided I would not write code like that, fascinating as it is - it turns out to be just a way to avoid declaring a local variable. It works just as well replacing all instances of that and "$_'_" with "$Column_Index", and becomes a lot more readable ("local *_'_" being replaced by "my $Column_Index" helps by functioning without resorting to "no strict 'refs'").

        I'm hoping this node helps other monks who may not have unraveled "_'_".

        I'm also curious as to how this would be written in idiomatic perl6.

             "As you get older three things happen. The first is your memory goes, and I can't remember the other two... " - Sir Norman Wisdom

Re: How to swap rows with columns?
by ysth (Canon) on Oct 10, 2007 at 02:27 UTC
    Yet another module:
    use Algorithm::Loops "MapCarU"; my @data = MapCarU { [@_] } ['1','2','3'],['a','b','c'],['x','y','z']; use Data::Dumper; $Data::Dumper::Terse=1; $Data::Dumper::Indent=0; print Dumper \@data; __END__ [['1','a','x'],['2','b','y'],['3','c','z']]
Re: How to swap rows with columns?
by blah (Novice) on Oct 09, 2007 at 21:55 UTC
Re: How to swap rows with columns?
by dragonchild (Archbishop) on Nov 01, 2007 at 20:12 UTC
    use List::MoreUtils qw( each_array ); my $iterator = each_array( @a, @b, @c ); while ( my @col = $iterator->() ) { print "@col\n"; }

    My criteria for good software:
    1. Does it work?
    2. Can someone else come in, make a change, and be reasonably certain no bugs were introduced?
Re: How to swap rows with columns?
by mwah (Hermit) on Oct 09, 2007 at 21:16 UTC
    Aside from the modules and the compact solutions,
    I'll try a non-obfuscated solution first ;-) This will
    work on any 'text-matrix' as far as it's rectangular ...
    my $inp=' 1 2 3 4 5 6 a b c x y z '; my @data; my @elem = grep length, split /\s+/, $inp; my $rows = () = $inp =~/(?<=^)\w/msg; my $cols = @elem / $rows; for my $col (0..$cols-1) { push @data, [ map $elem[$col + $cols*$_], 0..$rows-1 ] } # print the matrix print "@$_\n" for @data;
    Regards

    mwa
      For a beginner, do you really think that
      my $rows = () = $inp =~/(?<=^)\w/msg;

      is non-obfuscated?

      I'd rate that as beginner-obfuscated because you use...

      • =()=
      • (?<=^) in your regexp
      • /msg as a regexp modifier
      I think these are a bit "in at the deep end" for someone new to Perl.

Re: How to swap rows with columns?
by johngg (Canon) on Oct 10, 2007 at 14:22 UTC
    I wondered how to do this if the rows were not all the same length. This is what I came up with.

    use strict; use warnings; use Data::Dumper; use List::Util q{max}; my @arr = ( [ qw{ ein zwei drei } ], [ qw{ one two three four five } ], [ qw{ un deux trois quatre } ], [ qw{ uno due tre quattro cinque sei} ], ); print Data::Dumper->Dumpxs([\@arr], [q{*arr}]); my @transposed = map { my $col = $_; [ map { exists $arr[$_]->[$col] ? $arr[$_]->[$col] : undef } 0 .. $#arr ] } 0 .. max(map { $#{ $_ } } @arr); print Data::Dumper->Dumpxs([\@transposed], [q{*transposed}]);

    Here's the output.

    I imagine some of the modules mentioned could cope with this but haven't checked.

    Cheers,

    JohnGG

      Nice variation of the problem.

      @arr = ( [ qw{ ein zwei drei } ], [ qw{ one two three four five } ], [ qw{ un deux trois quatre } ], [ qw{ uno due tre quattro cinque sei} ], );; push @xformed, [ map{ shift @$_ } @arr ] while @{ $arr[-1 ] };; print pp @xformed;; ( ["ein", "one", "un", "uno"], ["zwei", "two", "deux", "due"], ["drei", "three", "trois", "tre"], [undef, "four", "quatre", "quattro"], [undef, "five", undef, "cinque"], [undef, undef, undef, "sei"], )

      Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
      "Science is about questioning the status quo. Questioning authority".
      In the absence of evidence, opinion is indistinguishable from prejudice.
        Now that's very neat! BrowserUk++

        Cheers,

        JohnGG

Re: How to swap rows with columns?
by snopal (Pilgrim) on Oct 09, 2007 at 21:09 UTC

    Really! How hard is it to transpose the indexes of a pair of foreach loops?

      Isn't this so much easier to understand?

      #!/usr/bin/perl use warnings; use strict; my @in = ([1,2,3,4,'a','b','c'], ['z','y','x','w',9,8,7], ['e','f','g','h',5,4,6], ); my @out; for my $y (0..(@in-1)) { for my $x (0..(@{$in[$y]}-1)) { $out[$x][$y] = $in[$y][$x]; } }

      No need to be a Perl geek to do this right! And think of the ease of maintenance!

        for my $y (0..(@in-1)) { for my $x (0..(@{$in[$y]}-1)) {

        I'm puzzled as to why you do the subtractions to find the upper bound of the arrays rather than use the $#array that Perl provides.

        for my $y ( 0 .. $#in ) { for my $x ( 0 .. $#{$in[$y]} ) {

        Cheers,

        JohnGG

    A reply falls below the community's threshold of quality. You may see it by logging in.