Unterschiede
Hier werden die Unterschiede zwischen zwei Versionen der Seite angezeigt.
Beide Seiten, vorherige Überarbeitung Vorherige Überarbeitung Nächste Überarbeitung | Vorherige Überarbeitung | ||
talit:spatial [2024-08-08 07:42] – [Python Generators] hof | talit:spatial [2024-09-23 11:49] (aktuell) – hof | ||
---|---|---|---|
Zeile 1: | Zeile 1: | ||
- | # Information Retrieval | ||
- | ## Build Your Own Google | ||
- | |||
- | <nodisp 1> | ||
- | ++++Repo| | ||
- | https:// | ||
- | ++++ | ||
- | </ | ||
- | |||
- | Beim _Information Retrieval_ geht es darum, aus einer potentiell riesigen Menge von Dokumenten (en. _corpus_) diejenigen herauszufinden, | ||
- | |||
- | Es gibt dabei zwei Probleme zu lösen: | ||
- | * Die Dokumente identifizieren, | ||
- | * Die Resultat-Liste der Relevanz entsprechend sortieren (_ranking_). | ||
- | |||
- | Um diese Aufgaben **schnell** zu lösen, ist es im allgemeinen nicht möglich, den ganzen Korpus für jede Anfrage zu durchsuchen. Stattdessen bauen wir einen **Index**, der es erlaubt, effizient die geforderten Dokumente zu finden. | ||
- | |||
- | ### Jupyter | ||
- | Wir verwenden Jupyter Notebooks, um Code zu schreiben und dokumentieren. Erstelle ein neues Github-Repo mit einen Jupyter Notebook, um darin die Aufgaben zu lösen. | ||
- | |||
- | ## Direkter Schlüssel | ||
- | |||
- | Haben unsere Elemente einen eindeutigen Schlüssel (_key_), der uns für die Suchanfrage genügt, so können wir beispielsweise ein Dictionary verwenden. Der _Lookup_ dauert mit Binärsuche $𝒪(log(n))$, | ||
- | |||
- | In der Datenbank-Sprache nennen wir einen eindeutigen Schlüssel auch _primary key_. | ||
- | |||
- | ### Toy-Dataset | ||
- | |||
- | Für die Entwicklung benötigen wir ein kleines Toy-Dataset. Zum Beispiel dieses hier: | ||
- | <code python> | ||
- | thurgau = { | ||
- | 0: {' | ||
- | 1: {' | ||
- | 2: {' | ||
- | 3: {' | ||
- | 4: {' | ||
- | 5: {' | ||
- | 6: {' | ||
- | } | ||
- | </ | ||
- | ### Aufgabe 1 - Lineare Suche | ||
- | |||
- | Der Zugriff auf einen Eintrag mit bekanntem _key_ ist trivial: `thurgau[6]` liefert Egnach blitzschnell zurück - und das täte es auch, wenn das Dataset 6 Millionen Einträge hätte. Nun möchten wir den Korpus aber nach den Ortsnamen durchsuchen können. | ||
- | |||
- | Schreibe eine Funktion `def linear_search(dataset, | ||
- | |||
- | `linear_search(thurgau, | ||
- | |||
- | <code python> | ||
- | def linear_search(dataset, | ||
- | """ | ||
- | in dataset for which the given attribute matches query.""" | ||
- | pass | ||
- | |||
- | for result in linear_search(thurgau, | ||
- | print(result) | ||
- | </ | ||
- | #### Python Generators | ||
- | Die einfachste Möglichkeit sammelt alle Resultate in einer Liste und gibt diese zurück. Das kann aber teuer werden, wenn das Resultset gross ist. | ||
- | |||
- | Eine schönere Variante sind [[generators|Python Generators]]: | ||
- | . | ||
- | #### TQDM | ||
- | Mit dem `tqdm` Modul lassen sich lange Schleifen mit einem Progress-Monitor versehen. | ||
- | |||
- | <nodisp 1> | ||
- | ++++Lösung| | ||
- | <code python> | ||
- | %pip install tqdm | ||
- | %pip install ipywidgets | ||
- | |||
- | from tqdm.auto import tqdm | ||
- | |||
- | def linear_search(dataset, | ||
- | """ | ||
- | for town in tqdm(dataset.values()): | ||
- | if town[attribute] == query: | ||
- | yield town | ||
- | </ | ||
- | ++++ | ||
- | </ | ||
- | |||
- | ### Aufgabe 2 - Ernsthafte Datasets | ||
- | |||
- | Die lineare Suche für das Toy-Dataset dauert nicht lange - aber was, wenn unser Dataset grösser ist? In dieser Aufgabe erstellst du ein (oder zwei) grössere Datasets. Nach dem einlesen sollen sie ein ähnliches Format haben wie das Toy-Dataset: | ||
- | |||
- | Lade die Datenbank aller Ortschaften der Welt von http:// | ||
- | |||
- | Das Dictionary sollte als Schlüssel die `geonameid` verwenden und als Wert wiederum ein Dictionary von den Spaltennamen (Attributen) zu den Werten jedes Ortes. **Obige URL beschreibt auch das Datenformat und die Spalten**. Verzichte dabei auf Pandas - wir wollen in diesem Block herausfinden, | ||
- | |||
- | Du hast alles richtig gemacht, wenn dein Code ungefähr das folgende ausgibt: | ||
- | |||
- | <code python> | ||
- | print(places[2658985]) | ||
- | </ | ||
- | <code bash> | ||
- | {' | ||
- | </ | ||
- | |||
- | ++++Hinweise: | ||
- | * https:// | ||
- | * https:// | ||
- | ++++ | ||
- | |||
- | <nodisp 1> | ||
- | ++++Lösung| | ||
- | Zuerst muss die Datei in `data/ | ||
- | |||
- | <code python> | ||
- | def read_geonames(filename): | ||
- | from io import TextIOWrapper | ||
- | import zipfile | ||
- | import csv | ||
- | | ||
- | result = {} | ||
- | with zipfile.ZipFile(f' | ||
- | with myzip.open(f' | ||
- | reader = csv.DictReader(TextIOWrapper(csv_file, | ||
- | for data in tqdm(reader, | ||
- | result[int(data[' | ||
- | |||
- | return result | ||
- | |||
- | cities = read_geonames(' | ||
- | places = read_geonames(' | ||
- | </ | ||
- | ++++ | ||
- | </ | ||
- | |||
- | ## Separater Index | ||
- | |||
- | ### Aufgabe 3a | ||
- | Wie lange dauert die lineare Suche nach Ortschaften mit dem Namen `London`? Wieviele Londons gibt es? | ||
- | |||
- | In Jupyter Notebooks lässt sich die Zeit einer Operation sehr einfach mit dem `%%time` Magic messen: | ||
- | |||
- | <code python> | ||
- | %%time | ||
- | for result in linear_search(places, | ||
- | </ | ||
- | ### Aufgabe 3b | ||
- | Lineare Suche dauert uns zulange - wie können wir die Suche beschleunigen? | ||
- | |||
- | ++++Lösungsidee| | ||
- | Wir speichern einen zusätzlichen _Index_, zum Beispiel als Dictionary vom gewünschten Such-Kriterium zum einem [[https:// | ||
- | |||
- | <code python> | ||
- | index = { | ||
- | ' | ||
- | # ... | ||
- | } | ||
- | </ | ||
- | |||
- | Der Lookup passiert dann zweistufig: zuerst werden die Ids der Resultate im Index gesucht, anschliessend werden die Dokumente aus dem Dataset geladen. | ||
- | ++++ | ||
- | |||
- | Das Erstellen des Index dauert ziemlich lange und lohnt sich natürlich nur, wenn wir mehr als einmal suchen. Wie oft müssen wir suchen, damit wir die Indexerstellung kompensiert haben? | ||
- | |||
- | **Tipp:** | ||
- | Mit dem `tqdm`-Modul lässt sich der Fortschritt bequem darstellen. Das Modul muss einmalig z.B. mit `%pip install tqdm` installiert werden. | ||
- | |||
- | <code python> | ||
- | from tqdm.auto import tqdm | ||
- | |||
- | def build_attribute_index(dataset, | ||
- | index = dict() | ||
- | for id, entity in tqdm(dataset.items()): | ||
- | pass # Your code here: add the entity to the index | ||
- | return index | ||
- | </ | ||
- | |||
- | Probiert zuerst mit dem Toy-Dataset, | ||
- | |||
- | <nodisp 1> | ||
- | ++++Lösung| | ||
- | |||
- | `name_idx` ist ein Dictionary vom `name` Attribut auf die Menge der passenden `geonameid`s. Erweiterungsidee: | ||
- | |||
- | <code python> | ||
- | from tqdm.auto import tqdm | ||
- | |||
- | def build_attribute_index(dataset, | ||
- | index = dict() | ||
- | for id, entity in tqdm(dataset.items()): | ||
- | # setdefault retrieves the value for key or adds | ||
- | # the given default value if the key does not exist. | ||
- | index.setdefault(entity[attribute], | ||
- | return index | ||
- | |||
- | def query_index(dataset, | ||
- | """ | ||
- | for id in idx[query]: | ||
- | yield dataset[id] | ||
- | |||
- | %time name_idx = build_attribute_index(places, | ||
- | </ | ||
- | |||
- | <code python> | ||
- | %%time | ||
- | for result in query_index(places, | ||
- | print(result) | ||
- | </ | ||
- | ++++ | ||
- | </ | ||
- | |||
## Range Queries | ## Range Queries | ||
Zeile 267: | Zeile 62: | ||
++++ | ++++ | ||
</ | </ | ||
+ | |||
### Aufgabe 4c | ### Aufgabe 4c | ||
Zeile 288: | Zeile 84: | ||
++++Hinweise| | ++++Hinweise| | ||
- | * Das Wurzel-Element | + | * Das Wurzel-Element |
* Rekursion ist praktisch: Schaffe einen neuen Knoten `node = Node(key, value)`, anschliessend rufe die Funktion für den linken und rechten Subtree auf: | * Rekursion ist praktisch: Schaffe einen neuen Knoten `node = Node(key, value)`, anschliessend rufe die Funktion für den linken und rechten Subtree auf: | ||
<code python> | <code python> | ||
Zeile 297: | Zeile 93: | ||
++++Lösung| | ++++Lösung| | ||
- | < | + | < |
<code python> | <code python> | ||
def build_tree(sorted_tuples, | def build_tree(sorted_tuples, | ||
Zeile 361: | Zeile 157: | ||
++++ | ++++ | ||
</ | </ | ||
+ | |||
+ | |||
### Graph Visualization ### | ### Graph Visualization ### | ||