[PYTHON] Verwenden Sie Dekorateure, um eine erneute Ausführung der Datenverarbeitung zu verhindern

Überblick

Es ist ein gängiger Prozess, Daten zu verarbeiten, einmal auf einer Festplatte zu speichern und wiederzuverwenden (Datenverarbeitung ab dem zweiten Mal überspringen), wobei jedoch die Abhängigkeit der Parameter zum Zeitpunkt der Wiederverwendung usw. berücksichtigt wird. Es neigt dazu, unerwartet kompliziert zu sein. Stellen Sie sich daher eine Implementierung vor, die nicht denselben Vorgang wiederholt, indem Sie mit einem Python-Dekorator ein Urteil überspringen.

Eine Bibliothek dieses Prozesses finden Sie unter github.com/sotetsuk/memozo:

Motivation

Angenommen, Sie haben jetzt eine große Menge an Anweisungsdaten (ein Satz pro Zeile):

1. I have a pen.
2. I have an apple.
3. ah! Apple pen!

...

9999...

# PPAP (copyright belongs to Pikotaro)

Angenommen, Sie möchten nur die Sätze, die ein bestimmtes Schlüsselwort enthalten, aus diesen Daten herausfiltern (z. B. den Satz, der das Schlüsselwort `` `pen``` enthält).

Eine naive Implementierung des Filters wäre die Erstellung eines Generators, der jedes Mal liefert, wenn er eine Aussage findet, die die Kriterien erfüllt:

def filter_data(keyword):
    path_to_raw_data = './data/sentences.txt'
    with codecs.open(path_to_raw_data, 'r', 'utf-8') as f:
        for line in f:
            if keyword in line:
                yield line

gen = filter_data('pen')
for line in gen:
    print(line, end='')

Und wenn diese verarbeiteten Daten (gefilterte Daten) viele Male wiederverwendet werden, ist es nicht immer eine gute Idee, jedes Mal alle Daten zu scannen. Möglicherweise möchten Sie die gefilterten Daten einmal auf der Festplatte zwischenspeichern und dann die zwischengespeicherten Daten verwenden. Dieser Datenverarbeitungsprozess hängt auch vom Parameter (`` Schlüsselwort `) ab. Wenn dieser Prozess also mit einem anderen` `Schlüsselwort ausgeführt wird, werden alle Daten erneut überprüft und auf die Festplatte gelegt. Es gibt auch den Aspekt, zwischenspeichern zu wollen. Und ich habe den Wunsch, diesen Prozess einfach durch Umwickeln der Funktion mit einem Dekorateur zu erreichen.

Zusammenfassend ist das Ziel, die Ausgabe vom Generator mit dem Dekorator `` `awesome_decorator``` wie folgt zwischenzuspeichern. Wenn diese Funktion mit denselben Parametern ausgeführt wird, verwenden Sie den Cache, um die Ausgabe zurückzugeben. ist:

@awesome_decorator
def filter_data(keyword):
    path_to_raw_data = './data/sentences.txt'
    with codecs.open(path_to_raw_data, 'r', 'utf-8') as f:
        for line in f:
            if keyword in line:
                yield line


#Beim ersten Mal werden alle Daten gescannt und das Ergebnis zurückgegeben.
#Zu diesem Zeitpunkt die gefilterte Anweisung'./data/pen.txt'Cache an.
gen_pen_sentences1 = filter_data('pen')
for line in gen_pen_sentences1:
    print(line, end='')

#Da es mit den gleichen Parametern ausgeführt wird, wird der Cache'./data/pen.txt'Gibt die Daten von zurück.
gen_pen_sentences2 = filter_data('pen')
for line in gen_pen_sentences2:
    print(line, end='')

#Da es sich um einen neuen Parameter handelt, werden wir ihn erneut aus den Rohdaten filtern.
gen_apple_sentences = filter_data('apple')
for line in gen_apple_sentences:
    print(line, end='')

Auch dieses Beispiel ist eine Funktion, die einen Generator zurückgibt. Es kann jedoch auch andere Situationen geben, in denen Sie das Ausführungsergebnis einer Funktion zwischenspeichern möchten, die ein Objekt zurückgibt, das von `pickle``` auf die Festplatte serialisiert werden kann (z. B. vorverarbeitet). `ndarray``` und parameterabhängig trainierte maschinelle Lernmodelle).

Implementierung

awesome_decoratorIst einfach zu implementieren, stellen Sie fest, ob bereits zwischengespeicherte Dateien vorhanden sind.

  1. Wenn ein Cache vorhanden ist, erstellen Sie einen neuen Generator, der den Wert aus dem Cache zurückgibt, und geben Sie ihn anstelle des ursprünglichen Generators zurück
  2. Wenn kein Cache vorhanden ist, wickeln Sie den ursprünglichen Generator ein und geben Sie einen Generator zurück, der jedes Mal in den Cache schreibt, wenn er einen Wert zurückgibt

Nur (auch wenn Sie "Gurke" usw. verwenden):

def awesome_decorator(func):

    @functools.wraps(func)
    def _wrapper(keyword):
        #Dieses Mal nehmen wir der Einfachheit halber an, dass das Argument der Funktion nur ein Schlüsselwort ist.
        #Allgemeines(*args, **kwargs)Verwenden Sie bei der Verwendung inspect usw., um die Argumente und ihre Werte zu extrahieren.
        file_path = './data/{}.txt'.format(keyword)

        #Wenn zwischengespeicherte Daten vorhanden sind, wird ein Generator zurückgegeben, der Anweisungen daraus liest.
        if os.path.exists(file_path):
            def gen_cached_data():
                with codecs.open(file_path, 'r', 'utf-8') as f:
                    for line in f:
                        yield line
            return gen_cached_data()

        #Wenn keine zwischengespeicherten Daten vorhanden sind, wird ein Dekorator generiert, der wie gewohnt eine Anweisung aus den Rohdaten zurückgibt.
        gen = func(keyword)

        #Außerdem werden die von den oben genannten Generatoren zurückgegebenen Werte zwischengespeichert.
        def generator_with_cache(gen, file_path):
            with codecs.open(file_path, 'w', 'utf-8') as f:
                for e in gen:
                    f.write(e)
                    yield e

        return generator_with_cache(gen, file_path)

    return _wrapper

Zur Erläuterung des Dekorators selbst ist der Artikel 12 Schritte zum Verständnis von Python-Dekoratoren leicht zu verstehen.

Alles in allem sieht es so aus (dies funktioniert gut mit `` `. / Data / satz.txt```):

awesome_generator.py


# -*- coding: utf-8 -*-

import os
import functools
import codecs


def awesome_decorator(func):

    @functools.wraps(func)
    def _wrapper(keyword):
        #Dieses Mal nehmen wir der Einfachheit halber an, dass das Argument der Funktion nur ein Schlüsselwort ist.
        #Allgemeines(*args, **kwargs)Verwenden Sie bei der Verwendung inspect usw., um die Argumente und ihre Werte zu extrahieren.
        file_path = './data/{}.txt'.format(keyword)

        #Wenn zwischengespeicherte Daten vorhanden sind, wird ein Generator zurückgegeben, der Anweisungen daraus liest.
        if os.path.exists(file_path):
            def gen_cached_data():
                with codecs.open(file_path, 'r', 'utf-8') as f:
                    for line in f:
                        yield line
            return gen_cached_data()

        #Wenn keine zwischengespeicherten Daten vorhanden sind, wird ein Dekorator generiert, der wie gewohnt eine Anweisung aus den Rohdaten zurückgibt.
        gen = func(keyword)

        #Außerdem werden die von den oben genannten Generatoren zurückgegebenen Werte zwischengespeichert.
        def generator_with_cache(gen, file_path):
            with codecs.open(file_path, 'w', 'utf-8') as f:
                for e in gen:
                    f.write(e)
                    yield e

        return generator_with_cache(gen, file_path)

    return _wrapper


@awesome_decorator
def filter_data(keyword):
    path_to_raw_data = './data/sentences.txt'
    with codecs.open(path_to_raw_data, 'r', 'utf-8') as f:
        for line in f:
            if keyword in line:
                yield line


if __name__ == '__main__':
    #Beim ersten Mal werden alle Daten gescannt und das Ergebnis zurückgegeben.
    #Zu diesem Zeitpunkt die gefilterte Anweisung'./data/pen.txt'Cache an.
    gen_pen_sentences1 = filter_data('pen')
    for line in gen_pen_sentences1:
        print(line, end='')

    #Da es mit den gleichen Parametern ausgeführt wird, wird der Cache'./data/pen.txt'Gibt die Daten von zurück.
    gen_pen_sentences2 = filter_data('pen')
    for line in gen_pen_sentences2:
        print(line, end='')

    #Da es sich um einen neuen Parameter handelt, werden wir ihn erneut aus den Rohdaten filtern.
    gen_apple_sentences = filter_data('apple')
    for line in gen_apple_sentences:
        print(line, end='')

memozo 今回の実装は,パラメータの形やファイル名等を固定された形で扱っていましたが,任意の形に少し拡張したものをパッケージとしてgithub.com/sotetsuk/memozoにまとめました. Damit kann dieser Prozess folgendermaßen geschrieben werden:

from memozo import Memozo

m = Memozo('./data')

@m.generator(file_name='filtered_sentences', ext='txt')
def filter_data(keyword):
    path_to_raw_data = './data/sentences.txt'
    with codecs.open(path_to_raw_data, 'r', 'utf-8') as f:
        for line in f:
            if keyword in line:
                yield line

Die Cache-Datei wird in `./ data / filtered_sentences_1fec01f.txt '` `gespeichert, und der Verlauf der in. / Data / .memozo``` verwendeten Parameter wird geschrieben. Der Hash wird berechnet aus (Dateiname, Funktionsname, Parameter). Wenn sowohl der Verlauf als auch die Cache-Datei, die denselben Hash verwenden, bereits vorhanden sind, wird die Funktionsausführung übersprungen. Mit anderen Worten, wenn Sie mit demselben (Dateiname, Funktionsname, Parameter) ausführen, wird der Wert aus dem Cache zurückgegeben, und wenn Sie einen ändern, ist das Ergebnis anders.

Zusätzlich zum Generator gibt es Versionen von Funktionen, die `pickle```,` codecs und gewöhnlichen `` `open entsprechen.

Ich denke, die Implementierung ist noch unvollständig, daher wäre ich Ihnen dankbar, wenn Sie Issue / PR usw. erwähnen könnten.

Beziehung

タスク間に複雑な依存関係がある場合はDAGベースのワークフローツールを使った方がいいでしょう.一例として,github.com/spotify/luigiなどが挙げられます.

Verweise

Recommended Posts

Verwenden Sie Dekorateure, um eine erneute Ausführung der Datenverarbeitung zu verhindern
Zusammenfassung der Verwendung von pandas.DataFrame.loc
Zusammenfassung der Verwendung von pyenv-virtualenv
Zusammenfassung der Verwendung von csvkit
Verarbeitung zur Verwendung von notMNIST-Daten in Python (und versucht, sie zu klassifizieren)
[Einführung in Data Scientist] Grundlagen der wissenschaftlichen Berechnung, Datenverarbeitung und Verwendung der Grafikzeichnungsbibliothek ♬ Grundlagen von Scipy
[Einführung in Data Scientist] Grundlagen der wissenschaftlichen Berechnung, Datenverarbeitung und Verwendung der Grafikzeichnungsbibliothek ♬ Grundlagen von Pandas
[Einführung in Data Scientist] Grundlagen der wissenschaftlichen Berechnung, Datenverarbeitung und Verwendung der Grafikzeichnungsbibliothek ♬ Grundlagen von Matplotlib
[Python] Zusammenfassung der Verwendung von Pandas
100 Sprachverarbeitung Knock-91: Vorbereitung von Analogiedaten
Konvertieren Sie Rasterdaten mithilfe von Pandas in zeilenhaltige (?) Daten
[Python2.7] Zusammenfassung der Verwendung von unittest
Jupyter Notebook Grundlagen der Verwendung
Verwendung von "deque" für Python-Daten
Grundlagen von PyTorch (1) - Verwendung von Tensor-
Zusammenfassung der Verwendung der Python-Liste
[Python2.7] Zusammenfassung der Verwendung des Unterprozesses
Beispiel für eine effiziente Datenverarbeitung mit PANDAS
[Einführung in Data Scientist] Grundlagen von Python ♬
[Frage] Wie verwende ich plot_surface von Python?
[Einführung in Data Scientist] Grundlagen der wissenschaftlichen Berechnung, Datenverarbeitung und Verwendung der Grafikzeichnungsbibliothek ♬ Umgebungskonstruktion
Verwendung von Folium (Visualisierung von Standortinformationen)
[Python] Verwendung von zwei Arten von type ()
Verwenden Sie den zu Boto3 hinzugefügten Wiederholungsverarbeitungsmodus
Konvertieren Sie Daten mit Form (Anzahl der Daten, 1) in (Anzahl der Daten,) mit numpy.
Nicht viel erwähnt, wie man Pickle benutzt
Zusammenfassung der Verwendung von MNIST mit Python
Verwendung von Datenanalysetools für Anfänger
[Pandas] Grundlagen der Verarbeitung von Datumsdaten mit dt
Vorbereitung zum Versuch "Data Science 100 Knock (Strukturierte Datenverarbeitung)"
Die Geschichte des Versuchs, Tensorboard mit Pytorch zu verwenden
Ich möchte die Daten von League of Legends ③ erhalten
Ich möchte die Daten von League of Legends ② erhalten
Verwendung vergangener Wetterdaten 1 (Amedas Punktanzeige)
[Einführung in cx_Oracle] (5.) Umgang mit japanischen Daten
Verwendung der PyTorch-basierten Bildverarbeitungsbibliothek "Kornia"
Zusammenfassung des Studiums von Python zur Verwendung von AWS Lambda
Datenbereinigung 3 Verwendung von OpenCV und Vorverarbeitung von Bilddaten
Ein cooles Diagramm zur Datenanalyse von Wiire!
Ich möchte League of Legends-Daten erhalten ①
Verwendung für Python-Stapel und -Warteschlangen (Geschwindigkeitsvergleich jeder Datenstruktur)
Verwenden Sie Pandas, um nur die angegebenen Zeilen des Datenrahmens in die Excel-Datei zu schreiben
Erstellen Sie einen Datensatz mit Bildern, die für das Training verwendet werden sollen
[Einführung in Python] Verwendung der while-Anweisung (wiederholte Verarbeitung)
100 Sprachverarbeitung Knock-92 (mit Gensim): Anwendung auf Analogiedaten
Zusammenfassung der Tools, die zum Analysieren von Daten in Python benötigt werden
Verarbeitung von CSV-Daten in voller und halber Breite in Python
Hinweise zur Verwendung von AIST Spacon ABCI
Ich habe versucht zusammenzufassen, wie man Matplotlib von Python verwendet
Informationen zur Datenvorverarbeitung von Systemen, die maschinelles Lernen verwenden
Sie brauchen Liebe, um zu vermeiden, dass Cron versehentlich gelöscht wird
[Kapitel 5] Einführung in Python mit 100 Klopfen Sprachverarbeitung
[Kapitel 6] Einführung in Scicit-Learn mit 100 Klopfen Sprachverarbeitung
Verwendung von Python Kivy ~ ~ Grundlagen der Kv-Sprache ~
Senden Sie Daten von Python über die Socket-Kommunikation an Processing
Leistungsüberprüfung der Datenvorverarbeitung in der Verarbeitung natürlicher Sprache
[Kapitel 3] Einführung in Python mit 100 Klopfen Sprachverarbeitung
DataNitro, Implementierung einer Funktion zum Lesen von Daten aus dem Blatt
Verwenden wir die offenen Daten von "Mamebus" in Python