4

In tensorflow it is possible to select every nth item with the slicing notation [::n].

But how to do the opposite? I want to select every item except every nth.

For example:

a = [1, 2, 3, 4, 5, 6, 7, 8]

a[2::3] would result in [3, 6]

Now I would like to have the opposite: [1, 2, 4, 5, 7, 8]

The array above is just an example. A solution should work for bigger matrices of the dimension [batch, width, height, channels] in tensorflow. The selection is only done on the channels. Also my matrix contains real values that are non unique. I will also not be able to reshape it further down than to two dimensions ( [batch, channels] )

Spenhouet
  • 6,556
  • 12
  • 51
  • 76
  • 1
    As the answers show you can construct a list (or boolean mask) of the items to keep. The resulting selection will be a copy of the original. It won't be a `view` (which `::n` would produce). – hpaulj Feb 19 '19 at 22:04

3 Answers3

7

One option is to create a boolean index by testing the a range index:

import numpy as np
start, step = 2, 3
a[np.arange(len(a)) % step != start]
# array([1, 2, 4, 5, 7, 8])

You can achieve this similarly in tensorflow using tf.boolean_mask:

import tensorflow as tf

a = tf.constant([1, 2, 3, 4, 5, 6, 7, 8])

start, step = 2, 3
mask = ~tf.equal(tf.range(a.shape[-1]) % step, start)

tf.boolean_mask(a, mask).eval()
# array([1, 2, 4, 5, 7, 8], dtype=int32)

If a is ND tensor, you can specify the axis with boolean_mask; with 4D tensor [batch, width, height, channels] for instance, to select by the fourth axis, i.e the channels, you can set axis=3:

mask = ~tf.equal(tf.range(a.shape[-1]) % step, start)
tf.boolean_mask(a, mask, axis=3)
Psidom
  • 209,562
  • 33
  • 339
  • 356
  • Only problem left with `tf.boolean_mask` is, that the last dimension is unknown ( `None` ). I used `tensor.set_shape(...)` to solve this (needed to calculate the new shape from the information I had) – Spenhouet Feb 19 '19 at 22:55
1

You can use the np.delete() method:

>>> np.delete(a, a[1::3])
array([1, 2, 4, 5, 7, 8])

Remembering that this operation does not modify the original array:

Return a new array with sub-arrays along an axis deleted. For a one dimensional array, this returns those entries not returned by arr[obj].

Daniel Labbe
  • 1,979
  • 3
  • 15
  • 20
1

A plain application of numpy.setdiff1d() will work for 1D arrays of int dtype.

In [16]: arr = np.array([1, 2, 3, 4, 5, 6, 7, 8])

In [17]: np.setdiff1d(arr, arr[2::3])
Out[17]: array([1, 2, 4, 5, 7, 8])

The corresponding TensorFlow equivalent is tf.setdiff1d()


To handle nD arrays (i.e. tensors), you can first reshape them to 1D arrays, do the selection using setdiff1d and then reshape the result back to nD.

kmario23
  • 57,311
  • 13
  • 161
  • 150
  • I can't reshape it more down than to two dimensions (because of batching). I will at least have `[batches, channels]` as dimensions. But more importantly my matrices contain real values. `tf.setdiff1d` will not work for that. – Spenhouet Feb 19 '19 at 22:15