2017-07-25 5 views
0

Ich habe eine Funktion, die eine Liste (von Strings) akzeptiert. Es führt eine Verarbeitung auf dieser Liste durch und gibt eine andere Liste von Strings zurück, möglicherweise von kürzerer Länge.Funktion auf jede Liste in einer Reihe von Listen anwenden

Jetzt habe ich eine Reihe von Eingabe-Listen von Strings. Ich möchte diese Transformationsfunktion auf jede Liste in meinem Array anwenden.

Von was für eine Suche ich bisher gemacht habe, es schien wie vectorize oder apply_along_axis könnte gute Kandidaten sein, aber keiner funktioniert wie erwartet.

Ich möchte das so effizient wie möglich tun. Letztendlich wird das Eingabe-Array in der Größenordnung von 100K Listen enthalten.

Ich nehme an, ich könnte über die numpy-Array in einer for Schleife, dann append jede Ausgabeliste in ein neues Ausgabe-Array einzeln nacheinander, aber das scheint schrecklich ineffizient.

Hier ist was ich versucht habe. Zu Testzwecken habe ich eine dumbed-down-Transformationsfunktion erstellt und das Eingabearray enthält nur 3 Listen.

def my_func(l): 
    # accepts list, returns another list 
    # dumbed down list transformation function 
    # for testing, just return the first 2 elems of original list 
    return l[0:2] 

test_arr = np.array([['the', 'quick', 'brown', 'fox'], ['lorem', 'ipsum'], ['this', 'is', 'a', 'test']]) 

np.apply_along_axis(my_func, 0, test_arr) 
Out[51]: array([['the', 'quick', 'brown', 'fox'], ['lorem', 'ipsum']], dtype=object) 

# Rather than applying item by item, this returns the first 2 elements of the entire outer array!! 

# Expected: 
# array([['the', 'quick'], ['lorem', 'ipsum'], ['this', 'is']]) 

# Attempt 2... 

my_func_vec = np.vectorize(my_func) 
my_func_vec(test_arr) 

Ergebnis:

Traceback (most recent call last): 

    File "<ipython-input-56-f9bbacee645c>", line 1, in <module> 
    my_func_vec(test_arr) 

    File "C:\Users\Tony\Anaconda2\lib\site-packages\numpy\lib\function_base.py", line 2218, in __call__ 
    return self._vectorize_call(func=func, args=vargs) 

    File "C:\Users\Tony\Anaconda2\lib\site-packages\numpy\lib\function_base.py", line 2291, in _vectorize_call 
    copy=False, subok=True, dtype=otypes[0]) 

ValueError: cannot set an array element with a sequence 
+1

Diese oft diskutiert worden ist. Weder "vektorisieren" noch "anwenden" ...'wird die' Effizienz' verbessern. Sie müssen Ihre Funktion immer noch auf jeder Liste aufrufen und die Ergebnisse in einem Array oder einer Liste zusammenfassen. Im Großen und Ganzen läuft die Funktion 100k mal so langsam, nicht das Iterationsframework. – hpaulj

+1

In früheren Tests habe ich festgestellt, dass 'np.frompyfunc' ein gutes Werkzeug zum Iterieren von Objekt-Arrays ist. – hpaulj

+1

Eigentlich dachte ich genau das Gleiche, entschied mich aber, die Frage zu beantworten, anstatt zu sagen, wie ich es machen würde. Aber dann habe ich es getestet und aus irgendeinem Grund war die Vektorisierung 10x schneller als Listenverständnis oder die map() Funktion ... irgendeine Idee warum? – dnalow

Antwort

3

Vom docstring von vectorize es über das optionale Argument lautet otypes

otypes : str or list of dtypes, optional 
    The output data type. It must be specified as either a string of 
    typecode characters or a list of data type specifiers. There should 
    be one data type specifier for each output. 

Es ermöglicht Ihnen, strukturierte Arrays zu erstellen komplexe Ausgang, sondern auch Ihre löst Problem, wo Sie Listen als Array-Element haben. Eine Ebene höher zu gehen, Ihre Lösung nur gibt die 2 ersten Elemente Ihres Arrays, anstatt die zwei ersten Elemente der einzelnen Elemente des Arrays

my_func_vec = np.vectorize(my_func, otypes=[list])

+1

numpy ist darauf ausgerichtet, effizient mit numerischen Arrays umzugehen. Ich würde sorgfältig prüfen, ob die Vektorisierung, die du machst, einen Leistungszuwachs im Vergleich zu der von @Wli angegebenen Liste ergibt. –

+0

@IgnacioVergaraKausel Leute bevorzugen komplexe Antworten :) – Wli

1
[my_func(x) for x in test_arr] 

Sie benötigen.

1

Einige Vergleiche und Zeittests; aber bedenken Sie, dass dies ein kleines Beispiel ist.

In [106]: test_arr = np.array([['the', 'quick', 'brown', 'fox'], ['lorem', 'ipsum'], ['this', 'is', 'a', 'test']]) 
    ...: 
In [107]: def my_func(l): 
    ...:  # accepts list, returns another list 
    ...:  # dumbed down list transformation function 
    ...:  # for testing, just return the first 2 elems of original list 
    ...:  return l[0:2] 
    ...: 

Die Liste Verständnis Methode gibt ein 2D-Array von Zeichenketten - da die Funktion 2 kehrt jedes Mal Elementlisten.

In [108]: np.array([my_func(x) for x in test_arr]) 
Out[108]: 
array([['the', 'quick'], 
     ['lorem', 'ipsum'], 
     ['this', 'is']], 
     dtype='<U5') 

Die Eingangsanordnung ist Gegenstand dtype weil die Unterlisten in der Länge unterscheiden:

In [109]: test_arr 
Out[109]: 
array([list(['the', 'quick', 'brown', 'fox']), list(['lorem', 'ipsum']), 
     list(['this', 'is', 'a', 'test'])], dtype=object) 

frompyfunc ein Objekt dtype Array zurückgibt; im Einklang mit meinen bisherigen Tests ist es bescheiden schneller (2x aber nie eine Größenordnung)

In [110]: np.frompyfunc(my_func,1,1)(test_arr) 
Out[110]: 
array([list(['the', 'quick']), list(['lorem', 'ipsum']), 
     list(['this', 'is'])], dtype=object) 

In [111]: timeit np.frompyfunc(my_func,1,1)(test_arr) 
5.68 µs ± 230 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each) 
In [112]: timeit np.array([my_func(x) for x in test_arr]) 
8.96 µs ± 25.6 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each) 

vectorize verwendet frompyfunc aber mehr Overhead hat. Die otypes wird müssen die sequence Fehler zu vermeiden (sonst versucht es, den Rückgabetyp aus einer Versuchsberechnung abzuleiten):

In [113]: np.vectorize(my_func,otypes=[object])(test_arr) 
Out[113]: 
array([list(['the', 'quick']), list(['lorem', 'ipsum']), 
     list(['this', 'is'])], dtype=object) 
In [114]: timeit np.vectorize(my_func,otypes=[object])(test_arr) 
30.4 µs ± 132 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each) 
+1

Schön, obwohl ich das für die reine Python-Lösung nicht sagen würde Es macht keinen Sinn, das Casting in ein numpliges Array zu tun oder die Eingabe als numpy Arrays zu haben. –

+1

Wie ich schon sagte, ich habe es auf die gleiche Weise getestet und hatte es 10x schneller mit Vektorisierung im Vergleich zu Listenverständnis. Aber ich habe eine "sehr lange" test_arr ... so etwas wie 100k verwendet – dnalow

Verwandte Themen