2016-08-19 2 views
13

Numpy hat einige sehr nützliche string operations, die die üblichen Python-String-Operationen vektorisieren.Wie kann ich jedes Element einer Reihe von Strings zerschneiden?

Verglichen mit diesen Operationen und pandas.str scheint das Modul numpy strings eine sehr wichtige zu fehlen: die Fähigkeit, jeden String im Array zu trennen. Zum Beispiel

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

Fehle ich eine offensichtliche Methode im Modul mit dieser Funktionalität? Gibt es sonst einen schnellen vektorisierten Weg, dies zu erreichen?

+2

Ich bin mir nicht sicher, was die Frage ist. Wer vermisst diese Funktion? –

+0

Numpys 'routines.char' scheint es zu vermissen. Ich habe die Frage bearbeitet, um das deutlicher zu machen. –

+0

Ich habe auch nach dieser Funktionalität gesucht. Ich glaube, ich habe immer eine Art Loop benutzt. – farenorth

Antwort

8

Hier ist ein vektorisiert Ansatz -

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

Probelauf -

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 Test -

die von anderen Autoren geschrieben alle Tests heraus Ansätze, die ich an meinem Ende laufen konnte und Dazu gehört auch der vektorisierte Ansatz von früher in diesem Beitrag.

Hier ist die Timings -

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

Wurde dies mit Python 2.x oder 3.x getestet? Mit 3.5.2 bekomme ich 'array ([b '', b '', b '', b ''], dtype = '| S2')' als Ausgabe? – Bart

+0

@Bart Dies war mit Python 2.7. Wird sehen, ob das Problem mit Python 3.x vorliegt. Danke für die Benachrichtigung. – Divakar

3

Interessante Auslassung ... Ich denke, Sie immer Ihre eigenen schreiben:

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: Hier einige Benchmarks sind den Text von Ulysses von James Joyce mit. Es scheint, der klare Gewinner ist @ hpauljs letzte Strategie. @Divakar steigt ins Rennen ein und verbessert die letzte Strategie von @ hpaulj. bestehenden str Methoden zu jedem Element des Arrays

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

Dies ist jedoch langsamer als eine normale Schleife. – ayhan

+0

Es ist etwas schneller als eine Schleife auf meinem Rechner (mit einem großen Array). –

3

Die meisten, wenn nicht alle Funktionen in np.char anzuwenden. Es ist ein wenig schneller als direkte Iteration (oder vectorize), aber nicht drastisch so.

Es gibt keine Schneidemaschine; Zumindest nicht mit dieser Art von Namen. Am nächsten ist, die Indizierung mit einer Scheibe:

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' 

Ein iterativer Ansatz mit frompyfunc sein kann (die auch von vectorize verwendet wird):

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

ich es als ein einzelnes Zeichen Array anzeigen könnte, und Slice, das

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

Ich muss immer noch herausfinden, wie man es zurück in 'U2' konvertieren kann.

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

Die erste Ansicht Schritt zeigt die Databuffer als py3 Zeichen (diese in einem S oder Py2 Zeichenfolge Fall sein Bytes würde):

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

Pflückbereich 1: 3 Säulen beträgt a.view('U1')[[1,2,6,7,11,12,16,17]] Kommissionierung und dann Umgestaltung und Ansicht. Ohne ins Detail zu gehen, überrascht es mich nicht, dass es eine Kopie benötigt.

+0

'a.view ('U1') .umformen (len (a), - 1) [:, 1: 3] .typ (Objekt) .sum (Achse = 1)' funktioniert und ist der klare Gewinner in Bezug auf Leistung --- siehe meine Antwort. –

+0

Es sieht so aus, als könnten Sie Ihr Array von 'U1' nach' U2' mit 'view' konvertieren, wenn Sie zuerst eine Kopie erstellen, aber wir sollten die Kopie nicht benötigen. Im Prinzip sollte dies nur eine einfache Manipulation von Schritten, Dimensionen und Offsets sein, aber ich weiß nicht, ob es einen Weg gibt, dies ohne direkte Anwendung von C-Routinen zu tun. – user2357112

+0

knifflige Verwendung von '.sum()'. In diesem Fall ist es die "String + String" -Kette. – hpaulj

1

Um dies zu lösen, so weit ich habe die numpy array zu einem Pandas Series und zurück zu verwandeln. Es ist keine schöne Lösung, aber es funktioniert und es funktioniert relativ schnell.

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

Eigentlich, wenn ich es auf einem großen Array timed war nicht schneller für regelmäßige Slicing (etwas wie .str [1: 5]). Ich habe sogar die Zeit für die Umwandlung des Arrays in Serie ausgeschlossen. Es war schneller für Dinge wie .str [:: - 1]. – ayhan

+0

Pandas scheint viele Objekt-Arrays zu verwenden. – hpaulj

Verwandte Themen