Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Select rows in a rank 2 array

2 views
Skip to first unread message

Guillaume_C

unread,
Jan 2, 2009, 4:13:42 PM1/2/09
to
Hello,

I have this array called A1 :

1 1 2 0 4
0 3 7 9 0
1 4 5 0 1
1 9 3 0 4
1 5 6 1 1
0 7 0 2 3

I would like to create an array A2 which would contain only the rows
of A1 where the value in the 4th column is not 0.

A2 being an allocatable array and using F2003, I have tried this:

A2 = pack(A1, Mask = (r(:,4) /= 0))

But the problem is that, if I'm correct, A2 will be of rank 1. How can
I get A2 to be of rank 2, with the correctly selected rows?

Thanks!

Guillaume

James Van Buskirk

unread,
Jan 2, 2009, 7:29:19 PM1/2/09
to
"Guillaume_C" <carnivor...@gmail.com> wrote in message
news:78bd355c-6b75-49cb...@w1g2000prm.googlegroups.com...

> But the problem is that, if I'm correct, A2 will be of rank 1. How can
> I get A2 to be of rank 2, with the correctly selected rows?

I don't see an easy way to do this.

C:\gfortran\clf\packtest>type packtest.f90
program packtest
implicit none
integer :: A1(6,5) = reshape([ &
1, 1, 2, 0, 4, &
0, 3, 7, 9, 0, &
1, 4, 5, 0, 1, &
1, 9, 3, 0, 4, &
1, 5, 6, 1, 1, &
0, 7, 0, 2, 3] &
, shape(A1), order = [2,1])
integer, allocatable :: A2(:,:)
character(80) fmt
integer i

write(fmt,'(a,i0,a)') '(',size(A1,2),'(i1:1x))'
write(*,'(a)') 'A1 ='
write(*,fmt) transpose(A1)
! Could skip next line if allocate on assignment is OK
allocate(A2(count(A1(:,4) /= 0),size(A1,2)))
A2 = reshape([(pack(A1(i,:),A1(i,4) /= 0), i = 1, size(A1,1))], &
shape = [count(A1(:,4) /= 0),size(A1,2)], &
order = [2,1])
write(*,'(/a)') 'A2 ='
write(*,fmt) transpose(A2)
end program packtest

C:\gfortran\clf\packtest>gfortran packtest.f90 -opacktest

C:\gfortran\clf\packtest>packtest
A1 =


1 1 2 0 4
0 3 7 9 0
1 4 5 0 1
1 9 3 0 4
1 5 6 1 1
0 7 0 2 3

A2 =


0 3 7 9 0

1 5 6 1 1
0 7 0 2 3

--
write(*,*) transfer((/17.392111325966148d0,6.5794487871554595D-85, &
6.0134700243160014d-154/),(/'x'/)); end


Guillaume_C

unread,
Jan 3, 2009, 11:44:08 AM1/3/09
to

> I don't see an easy way to do this.

>    allocate(A2(count(A1(:,4) /= 0),size(A1,2)))


>    A2 = reshape([(pack(A1(i,:),A1(i,4) /= 0), i = 1, size(A1,1))], &
>       shape = [count(A1(:,4) /= 0),size(A1,2)], &
>       order = [2,1])

Thanks, this is very useful. I initially wanted to do something like:

where (A1(:,4) /= 0)
A2 = A1
end where

but this doesnot work (I don't understand what is not correct).

Then, another thing I want to do after selecting the rows where A1(:,
4) /= 0, is to create a new array A4 where each of these rows are
going to be present A1(:,5) times.

A1 =
1 1 2 0 4
0 3 7 9 0
1 4 5 0 1
1 9 3 0 4
1 5 6 1 1
0 7 0 2 3

A4 =


1 5 6 1 1
0 7 0 2 3
0 7 0 2 3
0 7 0 2 3

I have done something that seems to work:

integer, allocatable :: A3(:)
integer, allocatable :: A4(:,:)
integer j, k

do k = 1, size(A1,1)
if ( (A1(k,4) /= 0) .and. (A1(k,5) /= 0) ) then
do j = 1, A1(k,5)
A3 = reshape((/A3, A1(k,:) /), (/size(A3,1)+size(A1,2)/))
end do
end if
end do

A4 = reshape(A3 , shape = [size(A3(:))/size(A1,2),size(A1,2)], order =
[2,1])

Is this "clean" fortran and/or is there a smarter or more elegant way
to do it?

The compiler tells me "LOOP WAS VECTORIZED", does it mean that it is
not worth trying to remove the loop and to write a single instruction
as the compiler will do it for me (and better than me)?

Thanks!

Guillaume

Ron Shepard

unread,
Jan 3, 2009, 12:16:31 PM1/3/09
to
In article
<c97e726a-7055-4a95...@w24g2000prd.googlegroups.com>,
Guillaume_C <carnivor...@gmail.com> wrote:

> Is this "clean" fortran and/or is there a smarter or more elegant way
> to do it?

I'm not fluent with the f2003 allocate-on-assignment yet, but it
looks to me like you are reallocating the A3 array multiple times,
copying the intermediate results each time, and then allocating the
A4 array in the end. If this is correct, then what you are doing is
not smart or elegant.

I think you should just scan through the A1 array to determine how
many rows you will eventually need, allocate the A4 array to be that
size, and then assign the rows appropriately. This avoids the need
for the intermediate A3 array, and it eliminates the multiple
reallocations and multiple copies of the intermediate values.
Sometimes "elegant" means simple and straightforward.

$.02 -Ron Shepard

Guillaume_C

unread,
Jan 3, 2009, 2:43:00 PM1/3/09
to

> I think you should just scan through the A1 array to determine how
> many rows you will eventually need, allocate the A4 array to be that
> size, and then assign the rows appropriately.  This avoids the need
> for the intermediate A3 array, and it eliminates the multiple
> reallocations and multiple copies of the intermediate values.  
> Sometimes "elegant" means simple and straightforward.

I did what you suggested - thanks for pointing to a simpler approach
(which is not always obvious to see)! Then I looked more at the code
of my first question, and by nesting implied loops, I was able to
achieve the same results:

A3 = [ ( (pack(A1(i,:),( (A1(i,4) /= 0) .and. (A1(i,5) /= 0) )), j =
1, A1(i,5)), i = 1, size(A1,1) ) ]
A5 = reshape(A3, shape = [size(A3)/size(A1,2),size(A1,2)], order =
[1,2])

glen herrmannsfeldt

unread,
Jan 3, 2009, 3:09:15 PM1/3/09
to
Guillaume_C <carnivor...@gmail.com> wrote:

>> I think you should just scan through the A1 array to determine how
>> many rows you will eventually need, allocate the A4 array to be that
>> size, and then assign the rows appropriately. ?This avoids the need

>> for the intermediate A3 array, and it eliminates the multiple
>> reallocations and multiple copies of the intermediate values. ?

>> Sometimes "elegant" means simple and straightforward.

> I did what you suggested - thanks for pointing to a simpler approach
> (which is not always obvious to see)! Then I looked more at the code
> of my first question, and by nesting implied loops, I was able to
> achieve the same results:

It may or may not use a temporary array allocated as an
intermediate.

> A3 = [ ( (pack(A1(i,:),( (A1(i,4) /= 0) .and. (A1(i,5) /= 0) )), j =
> 1, A1(i,5)), i = 1, size(A1,1) ) ]
> A5 = reshape(A3, shape = [size(A3)/size(A1,2),size(A1,2)], order =
> [1,2])

My general rule is that simple array expressions are as fast
or faster than using DO loops. Complicated ones are often
slower, many times requiring temporary arrays. In this case,
I would not be surprised if more than one temporary
array was used.

-- glen

Richard Maine

unread,
Jan 3, 2009, 3:16:07 PM1/3/09
to
Guillaume_C <carnivor...@gmail.com> wrote:

> Thanks, this is very useful. I initially wanted to do something like:
>
> where (A1(:,4) /= 0)
> A2 = A1
> end where
>
> but this doesnot work (I don't understand what is not correct).

Then you aren't understanding what "where" does (well, I guess that's
exactly what you were saying). It operates on particular elements of the
arrays, as selected by the condition. Important is that the element
selection applies to ALL the arrays in the assignment statement. In
particular, it applies to the A2 on the LHS of the assignment, as well
as the A1 on the right.

The WHERE does *NOT* build an array by somehow packing the elements
operated on. Instead, it operates on particular elements of the arrays.
The other elements do not get deleted from the arrays; they just stay
unchanged.

Then there is also the matter that your condition has different shape
(and even rank) from the arrays in the assignment, which won't work.
Since the condition picks which elements to operate on, it has to be the
same shape to make sense at all.

--
Richard Maine | Good judgment comes from experience;
email: last name at domain . net | experience comes from bad judgment.
domain: summertriangle | -- Mark Twain

Guillaume_C

unread,
Jan 3, 2009, 3:39:18 PM1/3/09
to
Thanks for your explanations, now this makes sense, except this:

> Then there is also the matter that your condition has different shape
> (and even rank) from the arrays in the assignment, which won't work.
> Since the condition picks which elements to operate on, it has to be the
> same shape to make sense at all.

So, if I understand you well, this instruction is not correct:

where (A1(:,4) /= 0)

A1(:,5) = 1
end where

then, this means it is not possible to use WHERE for an array with
rank > 1 and to modify some cells in a row where other cells of the
same row match some conditions. If so, how can I use WHERE on rank > 1
arrays?

Thanks!

Guillaume

Guillaume_C

unread,
Jan 3, 2009, 3:51:32 PM1/3/09
to
Well, I just checked this:

>
> where (A1(:,4) /= 0)
>      A1(:,5) = 1
> end where
>

and this works. If I'm correct my condition is a rank 1 array of TRUE
and FALSE elements, and whose shape is equal to the number of rows of
A1. So I suppose I suppose that what is required is that the condition
has a shape equal to the extent of the dimension of the array it is
applied on.

Richard Maine

unread,
Jan 3, 2009, 7:06:57 PM1/3/09
to
Guillaume_C <carnivor...@gmail.com> wrote:

Correct.

Guillaume_C

unread,
Jan 4, 2009, 3:06:14 PM1/4/09
to
I have one more question regarding array manipulation. I have this two
arrays:

A1 =
1 1 2 0 4
0 3 7 9 0
1 4 5 0 1
1 9 3 0 4
1 5 6 1 1
0 7 0 2 3

A2 =


0 3 7 9 0

1 5 6 1 1
0 7 0 2 3

And I would like to obtain an array A3 by binding A1 and A2 on the row
dimension. In fact, I'm trying to do in Fortran what R does with the
command rbind(). So at the end, I will have

A3 =


1 1 2 0 4
0 3 7 9 0
1 4 5 0 1
1 9 3 0 4
1 5 6 1 1
0 7 0 2 3
0 3 7 9 0
1 5 6 1 1
0 7 0 2 3

Using the reshape function for this, I would do:

reshape( [A1, A2], shape = [size(A1(:,1))+size(A2(:,1)),size(A1,2)],
order = [2,1] )

But this gives a result that is not correct:

A5 =
1 0 5 3 0
2 0 7 1 0
1 7 6 0 1
1 7 2 1 3
2 0 1 4 0
6 2 1 4 7
0 2 2 3 0
2 1 9 5 9
4 2 5 9 2

I can't even figure out the logic behind this result (i.e. how my
instruction plays with the rows and columns). In my instruction, I
thought I had told to reshape the arrays A1 and A2, into an array
whose number of rows is the number of rows of A1 + the number of rows
of A2, and whose number of columns is the number of columns of A1 (but
I could have put A2), and putting elements row by row (and column by
column inside a row).

I suspect that the mess comes from the order = [2,1]. I have looked at
the language definition and it says:

order (Input; optional) Must be an integer array with the same shape
as shape. Its elements must be a permutation of (1,2,...,n), where n
is the size of shape. If order is omitted, it is assumed to be
(1,2,...,n).

I think I don't understand what this means and how to use this option
correctly. Can someone explain me?

Thanks!

Guillaume

James Van Buskirk

unread,
Jan 4, 2009, 5:52:37 PM1/4/09
to
"Guillaume_C" <carnivor...@gmail.com> wrote in message
news:580c760c-9ace-4ef5...@r15g2000prd.googlegroups.com...

I can't figure it out either. It's a bug in your test code or in
the compiler you are using to test it with. The order=[2,1] is just
there to fit row-major-oriented pegs into a column-major-shaped hole.
If you want to add rows to your matrix you have to transpose both the
inputs and the outputs as in A4 below. Transposing only the inputs
as in A3 gives the result I would expect, not what you have quoted
above:

C:\gfortran\clf\packtest>type bindtest.f90
program bindtest


implicit none
integer :: A1(6,5) = reshape([ &
1, 1, 2, 0, 4, &
0, 3, 7, 9, 0, &
1, 4, 5, 0, 1, &
1, 9, 3, 0, 4, &
1, 5, 6, 1, 1, &
0, 7, 0, 2, 3] &
, shape(A1), order = [2,1])

integer :: A2(3,5) = reshape([ &


0, 3, 7, 9, 0, &

1, 5, 6, 1, 1, &
0, 7, 0, 2, 3] &

, shape(A2), order = [2,1])
integer, allocatable :: A3(:,:)


integer, allocatable :: A4(:,:)

character(80) fmt
integer i

write(fmt,'(a,i0,a)') '(',size(A1,2),'(i1:1x))'
write(*,'(a)') 'A1 ='
write(*,fmt) transpose(A1)

write(*,'(/a)') 'A2 ='
write(*,fmt) transpose(A2)

! Could skip next line if allocate on assignment is OK

allocate(A3(size(A1,1)+size(A2,1),size(A1,2)))
A3 = reshape([A1, A2], &
shape = [size(A1,1)+size(A2,1),size(A1,2)], &
order = [2,1])
write(*,'(/a)') 'A3 ='
write(*,fmt) transpose(A3)


! Could skip next line if allocate on assignment is OK

allocate(A4(size(A1,1)+size(A2,1),size(A1,2)))
A4 = reshape([transpose(A1), transpose(A2)], &
shape = [size(A1,1)+size(A2,1),size(A1,2)], &
order = [2,1])
write(*,'(/a)') 'A4 ='
write(*,fmt) transpose(A4)
end program bindtest

C:\gfortran\clf\packtest>gfortran bindtest.f90 -obindtest

C:\gfortran\clf\packtest>bindtest


A1 =
1 1 2 0 4
0 3 7 9 0
1 4 5 0 1
1 9 3 0 4
1 5 6 1 1
0 7 0 2 3

A2 =
0 3 7 9 0
1 5 6 1 1
0 7 0 2 3

A3 =
1 0 1 1 1
0 1 3 4 9
5 7 2 7 5
3 6 0 0 9
0 0 1 2 4
0 1 4 1 3
0 1 0 3 5
7 7 6 0 9
1 2 0 1 3

A4 =


1 1 2 0 4
0 3 7 9 0
1 4 5 0 1
1 9 3 0 4
1 5 6 1 1
0 7 0 2 3
0 3 7 9 0
1 5 6 1 1
0 7 0 2 3

--

Craig Powers

unread,
Jan 5, 2009, 2:37:39 PM1/5/09
to
Guillaume_C wrote:

> The compiler tells me "LOOP WAS VECTORIZED", does it mean that it is
> not worth trying to remove the loop and to write a single instruction
> as the compiler will do it for me (and better than me)?

Not really, no. Even if you wrote a single instruction, the loop would
still ensue. I don't think those messages are terribly useful, unless
it should lead you to find that something you think ought to be
vectorizable turns out not to be. The vectorization has to do with the
type of code the compiler generates to execute the loop.

0 new messages