Erkläre die nächsten Kandidaten

PythonBeginner
Jetzt üben

Einführung

Die Bibliothek kennt die heute nutzbaren Spiele. Jetzt braucht sie eine transparente Liste ähnlicher Alternativen, mit verständlichen Erklärungen für die Auswahl. Du berechnest Abstände aus zwei veröffentlichten Merkmalen, zeigst vorzeichenbehaftete Unterschiede in Originaleinheiten und behältst alle Gleichstände an der Auswahlgrenze.

Die Python-Eignungsprüfung und MinMax-Skalierung aus dem vorherigen Lab werden vorausgesetzt. Diese Umgebung ist unabhängig: Eingaben und fertige Eignungsfunktion sind vorhanden, ohne Dateien oder Prozesse aus einer früheren Umgebung. Du implementierst die Rangfolge und erzeugst echte JSON-Dateien, die der Leser unverändert anzeigt. 50 Minuten sind eine Autorenschätzung. Inhaltliche Ähnlichkeit sagt keine Vorlieben voraus und belegt keine Leistung bei großen Katalogen.

Miss jede geeignete Alternative

In diesem Schritt erstellst du die vollständige Distanztabelle und zeigst, was jeder Kandidat gegenüber der Referenz verändert.

Öffne den Tab Terminal und wechsle in den unabhängigen Arbeitsbereich. Öffnen oder erneutes Verbinden dieses Zugangs installiert den Arbeitsbereich nicht neu und löscht keinen Fortschritt. cd setzt das Arbeitsverzeichnis dieses Terminals; wiederhole es in neuen Terminals.

cd /home/labex/project/board-games

Der Katalog verwendet eindeutige game_id-Schlüssel, reine Anzeigetitel, die Dauer minutes, eine Bewertung complexity von 1 bis 5 und inklusive Grenzen min_players/max_players. Die getrennte Verfügbarkeitsdatei enthält ein true/false pro ID in anderer Reihenfolge. night.json nennt Suchbeispiel, players und gewünschtes k. Die Anfrage bleibt auch dann gültig, wenn das Referenzspiel nicht verfügbar oder für die Gruppe ungeeignet ist; nur Kandidaten müssen heute passen. Gleiche Titel oder identische Merkmale bei anderen IDs bleiben erlaubt, einschließlich Distanz null. Nur die genaue reference_id wird ausgeschlossen.

cat zeigt Text ohne Änderung. Lies Anfrage und mitgelieferte Funktion, die die Arbeit des vorherigen Labs umsetzt.

cat night.json
cat suitability.py

Führe diese vorhandene Schnittstelle vor dem Ergänzen einer Distanzberechnung aus. --mode suitable ruft choose_games auf; das erforderliche --output benennt die zu schreibende JSON-Datei. Ohne weitere Eingabeoptionen liest der Starter catalog.csv, availability.csv und night.json aus dem aktuellen Verzeichnis. Der Leser zeigt nur die gespeicherte Datei an.

python3 planner.py --mode suitable --output suitable.json
python3 show_results.py suitable.json

Du solltest die sechs alphabetischen geeigneten IDs left, near, raw, right, same und zero mit der folgenden Skala sehen. Dies ist ein funktionierendes Eignungsergebnis, keine fertige Empfehlung: Das Rangfolgemodul hat weiterhin leere Implementierungsbereiche.

choose_games ordnet Verfügbarkeit über IDs zu und liefert alle geeigneten IDs sowie eine vor dem Filtern aus dem gesamten Katalog berechnete Skala. Die Reihenfolge ist minutes, dann complexity. scale.min ist [10, 1], scale.range [100, 4]. Nutze dieselben Parameter für Anfrage und Kandidaten: Ein ausgeliehenes Spiel darf den Maßstab nicht verschieben. Es gibt keine Zielklassen und keine überwachte Trainings-/Testaufteilung.

planner.py verarbeitet Optionen und Formate mit coursekit.py, wählt aber keine Spiele aus und sortiert sie nicht. Ändern darfst du suitability.py, ranking.py und Ergebnisse. Erhalte alle CSV/JSON-Eingaben, planner.py, coursekit.py, show_results.py und beide starter-*.py.

MinMax bildet den Wert x auf (x - minimum) / range ab. Ein konstantes Merkmal speichert Spannweite null, verwendet aber Divisor eins; da die Anfrage Katalogmitglied ist, sind alle entsprechenden Unterschiede null. Für skalierte Anfrage q und Kandidat c ist die euklidische Distanz sqrt((c_minutes-q_minutes)^2 + (c_complexity-q_complexity)^2). Nach der Skalierung sind beide Gewichte gleich. Kleinere Distanz bedeutet Ähnlichkeit nach dieser Regel, nicht mehr Spielspaß.

Die Originalerklärung ist getrennt: minutes_delta und complexity_delta sind vor der Skalierung Kandidat minus Referenz. Eine negative Minutendifferenz bedeutet ein kürzeres Spiel. Die Spalten müssen vom tatsächlichen Kandidaten dieser ID stammen, nicht von einer anderen Zeile oder einer erfundenen Ableitung aus der Distanz.

Untersuche das Rangfolgegerüst vor dem Bearbeiten. from suitability import choose_games importiert die lokale Hilfsfunktion. Die vorhandenen Zeilen prepared, by_id und reference verbinden deren Ausgabe und die Anfrage mit über IDs erschlossenen Katalogdatensätzen. Die leere Liste rows, die Kandidatenschleife, Sortierung und abschließende Rückgabe bilden die umgebende Struktur. Drei Kommentare kennzeichnen die Bereiche, die du vervollständigst; sie berechnen weder die fehlenden Distanzen noch die Auswahl.

cat ranking.py

Erstelle eine Quelltextsicherung. cp kopiert die aktuelle Datei; diese Sicherung ist die unfertige Basis, keine fertige Antwort.

cp ranking.py ranking.py.bak

np.where wählt bei Spannweite null die eins, sonst den echten Wert. np.linalg.norm liefert die euklidische Länge des zweielementigen Differenzvektors. float macht NumPy-Werte JSON-kompatibel. Der Sortierschlüssel ist ein Paar: ungerundete Distanz, bei exakt gleicher Distanz game_id. Die kurze lambda definiert diesen Schlüssel je Zeile. Runde nicht vor dem Sortieren. Letzte Gleitkommastellen äquivalenter Formeln können fast gleiche Werte umordnen; angezeigte Dezimalstellen sind nicht die gespeicherte Sortierregel.

Öffne das Modul in nano. Wie im ersten Lab gilt LC_ALL=C.UTF-8 nur für diesen Editoraufruf. Behalte vier Leerzeichen pro Einrückungsebene; Python gruppiert Anweisungen durch Einrückung.

LC_ALL=C.UTF-8 nano ranking.py

Drücke Strg+W, suche nach ## Metric parameters und drücke Enter. Gehe nach unten zum folgenden pass, drücke Pos1 und dann einmal Strg+K, um diese Platzhalterzeile zu entfernen. Füge diese fünf Zeilen mit ihren vier führenden Leerzeichen ein. Erhalte rows = [] und die umgebende Funktion.

    minimum = np.array(prepared['scale']['min'])
    ranges = np.array(prepared['scale']['range'])
    divisor = np.where(ranges == 0, 1, ranges)
    query = np.array([reference['minutes'], reference['complexity']])
    query_scaled = (query - minimum) / divisor

Drücke danach Strg+W, suche nach ## Candidate record und drücke Enter. Gehe nach unten zum pass dieses Bereichs, drücke Pos1 und einmal Strg+K. Füge den folgenden Block ein. Er liegt innerhalb der vorhandenen Schleife, deshalb beginnen seine Anweisungen mit acht Leerzeichen; die Felder innerhalb von rows.append verwenden zwölf. Erhalte die for-Zeile, game = by_id[game_id] und das spätere rows.sort unverändert.

        values = np.array([game['minutes'], game['complexity']])
        candidate_scaled = (values - minimum) / divisor
        distance = float(np.linalg.norm(candidate_scaled - query_scaled))
        difference = values - query
        rows.append({
            'game_id': game_id, 'title': game['title'], 'distance': distance,
            'minutes_delta': float(difference[0]),
            'complexity_delta': float(difference[1]),
        })

Lasse den Bereich ## Cutoff selection vorerst als cutoff = None und selected = []. So bleibt diese Zwischentabelle lesbar, während die Auswahl ehrlich unfertig bleibt. Drücke Strg+O, Enter, dann Strg+X zum Speichern und Beenden. Bemerkst du vor dem Speichern eine unerwünschte Bearbeitung, verwirft Strg+X, dann N diese Editorsitzung; öffne erneut und korrigiere nur den beabsichtigten Bereich.

Starte planner.py im Modus recommend; --output benennt die echte Ausgabe. Ohne weitere Optionen liest es catalog.csv, availability.csv und night.json im aktuellen Verzeichnis. --catalog, --availability, --request erlauben ausdrückliche Pfade.

python3 planner.py --mode recommend --output recommendations.json

Zeige das gespeicherte JSON mit dem Leser an. Er öffnet nur diese Datei und berechnet keine fehlenden Zeilen aus dem Katalog nach.

python3 show_results.py recommendations.json

Du solltest sechs Zeilen sehen. zero hat Distanz null, near und same liegen ungefähr bei 0.1, left/right bei 0.3. raw liegt trotz nur einer zusätzlichen Minute bei etwa 0.5001, weil es zwei Komplexitätspunkte höher liegt. Minutes +/- und Complexity +/- zeigen die vorzeichenbehafteten Originalunterschiede. Die erste Fassung lässt bewusst Cutoff: None und leere Auswahl stehen; der nächste Schritt ergänzt die Regel.

Erscheint ein SyntaxError oder IndentationError, lies Dateinamen und Zeilennummer, öffne ranking.py erneut und korrigiere diese Zeile oder diesen Bereich. Überschreibe nicht das ganze Modul, um eine Zeile zu reparieren. Ein Fehler wegen einer fehlenden Datei bedeutet meist, dass das Terminal im falschen Verzeichnis ist: pwd zeigt es und das anfängliche cd stellt den richtigen Ort wieder her, wie im ersten Lab geübt. cp ranking.py.bak ranking.py stellt nur dann die unfertige Basis wieder her, wenn du die Arbeit dieses Schritts bewusst verwerfen möchtest. Erhalte Eingabedateien, statt sie für ein bestehendes Ergebnis zu ändern.

Behalte die vollständige Grenzmenge

In diesem Schritt vervollständigst du die Auswahl und verwendest dasselbe Programm für Abende mit weniger und mit keinen Kandidaten.

Arbeite weiter im Terminal-Tab mit folgendem Verzeichnis.

cd /home/labex/project/board-games

k ist bei ausreichendem Angebot eine angestrebte Mindestzahl, keine Zusage von genau k Zeilen. Berechne und sortiere zuerst alle zulässigen Distanzen. Bei N Kandidaten ist der Grenzwert die Distanz an Position min(k, N). Python-Indizes beginnen mit null, also lautet der Index min(k, N) - 1. Behalte alle Kandidaten mit ungerundeter Distanz höchstens cutoff + 1e-9. Die absolute Zugabe 1e-9 definiert Grenzgleichstände, keine Sortiergruppen und keine Erlaubnis zum vorherigen Runden.

Gibt es weniger als k, liefere die tatsächlichen Kandidaten. Bei keiner Alternative sind candidates und selected_ids leer und der JSON-Grenzwert ist null (None in Python). Weiche nie auf nicht verfügbare oder ungeeignete Spiele aus. Erst die nächsten k aus dem gesamten Katalog zu nehmen und danach zu filtern kann spätere zulässige Alternativen verlieren; die vorherige Eignungsprüfung verhindert das.

Die normale Anfrage hat k=2. zero ist am nächsten, near und same liegen nach der Gleichstandsregel gemeinsam an der Grenze. Alle drei müssen ausgewählt werden. Ihre Reihenfolge folgt den tatsächlich ungerundeten Zeilen, game_id löst exakte Gleichstände auf. Die vollständige Sechs-Zeilen-Tabelle bleibt erhalten, damit sichtbar ist, was berücksichtigt wurde.

Sichere deine funktionierende Distanztabelle vor dem Ergänzen der Auswahl. Diese Kopie erhält die gerade getesteten Metrik- und Kandidatendatensätze.

cp ranking.py ranking-distances.py.bak

Öffne dasselbe Modul, ohne seine vorherige Arbeit zu ersetzen.

LC_ALL=C.UTF-8 nano ranking.py

Drücke Strg+W, suche nach ## Cutoff selection und drücke Enter. Gehe nach unten zu cutoff = None, drücke Pos1 und dann zweimal Strg+K, um nur diese Zeile und selected = [] zu entfernen. Füge diesen Block mit vier Leerzeichen für if und else und acht innerhalb jedes Zweigs ein. Die abschließende Rückgabe bleibt nach dem Block mit ihrer vorhandenen Einrückung stehen.

    if rows:
        cutoff = rows[min(request['k'], len(rows)) - 1]['distance']
        selected = [row['game_id'] for row in rows
                    if row['distance'] <= cutoff + 1e-9]
    else:
        cutoff = None
        selected = []

Die Prüfung if rows verhindert einen Indexzugriff auf eine leere Liste. Eine List-Comprehension erhält passende IDs in der vorhandenen sortierten Reihenfolge. Erhalte Metrikparameter, Kandidatenschleife und Sortierung aus Schritt 1. Speichere mit Strg+O, Enter und beende mit Strg+X. Möchtest du diesen Schritt zurücknehmen, stellt cp ranking-distances.py.bak ranking.py die getestete Distanztabellenversion mit weiterhin unfertiger Auswahl wieder her; bearbeite die Grenzauswahl erneut, statt die ganze Datei zu wiederholen.

Das JSON benötigt scale.min, scale.range in Merkmalsreihenfolge; je Kandidat game_id, title, numerische distance, vorzeichenbehaftete minutes_delta, complexity_delta; dazu cutoff und selected_ids. Speichere endliche Zahlen mit berechneter Genauigkeit statt gerundeter Zeichenketten. Harmlose zusätzliche Erklärfelder sind erlaubt. Geprüft wird je ID mit absoluter und relativer Toleranz 1e-8, getrennt von der Gleichstandsregel 1e-9. Äquivalente Formeln und übliche Implementierungen sind zulässig; Vorlieben werden nicht benotet. Beide Schnittstellen müssen funktionieren: choose_games liefert alphabetische eligible_ids und die Katalogskala, rank_games das Empfehlungsergebnis. Eine unabhängige Implementierung ohne internen Hilfsfunktionsaufruf ist erlaubt. Keine zusätzliche Eignungsdatei im Arbeitsbereich ist erforderlich.

Erzeuge das normale Ergebnis erneut und lies es.

python3 planner.py --mode recommend --output recommendations.json
python3 show_results.py recommendations.json

Jetzt sollten zero, near, same ausgewählt sein; andere Kandidaten bleiben sichtbar. cat recommendations.json zeigt das gespeicherte JSON mit ungerundeten Distanzen.

Zwei weitere vorbereitete Anfragen ändern nur den Abend, nicht Katalog oder Ausleihstatus. Lies sie mit cat. --request wählt die Anfrage nur für diesen Aufruf; es ändert weder night.json noch eine Terminalvariable. Unterschiedliche Ausgaben erhalten dein normales Ergebnis.

cat sparse-night.json
python3 planner.py --mode recommend --request sparse-night.json --output sparse.json
python3 show_results.py sparse.json

Acht Personen können nur right nutzen, obwohl k=5. Ein ehrlicher Kandidat ist richtig. Die Skala bleibt die des gesamten Katalogs, die Distanz weiterhin 0.3.

cat empty-night.json
python3 planner.py --mode recommend --request empty-night.json --output empty.json
python3 show_results.py empty.json

Für zwanzig Personen gibt es keine geeigneten Spiele. Erwarte keine Tabellenzeilen, Cutoff: None und eine ausdrückliche Meldung. In Terminal zeigt cat empty.json den gespeicherten Grenzwert null. Der Leser zeigt deine leere Ausgabe, statt einen Ersatz auszuwählen.

Erforderlich sind recommendations.json, sparse.json, empty.json, jeweils passend zur Anfrage und aus unveränderten Eingaben erneut erzeugbar. Öffne zur Wiederherstellung nach einer versehentlichen Codeänderung nano erneut und korrigiere den betroffenen Bereich, während deine andere fertige Arbeit erhalten bleibt; wiederhole danach alle drei Befehle. cp starter-ranking.py ranking.py stellt zum erneuten Üben nur den unvollständigen Starter her. Ein neues Terminal setzt Daten nicht zurück und startet kein Setup.

Zusammenfassung

Du hast alle geeigneten Kandidaten mit derselben Katalogskala gemessen, Distanzen anhand echter vorzeichenbehafteter Originalunterschiede erklärt und alle Grenzgleichstände ohne vorherige Rundung ausgewählt. Drei Dateien behandeln den normalen Abend, zu wenige Kandidaten und keine Kandidaten. Die Empfehlung beschreibt transparente inhaltliche Ähnlichkeit, keine Vorhersage persönlicher Freude.