xtensor
xtensor copied to clipboard
N-dimensional Boolean Slicing
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));
We have two things for this:
- 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
- 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!
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?
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.
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 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!
Thanks for the update!
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());