2017-02-22 17 views
0

Это вдохновляет сообщение this.numpy расширенное индексирование: прозрачная оптимизация диапазонов?

Рассмотрим функцию f, которая возвращает 1d np.ndarrayidx индексов, которые пользователь обычно будет использовать для индексации других массивов. Предположим далее, что частый результат для f, чтобы вернуть весь спектр юридических показателей. В связанном сообщении предлагается, чтобы это было обжаловано по f, возвращая slice(None) вместо np.arange(maxind).

Поскольку расширенная индексация поставляется по цене

>>> a = np.arange(1_000_000) 
>>> direct = lambda: np.sum(a[:]) 
>>> indirect = lambda: np.sum(a[a]) 
>>> timeit(direct, number=100) 
0.07656216900795698 
>>> timeit(indirect, number=100) 
0.2885982050211169 

это выглядит разумную оптимизация на первый взгляд.

К сожалению, это неправда. Представьте себе, например, что пользователь хочет создать одноразовое представление idx. Один простой способ идти об это

result = np.zeros((k, maxind), dtype=int) 
result[np.arange(k), idx] = 1 

Это ломает, если np.arange(maxind) замещен slice(None) (Он заполнит весь result с них).

Так что мой вопрос: Можно ли иметь свою торт и съесть его здесь, т.е .:

Есть ли что-нибудь f может вернуться, что точно имитирует семантику np.arange(maxind), избегая при этом передовые индексации, где это возможно?

Поскольку я почти смирился с ответом является №:

Что следующая лучшая вещь?

Возможно, возвращен «улучшенный np.s_», то есть объект с инженерным __getitem__?

class smart_idx: 
    def __init__(self, n): 
     self.n = n 
    def __getitem__(self, idx): 
     idx = idx if isinstance(idx, tuple) else (idx,) 
     if idx: 
      count = idx.count('X') 
      need_adv = count > 1 
      if count == 1: 
       for i in idx: 
        if not isinstance(i, slice) and i != Ellipsis: 
         need_adv = True 
         break 
      repl = np.arange(self.n) if need_adv else slice(None) 
      return tuple(repl if i == 'X' else i for i in idx) 
     return slice(None) 

Пользователь должен использовать его как

data[idx[3, 4:9, 'X', [1,3,2,6]]] 
data[idx['X', ..., :4:-1]] 
data[idx[]] 

и __getitem__, обнаруживая расширенный индекс будет принять решение о замене «X» с np.arange(4) в первом и slice(None) в двух других примерах.

Но это довольно неуклюжий, не говоря уже о том, что дополнительные накладные расходы могут съесть любую скорость, которую мы получили.

Существуют ли более простые стратегии?

+2

Если 'idx' это список/массив вы собираетесь получить передовые индексации, независимо от того, используете ли вы срез или изменение для другого индекса. В индексировании '[arange ..., idx]' он выбирает один элемент из каждой строки. Индексирование на сплющенном массиве происходит быстрее, но уравновешивается стоимостью вычисления плоского индекса. – hpaulj

+0

@hpaulj «Если idx - это список/массив, вы получите расширенную индексацию, независимо от». Вот почему мы пытаемся заменить «idx» на «slice (None)» в особых случаях, когда это имеет смысл (в основном «idx == np.arange (maxind)» + никаких других расширенных индексов). –

+0

Что @hpaulj пытался сказать, что независимо от того, что такое 'idx', когда вы индексируете произвольные позиции в> 1D (2D в вашем примере), например. '[something ..., idx]' это расширенная индексация, независимо от того, что может быть idx, если только 'something' и' idx' являются объектами 'slice', но этот случай никогда не будет имитировать ваше желаемое поведение. –

ответ

0
In [104]: x=np.arange(12).reshape(4,3) 

Они выглядят одинаково, хотя одна копия, другой вид:

In [107]: x[np.arange(0,4,2),:] 
Out[107]: 
array([[0, 1, 2], 
     [6, 7, 8]]) 
In [108]: x[0:4:2,:] 
Out[108]: 
array([[0, 1, 2], 
     [6, 7, 8]]) 

Но если второй индекс является массивом, arange и slice не являются взаимозаменяемыми.

In [109]: idx=np.array([0,2]) 
In [110]: x[np.arange(0,4,2),idx] 
Out[110]: array([0, 8]) 
In [111]: x[0:4:2,idx] 
Out[111]: 
array([[0, 2], 
     [6, 8]]) 

Чтобы соответствовать нарезанную версии я должен добавить измерение к arange.

In [113]: x[np.ix_(np.arange(0,4,2),idx)] 
Out[113]: 
array([[0, 2], 
     [6, 8]]) 
In [114]: x[np.arange(0,4,2)[:,None],idx] 
Out[114]: 
array([[0, 2], 
     [6, 8]]) 

Я не знаю о выражении срез, который производит Out[110].

Таким образом, помимо замены arange на slice, мы должны обратить внимание на то, как расширенные массивы индексирования передаются друг против друга, и какое вещание подразумевается путем нарезки.

С 3 или более размерами, смешивая кусочки и расширенной индексацией становится еще более сложным, как описан в https://docs.scipy.org/doc/numpy/reference/arrays.indexing.html#combining-advanced-and-basic-indexing