KapokWu
KapokWu

Reputation: 21

What is the correct way to select rows from matrix by a boolean array?

I have a boolean array (from previous computations) and I would like to select the related rows from several matrices. That is why I need the proper index array (to be reused later). This is easy in Matlab and python but I do not crock the correct julian way of doing it...

I am aware of DataFrames, but would like to find an orthodox matrix and array way of doing this.

In Matlab I would say:

n= 9; temp= 1:n; A= 1.0 + temp;
someTest= mod(temp,2) == 0; % just a substitute of a more complex case

% now I have both someTest and A!
inds= find(someTest); Anew= A(inds,:); 
% I got inds (which I need)!

What I have got working is this:

n= 10; data= Array(1:n); A= 1.0 .+ data;
someTest= rem.(data,2) .== 0;

inds= [xy[2] for xy in zip(someTest,1:length(someTest)) if xy[1]]; # (*)
Anew= A[inds,:];

What I assumed is that there is some shorter way to express the above phrase. in v. 0.6 there was find() function, but I have not gotten good sense of the julia documentation yet (I am a very very newbie in this).

Upvotes: 2

Views: 680

Answers (3)

DNF
DNF

Reputation: 12664

Your Matlab code doesn't work. A is just a row-vector (1x9 matrix), so when you try to do A(inds, :) you get an error:

>> Anew= A(inds,:)
Index in position 1 exceeds array bounds
(must not exceed 1).

But if you just fix that, you can solve the problem in exactly the same way in both Matlab and Julia, using either logical indices or regular ones:

Matlab (I'm making sure it's a matrix this time):

n = 9;
temp = (1:n).'; 
A = temp * (1:4);
inds = mod(temp,2) == 0;

>> A(inds, :) % using logical indices

ans =

     2     4     6     8
     4     8    12    16
     6    12    18    24
     8    16    24    32

>> A(find(inds), :) % using regular indices

ans =

     2     4     6     8
     4     8    12    16
     6    12    18    24
     8    16    24    32

And now, Julia:

n = 9;
temp = 1:n;
A = temp .* (1:4)'; # notice that we're transposing the opposite vector from Matlab
inds = mod.(temp, 2) .== 0;  # you can use iseven.(temp) instead

julia> A[inds, :]  # logical indices (BitArray)
4×4 Array{Int64,2}:
 2   4   6   8
 4   8  12  16
 6  12  18  24
 8  16  24  32

julia> A[findall(inds), :]  # regular integer indices
4×4 Array{Int64,2}:
 2   4   6   8
 4   8  12  16
 6  12  18  24
 8  16  24  32

In this case, I would use the logical indices in both Julia and Matlab. In fact, the Matlab linter (in the editor) will tell that you should use logical indices here because it's faster. In Julia, however, there might be cases where it's more efficient to use inds = findall(iseven, temp), and just skip the logical BitArray, like @hckr says.

Upvotes: 0

hckr
hckr

Reputation: 5583

find in 0.6 was renamed to findall in Julia 1.0.

To get inds, you can simply do the following:

inds = findall(someTest)

You do not have to compute the intermediate someTest first, which would allocate an array you do not intend to use. Instead, you can do the test with findall directly passing a predicate function.

inds = findall(x -> rem(x,2) == 0, data)

This will return indices of data for which the predicate rem(x,2) == 0 returns true. This will not allocate an intermediate array to find the indices, and should be faster.

As a side note, most of the time you do not need to materialize a range in Julia. Ranges are already iterable and indexable. They will automatically be converted to an Array when there is a need. Array(1:n) or collect(1:n) are usually redundant, and allocates more memory.

Upvotes: 1

Przemyslaw Szufel
Przemyslaw Szufel

Reputation: 42264

You can use the BitArray just directly to select the elements:

julia> A[someTest]
5-element Array{Float64,1}:
  3.0
  5.0
  7.0
  9.0
 11.0

Fot your case:

julia> A[someTest,:] == A[inds,:]
true

Upvotes: 2

Related Questions