2016-08-19 36 views
13

Numpy ma kilka bardzo przydatnych string operations, które wektoryzują zwykłe operacje na łańcuchach Pythona.Jak wyciąć każdy element z tablicy numpy?

W porównaniu do tej operacji i do pandas.str, moduł numpy stringów wydaje się być pozbawiony bardzo ważnego: możliwość wycinania każdego ciągu w tablicy. Na przykład:

a = numpy.array(['hello', 'how', 'are', 'you']) 
numpy.char.sliceStr(a, slice(1, 3)) 
>>> numpy.array(['el', 'ow', 're' 'ou']) 

Czy brakuje mi oczywistej metody w module z tą funkcjonalnością? W przeciwnym razie, czy istnieje szybki wektorowy sposób, aby to osiągnąć?

+2

Nie jestem pewien, co to jest pytanie. komu brakuje tej funkcji? –

+0

Wygląda na to, że Numpy 'routines.char' go nie ma. Zmieniłem to pytanie, aby było to bardziej jasne. –

+0

Szukałem również tej funkcji. Myślę, że zawsze używałem jakiejś pętli. – farenorth

Odpowiedz

8

Oto wektorowy podejście -

def slicer_vectorized(a,start,end): 
    b = a.view('S1').reshape(len(a),-1)[:,start:end] 
    return np.fromstring(b.tostring(),dtype='S'+str(end-start)) 

run Sample -

In [68]: a = np.array(['hello', 'how', 'are', 'you']) 

In [69]: slicer_vectorized(a,1,3) 
Out[69]: 
array(['el', 'ow', 're', 'ou'], 
     dtype='|S2') 

In [70]: slicer_vectorized(a,0,3) 
Out[70]: 
array(['hel', 'how', 'are', 'you'], 
     dtype='|S3') 

Runtime testu -

Testowanie wszystkie podejścia wysłane przez innych autorów, które można uruchomić na mojej stronie i także w tym wektoryzacji z wcześniejszego postu.

Oto czasy -

In [53]: # Setup input array 
    ...: a = np.array(['hello', 'how', 'are', 'you']) 
    ...: a = np.repeat(a,10000) 
    ...: 

# @Alberto Garcia-Raboso's answer 
In [54]: %timeit slicer(1, 3)(a) 
10 loops, best of 3: 23.5 ms per loop 

# @hapaulj's answer 
In [55]: %timeit np.frompyfunc(lambda x:x[1:3],1,1)(a) 
100 loops, best of 3: 11.6 ms per loop 

# Using loop-comprehension 
In [56]: %timeit np.array([i[1:3] for i in a]) 
100 loops, best of 3: 12.1 ms per loop 

# From this post 
In [57]: %timeit slicer_vectorized(a,1,3) 
1000 loops, best of 3: 787 µs per loop 
+0

Czy testowano to w Pythonie w wersji 2.x lub 3.x? Z wersją 3.5.2 otrzymuję 'array ([b", b '', b '', b ''], dtype = '| S2') 'jako wyjście? – Bart

+0

@Bart To było w Pythonie 2.7. Zobacz, czy problem występuje w Pythonie 3.x. Dzięki za powiadomienie. – Divakar

3

Ciekawe pominięcie ... Chyba zawsze można napisać własny:

import numpy as np 

def slicer(start=None, stop=None, step=1): 
    return np.vectorize(lambda x: x[start:stop:step], otypes=[str]) 

a = np.array(['hello', 'how', 'are', 'you']) 
print(slicer(1, 3)(a)) # => ['el' 'ow' 're' 'ou'] 

EDIT: Oto niektóre benchmarki wykorzystujące tekst Ulissesa Jamesa Joyce'a. Wygląda na to, że zwycięzcą jest ostatnia strategia @ hpaulj. @Divakar wchodzi do wyścigu ulepszając ostatnią strategię @ hpaulj.

import numpy as np 
import requests 

ulysses = requests.get('http://www.gutenberg.org/files/4300/4300-0.txt').text 
a = np.array(ulysses.split()) 

# Ufunc 
def slicer(start=None, stop=None, step=1): 
    return np.vectorize(lambda x: x[start:stop:step], otypes=[str]) 

%timeit slicer(1, 3)(a) 
# => 1 loop, best of 3: 221 ms per loop 

# Non-mutating loop 
def loop1(a): 
    out = np.empty(len(a), dtype=object) 
    for i, word in enumerate(a): 
     out[i] = word[1:3] 

%timeit loop1(a) 
# => 1 loop, best of 3: 262 ms per loop 

# Mutating loop 
def loop2(a): 
    for i in range(len(a)): 
     a[i] = a[i][1:3] 

b = a.copy() 
%timeit -n 1 -r 1 loop2(b) 
# 1 loop, best of 1: 285 ms per loop 

# From @hpaulj's answer 
%timeit np.frompyfunc(lambda x:x[1:3],1,1)(a) 
# => 10 loops, best of 3: 141 ms per loop 

%timeit np.frompyfunc(lambda x:x[1:3],1,1)(a).astype('U2') 
# => 1 loop, best of 3: 170 ms per loop 

%timeit a.view('U1').reshape(len(a),-1)[:,1:3].astype(object).sum(axis=1) 
# => 10 loops, best of 3: 60.7 ms per loop 

def slicer_vectorized(a,start,end): 
    b = a.view('S1').reshape(len(a),-1)[:,start:end] 
    return np.fromstring(b.tostring(),dtype='S'+str(end-start)) 

%timeit slicer_vectorized(a,1,3) 
# => The slowest run took 5.34 times longer than the fastest. 
# This could mean that an intermediate result is being cached. 
# 10 loops, best of 3: 16.8 ms per loop 
+1

Jest to jednak wolniejsza od zwykłej pętli. – ayhan

+0

Jest nieco szybszy niż pętla na moim komputerze (z dużą tablicą). –

3

większość, jeśli nie wszystkie funkcje w np.char stosować istniejące str metod do każdego elementu tablicy. Jest trochę szybszy niż bezpośrednia iteracja (lub vectorize), ale nie drastycznie.

Nie ma krajarki strunowej; przynajmniej nie przez tego rodzaju imię. Najbliżej indeksowania z plasterka:

In [274]: 'astring'[1:3] 
Out[274]: 'st' 
In [275]: 'astring'.__getitem__ 
Out[275]: <method-wrapper '__getitem__' of str object at 0xb3866c20> 
In [276]: 'astring'.__getitem__(slice(1,4)) 
Out[276]: 'str' 

Iteracyjny podejście może być z frompyfunc (który jest także wykorzystywany przez vectorize)

In [277]: a = numpy.array(['hello', 'how', 'are', 'you']) 
In [278]: np.frompyfunc(lambda x:x[1:3],1,1)(a) 
Out[278]: array(['el', 'ow', 're', 'ou'], dtype=object) 
In [279]: np.frompyfunc(lambda x:x[1:3],1,1)(a).astype('U2') 
Out[279]: 
array(['el', 'ow', 're', 'ou'], 
     dtype='<U2') 

mogę go postrzegać jako pojedynczej macierzy charakter i warstwę, która

In [289]: a.view('U1').reshape(4,-1)[:,1:3] 
Out[289]: 
array([['e', 'l'], 
     ['o', 'w'], 
     ['r', 'e'], 
     ['o', 'u']], 
     dtype='<U1') 

Nadal muszę dowiedzieć się, jak przekonwertować go z powrotem na "U2".

In [290]: a.view('U1').reshape(4,-1)[:,1:3].copy().view('U2') 
Out[290]: 
array([['el'], 
     ['ow'], 
     ['re'], 
     ['ou']], 
     dtype='<U2') 

Początkowy etap widok pokazuje databuffer jako znaków Py3 (te byłyby bajtów w S lub PY2 przypadku String):

In [284]: a.view('U1') 
Out[284]: 
array(['h', 'e', 'l', 'l', 'o', 'h', 'o', 'w', '', '', 'a', 'r', 'e', '', 
     '', 'y', 'o', 'u', '', ''], 
     dtype='<U1') 

Picking 1: 3 kolumny Kwoty do pobrania a.view('U1')[[1,2,6,7,11,12,16,17]] a następnie zmiana kształtu i widok. Nie wdając się w szczegóły, nie dziwi mnie, że wymaga kopii.

+0

'a.view ('U1') .reformacja (len (a), - 1) [:, 1: 3] .astype (obiekt) .sum (oś = 1)' działa i jest wyraźnym zwycięzcą pod względem wydajność --- zobacz moją odpowiedź. –

+0

Wygląda na to, że możesz przekonwertować tablicę z 'U1' na' U2' z 'view', jeśli najpierw zrobisz kopię, ale nie powinniśmy potrzebować kopii. Zasadniczo powinno to być proste manipulowanie krokami, wymiarami i przesunięciami, ale nie wiem, czy istnieje sposób, aby to zrobić bez bezpośredniego stosowania procedur C. – user2357112

+0

Trudne użycie '.sum()'. W tym przypadku jest to konkatenacja 'string + string'. – hpaulj

1

Aby rozwiązać ten problem, do tej pory byłem przekształcenie numpy array do pand Series iz powrotem. To nie jest ładne rozwiązanie, ale działa i działa stosunkowo szybko.

a = numpy.array(['hello', 'how', 'are', 'you']) 
pandas.Series(a).str[1:3].values 
array(['el', 'ow', 're', 'ou'], dtype=object) 
+1

Właściwie, gdy je wyliczyłem na dużej tablicy pandy nie było szybciej do regularnego krojenia (coś w stylu .str [1: 5]). Nawet wykluczyłem czas na konwersję tablicy na serie. Dla rzeczy takich jak .str [:: - 1] było to jednak szybsze. – ayhan

+0

Wygląda na to, że Pandy używają wielu tablic obiektów. – hpaulj