Suchen mit Sternchen

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Nobuddy
User
Beiträge: 1023
Registriert: Montag 30. Januar 2012, 16:38

Hallo zusammen

Ich verwende ein Entry-Feld als Sucheingabe, mit der dann tab-getrennte Textdateien durchsucht werden.
Da ich große Datenmengen zu durchsuchen habe (ca. 80.000 Datensätze) und der Suchbegriff nicht immer eindeutig ist, habe ich mir überlegt, Sternchen (*) in der Sucheingabe zu verwenden.

Ich poste hier mal den Codeabschnitt, um den es geht und der auch funktioniert. :)
Der betreffende Codeteil beginnt ab der zweiten if-Bedinung.

Code: Alles auswählen

entry1 = self.txt_line.get().lower()
for row in self.data:
    if '*' not in entry1 and entry1 in str(row).lower():
        self.see.add('\t'.join(row))
        self.pool.add(tuple(row))
    if '*' in entry1:
        counter = 0
        for i in range(len(entry1.split('*'))):
            part = entry1.lower().split('*')[i]
            if part in str(row).lower():
                counter += 1
            if counter == len(entry1.split('*')):
                self.see.add('\t'.join(row))
                self.pool.add(tuple(row))
Meine Frage wäre, ob es da noch eine bessere Lösung dazu gibt?

Grüße Nobuddy
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Hallo.

Ich würde an deiner Stelle mit regulären Ausdrücken suchen. Dann musst du selber nichts programmieren und hast eine sehr performante Lösung.
Das Leben ist wie ein Tennisball.
BlackJack

@Nobuddy: Gibt es für Deinen Code nicht *immer* eine bessere Lösung? :twisted: Die innere ``for``-Schleife ist zum heulen und/oder schreiend weglaufen. Ich bin mir ziemlich sicher, dass schon mal erwähnt wurde, dass ``for i in range(len(sequence)):`` ein „anti pattern” ist. So etwas ist mit 99,999%iger Wahrscheinlichkeit unnötig indirekt. Und dann die nächste Zeile… Mach Dir doch mal bitte klar was diese beiden Zeilen vom Konzept her tun. Und dann was die `split()`-Methode als Ergebnis liefert. Dann sollte Dir auffallen, wie viel unsinnige Arbeit in den beiden Zeilen verrichtet wird.

Dann ist es unsauber eine Datenstruktur in eine Zeichenkette umzuwandeln um dann in der Zeichenkette etwas zu suchen. Das ist ein Hack wenn man zu faul ist die Werte in der Datenstruktur einzeln zu testen und es einem nichts ausmacht, dass zusätzliche Zeichen gesucht und gefunden werden können, die eigentlich gar nicht in den Daten vorkommen, sondern nur in der Zeichenketten-Repräsentation der Datenstruktur.

Über das letzte ``if`` in dem Quelltextschnippsel könntest Du auch noch mal nachdenken. Wie oft wird das ausgeführt? Bei welcher Ausführung kommt welches Ergebnis unter welchen Vorbedingungen? Daraus dann die Schlussfolgerung ziehen welche Ausführungen man sich sparen sollte, weil da die Bedingung immer `False` werden *muss*.

Neben dem wiederholten Ausführen von Code der bei jeder Ausführung das gleiche Ergebnis liefert, und deswegen nicht dauernd wiederholt werden müsste, gibt es statisch auch wieder Code-Duplikate, nämlich die zwei identischen Zeilen im Trefferfall.

So wie die Sternchensuche implementiert ist, wird nicht auf die Reihenfolge der Teilstücke geachtet. Die Suche 'ab*cd' würde nicht nur 'abxycd' finden, sondern auch 'cdxyab'. Das ist nicht was man bei einem '*' normalerweise erwartet.

Ich würde das in Funktionen aufspalten. Eine Funktion die eine Zeichenkette, eventuell mit '*' als Platzhalter für beliebige Zeichen, in einer anderen sucht. Dann eine die eine `row` prüft. Und dann kann man das ganze mit `itertools.ifilter()` (Python 2) oder `filter()` (Python 3) auf die Daten anwenden.

Edit (ungetestet):

Code: Alles auswählen

import re

# ...

def filter_rows(search_string, rows):
    test = re.compile(
        '.*?'.join(re.escape(s) for s in search_string.split('*')),
        re.IGNORECASE
    ).match
    return (row for row in rows if any(test(e) for e in row))


# ...

        for row in filter_rows(self.txt_line.get(), self.data):
            self.see.add('\t'.join(row))
            self.pool.add(tuple(row))
Nobuddy
User
Beiträge: 1023
Registriert: Montag 30. Januar 2012, 16:38

Hallo zusammen :wink:

@BlackJack: Das war mir schon klar. daß das nicht das Gelbe vom Ei ist.

Bei der ``for i in range(len(sequence)):`` habe ich keine andere Möglichkeit gesehen, alle Suchwörter die mit einem Stern getrennt sind, 'gleichzeitig' in einer Zeichenkette zu suchen. Nur wenn alles zutrifft, ist es ein Resultat.
Du hast Recht, daß so die Suchreihenfolge nicht eingehalten wird. In diesem Fall sehe ich das eher als Vorteil.

Hier ein Beispiel:
  • Susi und Strolchi gehen in den Wald.
    Im Wald hat sich Strolchi verlaufen und sucht Susi.
    Susi ist verzweifel, da sie Strolchi nicht finden kann.
suche: 'Su*Wald' = 2 Treffer (sonst 1 Treffer)
suche: 'Su*Strolch' = 3 Treffer (sonst 2 Treffer)

Mit dem Beispiel, wollte ich nur verdeutlichen, daß z.B. in einer Produktbeschreibung von verschiedenen Ersteller, eine genaue Suchreihenfolge nicht immer von Vorteil ist.

Ich hatte zuvor auch schon versucht, die Datenstruktur einzeln zu testen, was ohne Sternchen auch prima funktioniert.

Code: Alles auswählen

daten = ['Hallo ich heiße Mumba!']
suchstring = 'Ha'
[print(row) for row in daten for x in [row] if suchstring in x]
Bei Deinem Hinweis, auf das letzte ''if'', muß ich im Moment passen, aber vielleicht komme ich noch drauf.

Ich werde Deinen ungetesteten Vorschlag testen.
Melde mich danach wieder. :wink:

Danke und Grüße
Nobuddy
Nobuddy
User
Beiträge: 1023
Registriert: Montag 30. Januar 2012, 16:38

Hallo BlackJack,

Dein Vorschlag funktioniert prima und ist auch schneller, als bei meinem 'stümperhaften' Versuch.
Werde Deinen Vorschlag genau studieren, damit ich etwas dazu lerne!
Danke nochmals dafür! :wink:

Ich habe da noch eine Frage.
Ich möchte mir unbedingt, Bücher über Python3 und Tkinter3 zulegen.
Da ich an das deutschsprachige gebunden bin, gibt es da nicht so viel Auswahl.
Die Bücher von Galileo sind anscheinend nicht so gut, wie ich aus verschiedenen Meinungen schon heraus gelesen habe.
Aber welche deutschsprachige Bücher sind da wirklich empfehlenswert?

Vielleicht hast Du mir dazu Tipps.

Grüße Nobuddy
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Nobuddy hat geschrieben:Bei der ``for i in range(len(sequence)):`` habe ich keine andere Möglichkeit gesehen, alle Suchwörter die mit einem Stern getrennt sind, 'gleichzeitig' in einer Zeichenkette zu suchen.
Die Kritik war nicht das for, sondern wie du das for verwendest. Das Muster

Code: Alles auswählen

for i in xrange(len(xs)):
    x = xs[i]
ist eigentlich immer ein Fehler. Man kann in Python direkt über Listen etc. iterieren und sollte das auch tun. Also:

Code: Alles auswählen

for x in xs:
    ...
Wenn man doch mal einen Index braucht, dann benutzt man enumerate:

Code: Alles auswählen

for index, x in enumerate(xs):
    ...
Das Leben ist wie ein Tennisball.
Nobuddy
User
Beiträge: 1023
Registriert: Montag 30. Januar 2012, 16:38

Hallo EyDu,

Danke für Deinen Kommentar, habe da 'vor lauter Wald die Bäume nicht gesehen' ... :wink:

Grüße Nobuddy
lunar

@nobuddy Es gibt kaum empfehlenswerte deutschsprachige Literatur für Python-Einsteiger. Lerne Englisch.
Nobuddy
User
Beiträge: 1023
Registriert: Montag 30. Januar 2012, 16:38

Ich habe jetzt folgendes gemacht.
Zum Ersten natürlich, BlackJackś Vorschlag übernommen, welcher prima funktioniert.
Zum Zweiten, meinen Code versucht zu verbessern.

Ich möchte dazu noch erwähnen, daß ich die Suche in Produktdaten von verschiedenen Lieferanten durchführe.
Als erstes habe ich ein Suchfeld, speziell für Artikelnummern.
Als zweites, ein Suchfeld das den gesamten Artikelstammsatz bei der Suche berücksichtigt.

Mein Code sieht nun so aus, dabei werde ich die Funktion von BlackJack nicht mit aufführen, da sie ja oben bereits steht.

Code: Alles auswählen

                    self.pool = set()
                    article = dict()
                    article[self.article.get().lower()] = self.article.get()
                    [self.pool.add(tuple(row)) for row in self.data
                        if article.get(row[8])]
                    [self.pool.add(tuple(row)) for row in
                        filter_star_rows(self.txt_line.get(), self.data)]
@lunar: Ich versuche schon, mir Englisch nach und nach anzueigenen, aber das dauert halt seine Zeit, daher die Frage nach deutschsprachiger Literatur.

Grüße Nobuddy
BlackJack

@Nobuddy: Die beiden „list comprehensions” (LCs) sind unschön. Da werden Listen erzeugt, dafür sind LCs ja auch gedacht, die dann überhaupt gar nicht verwendet werden.
Nobuddy
User
Beiträge: 1023
Registriert: Montag 30. Januar 2012, 16:38

BlackJack hat geschrieben:@Nobuddy: Die beiden „list comprehensions” (LCs) sind unschön. Da werden Listen erzeugt, dafür sind LCs ja auch gedacht, die dann überhaupt gar nicht verwendet werden.
Verstehe das jetzt nicht, wie Du dies meinst.

Suche ich z.B. nach einer Artikelnummer, so verwende ich doch die erste LC und die zweite bleibt davon unberührt.
Oder ist das nicht so?

Grüße Nobuddy
Sirius3
User
Beiträge: 18335
Registriert: Sonntag 21. Oktober 2012, 17:20

Hallo Nobuddy,

List-Comprehension ist dazu da um Listen zu erzeugen.
Du nutzt es aber um eine for-Schleife möglichst unübersichtlich und in einer Zeile zu schreiben.
Die add-Methode von set liefert keinen Rückgabewert und es macht deshalb auch keinen Sinn,
diesen in einer Liste zu speichern, die dann auch nicht mal mehr verwendet wird.

Was soll das dict mit der Artikelnummer???
Auf Gleichheit wird mit == geprüft!

Code: Alles auswählen

article = self.article.get().lower()
pool1=set(tuple(row) for row in self.data if article==row[8])
pool2=set(tuple(row) for row in filter_star_rows(self.txt_line.get(), self.data))
self.pool = pool1 | pool2
Grüße
Sirius
Nobuddy
User
Beiträge: 1023
Registriert: Montag 30. Januar 2012, 16:38

Hallo Sirius3, hallo BlackJack,

mir ist gestern noch klar geworden, was BlackJack mir mit ''Listen, die gar nicht gebraucht werden'' gemeint war.
Hätte das Ganze wahrscheinlich etwas umständlicher gelöst, als das von Sirius3.
Das mit dem dict war hier auch unnötig und ein Gleichheitszeichen ist vollkommen ausreichend.

Vielen Dank für Eure Gedult und Euren Input! :wink:

Grüße Nobuddy
Antworten