5

I'd like to filter each row of my matrix a such that each row contains non-negative values.

First, I tried this:

julia> a = [-1 2 3; 4 5 6; -5 3 4; 3 5 5]
4×3 Matrix{Int64}:
 -1  2  3
  4  5  6
 -5  3  4
  3  5  5

julia> # Desired Operation after filtering should yield 2x3 matrix, [4 5 6; 3 5 5]

julia> mask1 = a .>= 0
4×3 BitMatrix:
 0  1  1
 1  1  1
 0  1  1
 1  1  1

julia> a[mask1]
10-element Vector{Int64}:
 4
 3
 2
 5
 3
 5
 3
 6
 4
 5

This first attempt flattens my matrix. The same thing happens when I do a[mask1, :].

My second attempt I tried this (the equivalent logic works using python's numpy):

julia> mask2 = minimum(a, dims=2) .>= 0
4×1 BitMatrix:
 0
 1
 0
 1

julia> a[mask2, :]
2×1 Matrix{Int64}:
 4
 3

My second attempt only captures the first element of the second and fourth rows, when I want the entire second and fourth rows. Note that if I use the equivalent boolean array for mask2, I do get the desired result:

julia> mask3 = [false; true; false; true]
4-element Vector{Bool}:
 0
 1
 0
 1

julia> a[mask3, :]
2×3 Matrix{Int64}:
 4  5  6
 3  5  5

So, is the idiomatic way to do this row by row filtering to cast BitMatrix to a Vector{Bool}, or is there a cleaner way? Additionally, the crux of the question is why BitMatrix only returns one element of one row while Vector{Bool} returns the entire row.

Arthur Lin
  • 85
  • 4

1 Answers1

6

One possible way:

julia> a[[all(row.>=0) for row in eachrow(a)], :]
2×3 Matrix{Int64}:
 4  5  6
 3  5  5

Another one:

julia> a[findall(x->all(x .>=0), eachrow(a)), :]
2×3 Matrix{Int64}:
 4  5  6
 3  5  5

The version with minimum you were trying to do would be:

julia> a[minimum.(eachrow(a)) .>= 0, :]
2×3 Matrix{Int64}:
 4  5  6
 3  5  5

or following @DNF suggestion which is actually the best:

julia> a[all.(>=(0), eachrow(a)), :]
2×3 Matrix{Int64}:
 4  5  6
 3  5  5
Przemyslaw Szufel
  • 40,002
  • 3
  • 32
  • 62
  • 1
    `a[all.(>(0), eachrow(a)), :]` This is the fastest among the proposed solutions. – DNF Feb 20 '22 at 20:29
  • Thank you. I'm still curious as to why bitmatrix returns one element of one row while Array{Bool} (which your solution creates) returns an entire row. Any docs about this would be appreciated :) – Arthur Lin Feb 20 '22 at 20:30
  • 2
    It seems that the difference is not about Bit vs Bool, but about matrix vs vector. `mask2` is a `BitMatrix`, if you turn it into a `BitVector` instead, it will return the expected result (use e.g. `vec(mask2)`). I don't really understand why `a[mask2, :]` returns what it does. To me, it looks like it should error. – DNF Feb 20 '22 at 20:38
  • Thank you both, it does seem that 4x1 BitVector and the 4x1 Array{Bool} masks yield desired results, while 4x1 BitMatrix does not mask properly. It's not very intuitive why some masks are BitVectors and some masks are BitMatrix, as well as why the behaviors between the two are different, but I appreciate your help. – Arthur Lin Feb 20 '22 at 21:50
  • 1
    `BitVector`s aren't 4x1, they are just 4. There is no x1. That's the important difference. Vectors are truly 1-dimensional, so there isn't a second dimension of length 1. A 4x1 `Array{Bool}` would not work either, since that is, again, a matrix, a two-dimensional array. Your `mask3` is a `Vector{Bool}`, of size 4 (not 4x1), that is why it works. The dimensionality distinction is crucial. – DNF Feb 20 '22 at 22:40