xtensor icon indicating copy to clipboard operation
xtensor copied to clipboard

N-dimensional Boolean Slicing

Open sparsecoder opened this issue 5 years ago • 7 comments

I'm wondering if there is an easy way to select specific columns (or rows, tubes, etc.) using a boolean mask.

In matlab it looks like

Z = rand(N,K) > 0.5; %boolean matrix
X = randn(M,K);
A = X(:, Z(:,3));

sparsecoder avatar Sep 30 '18 21:09 sparsecoder

We have two things for this:

  1. the xt::filter view that allows you to filter an expression by a boolean condition https://xtensor.readthedocs.io/en/latest/view.html?highlight=xt%3A%3Akeep#filter-views
  2. the xt::keep and xt::drop slices https://xtensor.readthedocs.io/en/latest/view.html?highlight=xt%3A%3Akeep that you could use in conjunction with xt::nonzero

Cheers!

wolfv avatar Oct 02 '18 14:10 wolfv

Thanks for your feedback. The filter view will not work because I need to do a matrix multiply after selecting the columns.

I tried this with nonzero() but it will not compile:

size_t M = 6, K = 12, N = 30;
auto Z = random::brnrnd({M,N}, 0.5); // xtensor<bool,2>
auto X = random::nrmrnd({P,N})); // xtensor<double,2>

size_t k = 3;
auto zk = view(Z, k, all());
xt::xtensor<double,2> A( view(Y, all(), keep(nonzero(zk))) ); //compile error
std:: cout << linalg::dot(transpose(A), random::nrmrnd({M,size_t(1)})) << std::endl;

The compile error is possibly because the return type of nonzero() isn't the expected input of keep(). It compiles and runs if I use, e.g. keep(1,5,12,17).

What would it take to add a slicing function like keep() and drop() that takes a boolean array/tensor the same size as the corresponding dimension?

sparsecoder avatar Oct 04 '18 16:10 sparsecoder

I also encounter this problem, I'd like to write codes, which looks like in numpy: a=np.random.randn(5, 4) b=np.random.randn(5) y = a[b>0.4, :]

I can't find how to write it in xtensor, xt::keep takes static parameters using template. I want it can take dynamic position parameter, thanks.

ericyan71 avatar Oct 16 '18 06:10 ericyan71

I have the same question as @ericyan71 except for a 3D case.

a = np.random.randn(2,2,3)
b = np.random.randn(2,2)
# boolean mask
mask = b > 0.5
# applying a 2D mask AND a slice at the same time
y = a[mask, :2]

If I figure it out I'll post the answer here.

EDIT: This is as far a I have gotten. It only gives me partly what I want:

// take a 2D slice of the 3D tensor. I actually want a height (last dim) slice more than i but it doesnt work for the next step
auto height_slice = xt::view(voxels, xt::all(), xt::all(), i);
// filter slice according to a *different* **2D** slice tensor
auto temp = xt::filter(height_slice, (height_values < height + zres) & (height_values > height));
temp.fill(255); // overwrite values

This works but it limits me to only applying the 2D mask to one dimension of the 3D array. I want to apply it to multiple height slices. Its like I want to combine view and filter seamlessly, like this:

auto height_slice_filter = xt::view(voxels, (height_values < height + zres) & (height_values > height), xt::range(_, i+1));

Also have concerns about performance.

JeremyBYU avatar Nov 29 '18 12:11 JeremyBYU

@JeremyBYU thanks for your example. Indeed, the keep slice and our other methods are not generalized enough to support multi-dimensional boolean indexing. We'll have to add that!

wolfv avatar Nov 29 '18 12:11 wolfv

Thanks for the update!

JeremyBYU avatar Nov 29 '18 15:11 JeremyBYU

Hi, guys

I believe multi-dimensional boolean indexing can be done with xt::keep & xt::where: auto masked = xt::view(arr, xt::keep(xt::where(bool_mask > 0)[0]), xt::all());

ultimatedigiman avatar Apr 25 '22 07:04 ultimatedigiman