Ich habe mich über maschinelles Lernen / datenwissenschaftliche Projektstruktur / Workflow gewundert und verschiedene Meinungen zu diesem Thema gelesen. Und wenn Leute anfangen, über Workflows zu sprechen, möchten sie, dass ihre Workflows reproduzierbar sind. Es gibt viele Posts, die empfehlen,
make zu verwenden
, um den Workflow reproduzierbar zu halten. Obwohl
make
sehr stabil und weit verbreitet ist, mag ich persönlich plattformübergreifende Lösungen. Es ist immerhin 2019, nicht 1977. Man kann argumentieren, dass make plattformübergreifend ist, aber in Wirklichkeit werden Sie Probleme haben und Zeit damit verbringen, Ihr Werkzeug zu reparieren, anstatt die eigentliche Arbeit zu erledigen. Also habe ich beschlossen, mich umzuschauen und herauszufinden, welche anderen Tools verfügbar sind. Ja, ich habe beschlossen, einige Zeit mit Werkzeugen zu verbringen.
Dieser Beitrag ist eher eine Einladung zum Dialog als ein Tutorial. Vielleicht ist Ihre Lösung perfekt. Wenn ja, wird es interessant sein, davon zu hören.
In diesem Beitrag werde ich ein kleines Python-Projekt verwenden und die gleichen Automatisierungsaufgaben mit verschiedenen Systemen ausführen:
Am Ende des Beitrags wird eine
Vergleichstabelle angezeigt .
Die meisten Tools, die ich mir ansehen werde, sind als
Build-Automatisierungssoftware oder
Build-Systeme bekannt . Es gibt unzählige von ihnen in allen verschiedenen Geschmacksrichtungen, Größen und Komplexitäten. Die Idee ist dieselbe: Der Entwickler definiert Regeln für die automatisierte und konsistente Erstellung einiger Ergebnisse. Ein Ergebnis kann beispielsweise ein Bild mit einem Diagramm sein. Um dieses Bild zu erstellen, müsste man die Daten herunterladen, die Daten bereinigen und einige Datenmanipulationen durchführen (wirklich ein klassisches Beispiel). Sie können mit ein paar Shell-Skripten beginnen, die den Job erledigen. Wenn Sie ein Jahr später zum Projekt zurückkehren, wird es schwierig sein, sich alle Schritte und deren Reihenfolge zu merken, die Sie ausführen müssen, um dieses Bild zu erstellen. Die naheliegende Lösung besteht darin, alle Schritte zu dokumentieren. Gute Nachrichten! Mit Build-Systemen können Sie die Schritte in Form eines Computerprogramms dokumentieren. Einige Build-Systeme ähneln Ihren Shell-Skripten, verfügen jedoch über zusätzliche Schnickschnack.
Die Grundlage dieses Beitrags ist eine Reihe von Beiträgen von
Mateusz Bednarski zum automatisierten Workflow für ein maschinelles Lernprojekt. Mateusz erklärt seine Ansichten und liefert Rezepte für die Verwendung von
make
. Ich ermutige Sie, zuerst seine Beiträge zu überprüfen. Ich werde hauptsächlich seinen Code verwenden, aber mit verschiedenen Build-Systemen.
Wenn Sie mehr über
make
erfahren möchten, finden Sie im Folgenden einige Referenzen für einige Beiträge.
Brooke Kennedy bietet einen allgemeinen Überblick in 5 einfachen Schritten, um Ihr Data Science-Projekt reproduzierbar zu machen.
Zachary Jones gibt weitere Details zur Syntax und zu den Funktionen sowie Links zu anderen Posts.
David Stevens schreibt einen sehr Hype-Beitrag darüber, warum Sie
make
sofort verwenden müssen. Er liefert schöne Beispiele, die
den alten und
den neuen Weg vergleichen .
Samuel Lampa hingegen schreibt darüber, warum die Verwendung von
make
eine schlechte Idee ist.
Meine Auswahl an Build-Systemen ist weder umfassend noch unvoreingenommen. Wenn Sie Ihre Liste erstellen möchten, ist
Wikipedia möglicherweise ein guter Ausgangspunkt. Wie oben erwähnt, werde ich
CMake ,
PyBuilder ,
Pynt ,
Paver ,
Doit und
Luigi behandeln . Die meisten Tools in dieser Liste basieren auf Python und sind sinnvoll, da sich das Projekt in Python befindet. In diesem Beitrag wird nicht beschrieben, wie die Tools installiert werden. Ich gehe davon aus, dass Sie Python ziemlich gut beherrschen.
Ich bin hauptsächlich daran interessiert, diese Funktionalität zu testen:
- Angeben einiger Ziele mit Abhängigkeiten. Ich möchte sehen, wie es geht und wie einfach es ist.
- Überprüfen, ob inkrementelle Builds möglich sind. Dies bedeutet, dass das Build-System nicht neu erstellt, was seit dem letzten Lauf nicht geändert wurde, d. H. Sie müssen Ihre Rohdaten nicht erneut herunterladen. Eine andere Sache, nach der ich suchen werde, sind inkrementelle Builds, wenn sich die Abhängigkeit ändert. Stellen Sie sich vor, wir haben ein Diagramm der Abhängigkeiten
A -> B -> C
Wird Ziel C
wiederhergestellt, wenn sich B
ändert? Wenn ein? - Überprüfen, ob eine Neuerstellung ausgelöst wird, wenn der Quellcode geändert wird, d. H. Wir ändern den Parameter des generierten Diagramms. Beim nächsten Erstellen des Bildes muss es neu erstellt werden.
- Überprüfen der Möglichkeiten zum Bereinigen von Build-Artefakten, d. H. Entfernen von Dateien, die während des Builds erstellt wurden, und Zurücksetzen auf den sauberen Quellcode.
Ich werde nicht alle Build-Ziele aus Mateusz 'Post verwenden, nur drei davon, um die Prinzipien zu veranschaulichen.
Der gesamte Code ist auf
GitHub verfügbar.
CMake
CMake ist ein Build-Skript-Generator, der Eingabedateien für verschiedene Build-Systeme generiert. Und sein Name steht für plattformübergreifende Marke. CMake ist ein Software-Engineering-Tool. Das Hauptanliegen ist das Erstellen von ausführbaren Dateien und Bibliotheken. CMake weiß also, wie man
Ziele aus dem Quellcode in unterstützten Sprachen erstellt. CMake wird in zwei Schritten ausgeführt: Konfiguration und Generierung. Während der Konfiguration ist es möglich, den zukünftigen Build nach Bedarf zu konfigurieren. In diesem Schritt werden beispielsweise vom Benutzer bereitgestellte Variablen angegeben. Die Generierung ist normalerweise unkompliziert und erzeugt Dateien, mit denen Build-Systeme arbeiten können. Mit CMake können Sie weiterhin
make
, aber anstatt Makefile direkt zu schreiben, schreiben Sie eine CMake-Datei, die das Makefile für Sie generiert.
Ein weiteres wichtiges Konzept ist, dass CMake
Out-of-Source-Builds fördert. Out-of-Source-Builds halten den Quellcode von Artefakten fern, die er erzeugt. Dies ist sehr sinnvoll für ausführbare Dateien, bei denen eine einzelne Quellcodebasis unter verschiedenen CPU-Architekturen und Betriebssystemen kompiliert werden kann. Dieser Ansatz kann jedoch der Arbeitsweise vieler Datenwissenschaftler widersprechen. Es scheint mir, dass die Data Science Community tendenziell eine hohe Kopplung von Daten, Code und Ergebnissen aufweist.
Mal sehen, was wir brauchen, um unsere Ziele mit CMake zu erreichen. Es gibt zwei Möglichkeiten, benutzerdefinierte Dinge in CMake zu definieren: benutzerdefinierte Ziele und benutzerdefinierte Befehle. Leider müssen wir beide verwenden, was zu mehr Typisierung im Vergleich zu Vanila-Makefile führt. Ein benutzerdefiniertes Ziel wird als immer veraltet angesehen, d. H. Wenn es ein Ziel zum Herunterladen von Rohdaten gibt, lädt CMake es immer wieder herunter. Eine Kombination aus benutzerdefiniertem Befehl und benutzerdefiniertem Ziel ermöglicht es, Ziele auf dem neuesten Stand zu halten.
Für unser Projekt erstellen wir eine Datei mit dem Namen
CMakeLists.txt und legen sie im Stammverzeichnis des Projekts ab. Schauen wir uns den Inhalt an:
cmake_minimum_required(VERSION 3.14.0 FATAL_ERROR) project(Cmake_in_ml VERSION 0.1.0 LANGUAGES NONE)
Dieser Teil ist grundlegend. Die zweite Zeile definiert den Namen Ihres Projekts und Ihre Version und gibt an, dass wir keine integrierte Sprachunterstützung verwenden (Sinus werden wir Python-Skripte nennen).
Unser erstes Ziel wird den IRIS-Datensatz herunterladen:
SET(IRIS_URL "https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data" CACHE STRING "URL to the IRIS data") set(IRIS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/data/raw) set(IRIS_FILE ${IRIS_DIR}/iris.csv) ADD_CUSTOM_COMMAND(OUTPUT ${IRIS_FILE} COMMAND ${CMAKE_COMMAND} -E echo "Downloading IRIS." COMMAND python src/data/download.py ${IRIS_URL} ${IRIS_FILE} COMMAND ${CMAKE_COMMAND} -E echo "Done. Checkout ${IRIS_FILE}." WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) ADD_CUSTOM_TARGET(rawdata ALL DEPENDS ${IRIS_FILE})
Die erste Zeile definiert den Parameter
IRIS_URL
, der dem Benutzer während des Konfigurationsschritts zur Verfügung gestellt wird. Wenn Sie die CMake-GUI verwenden, können Sie diese Variable über die GUI festlegen:

Als Nächstes definieren wir Variablen mit dem heruntergeladenen Speicherort des IRIS-Datasets. Dann fügen wir einen benutzerdefinierten Befehl hinzu, der
IRIS_FILE
als Ausgabe erzeugt. Am Ende definieren wir benutzerdefinierte Ziel-
rawdata
, die von
rawdata
abhängen.
IRIS_FILE
bedeutet, dass zum Erstellen von
rawdata
IRIS_FILE
muss. Option
ALL
des benutzerdefinierten Ziels besagt, dass
rawdata
eines der zu erstellenden Standardziele sind. Beachten Sie, dass ich
CMAKE_CURRENT_SOURCE_DIR
verwende, um die heruntergeladenen Daten im
CMAKE_CURRENT_SOURCE_DIR
und nicht im Build-Ordner zu speichern. Dies ist nur, um es das gleiche wie Mateusz zu machen.
Okay, mal sehen, wie wir es nutzen können. Ich führe es derzeit auf Windows mit installiertem MinGW-Compiler aus. Möglicherweise müssen Sie die Generatoreinstellung an Ihre Bedürfnisse anpassen (führen Sie
cmake --help
, um die Liste der verfügbaren Generatoren
cmake --help
). Starten Sie das Terminal und wechseln Sie in den übergeordneten Ordner des Quellcodes.
mkdir overcome-the-chaos-build cd overcome-the-chaos-build cmake -G "MinGW Makefiles" ../overcome-the-chaos
Ergebnis- Konfiguration abgeschlossen
- Generieren erledigt
- Build-Dateien wurden geschrieben in: C: / home / workspace / überwinden-das-Chaos-Build
Mit modernem CMake können wir das Projekt direkt aus CMake erstellen. Dieser Befehl ruft
build all
Befehl
build all
:
cmake --build .
ErgebnisScannen von Abhängigkeiten von Ziel-Rohdaten
[100%] Ziel-Rohdaten erstellt
Wir können auch die Liste der verfügbaren Ziele anzeigen:
cmake --build . --target help
Und wir können heruntergeladene Dateien entfernen durch:
cmake --build . --target clean
Stellen Sie sicher, dass wir das saubere Ziel nicht manuell erstellen mussten.
Gehen wir nun zum nächsten Ziel - den vorverarbeiteten IRIS-Daten. Mateusz erstellt zwei Dateien aus einer einzigen Funktion:
processed.pickle
und
processed.xlsx
. Sie können sehen, wie er diese Excel-Datei mit
rm
mit Platzhalter bereinigt. Ich denke, das ist kein sehr guter Ansatz. In CMake haben wir zwei Möglichkeiten, damit umzugehen. Die erste Option ist die Verwendung der Verzeichniseigenschaft
ADDITIONAL_MAKE_CLEAN_FILES . Der Code lautet:
SET(PROCESSED_FILE ${CMAKE_CURRENT_SOURCE_DIR}/data/processed/processed.pickle) ADD_CUSTOM_COMMAND(OUTPUT ${PROCESSED_FILE} COMMAND python src/data/preprocess.py ${IRIS_FILE} ${PROCESSED_FILE} --excel data/processed/processed.xlsx WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} DEPENDS rawdata ${IRIS_FILE} ) ADD_CUSTOM_TARGET(preprocess DEPENDS ${PROCESSED_FILE})
Die zweite Option besteht darin, eine Liste von Dateien als benutzerdefinierte Befehlsausgabe anzugeben:
LIST(APPEND PROCESSED_FILE "${CMAKE_CURRENT_SOURCE_DIR}/data/processed/processed.pickle" "${CMAKE_CURRENT_SOURCE_DIR}/data/processed/processed.xlsx" ) ADD_CUSTOM_COMMAND(OUTPUT ${PROCESSED_FILE} COMMAND python src/data/preprocess.py ${IRIS_FILE} data/processed/processed.pickle --excel data/processed/processed.xlsx WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} DEPENDS rawdata ${IRIS_FILE} src/data/preprocess.py ) ADD_CUSTOM_TARGET(preprocess DEPENDS ${PROCESSED_FILE})
In diesem Fall habe ich die Liste erstellt, sie jedoch nicht im benutzerdefinierten Befehl verwendet. Ich kenne keine Möglichkeit, auf Ausgabeargumente eines benutzerdefinierten Befehls darin zu verweisen.
Eine weitere interessante Sache ist die Verwendung von
depends
in diesem benutzerdefinierten Befehl. Wir legen die Abhängigkeit nicht nur von einem benutzerdefinierten Ziel fest, sondern auch von der Ausgabe und dem Python-Skript. Wenn wir
IRIS_FILE
keine Abhängigkeit
IRIS_FILE
,
iris.csv
manuelle Ändern von
iris.csv
nicht zur
IRIS_FILE
des
preprocess
. Nun, Sie sollten Dateien in Ihrem Build-Verzeichnis zunächst nicht manuell ändern. Lass es dich nur wissen. Weitere Details in
Sam Thursfields Beitrag . Die Abhängigkeit zum Python-Skript wird benötigt, um das Ziel neu zu erstellen, wenn sich das Python-Skript ändert.
Und schließlich das dritte Ziel:
SET(EXPLORATORY_IMG ${CMAKE_CURRENT_SOURCE_DIR}/reports/figures/exploratory.png) ADD_CUSTOM_COMMAND(OUTPUT ${EXPLORATORY_IMG} COMMAND python src/visualization/exploratory.py ${PROCESSED_FILE} ${EXPLORATORY_IMG} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} DEPENDS ${PROCESSED_FILE} src/visualization/exploratory.py ) ADD_CUSTOM_TARGET(exploratory DEPENDS ${EXPLORATORY_IMG})
Dieses Ziel ist im Grunde das gleiche wie das zweite.
Zum Abschluss. CMake sieht chaotisch und schwieriger aus als Make. In der Tat kritisieren viele Leute CMake wegen seiner Syntax. Nach meiner Erfahrung wird das Verständnis kommen und es ist absolut möglich, auch sehr komplizierte CMake-Dateien zu verstehen.
Sie werden immer noch viel selbst kleben, da Sie die richtigen Variablen weitergeben müssen. Ich sehe keine einfache Möglichkeit, die Ausgabe eines benutzerdefinierten Befehls in einem anderen zu referenzieren. Es scheint möglich zu sein, dies über benutzerdefinierte Ziele zu tun.
Pybuilder
Der PyBuilder-Teil ist sehr kurz. Ich habe Python 3.7 in meinem Projekt verwendet und PyBuilder aktuelle Version 0.11.17 unterstützt es nicht. Die vorgeschlagene Lösung ist die Verwendung der Entwicklungsversion. Diese Version ist jedoch an pip v9 gebunden. Pip ist zum Zeitpunkt des Schreibens Version 19.3. Schade. Nachdem ich ein bisschen herumgespielt hatte, funktionierte es bei mir überhaupt nicht. Die PyBuilder-Bewertung war von kurzer Dauer.
pynt
Pynt basiert auf Python, was bedeutet, dass wir Python-Funktionen direkt verwenden können. Es ist nicht erforderlich, sie mit einem
Klick zu verpacken und eine Befehlszeilenschnittstelle bereitzustellen. Pynt kann jedoch auch Shell-Befehle ausführen. Ich werde Python-Funktionen verwenden.
Build-Befehle werden in einer Datei
build.py
. Ziele / Aufgaben werden mit Funktionsdekoratoren erstellt. Aufgabenabhängigkeiten werden über denselben Dekorator bereitgestellt.
Da ich Python-Funktionen verwenden möchte, muss ich sie in das Build-Skript importieren. Pynt enthält das aktuelle Verzeichnis nicht als Python-Skript. Schreiben Sie also Folgendes:
from src.data.download import pydownload_file
wird nicht funktionieren. Wir müssen tun:
import os import sys sys.path.append(os.path.join(os.path.dirname(__file__), '.')) from src.data.download import pydownload_file
Meine anfängliche
build.py
Datei war wie
build.py
:
Und das
preprocess
hat nicht funktioniert. Es beschwerte sich ständig über Eingabeargumente der
pypreprocess
Funktion. Es scheint, dass Pynt optionale Funktionsargumente nicht sehr gut verarbeitet. Ich musste das Argument für die Erstellung der Excel-Datei entfernen. Denken Sie daran, wenn Ihr Projekt Funktionen mit optionalen Argumenten hat.
Wir können pynt aus dem Ordner des Projekts ausführen und alle verfügbaren Ziele auflisten:
pynt -l
Ergebnis Tasks in build file build.py: clean Clean all build artifacts exploratory Make an image with pairwise distribution preprocess Preprocess IRIS dataset rawdata Download IRIS dataset Powered by pynt 0.8.2 - A Lightweight Python Build Tool.
Machen wir die paarweise Verteilung:
pynt exploratory
Ergebnis [ build.py - Starting task "rawdata" ] Downloading from https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data to data/raw/iris.csv [ build.py - Completed task "rawdata" ] [ build.py - Starting task "preprocess" ] Preprocessing data [ build.py - Completed task "preprocess" ] [ build.py - Starting task "exploratory" ] Plotting pairwise distribution... [ build.py - Completed task "exploratory" ]
Wenn wir jetzt denselben Befehl erneut
pynt exploratory
(dh
pynt exploratory
), wird eine vollständige
pynt exploratory
. Pynt hat nicht verfolgt, dass sich nichts geändert hat.
Fertiger
Paver sieht fast genauso aus wie Pynt. Es unterscheidet sich geringfügig in einer Weise, wie man Abhängigkeiten zwischen Zielen definiert (ein anderer Dekorateur
@needs
). Paver führt jedes Mal eine vollständige Neuerstellung durch und spielt nicht gut mit Funktionen, die optionale Argumente haben. Bauanweisungen finden Sie in der Datei
pavement.py .
doit
Doit scheint ein Versuch zu sein, ein wirklich erstelltes Automatisierungstool in Python zu erstellen. Es kann Python-Code und Shell-Befehle ausführen. Es sieht ziemlich vielversprechend aus. Was (im Kontext unserer spezifischen Ziele) zu fehlen scheint, ist die Fähigkeit, mit Abhängigkeiten zwischen Zielen umzugehen. Angenommen, wir möchten eine kleine Pipeline erstellen, in der die Ausgabe von Ziel A als Eingabe von Ziel B verwendet wird. Nehmen wir an, wir verwenden Dateien als Ausgaben. Ziel A erstellt also eine Datei mit dem Namen
outA
.

Um eine solche Pipeline zu erstellen,
outA
wir die Datei
outA
zweimal in Ziel A angeben (als Ergebnis eines Ziels, aber auch den Namen als Teil der Zielausführung zurückgeben). Dann müssen wir es als Eingabe für Ziel B angeben. Es gibt also insgesamt 3 Stellen, an denen wir Informationen über die Datei
outA
. Und selbst nachdem wir dies getan haben, führt die Änderung der Datei
outA
nicht zu einer automatischen
outA
von Ziel B. Dies bedeutet, dass doit nur dann prüft, ob Ziel B auf dem neuesten Stand ist, ohne dass eines überprüft wird der Abhängigkeiten. Um dies zu überwinden, müssen wir
outA
viermal angeben - auch als
outA
von Ziel B. Ich sehe dies als Nachteil. Sowohl Make als auch CMake können mit solchen Situationen richtig umgehen.
Abhängigkeiten in doit sind dateibasiert und werden als Zeichenfolgen ausgedrückt. Dies bedeutet, dass die Abhängigkeiten
./myfile.txt
und
myfile.txt
als unterschiedlich angesehen werden. Wie ich oben geschrieben habe, finde ich die Art und Weise, wie Informationen von Ziel zu Ziel (bei Verwendung von Python-Zielen) weitergegeben werden, etwas seltsam. Das Ziel hat eine Liste von Artefakten, die es produzieren wird, aber ein anderes Ziel kann sie nicht verwenden. Stattdessen muss die Python-Funktion, die das Ziel bildet, ein Wörterbuch zurückgeben, auf das in einem anderen Ziel zugegriffen werden kann. Sehen wir uns ein Beispiel an:
def task_preprocess(): """Preprocess IRIS dataset""" pickle_file = 'data/processed/processed.pickle' excel_file = 'data/processed/processed.xlsx' return { 'file_dep': ['src/data/preprocess.py'], 'targets': [pickle_file, excel_file], 'actions': [doit_pypreprocess], 'getargs': {'input_file': ('rawdata', 'filename')}, 'clean': True, }
Hier hängt der
rawdata
von den
rawdata
. Die Abhängigkeit wird über
getargs
Eigenschaft
getargs
bereitgestellt. Es heißt, dass das Argument
input_file
der Funktion
doit_pypreprocess
der Ausgabedateiname der Ziel-
rawdata
. Schauen Sie sich das vollständige Beispiel in der Datei
dodo.py an.Es kann sich lohnen,
die Erfolgsgeschichten von doit zu lesen. Es hat definitiv nette Funktionen wie die Möglichkeit, eine benutzerdefinierte, aktuelle Zielprüfung bereitzustellen.
Luigi
Luigi unterscheidet sich von anderen Tools, da es sich um ein System zum Bau komplexer Pipelines handelt. Es erschien auf meinem Radar, nachdem mir ein Kollege erzählt hatte, dass er Make ausprobiert hatte, es nie unter Windows / Linux verwenden konnte und nach Luigi zog.
Luigi strebt serienreife Systeme an. Es wird mit einem Server geliefert, mit dem Sie Ihre Aufgaben visualisieren oder einen Verlauf der Aufgabenausführungen abrufen können. Der Server wird als
zentraler Schedler bezeichnet . Für Debugging-Zwecke steht ein lokaler Scheduler zur Verfügung.
Luigi unterscheidet sich auch von anderen Systemen darin, wie Aufgaben erstellt werden. Lugi reagiert nicht auf eine vordefinierte Datei (wie
dodo.py
,
pavement.py
oder
dodo.py
). Vielmehr muss man einen Python-Modulnamen übergeben. Wenn wir also versuchen, es auf ähnliche Weise wie andere Tools zu verwenden (eine Datei mit Aufgaben im Stammverzeichnis des Projekts platzieren), funktioniert es nicht. Wir müssen entweder unser Projekt installieren oder die Umgebungsvariable
PYTHONPATH
ändern, indem
PYTHONPATH
den Pfad zum Projekt hinzufügen.
Was an luigi großartig ist, ist die Art und Weise, Abhängigkeiten zwischen Aufgaben anzugeben. Jede Aufgabe ist eine Klasse. Die Methodenausgabe teilt Luigi mit, wo die Ergebnisse der Aufgabe landen werden. Ergebnisse können ein einzelnes Element oder eine Liste sein. Methode
requires
spezifiziert Aufgabenabhängigkeiten (andere Aufgaben; obwohl es möglich ist, eine Abhängigkeit von sich selbst zu machen). Und das war's. Was in Aufgabe A als
output
angegeben ist, wird als Eingabe an Aufgabe B übergeben, wenn Aufgabe B auf Aufgabe A beruht.

Luigi kümmert sich nicht um Dateimodifikationen. Es kümmert sich um die Existenz von Dateien. Es ist daher nicht möglich, Neuerstellungen auszulösen, wenn sich der Quellcode ändert. Luigi verfügt nicht über eine integrierte
Clean- Funktionalität.
Luigi-Aufgaben für dieses Projekt sind in der Datei
luigitasks.py verfügbar. Ich starte sie vom Terminal aus:
luigi --local-scheduler --module luigitasks Exploratory
Vergleich
Die folgende Tabelle fasst zusammen, wie verschiedene Systeme in Bezug auf unsere spezifischen Ziele funktionieren.