Ignacio
Ignacio

Reputation: 7938

subset array with Fortran given a condition?

Suppose I have an array A(n,m). Is it possible to subset that array in Fortran to create a new array? For example,

A = 11   22   43   55
    15   56   65   63
    54   56   32   78

I want to create an array B with m-1 columns and the rows that satisfies A(:,2) .eq. 56

So B should be:

B = 15   65   63
    54   32   78

I don't even know how to start because, for example, the dimension of B should be determined dynamically (I think)

Thanks for the help!

Upvotes: 3

Views: 2200

Answers (2)

innoSPG
innoSPG

Reputation: 4656

Are you looking for something like this?

function extractB(A) result(B)
    integer, dimension(:,:), intent(in) :: A
    integer, dimension(:,:), pointer :: B
    integer :: nrowB, i, pos

    nrowB = count( A(:,2)==56)
    allocate( B(nrowB, size(A,2)-1 ) )
    pos = 1
    do i = 1, size(A,1)
        if(A(i,2)==56)then
            B(pos,1) = A(i,1)
            B(pos,2:) = A(i,3:)
            pos = pos+1
        end if
    end do
end function extractB

That you call like

B = extractB(A)

with B defined like:

integer, dimension(:,:), allocatable :: B

I assumed integer for your arrays. If your compiler implement pointer as return value, you can used pointers in the place of allocatable.

====adding a full program ====

module extract
contains

    subroutine testExtract(A, B)
        double precision, dimension(:,:), intent(in)           :: A
        double precision, dimension(:,:), intent(out), allocatable :: B

        B = extractB(A)


    end subroutine testExtract


    function extractB(A) result(B)
        double precision, dimension(:,:), intent(in) :: A
        double precision, dimension(:,:), allocatable :: B
        integer :: nrowB, i, pos

        nrowB = count( A(:,2)==56)
        allocate( B(nrowB, size(A,2)-1 ) )
        pos = 1
        do i = 1, size(A,1)
            if(A(i,2)==56)then
                B(pos,1) = A(i,1)
                B(pos,2:) = A(i,3:)
                pos = pos+1
            end if
        end do
    end function extractB
end module extract

program test
    use extract
    integer, parameter :: n = 3
    integer, parameter :: m = 4
    double precision, dimension(3,4) :: A
    double precision, dimension(:,:), allocatable :: B


    A(1,:) = [11,   22,   43,   55]
    A(2,:) = [15,   56,   65,   63]
    A(3,:) = [54,   56,   32,   78]

    print*, 'A :'
    print*, int(A)

    !B = extractB(A)
    call testExtract(A, B)
    print*, 'B'
    print*, int(B)

end program

Upvotes: 3

francescalus
francescalus

Reputation: 32366

A loop is clearly a good way to go, but if you want concise, then

integer, dimension(N,M) :: A
integer, allocatable :: B(:,:)
integer i

A = ...

B = A(PACK([(i,i=1,SIZE(A,1))],A(:,2)==56),[1,(i,i=3,SIZE(A,2))])

I should explain this as there are a number of silly things being done here. First note that [..] is an array constructor, and [(..)] is an array constructor with an implied-do.

So [(i,i=1,SIZE(A,1))] creates an array with values 1, ..., N and [1,(i,i=3,SIZE(A,2))] an array with values 1, 3, ..., M. These form the indexes for the rows and columns of A missing out the second column. The PACK part selects those indexes for rows matching the mask condition A(:,2)==56.

Finally, we use vector subscripting to select the suitable rows with the restricted columns.

The only real reason for doing this is to benefit from automatic allocation of B. And that's very marginal.

Don't do this in real code without good documentation.

Upvotes: 2

Related Questions