Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations John Tel on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

2 d matrix from file 1

Status
Not open for further replies.

eatr

Technical User
Jan 20, 2006
48
just starting out learning Perl

need to create a 2d matrix from a comma delimited file (could be any number or rows of data ---columns will remain constant)
e.g
A,1,4,2,5
C,3,2,3,0
B,2,1,1,2

thanks in advance




thanks
 
pretty wide open question, one way might be:

Code:
my @matrix = ();
while (<DATA>) {
   chomp;
   push @matrix, [ split(/,/) ];
}
print dump(@matrix);
__DATA__
A,1,4,2,5
C,3,2,3,0
B,2,1,1,2

This creates an array @matrix which contains anonymous arrays for each line of the data file.
 
Can't get it to work: compilation errors.

added the following at the top:

$data_file="t.txt";
open(DATA, $data_file) || die("Could not open file!");
 
sorry, I used the Data::Dump module to print out the data set but didn't post that it my code example:

Code:
use Data::Dump qw(dump);
my @matrix = ();
while (<DATA>) {
   chomp;
   push @matrix, [ split(/,/) ];
}
print dump(@matrix);
__DATA__
A,1,4,2,5
C,3,2,3,0
B,2,1,1,2

but you may not have that module installed, you can use Data::Dumper though which is a core module:

Code:
use Data::Dumper;
my @matrix = ();
while (<DATA>) {
   chomp;
   push @matrix, [ split(/,/) ];
}
print Dumper(@matrix);
__DATA__
A,1,4,2,5
C,3,2,3,0
B,2,1,1,2

I used Data::Dumper only for testing the data set/structure.
Basically it's going to print:

Code:
$VAR1 = [
          'A',
          '1',
          '4',
          '2',
          '5'
        ];
$VAR2 = [
          'C',
          '3',
          '2',
          '3',
          '0'
        ];
$VAR3 = [
          'B',
          '2',
          '1',
          '1',
          '2'
        ];

which just shows that @matrix has three anonymous arrays stored in it.
 
thanks; just what I needed

Additionally:

for the data:

C, 3, 5, 1, 2,
A, 2, 3, 2, 1
D, 4, 0, 3, 2
B, 1, 2, 4, 0

these are race running lines
the second/4th column is the position (at midpoint and end)
and the 3rd/5th column is the number of lengths ahead of the one immediately behind it (in position)

for example, the C was in 3rd place, 5 lengths ahead of the D at the halfway point and in the lead at the finish by 2 lengths over the A

what I'd like to do:

sum the lengths behind at each point
then insert 0 for the value of the leader at each point

the above data would look like:
before:
C, 3, 5, 1, 2,
A, 2, 3, 2, 1
D, 4, 0, 3, 2
B, 1, 2, 4, 0

after:

C,3,5,1,0
A,2,2,2,2
D,4,10,3,3
B,1,0,4,5

algorithm: summing the values corresponding from position minus 1 to 1.

thanks
don't know the syntax well enough yet.

to be used for an Excel graph.



 
more clearly:
the value in the 3rd column represents, for a given horse, the lengths it is ahead of the horse immediately behind it

and the value in the 5th column represents, for a given horse, the number of lenghts it is behind TOTAL at a given point in the race.

At the end, print to a file
column 0, 3, 5
for graphing

thanks again
 
don't know the syntax well enough yet.

What have you tried so far? I hope you understand, this is not a script writing service, most people will not help unless you show that you are putting in effort to learn/understand and write some code on your own. Is this school/class work?
 
If I knew the syntax to loop through the array I could write the code; specifically how to refer to the parts of the array.
Broadly:
The outside loop goes through a particular column:

foreach $element(@array[2]) {
for (i=$element.@array[2]-1; i < 1; i--) {
$element.@array[3]= sum(@array[3]).
}}

something like that

I'm sure it's off base

this isn't for school

just practicing , trying to learn the langauge

I've already written the front part to this: where I parse
a selected piece of HTML sourcecode to pull out the data file.


 
What you have to do is loop through the named array (@matrix), not try and loop through columns of the array. For example:

Code:
my $sum = 0;
foreach my $element (@matrix) {
   $sum += $element->[2];
}
print $sum;

this adds up the numbers in the third column of each row:

5+3+0+2

you have to use the arrow notation -> because each array inside @matrix is a reference, the arrow notation is used to dereference a reference. Of course, to get from this:

C, 3, 5, 1, 2,
A, 2, 3, 2, 1
D, 4, 0, 3, 2
B, 1, 2, 4, 0

to this:


C,3,5,1,0
A,2,2,2,2
D,4,10,3,3
B,1,0,4,5

is going to take some work. You will have to figure out which position each "horse" is in and add only the appropriate columns to arrive at the total lengths for that particular horse. This whole thing might be better handled with a hash of hashes or an array of hashes, but it's hard to say without writing code.
 
Thanks for the input.

Why can't something like the following be implemented?
I can't get the loops to work but
perhaps you can tell me if the method is feasible.
(I might need an additional for loop)


my $count= 0;
my $position = 0;
my $lengths = 0;


while ($count < $#matrix + 1) {
$position = $matrix[$count][1];

for ($i = 0; $i < $#matrix + 1;$i++){
# the loop is not working here
if ($position < $matrix[$i][1]) {
$matrix[$count][2] += $matrix[$i][2];
}
}
$count++;
}

for $i ( 0 .. $#matrix ) { # the BEST of prints
print "@{$matrix[$i];}\n";
}


 
I'm pretty sure something like you have could work, and yes, you made need to add more loops. Are you using the original data with the above code:

C,3,5,1,2
A,2,3,2,1
D,4,0,3,2
B,1,2,4,0
 
This is a probably a bit more complicated than is necessary, but it does what you're looking for. I tried including comments, but I'm not sure if it's commented well enough. This code doesn't really do enough error checking, to make sure the data is what's expected, etc... but see if this makes sense:
Code:
my (@input, %lengths, @places);
[gray]# Creates Array of Arrays with data from file handle[/gray]
map {chomp; push @input, [split(/\s*,\s*/, $_)]} <DATA>;

[gray]# Calculate Split Distances:
# Creates an array of arrays in @places ordering the arrays
# based on the split positions starting at index 0 (so first place
# is 0, 2nd place is 1, etc.) It also stores the name/label and the
# number of lengths ahead for each horse[/gray]
foreach (@input) {
    map {$places[($_->[1])-1] = [$_->[0], $_->[2]]} $_;
}
get_lenths_behind(@places);
@places = ();

[gray]# Calculate Final Distances
# Does the same as the the bit above, except using the
# final data[/gray]
foreach (@input) {
    map {$places[($_->[3])-1] = [$_->[0], $_->[4]]} $_;
}
get_lenths_behind(@places);

[gray]# Print Results
# Prints the names/labels and the positions from the original
# input, stored in @input. The lengths behind is stored in %lengths,
# and they are printed from there.[/gray]
foreach (@input) {
    print join(',', $_->[0], $_->[1], ${$lengths{$_->[0]}}[0],
               $_->[3], ${$lengths{$_->[0]}}[1]), "\n";
}

##### SUBS #####
sub get_lenths_behind {
    my @temp = map {@{$_}} @_;  # Convert AOA into flat array
[gray]    # $ref_labels is a reference to array with labels/names
    # $ref_dist is a ref to a coorisponding array with lengths behind[/gray]
    my ($ref_labels, $ref_dist) = calc_lengths(@temp);

    foreach my $i (0..$#{$ref_labels}) {
        push @{$lengths{$ref_labels->[$i]}}, $ref_dist->[$i];
    }
}

sub calc_lengths {
    die "Need even number of args!\n" if (@_ % 2);
    my (@labels, @lengths);
    while (@_) {
        push @labels, shift;
        push @lengths, shift;
    }
[gray]    # Moves the last value of the lengths ahead (0) to beginning of the list[/gray]
    unshift @lengths, pop @lengths;
    my $sum = 0;
    @lengths = map {$sum += $_;} @lengths;  [gray]#Updates lengths based on sum[/gray]
    return \@labels, \@lengths;             [gray]#Return refs to labels array and lengths behind[/gray]
}

__DATA__
C, 3, 5, 1, 2,
A, 2, 3, 2, 1
D, 4, 0, 3, 2
B, 1, 2, 4, 0
 
A bit ad hoc (then again, the data is consistent and I don't know the language) but I was able to get this portion to work.
thanks for the feedback, Kevin

****************************
my @matrix = ();
$data_file="horseIN2.txt";
open(DATA, $data_file) || die("Could not open file!");


while (<DATA>) {
chomp;
push @matrix, [ split(/,/) ];

}
$bl=0;
$count= $#matrix + 1;
$row=0;

while ($count > 0 ) {
for ($i = 0; $i < 4 ; $i++) {
if ($count == $matrix[$i][1]) {
$row = $i;

} elsif ($matrix[$i][1] < $count) {
$bl += $matrix[$i][2];
$matrix[$row][2] = $bl;
}
}
$count--;
$bl=0;
}

for ($j=0; $j < $#matrix + 1; $j++) {
if ($matrix[$j][1] == 1) {
$leader=0;
$matrix[$j][2]=$leader;
}}

for $i ( 0 .. $#matrix ) { # the BEST of prints
print "@{$matrix[$i];}\n";
}


-----------------------------
data in:

D,3,4
C,4,0
A,2,1
B,1,3

data out:

D,3,4
C,4,8
A,2,3
B,1,0
 
some horribly hard to understand code:

Code:
my @matrix = ();
while (<DATA>) {
   chomp;
   push @matrix, [ split(/,/) ];
}

my @mid_point = sort {$a->[1] <=> $b->[1]} @matrix;
my @end_point = sort {$b->[3] <=> $a->[3]} @matrix;

my $L = 0;
my @mid_lengths = (0 , map {$L+=$mid_point[$_][2]} (0..$#mid_point-1) );

$L = 0;
my @end_lengths = map {$L+=$end_point[$_][4]} (0..$#end_point);

@matrix = sort {$a->[1] <=> $b->[1]} @matrix;

for my $i (0..$#matrix) {
   $matrix[$i][2] = $mid_lengths[$i];
}

@matrix = sort {$a->[3] <=> $b->[3]} @matrix;

for my $i (0..$#matrix) {
   $matrix[$i][4] = $end_lengths[$i];
}

print join(',',@{$_}),"\n" for (@matrix);

__DATA__
C,3,5,1,2
A,2,3,2,1
D,4,0,3,2
B,1,2,4,0
 
Yes.

Mike

I am not inscrutable. [orientalbow]

Want great answers to your Tek-Tips questions? Have a look at faq219-2884

 
maybe this will help explain that mess (the code is slightly altered):

Code:
[COLOR=green]# build the initial data set[/color]
my @matrix = ();
while (<DATA>) {
   chomp;
   push @matrix, [ split(/,/) ];
}
[COLOR=green]#just an intial value to add up the lengths with [/color]
my $L = 0;

[COLOR=green]# create an ascending ordered array of the mid point lengths.
# we know the first value will be 0 (zero) so we pop the last value off the end
# since we only want as many values as rows in the file. We get: (0, 2, 5, 10) [/color]
my @mid_lengths = (0, map { $L+=$_->[2] } sort { $a->[1] <=> $b->[1] } @matrix);
pop(@mid_lengths);

[COLOR=green]# create an ascending ordered array of the end point lenghts.
# We get: (0, 2, 3, 5)[/color]
$L = 0;
my @end_lengths = map { $L+=$_->[4] } sort {$b->[3] <=> $a->[3] } @matrix;

[COLOR=green]# sort the main array by the ascending order of the 2nd column
# so the 2nd columns correspond to the order of the @mid_lengths array[/color]
@matrix = sort {$a->[1] <=> $b->[1]} @matrix;

[COLOR=green]# now plug in the new values for the 3rd column (the mid lengths) of the main array (@matrix)[/color]
for my $i (0..$#matrix) {
   $matrix[$i][2] = $mid_lengths[$i];
}

[COLOR=green]# sort the main array by the ascending order of the 4th column
# so the 4th columns correspond to the order of the @end_lengths array[/color]
@matrix = sort {$a->[3] <=> $b->[3]} @matrix;

[COLOR=green]# now plug in the new values for the 5th column (the end lengths) of the main array (@matrix)[/color]
for my $i (0..$#matrix) {
   $matrix[$i][4] = $end_lengths[$i];
}

[COLOR=green]# print the results[/color]
print join(',',@{$_}),"\n" for (@matrix);
 
thanks for the commenting
learning alot here
it will take some time

the sort is more difficult than I antcipated
sorting a given sequence doesn't mean any other sequence can be sorted

my crappy code fails ---the problem is with capturing the value where the count equals the position. --- $row--
--if ($count == $matrix[$i][1]) {
$row = $i; ---
to use later when the beaten lengths need to be adjusted

any suggestions?

unfortunately, and I don't mean this in a critical sense, your code fails as well for the second sequence

thanks

 
when I run the last code I posted with this data:

C,3,5,1,2
A,2,3,2,1
D,4,0,3,2
B,1,2,4,0

I get this result:

C,3,5,1,0
A,2,2,2,2
D,4,10,3,3
B,1,0,4,5

which corresponds to what you previosuly posted:

before:
C, 3, 5, 1, 2,
A, 2, 3, 2, 1
D, 4, 0, 3, 2
B, 1, 2, 4, 0

after:

C,3,5,1,0
A,2,2,2,2
D,4,10,3,3
B,1,0,4,5

did I miss something?
 
I don't get the same results and I just realized why park of that is.

For whatever reason, Perl is not consistently reading $#matrix correctly.

If there are 4 rows in the matrix
what value should be returned by $#matrix ??

I get 5.

for example
when I run your code using the sample:

C, 3, 5, 1, 2,
A, 2, 3, 2, 1
D, 4, 0, 3, 2
B, 1, 2, 4, 0


the result is

,,0,,0
,,0,,2
C, 3,5, 1,3
D, 4,10, 3,5
B, 1,0, 4,5

trying it on

D,3,4,1,5
C,4,0,2,3
A,2,1,3,7
B,1,3,4,0


returns

,,0,,0
,,0,,7
D,3,4,1,15
C,4,8,2,15
A,2,3,3,15
B,1,0,4,15





 
you have "bad" data in the first file:

C, 3, 5, 1, 2,

the comma after the 2 will mess things up.

$#matrix is the index number of the last element, if there are five elements it will equal 4 since perl arrays start at index zero.


When I use:

D,3,4,1,5
C,4,0,2,3
A,2,1,3,7
B,1,3,4,0

I get:

D,3,4,1,0
C,4,8,2,7
A,2,3,3,10
B,1,0,4,15

which looks correct to me.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top