mattiav27
mattiav27

Reputation: 685

Delete row of an array if an element meets a condition in Fortran

I have a 2D array of real numbers, I want to eliminate a row if the first element is less than or equal to zero.

Currently I create another array, fill it with elements corresponding to lines in which the first element is positive and deallocate the original array, but it is inefficient since this should be called many times in my program. Also, it doubles the use of memory.

I have looked into the function PACK, but it seems that it gives as a result only 1D vectors. Is there an analogous function that gives a 2D array as a result?

For example, my array could be something like this:

0   1.2  5.0
-1. 5.0  2.3
2.3 6.7  0.1
3.5 2.9  0.0

And the result should be

2.3 6.7  0.1
3.5 2.9  0.0

Upvotes: 1

Views: 344

Answers (1)

This is a sketch how you can keep the data in place and get a pointer to a contiguous n x something array after deleting a "row". However, that only works if you can make the second index to be the row number.

  implicit none
  
  integer, allocatable, target :: a(:,:)
  integer, pointer :: b(:,:)
  integer :: row, rows, iter, n
  real :: x
  
  n = 1000
  
  allocate(a(n,n))
  
  a = 0
  
  rows = n
  do iter = 1, n/2
    call random_number(x)
    
    row = ceiling(x*rows)
    
    a(:,row:rows-1) = a(:,row+1:rows)
    rows = rows - 1
    
    b => a(:,1:rows)
    
    print *,size(b,1), size(b,2), is_contiguous(b)
    
  end do
end

If you need the row index to be the first index, it will work as well, but the resulting pointer won't be contiguous.

  ...
  do iter = 1, n/2
    call random_number(x)
    
    row = ceiling(x*rows)
    
    a(row:rows-1,:) = a(row+1:rows,:)
    rows = rows - 1
    
    b => a(1:rows,:)
    
    print *,size(b,1), size(b,2), is_contiguous(b)
    
  end do
end

If you need a to be truly reallocated, it won't be possible without a temporary array. If you want to avoid the compiler allocating a new temporary each time, you can use a fixed one manually. The compiler is likely to be re-use the space already being occupied by a (loc does not change), but it will cost some significant speed time anyway:

  implicit none
  
  integer, allocatable :: a(:,:), tmp(:,:)
  integer :: row, rows, iter, n
  real :: x
  
  n = 1000
  
  allocate(a(n,n), tmp(n,n))
  
  a = 0
  tmp = a
  
  rows = n
  do iter = 1, n/2
    call random_number(x)
    
    row = ceiling(x*rows)
    
    tmp(row:rows-1,:) = a(row+1:rows,:)
    rows = rows - 1
    
    a = tmp(1:rows,:)
    
    print *,size(a,1), size(a,2), loc(a)
    
  end do
end

Maybe my code will fail for x = 0, but I ignore this digression for simplicity.

Upvotes: 2

Related Questions