it-swarm.com.de

Wie entferne ich mich von der „for-loop“ -Denkschule?

Dies ist eine eher konzeptionelle Frage, aber ich hatte gehofft, dass ich hierzu einen guten Rat bekommen könnte. Ich programmiere viel mit ( NumPy ) Arrays; Ich muss oft Elemente in zwei oder mehr Arrays unterschiedlicher Größe abgleichen, und das erste, zu dem ich gehe, ist eine for-Schleife oder noch schlimmer eine verschachtelte for-Schleife. Ich möchte for-Schleifen so weit wie möglich vermeiden, da sie langsam sind (zumindest in Python).

Ich weiß, dass es für viele Dinge mit NumPy vordefinierte Befehle gibt, die ich nur recherchieren muss, aber haben Sie (als erfahrene Programmierer) einen allgemeinen Denkprozess, der mir in den Sinn kommt wenn du etwas iterieren musst?

Also habe ich oft so etwas, was schrecklich ist und ich möchte es vermeiden:

small_array = np.array(["one", "two"])
big_array = np.array(["one", "two", "three", "one"])

for i in range(len(small_array)):
    for p in range(len(big_array)):
        if small_array[i] == big_array[p]:
            print "This item is matched: ", small_array[i]

Ich weiß, dass es mehrere verschiedene Wege gibt, um dies zu erreichen, aber ich bin an einer allgemeinen Denkmethode interessiert, falls es sie gibt.

80
turnip

Dies ist eine häufige konzeptionelle Schwierigkeit, wenn Sie lernen, NumPy effektiv zu verwenden. Normalerweise wird die Datenverarbeitung in Python) am besten in Form von Iteratoren ausgedrückt, um die Speichernutzung gering zu halten und die Möglichkeiten zu maximieren zur Parallelität mit dem E/A-System und zur Wiederverwendung und Kombination von Teilen von Algorithmen.

Aber NumPy stellt all das auf den Kopf: Der beste Ansatz besteht darin, den Algorithmus als eine Folge von Ganz-Array-Operationen auszudrücken, um den Zeitaufwand zu minimieren im langsamen Python Interpreter und maximieren Sie den Zeitaufwand für schnell kompilierte NumPy-Routinen.

Hier ist der allgemeine Ansatz, den ich verfolge:

  1. Behalten Sie die Originalversion der Funktion bei (von der Sie sicher sind, dass sie korrekt ist), damit Sie sie anhand Ihrer verbesserten Versionen auf Richtigkeit und Geschwindigkeit testen können.

  2. Arbeiten Sie von innen nach außen: Beginnen Sie also mit der innersten Schleife und prüfen Sie, ob sie vektorisiert werden kann. Wenn Sie das getan haben, gehen Sie eine Ebene weiter und fahren Sie fort.

  3. Verbringen Sie viel Zeit mit dem Lesen der NumPy-Dokumentation . Es gibt viele Funktionen und Operationen, die nicht immer hervorragend benannt sind. Es lohnt sich also, sie kennenzulernen. Insbesondere wenn Sie denken: "Wenn es nur eine Funktion gäbe, die so und so funktioniert", lohnt es sich, zehn Minuten damit zu verbringen, danach zu suchen. Es ist normalerweise irgendwo drin.

Es gibt keinen Ersatz für das Üben, deshalb werde ich Ihnen einige Beispielprobleme geben. Das Ziel für jedes Problem ist es, die Funktion so umzuschreiben, dass sie vollständig vektorisiert ist : das heißt, dass sie aus einer Folge von NumPy-Operationen insgesamt besteht Arrays ohne native Python -Schleifen (keine for oder while Anweisungen, keine Iteratoren oder Verständnisse).

Problem 1

def sumproducts(x, y):
    """Return the sum of x[i] * y[j] for all pairs of indices i, j.

    >>> sumproducts(np.arange(3000), np.arange(3000))
    20236502250000

    """
    result = 0
    for i in range(len(x)):
        for j in range(len(y)):
            result += x[i] * y[j]
    return result

Problem 2

def countlower(x, y):
    """Return the number of pairs i, j such that x[i] < y[j].

    >>> countlower(np.arange(0, 200, 2), np.arange(40, 140))
    4500

    """
    result = 0
    for i in range(len(x)):
        for j in range(len(y)):
            if x[i] < y[j]:
                result += 1
    return result

Problem 3

def cleanup(x, missing=-1, value=0):
    """Return an array that's the same as x, except that where x ==
    missing, it has value instead.

    >>> cleanup(np.arange(-3, 3), value=10)
    ... # doctest: +NORMALIZE_WHITESPACE
    array([-3, -2, 10, 0, 1, 2])

    """
    result = []
    for i in range(len(x)):
        if x[i] == missing:
            result.append(value)
        else:
            result.append(x[i])
    return np.array(result)

Spoiler unten. Sie werden die besten Ergebnisse erzielen, wenn Sie es selbst versuchen, bevor Sie sich meine Lösungen ansehen!

Antwort 1

np.sum (x) * np.sum (y)

Antwort 2

np.sum (np.searchsorted (np.sort (x), y))

Antwort 3

np.where (x == fehlt, Wert, x)

90
Gareth Rees

Um die Dinge schneller zu machen, müssen Sie Ihre Datenstrukturen nachlesen und die entsprechenden verwenden.

Bei nicht trivialen Größen von kleinen und großen Arrays (sagen wir klein = 100 Elemente und groß = 10.000 Elemente) besteht eine Möglichkeit darin, das kleine Array zu sortieren, dann über das große Array zu iterieren und eine binäre Suche zu verwenden, um passende Elemente zu finden in der kleinen Reihe.

Dies würde die maximale Zeitkomplexität O (N log N) erhöhen (und für kleine kleine Arrays und sehr große große Arrays ist es näher an O(N)), wo sich Ihre verschachtelte Schleifenlösung befindet O (N ^ 2)

Jedoch. Welche Datenstrukturen am effizientesten sind, hängt stark vom eigentlichen Problem ab.

8
Pieter B