Sie sollten diesen Artikel lesen
Wenn Sie planen, in Knowledge Science zu gehen, sei es Absolvent oder ein Fachmann, der nach einer Karriereveränderung sucht, oder ein Supervisor, der für die Festlegung von Greatest Practices zuständig ist, ist dieser Artikel für Sie.
Knowledge Science zieht eine Vielzahl unterschiedlicher Hintergründe an. Aus meiner Berufserfahrung habe ich mit Kollegen zusammengearbeitet, die einst waren:
- Kernphysiker
- Publish-Docs-Erforschung von Gravitationswellen
- Doktoranden in der Computerbiologie
- Linguisten
Nur um nur einige zu nennen.
Es ist wunderbar, in der Lage zu sein, einen so unterschiedlichen Hintergrund zu treffen, und ich habe gesehen, wie eine Vielzahl von Köpfen zum Wachstum einer kreativen und effektiven Datenwissenschaftsfunktion führte.
Ich habe jedoch auch einen großen Nachteil dieser Sorte gesehen:
Jeder hatte unterschiedliche Konzepte für die Software program -Engineering, was zu einem Patchwork von Codierungsfähigkeiten führte.
Infolgedessen habe ich die Arbeit von einigen Datenwissenschaftlern gesehen, die brillant sind, aber:
- Unlesbar – Sie haben keine Ahnung, was sie versuchen zu tun.
- Flaky – es bricht den Second, in dem jemand anderes versucht, es zu laufen.
- Unbehaltbar – Code ist schnell veraltet oder bricht leicht.
- Nicht erweiterbar-Code ist Einzelnutzung und sein Verhalten kann nicht erweitert werden.
Dies dämpft letztendlich die Auswirkungen, die ihre Arbeit haben kann, und schafft alle möglichen Probleme auf der ganzen Linie.

In einer Reihe von Artikeln airplane ich additionally, einige Kernkonzepte für die Software program -Engineering zu skizzieren, die ich auf die Notwendigkeiten für Datenwissenschaftler zugeschnitten habe.
Sie sind einfache Konzepte, aber der Unterschied zwischen dem Kennen gegenüber ihnen gegenüber dem Nicht -Wissen zieht die Grenze zwischen Beginner und Profi eindeutig.
Das heutige Konzept: Erbschaft
Die Vererbung ist für das Schreiben sauberer, wiederverwendbarer Code von grundlegender Bedeutung, der Ihre Effizienz und Ihre Arbeitsproduktivität verbessert. Es kann auch verwendet werden, um die Artwork und Weise zu standardisieren, wie ein Workforce Code schreibt, was die Lesbarkeit und Wartbarkeit verbessert.
Wenn ich zurückblicke, wie schwierig es battle, diese Konzepte zu lernen, als ich zum ersten Mal mit dem Code gelernt habe, werde ich nicht mit einer abstrakten Definition auf hoher Ebene beginnen, die Ihnen zu diesem Zeitpunkt keinen Wert bietet. Es gibt viel im Web, das Sie googeln können, wenn Sie dies möchten.
Schauen wir uns stattdessen ein Beispiel für ein Datenwissenschaftsprojekt an.
Wir werden die Artwork von praktischen Problemen skizzieren, auf die ein Datenwissenschaftler begegnen könnte, sehen, was die Vererbung ist und wie es einem Datenwissenschaftler helfen kann, einen besseren Code zu schreiben.
Und von besser Wir meinen:
- Code, der leichter zu lesen ist.
- Code, der leichter zu warten ist.
- Code, der leichter wiederverwendet wird.
Beispiel: Daten aus mehreren verschiedenen Quellen einnehmen

Der mühsamste und zeitaufwändigste Teil des Auftrags eines Datenwissenschaftlers besteht darin, herauszufinden, wo Daten erhalten, wie man sie liest, wie man sie reinigt und wie man sie speichert.
Nehmen wir an, Sie haben Etiketten in CSV -Dateien, die aus fünf verschiedenen externen Quellen eingereicht wurden, jeweils ihr eigenes einzigartiges Schema.
Ihre Aufgabe ist es, jeden von ihnen zu reinigen und als Parquetdatei auszugeben. Damit diese Datei mit nachgeschalteten Prozessen kompatibel ist, müssen sie einem Schema entsprechen:
label_id: Ganzzahllabel_value: Ganzzahllabel_timestamp: String -Zeitstempel im ISO -Format.
Der schnelle und schmutzige Ansatz
In diesem Fall wäre der schnelle und schmutzige Ansatz, ein separates Skript für jede Datei zu schreiben.
# clean_source1.py
import polars as pl
if __name__ == '__main__':
df = pl.scan_csv('source1.csv')
overall_label_value = df.group_by('some-metadata1').agg(
overall_label_value=pl.col('some-metadata2').or_().over('some-metadata2')
)
df = df.drop(('some-metadata1', 'some-metadata2', 'some-metadata3'), axis=1)
df = df.be part of(overall_label_value, on='some-metadata4')
df = df.choose(
pl.col('primary_key').alias('label_id'),
pl.col('overall_label_value').alias('label_value').change((True, False), (1, 0)),
pl.col('some-metadata6').alias('label_timestamp'),
)
df.to_parquet('output/source1.parquet')
Und jedes Skript wäre einzigartig.
Additionally, was ist daran los? Es erledigt den Job richtig?
Kehren wir zu unserem Kriterium zurück, um einen guten Code zu erhalten, und bewerten Sie, warum dieser schlecht ist:
1. Es ist schwer zu lesen
Es gibt keine Organisation oder Struktur für den Code.
Die gesamte Logik für das Laden, Reinigen und Speichern liegt an derselben Stelle. Daher ist es schwierig zu erkennen, wo sich die Linie zwischen jedem Schritt befindet.
Denken Sie daran, dies ist ein erfundenes, einfaches Beispiel. In der realen Welt wäre der Code, den Sie schreiben würden, viel länger und komplex.
Wenn Sie schwer Code und fünf verschiedene Versionen davon haben, führt dies zu längerfristigen Problemen:
2. Es ist schwer zu pflegen
Der Mangel an Struktur macht es schwierig, neue Funktionen hinzuzufügen oder Fehler zu beheben. Wenn die Logik geändert werden musste, muss das gesamte Skript wahrscheinlich überarbeitet werden.
Wenn es eine gemeinsame Operation gab, die auf alle Ausgänge angewendet werden musste, muss jemand alle fünf Skripte separat ändern.
Jedes Mal müssen sie den Zweck von Zeilen und Codezeilen entschlüsseln. Weil es keine klare Unterscheidung dazwischen gibt
- wo Daten geladen werden,
- wo Daten verwendet werden,
- Welche Variablen sind von nachgeschalteten Operationen abhängig,
Es wird schwer zu wissen, ob die von Ihnen vorgenommenen Änderungen unbekannte Auswirkungen auf den nachgeschalteten Code haben oder gegen eine vorgelagerte Annahme verstoßen.
Letztendlich wird es für Fehler sehr einfach, sich hineinzuschleichen.
3. Es ist schwer wiederzuverwenden
Dieser Code ist die Definition einer einmaligen.
Es ist schwer zu lesen, Sie wissen nicht, was passiert, wenn Sie nicht viel Zeit investieren, um sicherzustellen, dass Sie jede Codezeile verstehen.
Wenn jemand die Logik davon wiederverwenden wollte, ist die einzige Possibility, die er hätte, das Kopieren der ganz Skript und modifizieren Sie es oder schreiben Sie ihre eigenen von Grund auf neu.
Es gibt bessere und effizientere Möglichkeiten zum Schreiben von Code.
Der bessere, professionelle Ansatz
Schauen wir uns nun an, wie wir unsere State of affairs verbessern können, indem wir die Vererbung verwenden.

1. Identifizieren Sie die Gemeinsamkeiten
In unserem Beispiel ist jede Datenquelle eindeutig. Wir wissen, dass für jede Datei erforderlich ist:
- Ein oder mehrere Reinigungsschritte
- Ein Speicherschritt, von dem wir bereits wissen, dass alle Dateien in einer einzigen Parkettdatei gespeichert werden.
Wir wissen auch, dass jede Datei demselben Schema entsprechen muss. Besten validieren wir die Ausgabedaten.
Diese Gemeinsamkeiten werden uns additionally informieren, welche Funktionen wir einmal schreiben und sie dann wiederverwenden könnten.
2. Erstellen Sie eine Basisklasse
Jetzt kommt der Erbschaftsteil.
Wir schreiben a base classoder dad or mum classwas die Logik für den Umgang mit den oben identifizierten Gemeinsamkeiten implementiert. Diese Klasse wird die Vorlage von welchen anderen Klassen werden ‚erben‘.
Klassen, die aus dieser Klasse erben (als Kinderklassen genannt), haben die gleiche Funktionalität wie die übergeordnete Klasse, können aber auch neue Funktionen hinzufügen oder diejenigen ändern, die bereits verfügbar sind.
import polars as pl
class BaseCSVLabelProcessor:
REQUIRED_OUTPUT_SCHEMA = {
"label_id": pl.Int64,
"label_value": pl.Int64,
"label_timestamp": pl.Datetime
}
def __init__(self, input_file_path, output_file_path):
self.input_file_path = input_file_path
self.output_file_path = output_file_path
def load(self):
"""Load the info from the file."""
return pl.scan_csv(self.input_file_path)
def clear(self, knowledge:pl.LazyFrame):
"""Clear the enter knowledge"""
...
def save(self, knowledge:pl.LazyFrame):
"""Save the info to parquet file."""
knowledge.sink_parquet(self.output_file_path)
def validate_schema(self, knowledge:pl.LazyFrame):
"""
Examine that the info conforms to the anticipated schema.
"""
for colname, expected_dtype in self.REQUIRED_OUTPUT_SCHEMA.gadgets():
actual_dtype = knowledge.schema.get(colname)
if actual_dtype is None:
increase ValueError(f"Column {colname} not present in knowledge")
if actual_dtype != expected_dtype:
increase ValueError(
f"Column {colname} has incorrect kind. Anticipated {expected_dtype}, acquired {actual_dtype}"
)
def run(self):
"""Run knowledge processing on the required file."""
knowledge = self.load()
knowledge = self.clear(knowledge)
self.validate_schema(knowledge)
self.save(knowledge)
3. Definieren Sie Kinderklassen
Jetzt definieren wir die Kinderklassen:
class Source1LabelProcessor(BaseCSVLabelProcessor):
def clear(self, knowledge:pl.LazyFrame):
# bespoke logic for supply 1
...
class Source2LabelProcessor(BaseCSVLabelProcessor):
def clear(self, knowledge:pl.LazyFrame):
# bespoke logic for supply 2
...
class Source3LabelProcessor(BaseCSVLabelProcessor):
def clear(self, knowledge:pl.LazyFrame):
# bespoke logic for supply 3
...
Da die gesamte gemeinsame Logik bereits in der übergeordneten Klasse implementiert ist, muss die gesamte untergeordnete Klasse die maßgeschneiderte Logik ist, die für jede Datei eindeutig ist.
Der Code, den wir für das schlechte Beispiel geschrieben haben, kann jetzt in Folgendes geändert werden:
from <someplace> import BaseCSVLabelProcessor
class Source1LabelProcessor(BaseCSVLabelProcessor):
def get_overall_label_value(self, knowledge:pl.LazyFrame):
"""Get general label worth."""
return knowledge.with_column(pl.col('some-metadata2').or_().over('some-metadata1'))
def conform_to_output_schema(self, knowledge:pl.LazyFrame):
"""Drop pointless columns and confrom required columns to output schema."""
knowledge = knowledge.drop(('some-metadata1', 'some-metadata2', 'some-metadata3'), axis=1)
knowledge = knowledge.choose(
pl.col('primary_key').alias('label_id'),
pl.col('some-metadata5').alias('label_value').change((True, False), (1, 0)),
pl.col('some-metadata6').alias('label_timestamp'),
)
return knowledge
def clear(self, knowledge:pl.LazyFrame) -> pl.DataFrame:
"""Clear label knowledge from Supply 1.
The next steps are vital to wash the info:
1. <some cause as to why we have to group by 'some-metadata1'>
2. <some cause for becoming a member of 'overall_label_value' to the dataframe>
3. Renaming columns and knowledge varieties to confrom to the anticipated output schema.
"""
overall_label_value = self.get_overall_label_value(knowledge)
df = df.be part of(overall_label_value, on='some-metadata4')
df = self.conform_to_output_schema(df)
return df
Und um unseren Code auszuführen, können wir ihn an einem zentralisierten Ort tun:
# label_preparation_pipeline.py
from <someplace> import Source1LabelProcessor, Source2LabelProcessor, Source3LabelProcessor
INPUT_FILEPATHS = {
'source1': '/path/to/file1.csv',
'source2': '/path/to/file2.csv',
'source3': '/path/to/file3.csv',
}
OUTPUT_FILEPATH = '/path/to/output.parquet'
def fundamental():
"""Label processing pipeline.
The label processing pipeline ingests knowledge sources 1, 2, 3 that are from
exterior distributors <blah>.
The output is written to a parquet file, prepared for ingestion by <downstream-process>.
The code assumes the next:
- <assumptions>
The person must specify the next inputs:
- <particulars on the enter config>
"""
processors = (
Source1LabelProcessor(FILEPATHS('source1'), OUTPUT_FILEPATH),
Source2LabelProcessor(FILEPATHS('source2'), OUTPUT_FILEPATH),
Source3LabelProcessor(FILEPATHS('source3'), OUTPUT_FILEPATH)
)
for processor in processors:
processor.run()
Warum ist das besser?
1. gute Verkapselung
Sie sollten nicht unter die Motorhaube schauen müssen, um zu wissen, wie man ein Auto fährt.
Jeder Kollege, der diesen Code erneut ausführen muss fundamental() Funktion. Sie hätten in den jeweiligen Funktionen genügend Dokumente bereitgestellt, um zu erklären, was sie tun und wie sie verwendet werden.
Aber sie müssen nicht wissen, wie jede einzelne Codezeile funktioniert.
Sie sollten in der Lage sein, Ihrer Arbeit zu vertrauen und sie auszuführen. Nur wenn sie einen Fehler beheben oder seine Funktionalität erweitern müssen, müssen sie tiefer gehen.
Das heißt Verkapselung – strategisch verbergen die Implementierungsdetails vor dem Benutzer. Es ist ein weiteres Programmierkonzept, das für das Schreiben guter Code unerlässlich ist.

Kurz gesagt, es sollte ausreichen, damit sich der Leser auf die Docstrings verlassen kann, um zu verstehen, was der Code tut und wie er sie benutzt.
Wie oft gehst du in die scikit-learn Quellcode, um zu lernen, wie man ihre Modelle verwendet? Du tust es nie. scikit-learn ist ein ideales Beispiel für Gutes Codierung Design durch Kapselung.
Ich habe bereits einen Artikel geschrieben, der der Kapselung gewidmet ist HierWenn Sie additionally mehr wissen möchten, schauen Sie es sich an.
2. Bessere Erweiterbarkeit
Was ist, wenn sich die Etiketten jetzt ändern müsste? Zum Beispiel müssen nachgelagerte Prozesse, die die Etiketten aufnehmen, jetzt in einer SQL -Tabelle gespeichert werden.
Nun, es wird sehr einfach, dies zu tun – wir müssen einfach die ändern save Methode in der BaseCSVLabelProcessor Klasse, und dann werden alle Kinderklassen diese Änderung automatisch erben.
Was ist, wenn Sie eine Inkompatibilität zwischen den Etikettenausgängen und einem Prozess stromabwärts finden? Vielleicht wird eine neue Spalte benötigt?
Nun, Sie müssten den jeweiligen ändern clear Methoden, um dies zu berücksichtigen. Sie können aber auch die Schecks in der erweitern validate Methode in der BaseCSVLabelProcessor Klasse, um diese neue Anforderung zu berücksichtigen.
Sie können dies sogar noch einen Schritt weiter gehen und viele weitere Schecks hinzufügen, um immer sicherzustellen, dass die Ausgaben wie erwartet sind. Möglicherweise möchten Sie sogar ein separat validate Verfahren.
Sie können sehen, wie das Verhalten unseres Etikettenverarbeitungscode sehr einfach wird.
Wenn der Code in separaten maßgeschneiderten Skripten lebte, würden Sie diese Schecks immer wieder kopieren und einfügen. Noch schlimmer ist, dass möglicherweise jede Datei eine maßgeschneiderte Implementierung erfordert. Dies bedeutet, dass das gleiche Downside fünfmal gelöst werden muss, wenn es nur einmal richtig gelöst werden kann.
Es ist Nacharbeit, seine Ineffizienz, ihre Verschwendung von Ressourcen und Zeit.
Letzte Bemerkungen
In diesem Artikel haben wir additionally behandelt, wie die Verwendung von Vererbung die Qualität unserer Codebasis erheblich verbessert.
Durch die angemessene Anwendung der Vererbung können wir häufige Probleme über verschiedene Aufgaben hinweg lösen, und wir haben aus erster Hand gesehen, wie dies zu:
- Code, der einfacher zu lesen ist – Lesbarkeit
- Code, der leichter zu debuggen und aufrechtzuerhalten ist – Wartbarkeit
- Code, der einfacher zu Funktionen hinzugefügt und erweitert wird – Erweiterbarkeit
Einige Leser sind jedoch immer noch skeptisch gegenüber der Notwendigkeit, einen solchen Code zu schreiben.
Vielleicht haben sie für ihre gesamte Karriere einmalige Skripte geschrieben, und bis jetzt battle alles in Ordnung. Warum sich die Mühe machen, Code komplizierter zu schreiben?

Nun, das ist eine sehr gute Frage – Und es gibt einen sehr klaren Grund, warum es notwendig ist.
Bis vor kurzem, Datenwissenschaft battle eine neue Nischenindustrie, in der Proof-of-Ideas und Forschung im Mittelpunkt der Arbeit standen. Die Codierungsstandards spielte damals keine Rolle, solange wir etwas durch die Türen herausbekommen und es funktionierte.
Die Datenwissenschaft nähert sich jedoch schnell zur Reife, wo sie nicht mehr ausreicht, um nur Modelle zu erstellen.
Wir müssen jetzt nicht nur Modelle, sondern auch auch aufrechterhalten, reparieren, debuggen und erneut übertragen alle Die Prozesse, die zum Erstellen des Modells erforderlich sind – so lange sie verwendet werden.
Dies ist die Realität, der sich Knowledge Science stellen muss – Modelle bauen ist die einfach Teil beim Aufrechterhalten dessen, was wir gebaut haben, ist die hart Teil.
In der Zwischenzeit macht Software program Engineering dies seit Jahrzehnten und hat durch Versuch und Irrtum alle Greatest Practices aufgebaut, die wir heute besprochen haben, damit der Code, den sie erstellen, leicht zu warten ist.
Daher müssen Datenwissenschaftler diese Greatest Practices in Zukunft kennen.
Diejenigen, die dies wissen, werden im Vergleich zu denen, die dies nicht tun, unweigerlich von Vorteil sein.
