2017-02-22 13 views
0

これはポストthisからインスピレーションを受けています。numpy高度なインデックス作成:透過的に最適化する範囲は?

ユーザーが通常他の配列のインデックスに使用するインデックスの1d np.ndarrayidxを返す関数fを考えます。さらに、多くの法的指標を返すことが頻繁な結果であると仮定してください。fリンクされた投稿では、これはでの代わりにslice(None)を返すことによってこれが特別なケースになることが示唆されています。

高度なインデックスがコスト

>>> 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のワンホット表現を作成したいとします。このついて行くの一つの簡単な方法は、np.arange(maxind)が(それはものとresultの全体を記入します)slice(None)で置換されている場合、これが壊れる

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

です。

だから、私の質問は:1は自分のケーキを持っており、ここではそれを食べることができ、すなわち:

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) 

ユーザーは、他の2つの例では最初でnp.arange(4)とし、slice(None)に「X」を置換することを決定します高度なインデックスを検出し、

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

__getitem__ようにそれを使用しなければならないでしょう。

しかし、それは追加されたオーバーヘッドが私たちが得たスピードを食べるかもしれないということは言うまでもなく、むしろぎりぎりです。

より簡単な戦略はありますか?

+2

で説明したようにスライスし、高度なインデックスを混合3次元以上の

は、さらに複雑になります 'idx'あなたは関係なく、あなたが使用しているかどうかの、高度なインデックスを得ようとしているリスト/配列の場合他のインデックスへのスライスまたは範囲を指定します。 '[arange ...、idx]'インデックスでは、各行から1つのアイテムを選んでいます。フラット化された配列のインデックス作成は高速ですが、フラットインデックスの計算コストと相殺されます。 – hpaulj

+0

@hpaulj "idxがリスト/配列の場合は、高度なインデックス作成を行います。"だからこそ、idxをスライス(None)に置き換えようとする特別な場合(基本的に 'idx == np.arange(maxind)' +それ以外の高度なインデックスはありません)。 –

+0

@hpauljが何を言っているかは、idxが何であるかにかかわらず、> 1D(あなたの例では2D)の任意の位置をインデックスするときです。 '[something ...、idx]' 'something'と' idx'の両方が 'slice'オブジェクトでない限り、' idx'の可能性にかかわらず高度なインデックス作成ですが、その場合はあなたの望む動作をまねすることはありません。 –

答えて

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

これらは、1つのコピーですが、同じように見える他のビュー:

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]]) 

しかし、第二インデックスが配列の場合、arangesliceは代替ではありません。

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]を生成するスライス式は認識していません。

arangesliceに置き換えただけでは、高度なインデックス配列が互いにどのようにブロードキャストされ、どのようなブロードキャストがスライシングによって暗示されるかに注意する必要があります。 https://docs.scipy.org/doc/numpy/reference/arrays.indexing.html#combining-advanced-and-basic-indexing

関連する問題