2017-01-05 4 views
4

Gibt es eine einfache Möglichkeit, alle relevanten Elemente in NumPy-Array nach einem Muster zu finden?Suche nach einem Muster in numpy Array

Betrachten wir zum Beispiel die folgende Array:

a = array(['zzzz', 'zzzd', 'zzdd', 'zddd', 'dddn', 'ddnz', 'dnzn', 'nznz', 
     'znzn', 'nznd', 'zndd', 'nddd', 'ddnn', 'dnnn', 'nnnz', 'nnzn', 
     'nznn', 'znnn', 'nnnn', 'nnnd', 'nndd', 'dddz', 'ddzn', 'dznn', 
     'znnz', 'nnzz', 'nzzz', 'zzzn', 'zznn', 'dddd', 'dnnd'], dtype=object) 

Und ich brauche, um alle Kombinationen zu finden, die '** dd' enthalten.

Ich brauche im Grunde eine Funktion, die das Array als Eingabe empfängt und gibt eine kleinere Array mit allen relevanten Elementen:

>> b = func(a, pattern='**dd') 
>> b = array(['zzdd', 'zddd', 'zndd', 'nddd', 'nndd', 'dddd'], dtype=object) 
+0

'" ** dd "' ist nicht die Regex, die Sie brauchen. Vielleicht meinst du Wildcard? In diesem Fall ist 'fnmatch' Ihre Lösung. Aber schreibe '" ?? dd "' –

+0

Beiseite: in vielen Fällen (nicht alle, aber wahrscheinlich am meisten), wenn Sie mit Strings in numpy Arrays arbeiten, sind Sie generell besser dran mit einer einfachen Liste arbeiten - auch wenn Sie dann wieder in ein ndarray konvertieren - oder eine pandas.Series. Wann immer Sie sich mit 'dtype = object' ndarrays beschäftigen, sollten Sie sich fragen, ob Sie falsch abgebogen sind. – DSM

+0

@DSM, Sie haben hier völlig Recht mit der Verwendung von numpy Arrays. Ich arbeite mit Pandas Datenrahmen und eine meiner Spalten enthält verschiedene Kombinationen von vier Buchstaben. Ich habe diese eine Spalte einfach extrahiert, um das Problem zu demonstrieren, das ich zur Hand habe. –

Antwort

6

Da es stellt sich heraus, Sie tatsächlich mit Pandas arbeiten, gibt es einfachere Möglichkeiten, um es auf der Ebene der Serie zu tun, anstatt nur ein ndarray, mit dem vectorized string operations:

In [32]: s = pd.Series(['zzzz', 'zzzd', 'zzdd', 'zddd', 'dddn', 'ddnz', 'dnzn', 'nznz', 
    ...:  'znzn', 'nznd', 'zndd', 'nddd', 'ddnn', 'dnnn', 'nnnz', 'nnzn', 
    ...:  'nznn', 'znnn', 'nnnn', 'nnnd', 'nndd', 'dddz', 'ddzn', 'dznn', 
    ...:  'znnz', 'nnzz', 'nzzz', 'zzzn', 'zznn', 'dddd', 'dnnd']) 

In [33]: s[s.str.endswith("dd")] 
Out[33]: 
2  zzdd 
3  zddd 
10 zndd 
11 nddd 
20 nndd 
29 dddd 
dtype: object 

, die eine Serie produziert, oder wenn Sie wirklich auf einem ndarray bestehen:

In [34]: s[s.str.endswith("dd")].values 
Out[34]: array(['zzdd', 'zddd', 'zndd', 'nddd', 'nndd', 'dddd'], dtype=object) 

Sie können auch reguläre Ausdrücke verwenden, wenn Sie es vorziehen:

In [49]: s[s.str.match(".*dd$")] 
Out[49]: 
2  zzdd 
3  zddd 
10 zndd 
11 nddd 
20 nndd 
29 dddd 
dtype: object 
+0

einfach genial !!!! –

+1

Das NumPy char-Modul hat ['startswith'] (https://docs.scipy.org/doc/numpy/reference/generated/numpy.core.defchararray.startswith.html), nur nicht so etwas wie' endswith' :) – Divakar

+0

@Divakar: FWIW Ich war ziemlich beeindruckt, dass du es geschafft hast. :-) – DSM

1
import fnmatch 
import numpy as np 
a = ['zzzz', 'zzzd', 'zzdd', 'zddd', 'dddn', 'ddnz', 'dnzn', 'nznz', 
     'znzn', 'nznd', 'zndd', 'nddd', 'ddnn', 'dnnn', 'nnnz', 'nnzn', 
     'nznn', 'znnn', 'nnnn', 'nnnd', 'nndd', 'dddz', 'ddzn', 'dznn', 
     'znnz', 'nnzz', 'nzzz', 'zzzn', 'zznn', 'dddd', 'dnnd'] 


b=[] 
for item in a: 
    if fnmatch.fnmatch(item, "z*dd"): 
     b.append(item) 
print b 

Ausgang

['zzdd', 'zddd', 'zndd'] 
-1

Python in Funktion ein gebaut genannt .endswith(). Der Hinweis ist in dem Namen, es findet jeden Wert in einer Zeichenfolge, die mit dem Wert in den Klammern endet. Um dies zu tun, in Ihrem Fall jedoch könnten Sie Folgendes tun:

i = 0 
while i < len(a) : 
    if a[i].endswith("dd") : 
     print(a[i]) 
    i += 1 
+0

Dies verwendet nicht 'numpy' – roganjosh

+0

Auch die erwartete Ausgabe enthält Elemente mit' ddd'. – roganjosh

+0

Er hat nicht gesagt, dass er eine Antwort braucht, die * numpy benutzt, nur dass er Daten * in einem numpligen Array * hat. Es ist natürlich nicht die beste Lösung, aber vOv –

3

Ich bin kein Spezialist numpy. Allerdings verstehe ich, dass Sie eine gefilterte numpy Array, kein Standard-Python-Array erstellen möchten, und die Konvertierung von Python-Array in numpy Array braucht Zeit und Speicher, so schlechte Option.

Nicht sicher, dass Sie regex bedeuten , sondern Wildcard, wobei in diesem Fall die richtige Wahl fnmatch Modul mit ??dd Muster (2 beliebige Zeichen + dd am Ende)

(alternative Lösung würde bedeuten, re.match() mit ..dd$ als Muster).

Ich würde die Indizes berechnet Ihre Kriterien übereinstimmen, würde dann take verwenden, um eine Unterliste zu extrahieren:

from numpy import array 
import fnmatch 

a = array(['zzzz', 'zzzd', 'zzdd', 'zddd', 'dddn', 'ddnz', 'dnzn', 'nznz', 
     'znzn', 'nznd', 'zndd', 'nddd', 'ddnn', 'dnnn', 'nnnz', 'nnzn', 
     'nznn', 'znnn', 'nnnn', 'nnnd', 'nndd', 'dddz', 'ddzn', 'dznn', 
     'znnz', 'nnzz', 'nzzz', 'zzzn', 'zznn', 'dddd', 'dnnd'], dtype=object) 

def func(ar,pattern): 
    indices = [i for i,x in enumerate(ar) if fnmatch.fnmatch(x,pattern)] 
    return ar.take(indices) 

print(func(a,"??dd")) 

Ergebnis:

['zzdd' 'zddd' 'zndd' 'nddd' 'nndd' 'dddd'] 

regex Version (die gleiche Ergebnis am Ende natürlich):

from numpy import array 
import re 

def func(ar,pattern): 
    indices = [i for i,x in enumerate(ar) if re.match(pattern,x)] 
    return ar.take(indices) 

print(func(a,"..dd$")) 
+0

Interessant, dass dies nur 4x länger dauert als die 'numpy' Lösung von Divakar und dennoch ein Listenverständnis verwendet. Dies ist viel einfacher zu folgen, ich denke, es ist besser, um Lesbarkeit für dieses Problem zu bleiben :) – roganjosh

+0

Ich habe versucht, ein Generator Verständnis zu schaffen, aber 'nehmen 'würde mich nicht lassen. Ja, die puren numpigen Antworten sind sehr komplex und doch schneller. Ich nehme an, dass 'numpy' nicht getan wird, um String-Daten zu verarbeiten/zu filtern. –

4

Hier ist ein Ansatz mit numpy.core.defchararray.rfind, um uns die letzte zu erhalten Index einer Übereinstimmung und dann überprüfen wir, ob dieser Index 2 minus der Länge jeder Zeichenfolge ist. Jetzt ist die Länge jeder Zeichenfolge hier 4, also würden wir nach den letzten Indizes suchen, die 4 - 2 = 2 sind.

Somit wäre eine Implementierung sein -

a[np.core.defchararray.rfind(a.astype(str),'dd')==2] 

Wenn die Saiten nicht gleich lang sind, müssen wir die Längen bekommen, subtrahieren 2 und dann vergleichen -

len_sub = np.array(list(map(len,a)))-len('dd') 
a[np.core.defchararray.rfind(a.astype(str),'dd')==len_sub] 

Um dies zu testen, fügen wir am Ende der angegebenen Stichprobe eine längere Zeichenfolge mit der Endung dd hinzu -

In [121]: a = np.append(a,'ewqjejwqjedd') 

In [122]: len_sub = np.array(list(map(len,a)))-len('dd') 

In [123]: a[np.core.defchararray.rfind(a.astype(str),'dd')==len_sub] 
Out[123]: array(['zzdd', 'zddd', 'zndd', 'nddd', 'nndd', 'dddd',\ 
       'ewqjejwqjedd'], dtype=object) 
Verwandte Themen