Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

From Idea to Code

Beim Programmieren gibt es oft einen sehr starken Impuls “einfach loszulegen”, also direkt mit dem Schreiben von Code zu beginnen. Bei kleineren Aufgaben, z.B. Übungsaufgaben, ist das in der Regel auch OK. Sobald es aber um ein größeres Programm geht ist es nicht nur sinnvoll, sondern oft fast unverzichtbar zuerst ein Konzept oder ein Code Design zu erstellen. Gerade bei komplexen Aufgaben, ist es aber gar nicht so leicht sich eine erste Vorstellung davon zu machen wie der Code aufgebaut sein sollte.

Anstatt uns lange allgemeine Best Practices und Prinzipien anzuschauen, ist das Ziel hier, dass wir anhand eines konkreten Beispiels einmal durchspielen wie wir von einer Idee zum Code kommen können. Wir hangelnd uns dazu aber entlang etablierter Entwurfsphasen entlang. Diese werden wir aber zuerst zumindest kurz einführen müssen um den generellen Ablauf zu verstehen.

Es gibt viele verschiedene Beschreibungen typischer Softwareentwicklungs-Prozesse. Sehr bekannt ist der Unified Software Development Process (oder Unified Process), von dem es viele Variationen gibt wie z.B. den Rational Unified Process oder den Agile Unified Process. Eine Variante de Unified Process wird auf geeksforgeeks.org dargestellt.

Im folgenden halten wir uns nicht an einen spezifischen dieser Prozesse, sondern geben nur eine (an vielen Stellen vereinfachte) Struktur vor mit deren Hilfe sich ein Code Design erstellen lässt.

Entwicklungsphasen von der Idee zum Code

Absolut, für Data-Science-Studenten mit begrenzter Softwareentwicklungserfahrung ist es wichtig, einen klaren und praktischen Prozess zu vermitteln, der den Übergang von einer Idee zu einem konkreten Softwaredesign erleichtert. Das Ziel ist, ihnen zu helfen, ihre Problemlösungsansätze systematisch in strukturierten Code umzusetzen. Hier ist ein vereinfachter, aber professioneller Workflow, der sich an aktuellen Best Practices in der Softwareentwicklung orientiert:

Problemdefinition und Anforderungsanalyse

Ziel: Klare Verständigung darüber, was gelöst werden muss.

Aktivitäten:

Ergebnis:

High-Level Lösungsplanung

Ziel: Einen allgemeinen Ansatz zur Lösung des Problems skizzieren.

Aktivitäten:

Ergebnis:

Zerlegung des Problems in Komponenten

Ziel: Die Lösung in handhabbare Teile aufteilen.

Aktivitäten:

Ergebnis:

Detailliertes Design mit Flussdiagrammen und Diagrammen

Ziel: Die Logik und Struktur der Software visualisieren.

Aktivitäten:

Ergebnis:

Definition von Schnittstellen und Interaktionen

Ziel: Spezifizieren, wie die verschiedenen Komponenten kommunizieren.

Aktivitäten:

Ergebnis:

Implementierungsplanung

Ziel: Sich mit einem klaren Fahrplan auf die Codierungsphase vorbereiten.

Aktivitäten:

Ergebnis:

Überprüfung und Feedback

Ziel: Das Design vor der Implementierung validieren.

Aktivitäten:

Ergebnis:


Erläuterung und Ausrichtung an Best Practices

Dieser vereinfachte Prozess spiegelt wesentliche Phasen der professionellen Softwareentwicklung wider, ist aber für Software-Development-Anfänger angepasst:


Zusätzliche Tipps


Praktisches Python Beispiel

Wenden wir diesen Prozess auf ein konkretes Projekt an: Erstellung eines ASCII-Scatterplots

Problemdefinition und Anforderungsanalyse

High-Level Lösungsplanung

Zerlegung des Problems in Komponenten

Detailliertes Design mit Flussdiagrammen und Diagrammen

Definition von Schnittstellen und Interaktionen

Implementierungsplanung

In vielen Fällen, besonders bei Projekten die im Team bearbeitet werden kann (und soll) die Implementierungsplanung natürlich kein streng linearer Ablaufplan sein. Die folgende Reihenfolge ist an vielen Stellen beliebig, wichtig daran ist vor allem kleinschrittige Aufgaben zu definieren und im Team zu verteilen und zu bearbeiten.

Überprüfung und Feedback

Ausblick

Wir kommen in folgenden Teilen zu weiteren Möglichkeiten die die eigentliche Implementierung begleiten können wie z.B. Testing.

  1. Anforderungsanalyse

    • Ziel: Verständnis dafür gewinnen, was das Programm erreichen soll.

    • Schritte: Anforderungen und Zielvorgaben klären, häufige Anwendungsfälle (Use cases) durchdenken und dokumentieren.

  2. Konzeptionelles Modell

    • Ziel: Ein Modell entwickeln, das die Kernkomponenten und deren Beziehungen skizziert.

    • Schritte: Hauptelemente und deren Verantwortlichkeiten identifizieren; oft durch Diagramme unterstützt.

  3. Zerlegung und Abstraktion

    • Ziel: Das Problem in kleinere, überschaubare Teile aufteilen und logisch strukturieren.

    • Schritte: Jedes Teilmodul für eine bestimmte Aufgabe definieren, z. B. Daten laden, verarbeiten und darstellen.

  4. Pseudocode und Flussdiagramme

    • Ziel: Den Ablauf des Programms skizzieren, ohne sich um den eigentlichen Code zu kümmern.

    • Schritte: Pseudocode schreiben und/oder Flussdiagramme erstellen, um die Logik des Programms klar darzustellen.

  5. Prototyping und Feedback

    • Ziel: Eine erste, einfache Version des Programms entwickeln und testen.

    • Schritte: Kernfunktionen implementieren, testen und auf Basis des Feedbacks weiter verbessern.

  6. Iterative Verfeinerung

    • Ziel: Das Programm kontinuierlich verbessern und verfeinern.

    • Schritte: Rückmeldungen und Tests nutzen, um die Struktur, Logik und Benutzerfreundlichkeit des Programms zu optimieren.

Diese sechs Phasen sind nur eine Möglichkeit den Prozess einzuteilen. Außerdem darf man sich das Ganze nicht als einen streng linearen Prozess vorstellen. Viele der Phasen können auch parallel in Angriff genommen werden, oder es häufig auch Gründe wieder zu einer “früheren” Phase zurückzukehren, z.B. weil an einer Stelle Unstimmigkeiten oder Unvereinbarkeiten entdeckt werden.

Building an ASCII Scatter Plot Tool in Python

Problemstellung

Wir wollen ein Python-Tool erstellen, das:

  1. x- und y-Koordinaten aus einer CSV-Datei lädt.

  2. Ein ASCII-Scatter-Plot in der Konsole anzeigt, wobei ein Zeichen unserer Wahl verwendet wird (z. B. “*”, “x”, “o”).

  3. Einfache x- und y-Achsen hinzufügt, um das Diagramm lesbarer zu machen.


Phasen der Entwicklung


Endprodukt

Hier eine mögliche Lösung. Der folgende Code kann jede CSV-Datei mit x, y-Koordinaten laden und ein sauberes ASCII-Scatterplot mit klaren Achsen in der Konsole darstellen.

import pandas as pd


class AsciiScatterPlot:
    def __init__(self, char="*", width=50, height=50, aspect_ratio=2.0):
        """
        Initializes the ASCII scatter plot with specified character, grid dimensions, and aspect ratio.
        - char: The character used to represent each point.
        - width: Width of the ASCII grid.
        - height: Height of the ASCII grid.
        - aspect_ratio: Adjusts scaling for the difference in character dimensions.
        """
        self.char = char
        self.width = int(width * aspect_ratio)
        self.height = height
        self.grid = [[" " for _ in range(self.width)] for _ in range(self.height)]
        self.add_axes()

    def load_csv(self, filepath):
        """
        Loads CSV data with columns x and y.
        """
        data = pd.read_csv(filepath)
        if not all(col in data.columns for col in ["x", "y"]):
            raise ValueError("CSV file must contain 'x' and 'y' columns.")
        return data[["x", "y"]]

    def scale_coordinates(self, data):
        """
        Scales coordinates to fit the ASCII grid.
        """
        x_min, x_max = data["x"].min(), data["x"].max()
        y_min, y_max = data["y"].min(), data["y"].max()

        # Scale x and y to the grid dimensions
        data["x_scaled"] = ((data["x"] - x_min) / (x_max - x_min)) * (self.width - 1)
        data["y_scaled"] = ((data["y"] - y_min) / (y_max - y_min)) * (self.height - 1)

        # Rounding coordinates to nearest integer grid point and clamping to grid boundaries
        data["x_scaled"] = data["x_scaled"].round().clip(0, self.width - 1).astype(int)
        data["y_scaled"] = data["y_scaled"].round().clip(0, self.height - 1).astype(int)
        
        return data[["x_scaled", "y_scaled"]]

    def add_axes(self):
        """
        Adds x and y axes to the grid.
        """
        # X-axis
        for x in range(self.width):
            self.grid[self.height - 1][x] = "-"

        # Y-axis
        for y in range(self.height):
            self.grid[y][0] = "|"

        # Origin point
        self.grid[self.height - 1][0] = "+"

    def plot_points(self, scaled_coords):
        """
        Maps scaled coordinates onto the ASCII grid.
        """
        for _, (x, y) in scaled_coords.iterrows():
            # Invert y to plot from top to bottom
            self.grid[self.height - 1 - y][x] = self.char

    def display(self):
        """
        Prints the ASCII grid to the console.
        """
        for row in self.grid:
            print("".join(row))

    def plot_from_csv(self, filepath):
        """
        High-level function to load, scale, plot, and display the scatter plot from a CSV file.
        """
        data = self.load_csv(filepath)
        scaled_coords = self.scale_coordinates(data)
        self.plot_points(scaled_coords)
        self.display()

# Usage example
plotter = AsciiScatterPlot(char="*", width=30, height=30, aspect_ratio=2.0)

# Assuming 'data.csv' contains x, y coordinates
plotter.plot_from_csv("data.csv")