2011-03-07 83 views
5

我需要從給定索引切片數組,直到滿足特定條件。Python - 切片數組,直到滿足某些條件

>>> a = numpy.zeros((10), dtype='|S1') 
>>> a[2] = 'A' 
>>> a[4] = 'X' 
>>> a[8] = 'B' 
>>> a 
array(['', '', 'A', '', 'X', '', '', '', 'B', ''], dtype='|S1') 

例如,對於上述陣列我想從一個給定的索引,直到在兩個方向上的第一非零值的子集。例如,對於指數值2,4,8箇中的結果將是:

['', '', A, '']  # 2 
['', X, '', '', ''] # 4 
['', '', '', B, ''] # 8 

上做到這一點最簡單的方法使用numpy的API有什麼建議?學習python和numpy,將不勝感激任何幫助。謝謝!

+0

你能澄清你的問題嗎?你是什​​麼意思「直到第一個非None值在兩個方向」? – 2011-03-07 05:55:42

+0

當您嘗試確定非無數組項目的索引時,使用'object'數組(不是很常見,也不是非常有效的內存)的事實在嘗試確定非無數組項目的索引時會出現一個特殊問題。你能說服使用固定字節的dtype嗎?如果你致力於'object' dtype,那麼當類型爲'bool'時,任何「non-None」都會評估爲「True」嗎?這兩者都可以幫助簡化很多事情。 – Paul 2011-03-07 06:04:13

+0

@Paul我正在使用'object'數組來存儲單個字符串。從本質上講,我需要的只是一個'char'數組。有沒有另一種'dtype'我可以使用'dtype'? – armandino 2011-03-07 06:15:31

回答

6

這是蒙面陣列工作,numpy.ma有很多的功能與子集工作。

a = np.zeros((10), dtype=str) 
a[2] = 'A' 
a[4] = 'X' 
a[8] = 'B' 

讓屏蔽掉不空元素:

am=np.ma.masked_where(a!='', a) 

np.ma.notmasked_contiguous穿過陣列(非常有效),並發現其中陣列沒有被屏蔽的連續元素的所有切片:

slices = np.ma.notmasked_contiguous(am) 
[slice(0, 1, None), slice(3, 3, None), slice(5, 7, None), slice(9, 9, None)] 

因此,例如,元素5和7之間的數組連續爲空。 現在,你只需要加入你感興趣的切片,首先你會得到每片的起始索引:

slices_start = np.array([s.start for s in slices]) 

那麼你得到的索引的位置,你正在尋找:

slices_start.searchsorted(4) #4 
Out: 2 

所以你想片1和片2: a [slices [1] .start:slices [2] .stop + 1] array(['','X','','',''], dtype ='| S1')

或讓我們試試8:

i = slices_start.searchsorted(8) 
a[slices[i-1].start:slices[i].stop+1] 
Out: array(['', '', '', 'B', ''], 
    dtype='|S1') 

如果應該在ipython中使用它來更好地理解它。

+0

非常有趣的安德烈。感謝您的解釋。非常感激! – armandino 2011-03-07 17:42:09

0

想到兩個循環是首先想到的。像這樣的東西會工作:

'''Given an array and an index...''' 
def getNoneSlice(a, i): 

    # get the first non-None index before i 
    start = 0 
    for j in xrange(i - 1, -1, -1): 
     if a[j] is not None: # or whatever condition 
      start = j + 1 
      break 

    # get the first non-None index after i 
    end = len(a) - 1 
    for j in xrange(i + 1, len(a)): 
     if a[j] is not None: # or whatever condition 
      end = j - 1 
      break 

    # return the slice 
    return a[start:end + 1] 
+0

謝謝邁克。該解決方案完美運作(+1)。但是我希望不會有類似這樣的「numpy」方法。 – armandino 2011-03-07 06:21:17

+0

我低估了,因爲對於大型稀疏數組來說這是非常低效的。使用其他答案的numpy方法。 – steabert 2011-03-07 06:48:45

+0

是的,Steabert,同意......至少我學到了一些新東西:-P – 2011-03-07 22:07:19

-2
def getSlice(a, n): 
    try: 
     startindex = a[:n].nonzero()[0][-1] 
    except IndexError: 
     startindex = 0 
    try: 
     endindex = a[(n+1):].nonzero()[0][0] + n+1 
    except IndexError: 
     endindex = len(a) 
    return a[startindex: endindex] 
+0

恐怕它不起作用。我得到'['''A'] ['''X'] ['''B'] ' – armandino 2011-03-07 06:48:13

+0

非零方法不適用於空字符串 – steabert 2011-03-07 07:03:37

+0

該問題沒有空字符串當我回答時,它有'沒有'。它的非零方法適用於'None'。 – pwdyson 2011-03-08 01:57:09

7

如果您設置的問題是這樣的:

import numpy 
a = numpy.zeros((10), dtype=str) 
a[2] = 'A' 
a[4] = 'X' 
a[8] = 'B' 

你可以很容易地得到非空字符串的指數,像這樣:

i = numpy.where(a!='')[0] # array([2, 4, 8]) 

或者,numpy.argwhere(..)也適用。

然後你就可以離開切片使用此陣:

out2 = a[:i[1]]  # 2 ['' '' 'A' ''] 
out4 = a[i[0]+1:i[2]] # 4 ['' 'X' '' '' ''] 

+0

謝謝保羅。這看起來像我之後。 – armandino 2011-03-07 06:49:32

2

請注意,這可以在純Python中使用itertools和functools乾淨地完成。

import functools, itertools 
arr = ['', '', 'A', '', 'X', '', '', '', 'B', ''] 

f = functools.partial(itertools.takewhile, lambda x: not x) 
def g(a, i): 
    return itertools.chain(f(reversed(a[:i])), [a[i]], f(a[i+1:])) 

我們將f通過尋找,直到元素評估爲真找到了子迭代器,和g作爲應用此的指數和指標後,列表前的列表的反白區域的組合。

這將返回可以轉換爲包含我們結果的列表的生成器。

>>> list(g(arr, 2)) 
['', '', 'A', ''] 
>>> list(g(arr, 4)) 
['', 'X', '', '', ''] 
>>> list(g(arr, 8)) 
['', '', '', 'B', '']