Julia Equivalent for ismember in MATLAB for rows

Hello!

I’m trying to convert the following MATLAB code into Julia:

bt_edges =

     6     7
     6     8
     7     8

bt_edges(i,:)

ans =

     7     8

uval = ismember(bt_edges,bt_edges(i,:),'rows');

uval =

  3×1 logical array

   0
   0
   1

In other words, I’d like to see if this row vector bt_edges(i,:) matches any row in the matrix bt_edges and return a logical vector like in the MATLAB code.

I’ve tried the following in Julia to no avail:

julia> uval = in.(bt_edges,bt_edges[i,1])
3×2 BitArray{2}:
 1  0
 1  0
 0  0

julia> in.(bt_edges[1,:], Ref(bt_edges))
2-element BitArray{1}:
 1
 1

julia> indexin(bt_edges,bt_edges[1,:])
3×2 Array{Union{Nothing, Int64},2}:
 1  2       
 1   nothing
 2   nothing

If anyone can provide a solution, I’d appreciate it.

Thank you.

You are trying to check equality for each row, so you can do the following.

bt_edges=[6 7; 6 8; 7 8]

Ref(bt_edges[3,:]) .== eachrow(bt_edges)
3-element BitArray{1}:
 0
 0
 1

uval = all(in.(bt_edges[3,:]',bt_edges),dims=2)

My MATLAB competence is a little bit rusty, but what is the value of i in your MATLAB code? I assume you have set i=3 somewhere?

Anyway, is this what you want to do?

julia> bt_edges = [6 7; 6 8; 7 8]
3×2 Array{Int64,2}:
 6  7
 6  8
 7  8
julia> uval = [bt_edges[i,:] == bt_edges[3,:] for i in 1:3]
3-element Array{Bool,1}:
 0
 0
 1

I like this solution. It seems to be slightly faster than

uval = all(bt_edges[3,:]' .== bt_edges),dims=2)

I can’t see much difference between the two:

using BenchmarkTools
bt_edges = ones(100_000,2)
bt_edges = vcat(bt_edges,[7 8])
m,n = size(bt_edges)
k = 100_001

#1. "eachrow"
@benchmark Ref(bt_edges[k,:]) .== eachrow(bt_edges)
  memory estimate:  5.36 MiB
  allocs estimate:  100016
  --------------
  minimum time:     4.382 ms (0.00% GC)
  median time:      4.934 ms (0.00% GC)
  mean time:        5.680 ms (12.84% GC)
  maximum time:     15.244 ms (61.94% GC)

#2. broadcasted "in"
@benchmark uval = all(in.(bt_edges[k,:]',bt_edges),dims=2)
  memory estimate:  41.58 KiB
  allocs estimate:  20
  --------------
  minimum time:     1.882 ms (0.00% GC)
  median time:      1.916 ms (0.00% GC)
  mean time:        1.937 ms (0.43% GC)
  maximum time:     12.750 ms (84.85% GC)

#3. "comprehension"
@benchmark uval = [bt_edges[i,:] == bt_edges[k,:] for i in 1:m]
  memory estimate:  29.06 MiB
  allocs estimate:  898482
  --------------
  minimum time:     313.920 ms (0.00% GC)
  median time:      316.718 ms (0.93% GC)
  mean time:        319.315 ms (0.58% GC)
  maximum time:     336.991 ms (0.88% GC)

#4. "broadcasted =="
@benchmark uval = all((bt_edges[k,:]' .== bt_edges),dims=2)
  memory estimate:  41.58 KiB
  allocs estimate:  20
  --------------
  minimum time:     1.868 ms (0.00% GC)
  median time:      1.913 ms (0.00% GC)
  mean time:        1.930 ms (0.43% GC)
  maximum time:     12.869 ms (84.81% GC)

Note that logical indexing creates a temporary array (the vector saving the indices), potentially leading to slower than necessary code if they are created in a hot loop.

From the docs:

In MATLAB, an idiomatic way to remove unwanted values is to use logical indexing, like in the expression x(x>3) or in the statement x(x>3) = [] to modify x in-place. In contrast, Julia provides the higher order functions filter and filter! , allowing users to write filter(z->z>3, x) and filter!(z->z>3, x) as alternatives to the corresponding transliterations x[x.>3] and x = x[x.>3] . Using filter! reduces the use of temporary arrays.

Since you seem to be iterating over the rows of the matrix, maybe you can tell us more about what you want to do with those logical indices?

Also, you can get the unique rows of your matrix by a simple call to unique:

julia> A = vcat((a,b,a,a,a,a,b,b,b)...)
9×3 Array{Int64,2}:                    
 1  2  3                               
 3  4  5                               
 1  2  3                               
 1  2  3                               
 1  2  3                               
 1  2  3                               
 3  4  5                               
 3  4  5                               
 3  4  5                                                                      

julia> unique(A, dims=1)               
2×3 Array{Int64,2}:                    
 1  2  3                               
 3  4  5
1 Like

First, thank you to all of you for providing answers; it is much appreciated!

In terms of what I want to do, the next lines of MATLAB code are the following:

if sum(uval)==1
            edgep = [edgep;bt_edges(i,:)];
        end

The variable uval is created and then subjected to the test. The logical vector uval is summed and if it equals 1, then the edgep variable is created, which basically builds a matrix and appends the next row. Apparently, MATLAB allows for this type of recursive appending, but I’ve had to try different things in Julia to make it work, and I have not been successful.

My ultimate goal is to convert some Delaunay triangularization code from MATLAB to Julia, which I’ll share when done.

Thank you all again.

In terms of “growing” an array you can use append! or vcat functions, although it’s not always the most efficient way.

Concatenating rows is really inefficient, because you have to reallocate the entire array each time, and since Julia (like Matlab) arrays are column major, it’s especially problematic.

If you care about performance, I would suggest that you think in columns instead, then you can append data without having to make a new array each time. To do this, start with a vector, append data, and then in the end reshape into a matrix. Doing this could be 10-100x faster than appending rows (depending on sizes etc.)

Hm. I just did @btime a couple of times on my media computer, and for a couple of cases there, #2 was some 10% faster. But perhaps that was a coincidence. @benchmark probably gives a better picture.

Unless there’s some actual linear algebra taking place in the code, an even better option may be to work with a vector of vectors or a vector of tuples.

1 Like

BTW, I think your Matlab code, too, would benefit from being column-oriented.

Translating Matlab code line-by-line to Julia may not be the best approach: even when you can make it work, it is very likely that it will be suboptimal and unnecessarily complicated.

Instead, I would recommend that you invest into

  1. understanding your algorithm,
  2. learning Julia, possibly on a simpler project,
  3. exploring the many nice data structures that are available in packages

and build up your code from small, simple functions (which is the opposite of prevalent Matlab style).

For example, since the dimension should determine the number of edges,

Alternatively,

can provide matrices that grow.

1 Like

I agree 100% with this but I’m looking for a solution in Julia, for the time being, that is quick and dirty. I’ve tried to find what I need but it turn out rolling my own code is the solution.

I think one of the appealing aspects of Julia is that one can open an IJulia session and interactively try out ideas.

Of course, there are more elegant solutions, but again, this is a minor part of my bigger project which is building a spatial econometrics package for Julia.

I’m almost done and need this particular code to just make sure I have an alpha version to make sure everything works before optimizing.

I would like to thank everyone for their suggestions and the great ideas.