<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>GREL | FDMLab@LABW</title><link>https://fdmlab.landesarchiv-bw.de/tag/grel/</link><atom:link href="https://fdmlab.landesarchiv-bw.de/tag/grel/index.xml" rel="self" type="application/rss+xml"/><description>GREL</description><generator>Wowchemy (https://wowchemy.com)</generator><language>de-de</language><lastBuildDate>Wed, 14 May 2025 00:00:00 +0000</lastBuildDate><image><url>https://fdmlab.landesarchiv-bw.de/media/sharing.jpg</url><title>GREL</title><link>https://fdmlab.landesarchiv-bw.de/tag/grel/</link></image><item><title>Workshop - Python in OpenRefine</title><link>https://fdmlab.landesarchiv-bw.de/workshop/openrefine-fortgeschrittene/20-python-mit-openrefine/</link><pubDate>Wed, 14 May 2025 00:00:00 +0000</pubDate><guid>https://fdmlab.landesarchiv-bw.de/workshop/openrefine-fortgeschrittene/20-python-mit-openrefine/</guid><description>&lt;p>Wir verwenden Python zusammen mit OpenRefine.&lt;/p>
&lt;blockquote>
&lt;p>Jython in der &lt;a href="https://openrefine.org/docs/manual/jythonclojure#jython" target="_blank" rel="noopener">OpenRefine Dokumentation&lt;/a>.&lt;br>
Einsteiger Workshop &lt;a href="https://fdmlab.landesarchiv-bw.de/workshop/openrefine-fortgeschrittene/19-erweitertes-clustering/">19 Daten mit OpenRefine clustern&lt;/a>.&lt;br>
Blogbeitrag &lt;a href="https://fdmlab.landesarchiv-bw.de/post/2021-08-ner-mit-openrefine-und-spacy/">Named Entity Recognition mit OpenRefine und spaCy&lt;/a>.&lt;/p>
&lt;/blockquote>
&lt;div class="alert alert-note">
Dieser Workshop wurde erstellt mit OpenRefine Version 3.5.0.&lt;br>
Dieser Workshop wurde zuletzt getestet mit OpenRefine Version &lt;strong>3.9.3&lt;/strong>.
&lt;/div>
&lt;div class="alert alert-warning">
&lt;div>
Die in diesem Tutorial behandelten Funktionen sind teilweise erst verfügbar seit der Version OpenRefine &lt;strong>3.9.3&lt;/strong>.
&lt;/div>
&lt;/div>
&lt;h2 id="jython">Jython&lt;/h2>
&lt;p>Quasi überall, wo man in OpenRefine GREL-Expressions schreiben kann, kann man stattdessen auch auf Jython umstellen.
Also beispielsweise für den
&lt;a href="https://fdmlab.landesarchiv-bw.de/workshop/openrefine-einsteiger/03-transformieren/">Transformations-Dialog&lt;/a>,
&lt;a href="https://fdmlab.landesarchiv-bw.de/workshop/openrefine-fortgeschrittene/13-die-welt-der-facets/">Facets&lt;/a> oder neu auch für
&lt;a href="https://fdmlab.landesarchiv-bw.de/workshop/openrefine-fortgeschrittene/19-erweitertes-clustering/">eigene Clustering Methoden&lt;/a>.&lt;/p>
&lt;figure id="figure-bildschirmfoto-des-transformations-dialogs-mit-jython">
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="Bildschirmfoto des Transformations Dialogs mit Jython." srcset="
/workshop/openrefine-fortgeschrittene/20-python-mit-openrefine/screenshot-openrefine-transform-with-jython_hu0d55e479ad7a3691a7fe4df239345fc3_13960_8a8daaf36e58895df4e2bb5849538213.webp 400w,
/workshop/openrefine-fortgeschrittene/20-python-mit-openrefine/screenshot-openrefine-transform-with-jython_hu0d55e479ad7a3691a7fe4df239345fc3_13960_c46dff229eb7cc4e0469107a1fc091c1.webp 760w,
/workshop/openrefine-fortgeschrittene/20-python-mit-openrefine/screenshot-openrefine-transform-with-jython_hu0d55e479ad7a3691a7fe4df239345fc3_13960_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://fdmlab.landesarchiv-bw.de/workshop/openrefine-fortgeschrittene/20-python-mit-openrefine/screenshot-openrefine-transform-with-jython_hu0d55e479ad7a3691a7fe4df239345fc3_13960_8a8daaf36e58895df4e2bb5849538213.webp"
width="530"
height="162"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;figcaption data-pre="Abbildung&amp;nbsp;" data-post=":&amp;nbsp;" class="numbered">
Bildschirmfoto des Transformations Dialogs mit Jython.
&lt;/figcaption>&lt;/figure>
&lt;p>Praktisch wird das Jython-Snippet, welches man wie in Abbildung 1 gezeigt im Dialog eingibt, in eine Python-Funktion gepackt und dann von Jython ausgeführt.
Daher benötigt man im Unterschied zu GREL ein &lt;code>return&lt;/code> Statement, um anzugeben, welcher Wert zurückgegeben wird.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">some_fun_name&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">value&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">cell&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">cells&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">row&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">rowIndex&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">value1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">value2&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">...&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Das eingegebene Snippet wird quasi anstelle von &lt;code>...&lt;/code> eingesetzt.&lt;/p>
&lt;p>Zu den in &lt;a href="https://fdmlab.landesarchiv-bw.de/workshop/openrefine-fortgeschrittene/09-arbeiten-mit-grel/#variablen">GREL zur Verfügung stehenden Variablen&lt;/a> haben wir schon einiges geschrieben. Diese stehen mit Ausnahmen in Jython unter OpenRefine ebenfalls zur Verfügung.
Beispielsweise fehlt die Variable &lt;code>columnName&lt;/code> mit dem aktuellen Spaltennamen.&lt;/p>
&lt;p>&lt;a href="https://www.jython.org/" target="_blank" rel="noopener">Jython&lt;/a> selbst ist eine Java Implementierung von Python.
Das bedeutet, dass es innerhalb von Java interpretiert wird und man auch von Jython Code aus auf verfügbare Java Methoden zugreifen kann.&lt;/p>
&lt;p>Leider hat das Projekt den Sprung auf Python 3 nicht geschafft und unterstützt daher nur die Funktionalität von &lt;a href="https://docs.python.org/2.7/" target="_blank" rel="noopener">Python 2.7&lt;/a>.
Das macht auch die &lt;a href="https://github.com/OpenRefine/OpenRefine/wiki/Extending-Jython-with-pypi-modules" target="_blank" rel="noopener">Verwendung von externen Python Bibliotheken&lt;/a> innerhalb von Jython deutlich komplexer.&lt;/p>
&lt;p>Es gibt nur noch selten Python Bibliotheken, die Python 2.7 unterstützen.
Und es können auch nur Python Bibliotheken verwendet werden, die komplett in Python geschrieben wurden.
Einige gerade sehr beliebte Python Projekte verwenden aus Performance Gründen unter der Haube kompilierte Programmiersprachen wie &lt;a href="https://www.c-language.org/" target="_blank" rel="noopener">C&lt;/a>, &lt;a href="https://fortran-lang.org/" target="_blank" rel="noopener">Fortran&lt;/a> oder &lt;a href="https://www.rust-lang.org/" target="_blank" rel="noopener">Rust&lt;/a>.
Diese können unter Jython &lt;strong>nicht&lt;/strong> direkt verwendet werden.&lt;/p>
&lt;p>Praktisch ist die Ausführung von Jython Code in OpenRefine auch deutlich langsamer, als z.B. GREL oder Clojure Code.
Das macht es eher schwierig komplexere Algorithmen z.B. für das Clustering direkt zu hinterlegen.&lt;/p>
&lt;p>Alternativ kann von OpenRefine aus, via Jython auf externen Python Code zugegriffen werden, was im folgenden Teil vertieft wird.&lt;/p>
&lt;h2 id="python">Python&lt;/h2>
&lt;h3 id="standard-setup">Standard Setup&lt;/h3>
&lt;p>Wir verfolgen die Idee von OpenRefine aus auf Python Code zuzugreifen, der &lt;strong>nicht&lt;/strong> innerhalb von Jython ausgeführt wird.
Dafür benötigen wir einen Blick in das Python Projekt Setup.&lt;/p>
&lt;div class="mermaid">---
title: Python Setup
config:
look: handDrawn
theme: neutral
---
flowchart LR
subgraph web[fas:fa-globe Web]
subgraph python[fab:fa-python Python]
python_3_12[fab:fa-python 3.12]
python_3_13[fab:fa-python 3.13]
end
subgraph packages[fas:fa-folder-tree Packages]
typer_0_15[Typer 0.15]
fastapi_0_115[FastAPI 0.115]
spacy_3_8[spaCy 3.8]
my_project_0_1[My Project 0.1]
end
end
subgraph laptop[fas:fa-laptop Laptop]
subgraph local_python[fab:fa-python Python]
local_python_3_12[fab:fa-python 3.12]
local_python_3_13[fab:fa-python 3.13]
end
subgraph local_packages[fas:fa-folder-tree Packages]
local_typer_0_15[Typer 0.15]
local_fastapi_0_115[FastAPI 0.115]
subgraph local_spacy_3_8[spaCy 3.8]
c_code[fab:fa-c Code]
compiled_c_code[fab:fa-c compiled]
end
end
subgraph my_project[My Project]
subgraph venv[fas:fa-folder-open venv]
venv_python_3_12[fab:fa-python 3.12]
venv_typer_0_15[Typer 0.15]
venv_fastapi_0_115[FastAPI 0.115]
venv_spacy_3_8[spaCy 3.8]
end
package_my_project[My Project]
end
end
python --(1)--> local_python
python_3_12 ~~~ local_python_3_12
python_3_13 ~~~ local_python_3_13
packages --(2)--> local_packages
typer_0_15 ~~~ local_typer_0_15
fastapi_0_115 ~~~ local_fastapi_0_115
spacy_3_8 ~~~ local_spacy_3_8
c_code --(3)--> compiled_c_code
local_python_3_12 --(4)--> venv_python_3_12
local_typer_0_15 --(4)--> venv_typer_0_15
local_fastapi_0_115 --(4)--> venv_fastapi_0_115
local_spacy_3_8 --(4)--> venv_spacy_3_8
my_project_0_1 ~~~ package_my_project
package_my_project --(5)--> my_project_0_1
&lt;/div>
&lt;p>Für ein typisches Python Projekt benötigt man fünf Schritte, die durch verschiedene Tools in unterschiedlichem Ausmaß unterstützt werden.&lt;/p>
&lt;h4 id="schritt-1-python-interpreter">Schritt 1: Python Interpreter&lt;/h4>
&lt;p>Zuerst benötigt man einen Python Interpreter, der es ermöglicht Python Code auf dem eigenen Rechner auszuführen.
Zur Verwaltung verschiedener Python Versionen auf dem Rechner gibt es zum Beispiel das Projekt &lt;a href="https://github.com/pyenv/pyenv" target="_blank" rel="noopener">pyenv&lt;/a>.&lt;/p>
&lt;h4 id="schritt-2-dependency-management">Schritt 2: Dependency Management&lt;/h4>
&lt;p>Üblicherweise werden verschiedene externe Bibliotheken benötigt.
Diese stehen zum Beispiel im &lt;a href="https://pypi.org/" target="_blank" rel="noopener">Python Package Index (PyPI)&lt;/a> zur Verfügung und können mit &lt;a href="https://pip.pypa.io/" target="_blank" rel="noopener">pip&lt;/a> verwaltet werden.&lt;/p>
&lt;p>Alternativen zur Verwaltung der Abhängigkeiten sind &lt;a href="https://python-poetry.org/" target="_blank" rel="noopener">Poetry&lt;/a>, &lt;a href="https://pip-tools.readthedocs.io/" target="_blank" rel="noopener">pip-tools&lt;/a> und &lt;a href="https://pipx.pypa.io/" target="_blank" rel="noopener">pipx&lt;/a>.&lt;/p>
&lt;h4 id="schritt-3-compiler">Schritt 3: Compiler&lt;/h4>
&lt;p>Wie schon angesprochen beinhalten manche Python Projekte nicht nur Python Code, sondern auch Code in kompilierten Sprachen.
Wenn für die von euch verwendete Kombination aus CPU und Betriebssystem (noch) kein kompiliertes Binärpaket für die Bibliothek zur Verfügung steht, dann muss das Paket ggf. auf eurem Rechner kompiliert werden.
Dafür werden entsprechende Compiler und Entwicklungspakete auf dem Rechner benötigt.&lt;/p>
&lt;p>Alternativ gibt es auf &lt;a href="https://conda-forge.org/" target="_blank" rel="noopener">conda-forge&lt;/a> eine Sammlung schon kompilierter Pakete.&lt;/p>
&lt;h4 id="schritt-4-virtuelle-umgebung">Schritt 4: Virtuelle Umgebung&lt;/h4>
&lt;p>Erfahrungsgemäß bleibt es nicht bei einem Python Projekt auf dem eigenen Rechner.
Um Konflikte zwischen den Python Versionen und/oder Abhängigkeiten zu vermeiden, ist es üblich für jedes Projekt eine eigene virtuelle Umgebung anzulegen.&lt;/p>
&lt;p>Dafür gibt es das Werkzeug &lt;a href="https://docs.python.org/3/library/venv.html" target="_blank" rel="noopener">venv&lt;/a>, welches früher ein separates Paket war, und seit Python 3.3 in den Standard übernommen wurde.&lt;/p>
&lt;h4 id="schritt-5-package-und-release-management">Schritt 5: Package und Release Management&lt;/h4>
&lt;p>Möchte man das Projekt anschließend für andere z.B. im &lt;em>Python Package Index&lt;/em> zur Verfügung stellen, dann muss es davor noch in das passende Paketformat umgewandelt werden.
Da das für die Benutzung mit OpenRefine nicht relevant ist, wird dieser Schritt hier nicht vertieft.&lt;/p>
&lt;h2 id="uv">uv&lt;/h2>
&lt;p>Das oben beschriebene Setup klingt recht kompliziert und daher abschreckend gerade für kleinere Experimente.
Spätestens seit 2025 setzt sich bei Python Entwicklern hier das Projekt &lt;a href="https://docs.astral.sh/uv/" target="_blank" rel="noopener">uv&lt;/a> durch, welches die Schritte 1, 2, 4 und 5 in einem Werkzeug vereint und das Python Setup dadurch deutlich vereinfacht.&lt;/p>
&lt;p>Für die &lt;a href="https://docs.astral.sh/uv/getting-started/installation/" target="_blank" rel="noopener">Installation von uv&lt;/a> werden mehrere einfache und direkte Wege beschrieben.&lt;/p>
&lt;div class="alert alert-info">
&lt;div>
Sollte man mit dem &lt;em>Windows-Subsystem for Linux&lt;/em> (WSL) arbeiten, dann benötigt man die Installationsanleitung für Linux, auch wenn man Windows als Betriebssystem hat.
&lt;/div>
&lt;/div>
&lt;p>&lt;strong>uv&lt;/strong> hat (quasi) drei für uns relevante Modi:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>uvx&lt;/strong> zum Ausführen von Tools.&lt;/li>
&lt;li>&lt;strong>uv run&lt;/strong> zum Ausführen von Skripten.&lt;/li>
&lt;li>&lt;strong>uv&lt;/strong> zum Arbeiten mit Projekten&lt;br>
(mehrere abhängige Python Dateien, separates Dependency Management).&lt;/li>
&lt;/ul>
&lt;p>Wir konzentrieren uns in dieser Anleitung auf die ersten beiden.&lt;/p>
&lt;h2 id="reconciliation-service">Reconciliation Service&lt;/h2>
&lt;p>Um die Verwendung von &lt;em>uvx&lt;/em> zum Ausführen von externen Python Werkzeugen zu demonstrieren, erstellen wir einen eigenen &lt;em>Reconciliation Service&lt;/em> basierend auf einer CSV-Datei mit &lt;em>uvx&lt;/em> und &lt;a href="https://github.com/gitonthescene/csv-reconcile" target="_blank" rel="noopener">csv-reconcile&lt;/a>.
Zum Herunterladen der CSV-Datei von &lt;a href="https://fdmlab.landesarchiv-bw.de/workshop/openrefine-fortgeschrittene/12-daten-zwischen-projekten-abgleichen/">12 Daten zwischen Projekten abgleichen&lt;/a> verwenden wir &lt;em>uvx&lt;/em> mit &lt;a href="https://httpie.io/" target="_blank" rel="noopener">httpie&lt;/a>.&lt;/p>
&lt;p>In einem Terminal wie iTerm, xTerm, Bash oder Powershell führen wir den folgenden Befehl aus.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">uvx --from httpie http --download GET https://fdmlab.landesarchiv-bw.de/data/openrefine-workshop/12_staedte-in-bw-geokoordinaten.csv
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Dieses kompakte Snippet lädt die zu diesem Zeitpunkt standardmäßige Python Version und die aktuellste Version von &lt;em>httpie&lt;/em> herunter, erstellt damit eine virtuelle Umgebung (&lt;em>venv&lt;/em>) und führt darin den Befehl &lt;code>http --download GET ...&lt;/code> aus.&lt;/p>
&lt;p>Den &lt;em>Reconciliation Service&lt;/em> basierend auf der im letzten Schritt heruntergeladenen Datei erstellen wir in zwei Schritten:&lt;/p>
&lt;ol>
&lt;li>Datenbank für &lt;em>Reconcilation Service&lt;/em> erstellen&lt;/li>
&lt;li>&lt;em>Reconciliation Service&lt;/em> starten&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">uvx csv-reconcile init 12_staedte-in-bw-geokoordinaten.csv &lt;span class="s2">&amp;#34;GND ID&amp;#34;&lt;/span> &lt;span class="s2">&amp;#34;Name&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Mit diesem Snippet erstellen wir die Datenbank für den &lt;em>Reconciliation Service&lt;/em> und legen die Spalte &amp;ldquo;GND ID&amp;rdquo; als ID-Spalte fest und die Spalte &amp;ldquo;Name&amp;rdquo; als Standardspalte für den Abgleich.
Den &lt;em>Reconciliation Service&lt;/em> selbst starten wir mit dem folgenden Befehl.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">uvx csv-reconcile serve
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>In OpenRefine können wir den &lt;em>Reconciliation Service&lt;/em> anschließend hinzufügen mit: &lt;code>http://127.0.0.1:5000/reconcile&lt;/code>.&lt;/p>
&lt;h3 id="datenbank-mit-eigener-konfiguration">Datenbank mit eigener Konfiguration&lt;/h3>
&lt;p>Der oben erstelle &lt;em>Reconciliation Service&lt;/em> ist recht generisch und es fehlt z.B. noch die Vorschaufunktion für die einzelnen Elemente.
Um den Service nach unseren Anforderungen zu konfigurieren, erstellen wir eine Datei &lt;code>config.py&lt;/code> mit dem folgenden Inhalt.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="n">SERVER_NAME&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;localhost:5000&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">CSVKWARGS&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="s1">&amp;#39;delimiter&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s1">&amp;#39;,&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;quotechar&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s1">&amp;#39;&amp;#34;&amp;#39;&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">CSVENCODING&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s1">&amp;#39;UTF-8&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">MANIFEST&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Städte in BW Reconciliation Service&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;identifierSpace&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;https://normdaten.landesarchiv-bw.de/ids&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;schemaSpace&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;https://normdaten.landesarchiv-bw.de/schema&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;extend&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;propose_properties&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;service_url&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;http://localhost:5000&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;service_path&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;/properties&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;preview&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;url&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;http://localhost:5000/preview/{{id}}&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;width&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">500&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;height&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">300&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Anschließend erstellen wir die &lt;em>Reconciliation&lt;/em> Datenbank mit der neuen Konfiguration.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">uvx csv-reconcile init --config config.py 12_staedte-in-bw-geokoordinaten.csv &lt;span class="s2">&amp;#34;GND ID&amp;#34;&lt;/span> &lt;span class="s2">&amp;#34;Name&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Der neue Service ist nach dem Starten unter &lt;code>http://localhost:5000/reconcile&lt;/code> erreichbar.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">uvx csv-reconcile serve
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="alert alert-info">
&lt;div>
In OpenRefine müssen wir den CSV &lt;em>Reconciliation Service&lt;/em> aktuell bei jeder Änderung der &lt;strong>Konfiguration&lt;/strong> neu hinzufügen.
&lt;/div>
&lt;/div>
&lt;p>Der &lt;em>Reconciliation Service&lt;/em> kann jetzt wie in Abbildung 2 mit dem Beispiel aus &lt;a href="https://fdmlab.landesarchiv-bw.de/workshop/openrefine-fortgeschrittene/12-daten-zwischen-projekten-abgleichen/">12 Daten zwischen Projekten abgleichen&lt;/a> getestet werden.&lt;/p>
&lt;figure id="figure-screencast-zum-reconciling-mit-openrefine-und-csv-datei">
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="Screencast zum *Reconciling* mit OpenRefine und CSV-Datei."
src="https://fdmlab.landesarchiv-bw.de/workshop/openrefine-fortgeschrittene/20-python-mit-openrefine/screencast-openrefine-csv-reconciling.gif"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;figcaption data-pre="Abbildung&amp;nbsp;" data-post=":&amp;nbsp;" class="numbered">
Screencast zum &lt;em>Reconciling&lt;/em> mit OpenRefine und CSV-Datei.
&lt;/figcaption>&lt;/figure>
&lt;h2 id="eigene-skripte">Eigene Skripte&lt;/h2>
&lt;p>Um &lt;em>Command Line Interfaces&lt;/em> (CLIs) zu erstellen, gibt es in Python die Standardbibliothek &lt;a href="https://docs.python.org/3/library/argparse.html" target="_blank" rel="noopener">argparse&lt;/a>.
Meistens möchte man aber &amp;ldquo;nur mal schnell&amp;rdquo; aus einer Python Funktion ein Werkzeug erstellen, welches von der Kommandozeile aufgerufen werden kann.
Ein nützliches Python Projekt dafür ist &lt;a href="https://typer.tiangolo.com/" target="_blank" rel="noopener">typer&lt;/a>.&lt;/p>
&lt;h3 id="typer-hello-world-beispiel">Typer Hello World Beispiel&lt;/h3>
&lt;p>Ein einfaches &amp;ldquo;Hello World&amp;rdquo; Beispiel mit &lt;em>uv&lt;/em> und &lt;em>Typer&lt;/em> ist im folgenden Quellcode aus &lt;code>hello_world.py&lt;/code> abgebildet.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># /// script&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># requires-python = &amp;#34;&amp;gt;=3.12&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># dependencies = [&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># &amp;#34;typer&amp;#34;,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># ]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># ///&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">typer&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">hello_world&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Hello World&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="vm">__name__&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s2">&amp;#34;__main__&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">typer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">run&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">hello_world&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Zu Beginn wird über &lt;a href="https://packaging.python.org/en/latest/specifications/inline-script-metadata/#script-type" target="_blank" rel="noopener">Inline Metadaten&lt;/a> definiert, dass das Skript mit Python 3.12 oder neuer ausgeführt werden soll und das Skript zusätzlich die externe Abhängigkeit &lt;a href="https://typer.tiangolo.com/" target="_blank" rel="noopener">typer&lt;/a> benötigt.&lt;/p>
&lt;p>Das Werkzeug &lt;em>uv&lt;/em> unterstützt diese inline Metadaten, so dass zum Ausführen dieses Skriptes in einem Terminal lediglich &lt;code>uv run hello_world.py&lt;/code> ausgeführt werden muss.&lt;/p>
&lt;p>Das Werkzeug &lt;em>Typer&lt;/em> übersetzt den CLI-Aufruf für die Python Funktion, und validiert und konvertiert dabei die Eingabedaten.&lt;/p>
&lt;h3 id="typer-beispiel-mit-parametern">Typer Beispiel mit Parametern&lt;/h3>
&lt;p>Wir beginnen mit einem einfachen Beispiel.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># /// script&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># requires-python = &amp;#34;&amp;gt;=3.12&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># dependencies = [&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># &amp;#34;typer&amp;#34;,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># ]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># ///&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">typer&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="vm">__name__&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s2">&amp;#34;__main__&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">typer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">run&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">add&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Die Funktion &lt;code>add&lt;/code> soll zwei Parameter &lt;code>a&lt;/code> und &lt;code>b&lt;/code> miteinander addieren und dann ausgeben.
Zum Ausgeben des Ergebnisses verwenden wir die Funktion &lt;code>print&lt;/code>.
Das Werkzeug &lt;code>typer&lt;/code> erkennt automatisch die beiden Parameter &lt;code>a&lt;/code> und &lt;code>b&lt;/code> und meldet, wenn diese nicht gesetzt sind.
Unter &lt;code>uv run add.py --help&lt;/code> erhalten wir einen Informationstext zur Benutzung unseres CLI-Tools.&lt;/p>
&lt;p>Standardmäßig werden die Parameter als Text interpretiert.
Mit so genannten &lt;a href="https://docs.python.org/3/library/typing.html" target="_blank" rel="noopener">Type Hints&lt;/a> können wir &lt;em>Typer&lt;/em> mitteilen, dass wir hier Zahlen erwarten.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">int&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">int&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Es ist auch möglich, optionale Parameter wie &lt;code>c&lt;/code> mit anzugeben.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">int&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">int&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">c&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">int&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">b&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">c&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Individuelle Konfigurationen können mit &lt;a href="https://docs.python.org/3/library/typing.html#typing.Annotated" target="_blank" rel="noopener">Type Annotations&lt;/a> hinterlegt werden.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">c&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Annotated&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">typer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">Argument&lt;/span>&lt;span class="p">()]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">b&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">c&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Mit den Annotationen lässt sich bei Bedarf der Text für die Hilfe weiter spezifizieren.
Hier das fertige Skript mit ausführlicherem Hilfstext.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># /// script&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># requires-python = &amp;#34;&amp;gt;=3.12&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># dependencies = [&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># &amp;#34;typer&amp;#34;,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># ]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># ///&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">typer&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">typing_extensions&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">Annotated&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">add&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">a&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Annotated&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">typer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">Argument&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">help&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;The first number to add.&amp;#34;&lt;/span>&lt;span class="p">)],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">b&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Annotated&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">typer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">Argument&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">help&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;The second number to add.&amp;#34;&lt;/span>&lt;span class="p">)],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">c&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Annotated&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">typer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">Argument&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">help&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;The optionally third number to add.&amp;#34;&lt;/span>&lt;span class="p">)]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> Add the numbers in `a` and `b` and optionally `c` if `c` is provided.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> &amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">b&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">c&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="vm">__name__&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s2">&amp;#34;__main__&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">typer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">run&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">add&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Das Praktische an &lt;em>Typer&lt;/em> ist, dass man &amp;ldquo;nur zum Ausprobieren&amp;rdquo; lediglich die &lt;em>type hints&lt;/em> und den Aufruf von &lt;em>Typer&lt;/em> im Skript ergänzen muss.
Bei Bedarf können ausführlichere Hilfstexte oder Validierungen hinterlegt werden, deren Ergebnis in Abbildung 3 gezeigt ist.&lt;/p>
&lt;figure id="figure-screenshot-terminal-mit-von-typer-generierten-hilfstext">
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="Screenshot Terminal mit von *Typer* generierten Hilfstext." srcset="
/workshop/openrefine-fortgeschrittene/20-python-mit-openrefine/screenshot-terminal-typer_hu0d8bcb7ebbd1732e66a8e438ddf2504c_22144_b0035b32e843cd9c416ec72c36cdae2a.webp 400w,
/workshop/openrefine-fortgeschrittene/20-python-mit-openrefine/screenshot-terminal-typer_hu0d8bcb7ebbd1732e66a8e438ddf2504c_22144_02ae2c3f464e13330fa05f52c91a98b6.webp 760w,
/workshop/openrefine-fortgeschrittene/20-python-mit-openrefine/screenshot-terminal-typer_hu0d8bcb7ebbd1732e66a8e438ddf2504c_22144_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://fdmlab.landesarchiv-bw.de/workshop/openrefine-fortgeschrittene/20-python-mit-openrefine/screenshot-terminal-typer_hu0d8bcb7ebbd1732e66a8e438ddf2504c_22144_b0035b32e843cd9c416ec72c36cdae2a.webp"
width="542"
height="231"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;figcaption data-pre="Abbildung&amp;nbsp;" data-post=":&amp;nbsp;" class="numbered">
Screenshot Terminal mit von &lt;em>Typer&lt;/em> generierten Hilfstext.
&lt;/figcaption>&lt;/figure>
&lt;h3 id="zugriff-von-openrefine-auf-python-skript">Zugriff von OpenRefine auf Python Skript&lt;/h3>
&lt;p>Von OpenRefine heraus, lässt sich mit Jython recht unkompliziert auf das externe Python Skripte zugreifen.
Es muss lediglich der Pfad des Skriptes in dem Jython Snippet unten angepasst werden.
Ggf. müssen auch noch Parameter für das Skript ergänzt werden.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">subprocess&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">script&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;C:&lt;/span>&lt;span class="se">\\&lt;/span>&lt;span class="s2">...&lt;/span>&lt;span class="se">\\&lt;/span>&lt;span class="s2">python_script.py&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">res&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">subprocess&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">check_output&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;uv&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;run&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">script&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="se">\&amp;#34;&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="n">value&lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="se">\&amp;#34;&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">shell&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">return&lt;/span> &lt;span class="n">res&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="probleme-beim-zugriff-von-openrefine-auf-python-skripte">Probleme beim Zugriff von OpenRefine auf Python Skripte&lt;/h3>
&lt;p>Der direkte Zugriff auf ein Skript ist erst einmal relativ performant, hat in der Praxis aber mehrere Probleme.&lt;/p>
&lt;ol>
&lt;li>Die korrekte Übergabe von Daten an das Skript via Parametern ist nicht ganz so einfach. Besonders, wenn die Daten größer sind ggf. Sonderzeichen enthalten, &amp;hellip;&lt;/li>
&lt;li>Man ist selbst für die Fehlerbehandlung verantwortlich. Also dass die Skripte auch irgendwann beendet werden, bei zu langer Laufzeit abgebrochen werden, usw.&lt;/li>
&lt;li>Es wird lediglich Text zurückgegeben. Die Interpretation in ein bestimmtes Format muss dann in Jython oder in einem Nachbearbeitungsschritt erfolgen.&lt;/li>
&lt;li>Manche Skripte oder Tools müssen initial erst einmal Daten in den Arbeitsspeicher laden. Insbesondere bei Machine Learning Anwendungen kann das mehrere Sekunden dauern. Dies erfolgt bei jedem Skriptaufruf wieder erneut.&lt;/li>
&lt;/ol>
&lt;p>Daher ist es wesentlich stabiler stattdessen mit eigenen Web-APIs zu arbeiten.&lt;/p>
&lt;h2 id="eigene-web-apis-mit-fastapi">Eigene Web-APIs mit FastAPI&lt;/h2>
&lt;div class="mermaid">---
title: Script vs Web-API
config:
look: handDrawn
theme: neutral
---
flowchart LR
subgraph openrefine["OpenRefine"]
subgraph jython["Jython"]
jython_code["Jython Code"]
end
end
subgraph venv
script["fab:fa-python Skript"]
end
subgraph uvicorn["Uvicorn"]
subgraph fastapi["FastAPI"]
python_code["fab:fa-python Code"]
end
end
jython_code -.-> script
jython_code -.-> script
jython_code -.-> script
jython_code -.-> script
jython_code -.-> script
jython_code -.-> script
jython_code -.-> script
jython_code -.-> script
jython_code -.-> script
jython_code &lt;--HTTP--> python_code
&lt;/div>
&lt;p>Für Web-APIs gibt es etablierte und stabile Protokolle zum Austausch von Daten.
Ähnlich wie mit &lt;em>Typer&lt;/em> können wir mit &lt;a href="https://fastapi.tiangolo.com/" target="_blank" rel="noopener">FastAPI&lt;/a> unsere Python-Funktionen als Web-API zur Verfügung stellen.
Mit einem Server wie &lt;a href="https://www.uvicorn.org/" target="_blank" rel="noopener">uvicorn&lt;/a> sorgen wir dabei für eine Stabilität der angestoßenen Prozesse für die Abfragen.&lt;/p>
&lt;h3 id="beispielcode-für-spacy">Beispielcode für spaCy&lt;/h3>
&lt;p>Das folgende Skript lädt ein deutschsprachiges Modell von &lt;a href="https://spacy.io" target="_blank" rel="noopener">spaCy&lt;/a> und berechnet über &lt;a href="https://de.wikipedia.org/wiki/Worteinbettung" target="_blank" rel="noopener">Word Embeddings&lt;/a> die Ähnlichkeit bzw. hier die Distanz der beiden übergebenen Texte.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># /// script&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># requires-python = &amp;#34;&amp;gt;=3.12,&amp;lt;3.13&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># dependencies = [&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># &amp;#34;spacy&amp;#34;,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># &amp;#34;typer&amp;#34;,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># &amp;#34;de_core_news_lg @ https://github.com/explosion/spacy-models/releases/download/de_core_news_lg-3.8.0/de_core_news_lg-3.8.0.tar.gz&amp;#34;,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># ]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># ///&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">typer&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">de_core_news_lg&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="nn">model&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">spacy_distance&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">nlp&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">model&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">load&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">doc_a&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">nlp&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">doc_b&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">nlp&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">b&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">dist&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="n">doc_a&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">similarity&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">doc_b&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">dist&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="vm">__name__&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s2">&amp;#34;__main__&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">typer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">run&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">spacy_distance&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Das Skript hat den eindeutigen Nachteil, dass es bei jedem Aufruf das &amp;ldquo;große&amp;rdquo; Sprachmodell laden muss, was mehrere Sekunden dauert.
Hier im Vergleich das Skript &lt;code>spacy_distance_fastapi.py&lt;/code>, welches die Funktionalität via &lt;a href="https://fastapi.tiangolo.com/" target="_blank" rel="noopener">FastAPI&lt;/a> zur Verfügung stellt.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># /// script&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># requires-python = &amp;#34;&amp;gt;=3.12,&amp;lt;3.13&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># dependencies = [&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># &amp;#34;spacy&amp;#34;,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># &amp;#34;fastapi&amp;#34;,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># &amp;#34;uvicorn&amp;#34;,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># &amp;#34;de_core_news_lg @ https://github.com/explosion/spacy-models/releases/download/de_core_news_lg-3.8.0/de_core_news_lg-3.8.0.tar.gz&amp;#34;,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># ]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># ///&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">de_core_news_lg&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="nn">model&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">uvicorn&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">fastapi&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">FastAPI&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">app&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">FastAPI&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">nlp&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">model&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">load&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@app.get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;/distance&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">spacy_distance&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&amp;gt;&lt;/span> &lt;span class="nb">float&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">doc_a&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">nlp&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">doc_b&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">nlp&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">b&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">dist&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="n">doc_a&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">similarity&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">doc_b&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">dist&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="vm">__name__&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s2">&amp;#34;__main__&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">uvicorn&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">run&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;spacy_distance_fastapi:app&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">host&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;127.0.0.1&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">port&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">5000&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Die Verarbeitungspipeline für die Texte mit dem &amp;ldquo;großen&amp;rdquo; Sprachmodell wird außerhalb der Python Methode definiert, weshalb das Modell pro Aufruf nur einmal aufgerufen werden muss.
Im Vergleich zum direkten Skriptaufruf, ist der Zugriff auf unsere lokale Web-API zwar etwas langsamer.
Wir profitieren aber von der Müglichkeit ladeintensive Prozesse nur einmal beim Starten auszuführen, sowie von dem zusätzlichen Protokoll im Bereich Fehlerbehandlung, Validierung von Eingaben und Antworten.&lt;/p>
&lt;p>Ähnlich wie bei &lt;em>Typer&lt;/em> übernimmt &lt;em>FastAPI&lt;/em> hier das Mapping der Benutzeranfrage auf die entsprechende Python Funktion.
Gleich wie bei &lt;em>Typer&lt;/em> werden &lt;em>Type Hints&lt;/em> für das Mapping, die Dokumentation und die Validierung verwendet.
Auch bei &lt;em>FastAPI&lt;/em> kann die Dokumentation mit weiteren Annotationen erweitert und personalisiert werden.&lt;/p>
&lt;p>Somit lassen sich mit FastAPI eigene Web-APIs ähnlich leicht erstellen, wie einfache Python Skripte mit &lt;em>Typer&lt;/em>.
Ausführliche Beispiele stehen im Anhang bzw. als GitHub Gist unter &lt;a href="https://gist.github.com/b2m/cadf88263f7978be96c164a89968c44c" target="_blank" rel="noopener">spacy_fastapi.py&lt;/a> und &lt;a href="https://gist.github.com/b2m/91f3b812bcf0975c4d2cb3230099366a" target="_blank" rel="noopener">rapidfuzz_fastapi.py&lt;/a> zur Verfügung.&lt;/p>
&lt;p>Von OpenRefine heraus kann man via Jython auf die (lokalen) Web-APIs zugreifen.&lt;/p>
&lt;h3 id="zugriff-via-get">Zugriff via GET&lt;/h3>
&lt;p>Web-APIs unterscheiden für den Abruf von Daten zwischen &lt;em>GET-&lt;/em> und &lt;em>POST-Requests&lt;/em> (Anfragen).
Für &lt;em>GET-Requests&lt;/em> gibt es in OpenRefine die Möglichkeit den Dialog &lt;a href="https://openrefine.org/docs/manual/columnediting#add-column-by-fetching-urls" target="_blank" rel="noopener">Add column by fetching URLs&lt;/a> zum Nachladen von Daten zu verwenden.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;http://localhost:5000/ner?text=&amp;#34;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nx">escape&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">value&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;url&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Hier kann man mit einfachem GREL die benötigte URL zusammen bauen.
Man sollte jedoch daran denken, die Parameter mit &lt;a href="https://openrefine.org/docs/manual/grelfunctions#escapes-s-mode" target="_blank" rel="noopener">escape&lt;/a> für die URL kompatibel aufzubereiten.&lt;/p>
&lt;p>In Abbildung 4 ist der Dialog aus &lt;a href="https://fdmlab.landesarchiv-bw.de/workshop/openrefine-fortgeschrittene/18-nachladen-von-geokoordinaten/">18 Nachladen von Geokoordinaten&lt;/a> gezeigt.&lt;/p>
&lt;figure id="figure-bildschirmfoto-des-dialogs-zum-nachladen-von-urls-für-geonames">
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img src="../18-nachladen-von-geokoordinaten/screenshot-openrefine-add-by-url.png" alt="Bildschirmfoto des Dialogs zum Nachladen von URLs für GeoNames." loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;figcaption data-pre="Abbildung&amp;nbsp;" data-post=":&amp;nbsp;" class="numbered">
Bildschirmfoto des Dialogs zum Nachladen von URLs für GeoNames.
&lt;/figcaption>&lt;/figure>
&lt;p>Möchte man eine Web-API z.B. von einem &lt;em>Facet&lt;/em> oder einem Clustering-Dialog heraus aufrufen, benötigt man dafür das folgende Jython-Snippet um einen validen &lt;em>GET-Request&lt;/em> zu erstellen.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">json&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="nn">urllib&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="nn">urllib2&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">url&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;http://localhost:5000/ner&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">request_data&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">urllib&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">urlencode&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;text&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">value&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">encode&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;utf-8&amp;#34;&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">urllib2&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">urlopen&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">url&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="s2">&amp;#34;?&amp;#34;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">request_data&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">return&lt;/span> &lt;span class="n">json&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">dumps&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">json&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">load&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">response&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="n">ensure_ascii&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">False&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="alert alert-note">
&lt;div>
&lt;strong>Zur Erinnerung:&lt;/strong> Es gibt Python Bibliotheken wie &lt;a href="https://requests.readthedocs.io/" target="_blank" rel="noopener">requests&lt;/a>, die das Erstellen und Verarbeiten von Web-Requests deutlich vereinfachen. Wir arbeiten in OpenRefine aber mit Jython und können daher nicht einfach auf externe Bibliotheken zugreifen.
&lt;/div>
&lt;/div>
&lt;h3 id="zugriff-via-get-und-form">Zugriff via GET und Form&lt;/h3>
&lt;p>Manche Web-APIs erwarten einen &lt;em>GET-Request&lt;/em> mit zusätzlichem Nutzdaten z.B. aus einem Formular.
Das lässt sich in Jython / Python 2.7 mit folgendem Snippet umsetzen.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">json&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="nn">urllib&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="nn">urllib2&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">url&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;http://localhost:5000/ner&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">request_data&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">urllib&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">urlencode&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;text&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">value&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">encode&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;utf-8&amp;#34;&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">urllib2&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">urlopen&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">url&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">request_data&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">return&lt;/span> &lt;span class="n">json&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">dumps&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">json&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">load&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">response&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="n">ensure_ascii&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">False&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Der Unterschied zum letzten Beispiel ist, dass &lt;code>request_data&lt;/code> als zusätzliche Nutzdaten mit einem Komma getrennt an &lt;code>urllib2&lt;/code> übergeben werden, anstatt sie mit an die URL zu kodieren.&lt;/p>
&lt;h3 id="zugriff-via-post">Zugriff via POST&lt;/h3>
&lt;p>Der in den Beispielen im Anhang verwende &lt;em>Use-Case&lt;/em> ist das Verwenden von &lt;em>POST-Requests&lt;/em>.
Hier werden die Nutzdaten als &lt;a href="https://www.json.org/" target="_blank" rel="noopener">JSON&lt;/a> kodiert an den &lt;em>Request&lt;/em> angehängt.
Das macht die Kodierung der Daten in der Anfrage komplexer, gleichzeitig aber die Übermittlung stabiler.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">json&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="nn">urllib&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="nn">urllib2&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">url&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;http://localhost:5000/ner&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">request_data&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">json&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">dumps&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;text&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">value&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">encode&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;utf-8&amp;#34;&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">request&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">urllib2&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">Request&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">url&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">request_data&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;Content-Type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;application/json&amp;#34;&lt;/span>&lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">urllib2&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">urlopen&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">request&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">return&lt;/span> &lt;span class="n">json&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">dumps&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">json&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">load&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">response&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="n">ensure_ascii&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">False&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Der komplette Workflow vom Starten des Web-Service, bis zur Verwendung von &lt;a href="https://rapidfuzz.github.io/RapidFuzz/" target="_blank" rel="noopener">RapidFuzz&lt;/a> als eigene Clustering Methode in OpenRefine ist in dem Screencast in Abbildung 5 gezeigt.&lt;/p>
&lt;figure id="figure-screencast-zur-verwendung-von-uv-fastapi-und-rapidfuzz-als-eigene-clustering-methode-bei-openrefine">
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="Screencast zur Verwendung von uv, FastAPI und RapidFuzz als eigene Clustering Methode bei OpenRefine."
src="https://fdmlab.landesarchiv-bw.de/workshop/openrefine-fortgeschrittene/20-python-mit-openrefine/screencast-openrefine-custom-clustering-distance-rapidfuzz.gif"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;figcaption data-pre="Abbildung&amp;nbsp;" data-post=":&amp;nbsp;" class="numbered">
Screencast zur Verwendung von uv, FastAPI und RapidFuzz als eigene Clustering Methode bei OpenRefine.
&lt;/figcaption>&lt;/figure>
&lt;h2 id="fazit">Fazit&lt;/h2>
&lt;p>Kleinere Skripte mit Jython / Python 2.7 in OpenRefine sind möglich, jedoch gerade beim Clustering oder bei der Nachnutzung von externen Python Projekten problematisch.&lt;/p>
&lt;p>Mit &lt;a href="https://typer.tiangolo.com/" target="_blank" rel="noopener">Typer&lt;/a> und &lt;a href="ttps://fastapi.tiangolo.com/">FastAPI&lt;/a> lassen sich aus Python Funktionen heraus schnell CLI-Tools oder Web-APIs erstellen.
Die beiden Werkzeuge ermöglichen es sowohl mit wenig Aufwand Prototypen zu erstellen, als auch diese zu dokumentierten praktischen Tools zu erweitern.&lt;/p>
&lt;p>Mit &lt;a href="https://docs.astral.sh/uv/" target="_blank" rel="noopener">uv&lt;/a> sind die Hürden für das Erstellen von indivduellen Python Umgebungen für einzelne Skripte sehr stark gesenkt worden.
Das reduziert nicht nur den Entwicklungsaufwand, sondern erleichtert auch die Nachnutzung von Skripten für Nutzerinnen und Nutzer.&lt;/p>
&lt;p>Mit der Kombination aus Jython, uv und wahlweise Typer oder FastAPI lassen sich dadurch modernste Python Anwendungen direkt mit OpenRefine verbinden.&lt;/p>
&lt;h2 id="anhang">Anhang&lt;/h2>
&lt;h3 id="fastapi-wrapper-für-spacy">FastAPI Wrapper für spaCy&lt;/h3>
&lt;script type="application/javascript" src="https://gist.github.com/b2m/cadf88263f7978be96c164a89968c44c.js?file=spacy_fastapi.py">&lt;/script>
&lt;h3 id="fastapi-wrapper-für-rapidfuzz">FastAPI Wrapper für RapidFuzz&lt;/h3>
&lt;script type="application/javascript" src="https://gist.github.com/b2m/91f3b812bcf0975c4d2cb3230099366a.js?file=rapidfuzz_fastapi.py">&lt;/script></description></item><item><title>Workshop - Erweitertes Clustering</title><link>https://fdmlab.landesarchiv-bw.de/workshop/openrefine-fortgeschrittene/19-erweitertes-clustering/</link><pubDate>Mon, 05 May 2025 00:00:00 +0000</pubDate><guid>https://fdmlab.landesarchiv-bw.de/workshop/openrefine-fortgeschrittene/19-erweitertes-clustering/</guid><description>&lt;p>Wir verwenden &lt;strong>eigene&lt;/strong> Clustering Methoden in OpenRefine, um Schreibweisen zu vereinheitlichen.&lt;/p>
&lt;blockquote>
&lt;p>Clustering in der &lt;a href="https://openrefine.org/docs/manual/cellediting#custom-clustering-methods" target="_blank" rel="noopener">OpenRefine Dokumentation&lt;/a>.&lt;br>
Clustering im &lt;a href="https://openrefine.org/docs/technical-reference/clustering-in-depth" target="_blank" rel="noopener">OpenRefine Wiki&lt;/a>.&lt;br>
Einsteiger Workshop &lt;a href="https://fdmlab.landesarchiv-bw.de/workshop/openrefine-einsteiger/05-clustering/">05 Daten mit OpenRefine clustern&lt;/a>.&lt;/p>
&lt;/blockquote>
&lt;div class="alert alert-note">
Dieser Workshop wurde erstellt mit OpenRefine Version 3.5.0.&lt;br>
Dieser Workshop wurde zuletzt getestet mit OpenRefine Version &lt;strong>3.9.3&lt;/strong>.
&lt;/div>
&lt;div class="alert alert-warning">
&lt;div>
Die in diesem Tutorial behandelte Funktionalität ist verfügbar seit OpenRefine &lt;strong>3.9.3&lt;/strong>.
&lt;/div>
&lt;/div>
&lt;h2 id="daten">Daten&lt;/h2>
&lt;p>Wir benötigen die folgenden Dateien als OpenRefine Projekt:&lt;/p>
&lt;blockquote>
&lt;p>💾 Wir benötigen die folgende Datei (Rechtsklick und &amp;ldquo;Ziel speichern unter&amp;hellip;&amp;rdquo;):&lt;/p>
&lt;/blockquote>
&lt;a href="https://fdmlab.landesarchiv-bw.de/data/openrefine-workshop/19_erweitertes-clustering.csv" target="_blank">
&lt;i class="fas fa-file-csv pr-1 fa-fw">&lt;/i>Erweitertes Clustering&lt;/a>
&lt;hr>
&lt;p>In der nachfolgenden Tabelle sind typische Probleme dargestellt, die mit den Standard Clusteringverfahren von OpenRefine nicht direkt zu lösen sind.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>&lt;strong>Ort&lt;/strong>&lt;/th>
&lt;th>&lt;strong>Name&lt;/strong>&lt;/th>
&lt;th>&lt;strong>Name 2&lt;/strong>&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>XY, Ort in Bayern&lt;/td>
&lt;td>Hans Meier&lt;/td>
&lt;td>Hans Meier&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>XY (Bayern)&lt;/td>
&lt;td>Maier, Hans&lt;/td>
&lt;td>Hans Peter Meier&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>XY in Bayern&lt;/td>
&lt;td>Hans Peter Meyer&lt;/td>
&lt;td>Hans P. Meier&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>XY; Bayern&lt;/td>
&lt;td>Hans (Peter) Maier&lt;/td>
&lt;td>Hans (Peter) Meier&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>XY | Bayern&lt;/td>
&lt;td>Hans P. Mayer&lt;/td>
&lt;td>Hans Müller&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>XY, liegt in Bayern&lt;/td>
&lt;td>Meier, Hans (Peter)&lt;/td>
&lt;td>Hans Peter Müller&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>In der Spalte &lt;em>Ort&lt;/em> gibt es unterschiedliche Ansetzungen für einen &lt;em>Ort in Bayern&lt;/em>.
Die Spalte &lt;em>Name&lt;/em> enthält unterschiedliche Schreibweisen für den Namen &lt;em>Hans Peter Meier&lt;/em>.
Und die Spalte &lt;em>Name 2&lt;/em> enthält &lt;em>Hans Peter &lt;strong>Meier&lt;/strong>&lt;/em> und &lt;em>Hans Peter &lt;strong>Müller&lt;/strong>&lt;/em> jeweils mit und ohne Zweitnamen.&lt;/p>
&lt;h2 id="keying-functions">Keying Functions&lt;/h2>
&lt;p>Eine von OpenRefine unterstützte Art des Clusterings sind so genannte &lt;em>key collision&lt;/em> Verfahren.&lt;/p>
&lt;p>Die Idee dahinter ist, dass ein Eingabetext nach einheitlichen Regeln umgewandelt wird.
Die Ergebnisse der Umwandlungen werden dann verglichen und die gleichen Ergebnisse zusammengefasst.&lt;/p>
&lt;p>Typische Beispiele für Umwandlungsregein sind:&lt;/p>
&lt;ul>
&lt;li>Wörter sortieren&lt;/li>
&lt;li>Sonderzeichen entfernen oder umwandeln&lt;/li>
&lt;li>Wörter auf Normalformen bringen&lt;/li>
&lt;li>Synonyme auflösen&lt;/li>
&lt;li>n-Gramme erstellen&lt;/li>
&lt;li>&amp;hellip;&lt;/li>
&lt;/ul>
&lt;p>Einige dieser Methoden sind in OpenRefine implementiert.
Dennoch sind sie häufig nicht ausreichend für spezifischere Probleme.
Seit OpenRefine 3.9.3 ist es möglich eigene Clustering Algorithmen in den von OpenRefine unterstützten Programmiersprachen zu hinterlegen.&lt;/p>
&lt;p>Im Folgenden stellen werden zwei Algorithmen für das &lt;em>Keying&lt;/em> vorstellt.&lt;/p>
&lt;h3 id="ortfingerprint">OrtFingerprint&lt;/h3>
&lt;p>Das Problem bei den Werten in der Spalte &lt;em>Ort&lt;/em> ist, dass wir unterschiedliche Sonderzeichen, als auch &amp;ldquo;überflüssige&amp;rdquo; Wörter haben, die eine Zusammenführung via Clustering erschweren.&lt;/p>
&lt;p>Der Screencast in Abbildung 1 zeigt, wie im &lt;em>Clustering Dialog&lt;/em> unter &lt;em>Manage clustering functions&lt;/em> eigene Clustering Funktionen hinterlegt werden.
In dem Beispiel ist das ein GREL-Ausdruck, der die &lt;em>Fingerprint&lt;/em> Methode von OpenRefine mit unserem domänenspezifischem Wissen kombiniert.&lt;/p>
&lt;figure id="figure-screencast-zum-anlegen-und-anwenden-eigener-clustering-funktionen">
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="Screencast zum Anlegen und Anwenden eigener Clustering Funktionen."
src="https://fdmlab.landesarchiv-bw.de/workshop/openrefine-fortgeschrittene/19-erweitertes-clustering/screencast-openrefine-openrefine-custom-clustering-keying.gif"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;figcaption data-pre="Abbildung&amp;nbsp;" data-post=":&amp;nbsp;" class="numbered">
Screencast zum Anlegen und Anwenden eigener Clustering Funktionen.
&lt;/figcaption>&lt;/figure>
&lt;p>In dem nachfolgenden GREL-Ausdruck werden vor der Anwendung der &lt;em>Fingerprint&lt;/em> Methode von Openrefine die folgenden Schritte durchgeführt:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Sonderzeichen&lt;/strong> entfernen, die die &lt;em>Fingerprint&lt;/em> Methode nicht berücksichtigt (&lt;code>&amp;quot;|&amp;quot;&lt;/code>).&lt;/li>
&lt;li>&lt;strong>Stoppworte&lt;/strong> entfernen (&lt;code>&amp;quot;Ort&amp;quot;&lt;/code>).&lt;/li>
&lt;li>&lt;strong>Klein geschriebene Wörter&lt;/strong> entfernen (&lt;code>/\b[a-z]+\b/&lt;/code>).&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">fingerprint&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">value&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">replace&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;|&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">replace&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Ort&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">replace&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sr">/\b[a-z]+\b/&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Das Ergebnis ist eine spezifische Fingerprintmethode, die wie in Abbildung 1 gezeigt, unterschiedliche Ansetzungen bei Orten berücksichtigen kann.&lt;/p>
&lt;h3 id="colognefingerprint">CologneFingerprint&lt;/h3>
&lt;p>Die in OpenRefine implementierten Clustering-Methoden lassen sich nachnutzen und kombinieren.
Der folgende GREL-Ausdruck wendet zuerst das &lt;em>Fingerprinting&lt;/em> und dann die Normalisierung bzgl. der deutschen Aussprache mit &lt;em>Cologne Phonetic&lt;/em> an.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">fingerprint&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">value&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">phonetic&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;cologne-phonetic&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Auf diese Weise können die Werte in der Spalte &lt;em>Name&lt;/em> wie in Abbildung 2 gezeigt (größtenteils) zusammengeführt werden.&lt;/p>
&lt;figure id="figure-bildschirmfoto-des-clustering-dialogs-mit-kombiniertem-fingerprinting-und-cologne-phonetic-algorithmus">
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="Bildschirmfoto des Clustering Dialogs mit kombiniertem *Fingerprinting* und *Cologne Phonetic* Algorithmus." srcset="
/workshop/openrefine-fortgeschrittene/19-erweitertes-clustering/_hu557c93bd5851f710a57d61642747f8c7_39907_30051010093b8fbe6868182c1ab9915f.webp 400w,
/workshop/openrefine-fortgeschrittene/19-erweitertes-clustering/_hu557c93bd5851f710a57d61642747f8c7_39907_cb76e61c1d2e762b3ba3f689826bbfc2.webp 760w,
/workshop/openrefine-fortgeschrittene/19-erweitertes-clustering/_hu557c93bd5851f710a57d61642747f8c7_39907_1b7d0e091f8e7a9de9fc63c9405bb16a.webp 1200w"
src="https://fdmlab.landesarchiv-bw.de/workshop/openrefine-fortgeschrittene/19-erweitertes-clustering/_hu557c93bd5851f710a57d61642747f8c7_39907_30051010093b8fbe6868182c1ab9915f.webp"
width="760"
height="510"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;figcaption data-pre="Abbildung&amp;nbsp;" data-post=":&amp;nbsp;" class="numbered">
Bildschirmfoto des Clustering Dialogs mit kombiniertem &lt;em>Fingerprinting&lt;/em> und &lt;em>Cologne Phonetic&lt;/em> Algorithmus.
&lt;/figcaption>&lt;/figure>
&lt;h2 id="distance-functions">Distance Functions&lt;/h2>
&lt;p>Bei den Distanzfunktionen werden die Texte nicht umgewandelt, sondern mit unterschiedlichen Metriken berechnet, wie &amp;ldquo;weit&amp;rdquo; sie voneinander entfernt sind. Dafür erhält man Zugriff auf die Variablen &lt;code>value1&lt;/code> und &lt;code>value2&lt;/code> und gibt eine Zahl zurück, die die Distanz zwischen den beiden Werten repräsentiert. Je größer die Zahl, desto &amp;ldquo;unähnlicher&amp;rdquo; sind die Texte.&lt;/p>
&lt;p>Manche Algorithmen betrachten auch positiv die Ähnlichkeit (&lt;em>Similarity&lt;/em>) von Texten.
Dabei sind die beiden Metriken einfach umzurechnen: $distance = 1 - similarity$.&lt;/p>
&lt;p>Manche Algorithmen betrachten die Ähnlichkeit bzw. die Distanz auch nicht als Wert zwischen 0 und 1.0, sondern zwischen 0 und 100.&lt;/p>
&lt;h3 id="fingerprintdistance">FingerprintDistance&lt;/h3>
&lt;p>Ein typisches Problem ist, dass man sowohl unterschiedliche Ansetzungen, als auch leichte Abweichungen hat.
Hier lässt sich zum Beispiel das &lt;em>Fingerprinting&lt;/em> in OpenRefine mit der Implementierung der &lt;a href="https://de.wikipedia.org/wiki/Levenshtein-Distanz" target="_blank" rel="noopener">Levenshtein-Distanz&lt;/a> kombinieren.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">levenshteinDistance&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">fingerprint&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">value1&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="nx">fingerprint&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">value2&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>In Abbildung 3 sind die Ergebnisse der kombinierten Algorithmen auf der Spalte &lt;em>Name&lt;/em> gezeigt.&lt;/p>
&lt;figure id="figure-bildschirmfoto-des-clustering-dialogs-mit-kombiniertem-fingerprinting-und-levenshtein-algorithmus">
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="Bildschirmfoto des Clustering Dialogs mit kombiniertem *Fingerprinting* und Levenshtein-Algorithmus." srcset="
/workshop/openrefine-fortgeschrittene/19-erweitertes-clustering/_hu54ae07f60dcf3d447427ef35ad01c799_55161_70dfc912f5aaa293e8c8480382c7f85d.webp 400w,
/workshop/openrefine-fortgeschrittene/19-erweitertes-clustering/_hu54ae07f60dcf3d447427ef35ad01c799_55161_c7cda403648e0733ff558be0ed940ce5.webp 760w,
/workshop/openrefine-fortgeschrittene/19-erweitertes-clustering/_hu54ae07f60dcf3d447427ef35ad01c799_55161_787c91335cb08a2bd3558ca70ef4e0fd.webp 1200w"
src="https://fdmlab.landesarchiv-bw.de/workshop/openrefine-fortgeschrittene/19-erweitertes-clustering/_hu54ae07f60dcf3d447427ef35ad01c799_55161_70dfc912f5aaa293e8c8480382c7f85d.webp"
width="760"
height="513"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;figcaption data-pre="Abbildung&amp;nbsp;" data-post=":&amp;nbsp;" class="numbered">
Bildschirmfoto des Clustering Dialogs mit kombiniertem &lt;em>Fingerprinting&lt;/em> und Levenshtein-Algorithmus.
&lt;/figcaption>&lt;/figure>
&lt;h3 id="absolutedistance">AbsoluteDistance&lt;/h3>
&lt;p>In der Spalte &lt;em>Name 2&lt;/em> ist das Problem mit Extrabegriffen wie Zweitnamen etwas deutlicher gezeigt.
Hier könnte man Methoden anwenden, die Distanzen auf Wortebene berechnen.&lt;/p>
&lt;p>Im Folgenden Code Beispiel ist ein &lt;em>Jython&lt;/em>-Algorithmus, der die Datenstruktur &lt;a href="https://docs.python.org/2.7/library/collections.html#collections.Counter" target="_blank" rel="noopener">Counter&lt;/a> verwendet um eine Art &lt;a href="https://en.wikipedia.org/wiki/Bag-of-words_model" target="_blank" rel="noopener">Bag-of-words Model&lt;/a> zu erstellen.&lt;/p>
&lt;p>Die Distanz ist hier, die Anzahl an &lt;strong>unterschiedlichen Worten&lt;/strong> zwischen den beiden Texten.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">re&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">collections&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">Counter&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">splitter&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">re&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">compile&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">r&lt;/span>&lt;span class="s2">&amp;#34;\W+&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">words&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Counter&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">splitter&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">split&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">value1&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">strip&lt;/span>&lt;span class="p">()))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">words&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">subtract&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Counter&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">splitter&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">split&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">value2&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">strip&lt;/span>&lt;span class="p">())))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">dist&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">sum&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">abs&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">count&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="n">count&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">words&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">values&lt;/span>&lt;span class="p">())&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">return&lt;/span> &lt;span class="n">dist&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Wie in Abbildung 4 gezeigt, hilft diese Methode bei dem Problem von einzelnen Extrabegriffen, die den auf Buchstabenebene arbeitenden Levenshtein-Algorithmus &amp;ldquo;überfordern&amp;rdquo;.&lt;/p>
&lt;figure id="figure-bildschirmfoto-des-clustering-dialogs-mit-absolutedistance-funktion">
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="Bildschirmfoto des Clustering Dialogs mit *AbsoluteDistance* Funktion." srcset="
/workshop/openrefine-fortgeschrittene/19-erweitertes-clustering/_huaa044c427fe71821db5b58c1a0606c2a_54454_a45af42208a0da073390ddd309881e05.webp 400w,
/workshop/openrefine-fortgeschrittene/19-erweitertes-clustering/_huaa044c427fe71821db5b58c1a0606c2a_54454_ebe42d56ba0b5db2dedfdb842e06aeb7.webp 760w,
/workshop/openrefine-fortgeschrittene/19-erweitertes-clustering/_huaa044c427fe71821db5b58c1a0606c2a_54454_122a702caeef589ceeeb044ffd77e0ad.webp 1200w"
src="https://fdmlab.landesarchiv-bw.de/workshop/openrefine-fortgeschrittene/19-erweitertes-clustering/_huaa044c427fe71821db5b58c1a0606c2a_54454_a45af42208a0da073390ddd309881e05.webp"
width="760"
height="513"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;figcaption data-pre="Abbildung&amp;nbsp;" data-post=":&amp;nbsp;" class="numbered">
Bildschirmfoto des Clustering Dialogs mit &lt;em>AbsoluteDistance&lt;/em> Funktion.
&lt;/figcaption>&lt;/figure>
&lt;h3 id="cosinedistance">CosineDistance&lt;/h3>
&lt;p>Eine wichtige Distanzmetrik zur Berechnung der Ähnlichkeit (oder in dem Fall der Distanz) zwischen Vektoren ist die &lt;a href="https://de.wikipedia.org/wiki/Kosinus-%C3%84hnlichkeit" target="_blank" rel="noopener">Kosinus-Ähnlichkeit&lt;/a>.
Dies ist exemplarisch in dem folgenden &lt;em>Jython&lt;/em>-Code umgesetzt.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">re&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">collections&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">Counter&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">splitter&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">re&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">compile&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">r&lt;/span>&lt;span class="s2">&amp;#34;\W+&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">a&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Counter&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">splitter&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">split&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">value1&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">strip&lt;/span>&lt;span class="p">()))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">b&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Counter&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">splitter&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">split&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">value2&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">strip&lt;/span>&lt;span class="p">()))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">words&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">list&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">set&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="nb">set&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">b&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">a_vec&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">word&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="n">word&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">words&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">b_vec&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="n">b&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">word&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="n">word&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">words&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">v_len&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">lambda&lt;/span> &lt;span class="n">v&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">sum&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">x&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">x&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="n">x&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">v&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">**&lt;/span> &lt;span class="mf">0.5&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">v_dot&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">lambda&lt;/span> &lt;span class="n">v1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">v2&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">sum&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">x&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">y&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="n">x&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">y&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="nb">zip&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">v1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">v2&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">dist&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="n">v_dot&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a_vec&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">b_vec&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">v_len&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a_vec&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">v_len&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">b_vec&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">return&lt;/span> &lt;span class="n">dist&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Ähnlich wie bei der &lt;em>AbsoluteDistance&lt;/em> zählen wir, wie oft ein Wort im Text vorkommt.
Diesmal berücksichtigen wir (vereinfacht ausgedrückt) zusätzlich die Länge der Texte.
Dadurch ist der Rückgabewert eine Zahl zwischen 0 und 1.
Wie in Abbildung 5 gezeigt, erhält man mit dem in OpenRefine voreingestellten Standardradius von 1.0 dadurch alle möglichen Cluster.
Für sinnvolle Ergebnisse sollte man den Radius bei Verwendung der Kosinus-Ähnlichkeit also auf Werte zwischen 0.1 und 0.3 reduzieren.&lt;/p>
&lt;figure id="figure-bildschirmfoto-des-clustering-dialogs-mit-cosinedistance-funktion">
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="Bildschirmfoto des Clustering Dialogs mit *CosineDistance* Funktion." srcset="
/workshop/openrefine-fortgeschrittene/19-erweitertes-clustering/_hu8fcf2c0e37b6552bcd65b3342dd4aa4a_57708_be5ee4258255c5ce0160acae8c3cb245.webp 400w,
/workshop/openrefine-fortgeschrittene/19-erweitertes-clustering/_hu8fcf2c0e37b6552bcd65b3342dd4aa4a_57708_09d64a7bf8aa4842d54fcfb9a6560f78.webp 760w,
/workshop/openrefine-fortgeschrittene/19-erweitertes-clustering/_hu8fcf2c0e37b6552bcd65b3342dd4aa4a_57708_e59c1b10f2c1615cb85a7cee3d60ce7c.webp 1200w"
src="https://fdmlab.landesarchiv-bw.de/workshop/openrefine-fortgeschrittene/19-erweitertes-clustering/_hu8fcf2c0e37b6552bcd65b3342dd4aa4a_57708_be5ee4258255c5ce0160acae8c3cb245.webp"
width="760"
height="513"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;figcaption data-pre="Abbildung&amp;nbsp;" data-post=":&amp;nbsp;" class="numbered">
Bildschirmfoto des Clustering Dialogs mit &lt;em>CosineDistance&lt;/em> Funktion.
&lt;/figcaption>&lt;/figure>
&lt;h2 id="weitere-ideen">Weitere Ideen&lt;/h2>
&lt;p>Mit &lt;em>Jython&lt;/em> lassen sich auch Skripte auf dem eigenen Rechner ausführen oder Web-APIs ansprechen.
Damit kann man komplexere und effizientere Algorithmen implementieren und/oder auf andere Python Pakete zurückgreifen.&lt;/p>
&lt;p>Hier ist eine kleine Sammlung von weiteren Ideen:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Keying&lt;/strong>
&lt;ul>
&lt;li>Liste mit Synonymen einlesen und Synonym-Cluster-ID zurückgeben.&lt;/li>
&lt;li>Lemma, Normalform oder Lexem berechnen z.B. mit &lt;a href="https://spacy.io/api/token#attributes" target="_blank" rel="noopener">spaCy&lt;/a>.&lt;/li>
&lt;li>&amp;hellip;&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>Distance&lt;/strong>
&lt;ul>
&lt;li>Texte z.B. mit &lt;a href="https://spacy.io/usage/linguistic-features#vectors-similarity" target="_blank" rel="noopener">spaCy&lt;/a> in Wortvektoren umwandeln und Abstand berechnen.&lt;/li>
&lt;li>Levenshtein-Distanz mit mehr Einstellmöglichkeiten via &lt;a href="https://github.com/rapidfuzz/RapidFuzz" target="_blank" rel="noopener">RapidFuzz&lt;/a>.&lt;/li>
&lt;li>&amp;hellip;&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h2 id="fazit">Fazit&lt;/h2>
&lt;p>Eigene Clustering-Funktionen hinterlegen zu können ermöglicht komplett andere Arbeitsabläufe und reduziert regelmäßige manuelle Nacharbeiten.&lt;/p>
&lt;p>In Kombination mit der Möglichkeit z.B. über &lt;em>Jython&lt;/em> Web-APIs anzusprechen, können zusätzlich Funktionalitäten von externen Werkzeugen für das Clustering genutzt werden.&lt;/p>
&lt;hr>
&lt;p>Im nächsten Teil beschäftigen wir uns Python mit OpenRefine zu verwenden.&lt;/p>
&lt;ul class="cta-group">
&lt;li>
&lt;a href="https://fdmlab.landesarchiv-bw.de/workshop/openrefine-fortgeschrittene/20-python-mit-openrefine" class="btn btn-primary px-3 py-3">20 Python mit OpenRefine&lt;/a>
&lt;/li>
&lt;/ul></description></item><item><title>Workshop - Arbeiten mit GREL</title><link>https://fdmlab.landesarchiv-bw.de/workshop/openrefine-fortgeschrittene/09-arbeiten-mit-grel/</link><pubDate>Sun, 01 May 2022 00:00:00 +0000</pubDate><guid>https://fdmlab.landesarchiv-bw.de/workshop/openrefine-fortgeschrittene/09-arbeiten-mit-grel/</guid><description>&lt;p>In diesem Tutorial beschäftigen wir uns mit der &amp;ldquo;General Refine Expression Language&amp;rdquo; (GREL).&lt;/p>
&lt;h2 id="einführung">Einführung&lt;/h2>
&lt;blockquote>
&lt;p>GREL in der &lt;a href="https://docs.openrefine.org/manual/expressions" target="_blank" rel="noopener">OpenRefine Dokumentation&lt;/a>.&lt;br>
GREL Rezepte im &lt;a href="https://github.com/OpenRefine/OpenRefine/wiki/Recipes" target="_blank" rel="noopener">OpenRefine Wiki&lt;/a>.&lt;/p>
&lt;/blockquote>
&lt;div class="alert alert-note">
Dieser Workshop wurde erstellt mit OpenRefine Version 3.5.0.&lt;br>
Dieser Workshop wurde zuletzt getestet mit OpenRefine Version &lt;strong>3.9.3&lt;/strong>.
&lt;/div>
&lt;p>Mit der &lt;em>General Refine Expression Language&lt;/em> (GREL) werden in OpenRefine die einzelnen Operationen auf den Daten ausgeführt.
Die meisten Funktionen und Dialoge in OpenRefine sind quasi nur ein Eingabeformular für GREL Ausdrücke,
die in den Facets angezeigten Daten basieren auf GREL Ausdrücken und in verschiedenen Dialogen können wir auch direkt GREL Ausdrücke verwenden, um zum Beispiel Daten zu transformieren.&lt;/p>
&lt;p>Die Syntax von GREL ist dabei an die Programmiersprache &lt;a href="https://de.wikipedia.org/wiki/JavaScript" target="_blank" rel="noopener">JavaScript&lt;/a> angelehnt, die unterliegende Technologie basiert jedoch auf der Programmiersprache &lt;a href="https://de.wikipedia.org/wiki/Java-Technologie" target="_blank" rel="noopener">Java&lt;/a>.
Dadurch entstehen Verständnisprobleme, wenn zum Beispiel reguläre Ausdrücke im Stil von JavaScript geschrieben werden,
dann jedoch in OpenRefine intern von Java ausgewertet werden und entsprechend die Funktionalität von regulären Ausdrücken in Java unterstützen.&lt;/p>
&lt;p>Die Eingabe von GREL Ausdrücken erfolgt (meistens) über Dialogfenster wie in Abbildung 1.
Hier sehen wir nicht nur eine Vorschau der Umwandlung, die wir gerade anstreben, sondern können auch vorherige oder gespeicherte (&lt;em>Starred&lt;/em>) GREL Ausdrücke laden.&lt;/p>
&lt;figure id="figure-bildschirmfoto-eines-dialog-fensters-zur-transformation-von-daten-in-openrefine">
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="Bildschirmfoto eines Dialog Fensters zur Transformation von Daten in OpenRefine." srcset="
/workshop/openrefine-fortgeschrittene/09-arbeiten-mit-grel/screenshot-openrefine-grel-dialog_hua71ff9a0142e0958db5b592ee2c14782_23356_aaddd7ddcf7efbe93ed3bb28ae4854b6.webp 400w,
/workshop/openrefine-fortgeschrittene/09-arbeiten-mit-grel/screenshot-openrefine-grel-dialog_hua71ff9a0142e0958db5b592ee2c14782_23356_fdcf9e0e2fff7ecc7edec1973a2a3769.webp 760w,
/workshop/openrefine-fortgeschrittene/09-arbeiten-mit-grel/screenshot-openrefine-grel-dialog_hua71ff9a0142e0958db5b592ee2c14782_23356_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://fdmlab.landesarchiv-bw.de/workshop/openrefine-fortgeschrittene/09-arbeiten-mit-grel/screenshot-openrefine-grel-dialog_hua71ff9a0142e0958db5b592ee2c14782_23356_aaddd7ddcf7efbe93ed3bb28ae4854b6.webp"
width="628"
height="647"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;figcaption data-pre="Abbildung&amp;nbsp;" data-post=":&amp;nbsp;" class="numbered">
Bildschirmfoto eines Dialog Fensters zur Transformation von Daten in OpenRefine.
&lt;/figcaption>&lt;/figure>
&lt;h3 id="werte-variablen-objekte">Werte, Variablen, Objekte&lt;/h3>
&lt;p>In Programmiersprachen gibt es verschiedene Konzepte um mit Daten zu arbeiten.&lt;/p>
&lt;p>Ein Konzept ist die Typisierung von Daten. So wird bei Werten zum Beispiel zwischen Text und Zahlen unterschieden.
Mit diesen Werten können dann verschiedene Operationen und Berechnungen durchgeführt werden.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="m">5&lt;/span> &amp;gt; &lt;span class="m">2&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;gt; &lt;span class="nb">true&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Diesen Werten können via Variablen auch Namen zugewiesen werden.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="nv">laenge&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="m">5&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">breite&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="m">2&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">laenge &amp;gt; breite
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;gt; &lt;span class="nb">true&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Oder es gibt spezielle Funktionen für Werte eines bestimmten Typs:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="nv">text&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;Lorem ipsum&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">length&lt;span class="o">(&lt;/span>text&lt;span class="o">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;gt; &lt;span class="m">11&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Werte und Funktionen lassen sich zu Objekten zusammenfassen.
Via Variable kann ein bestimmtes Objekt gespeichert und darauf zugegriffen werden.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">Rechteck&lt;span class="o">(&lt;/span>laenge, breite&lt;span class="o">)&lt;/span>:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">laenge&lt;/span> &lt;span class="o">=&lt;/span> leange
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">breite&lt;/span> &lt;span class="o">=&lt;/span> breite
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> flaeche&lt;span class="o">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> laenge * breite
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">rechteck1&lt;/span> &lt;span class="o">=&lt;/span> Rechteck&lt;span class="o">(&lt;/span>5, 2&lt;span class="o">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">rechteck2&lt;/span> &lt;span class="o">=&lt;/span> Rechteck&lt;span class="o">(&lt;/span>4, 2&lt;span class="o">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">rechteck1.flaeche &amp;gt; rechteck2.flaeche
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;gt; &lt;span class="nb">true&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>In GREL verwenden wir hauptsächlich schon existierende Variablen, Funktionen und Objekte.&lt;/p>
&lt;h3 id="datentypen">Datentypen&lt;/h3>
&lt;p>In der OpenRefine Oberfläche arbeiten wir mit vier Datentypen: &lt;em>boolean&lt;/em>, &lt;em>date&lt;/em>, &lt;em>number&lt;/em> und &lt;em>string&lt;/em>.
In GREL gibt es noch die Datentypen &lt;em>array&lt;/em>, &lt;em>object&lt;/em> und &lt;em>regex&lt;/em>.
Texte (&lt;em>string&lt;/em>) können zusätzlich in eine Repräsentation für &lt;em>HTML&lt;/em>, &lt;em>JSON&lt;/em> oder &lt;em>XML&lt;/em> geparsed werden.
Das bedeutet vereinfacht, dass die (Text-)Daten intern von OpenRefine in eine Repräsentation überführt werden, die zum Beispiel XML versteht und dafür passende Funktionen anbietet. Also zum Beispiel die Funktion über alle XML-Knoten mit einem bestimmten Namen zu iterieren.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Typ&lt;/th>
&lt;th>Kürzel&lt;/th>
&lt;th>GUI&lt;/th>
&lt;th>Beispiel(e)&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>array&lt;/td>
&lt;td>a&lt;/td>
&lt;td>&lt;/td>
&lt;td>&lt;code>[&amp;quot;a&amp;quot;, &amp;quot;b&amp;quot;, &amp;quot;c&amp;quot;]&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>boolean&lt;/td>
&lt;td>b&lt;/td>
&lt;td>Ja&lt;/td>
&lt;td>&lt;code>true&lt;/code>, &lt;code>false&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>date&lt;/td>
&lt;td>d&lt;/td>
&lt;td>Ja&lt;/td>
&lt;td>&lt;code>[date 2022-05-03T15:00:00Z]&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>number&lt;/td>
&lt;td>n&lt;/td>
&lt;td>Ja&lt;/td>
&lt;td>&lt;code>1&lt;/code>, &lt;code>3.1415&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>object&lt;/td>
&lt;td>o&lt;/td>
&lt;td>&lt;/td>
&lt;td>&lt;code>[object Cell]&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>regex&lt;/td>
&lt;td>p&lt;/td>
&lt;td>&lt;/td>
&lt;td>&lt;code>/\d{4}/&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>string&lt;/td>
&lt;td>s&lt;/td>
&lt;td>Ja&lt;/td>
&lt;td>&lt;code>Lorem ipsum...&lt;/code>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="variablen">Variablen&lt;/h3>
&lt;p>Beim Arbeiten mit GREL stehen uns zusätzlich einige &lt;a href="https://docs.openrefine.org/manual/expressions#variables" target="_blank" rel="noopener">Umgebungsvariablen&lt;/a> zur Verfügung.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Variable&lt;/th>
&lt;th>Alternative&lt;/th>
&lt;th>Beschreibung&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>cell&lt;/td>
&lt;td>row.cells[columnName]&lt;/td>
&lt;td>Aktuelle Zelle der ausgewählten Spalte.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>cell.recon&lt;/td>
&lt;td>&lt;/td>
&lt;td>Reconciliation Daten für die Zelle.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>cell.recon. &amp;hellip;&lt;/td>
&lt;td>&lt;/td>
&lt;td>&lt;a href="https://docs.openrefine.org/manual/expressions#reconciliation" target="_blank" rel="noopener">Dokumentation zu Recon&lt;/a>.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>cell.value&lt;/td>
&lt;td>value&lt;/td>
&lt;td>Wert der Zelle.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>cell. &amp;hellip;.&lt;/td>
&lt;td>&lt;/td>
&lt;td>&lt;a href="https://docs.openrefine.org/manual/expressions#cell" target="_blank" rel="noopener">Dokumentation zu Cell&lt;/a>.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>columnName&lt;/td>
&lt;td>&lt;/td>
&lt;td>Name der ausgewählten Spalte.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>row&lt;/strong>&lt;/td>
&lt;td>&lt;/td>
&lt;td>Aktuelle Zeile.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>row.cells&lt;/td>
&lt;td>cells&lt;/td>
&lt;td>Zellen in der aktuellen Zeile.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>row.cells. &amp;hellip;&lt;/td>
&lt;td>&lt;/td>
&lt;td>&lt;a href="https://docs.openrefine.org/manual/expressions#cells" target="_blank" rel="noopener">Dokumentation zu Cells&lt;/a>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>row.index&lt;/td>
&lt;td>rowIndex&lt;/td>
&lt;td>Index der aktuellen Zeile.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>row.record&lt;/td>
&lt;td>&lt;/td>
&lt;td>Record der aktuellen Zeile.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>row.record. &amp;hellip;&lt;/td>
&lt;td>&lt;/td>
&lt;td>&lt;a href="https://docs.openrefine.org/manual/expressions#record" target="_blank" rel="noopener">Dokumentation zu Record&lt;/a>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>row. &amp;hellip;.&lt;/td>
&lt;td>&lt;/td>
&lt;td>&lt;a href="https://docs.openrefine.org/manual/expressions#row" target="_blank" rel="noopener">Dokumentation zu Row&lt;/a>.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>value1&lt;/td>
&lt;td>&lt;/td>
&lt;td>Neu seit OpenRefine 3.9 für &lt;a href="https://fdmlab.landesarchiv-bw.de/workshop/openrefine-fortgeschrittene/19-erweitertes-clustering/#distance-functions">Distanz basiertes Clustering&lt;/a>.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>value2&lt;/td>
&lt;td>&lt;/td>
&lt;td>Neu seit OpenRefine 3.9 für &lt;a href="https://fdmlab.landesarchiv-bw.de/workshop/openrefine-fortgeschrittene/19-erweitertes-clustering/#distance-functions">Distanz basiertes Clustering&lt;/a>.&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Die Variablen beinhalten teilweise einfache Werte (&lt;code>value&lt;/code>, &lt;code>rowIndex&lt;/code>) wie einen Text oder eine Zahl.
Teilweise verweisen sie auf komplexe Objekte, worin die Werte gesammelt oder verschachtelt gespeichert sind (&lt;code>row&lt;/code>).
Manche Variablen stellen auch Abkürzungen dar. So kann auf den Wert der aktuellen Zeile in der ausgewählten Spalte direkt mit &lt;code>value&lt;/code> zugegriffen werden, anstatt dies via &lt;code>row.cells[columnName].value&lt;/code> machen zu müssen.&lt;/p>
&lt;p>Hier eine Übersicht der Objekte und Variablen.&lt;/p>
&lt;div class="mermaid">---
title: Datenstruktur
config:
look: handDrawn
theme: neutral
---
flowchart LR
columnName
row.index[index]
row.cells[cells]
row.columnNames[columnNames]
row.starred[starred]
row.flagged[flagged]
row.record[record]
row.cells.cell[cell...]
row.cells.cell.value[value]
row.cells.cell.recon[recon]
row.cells.cell.recon.properties[...]
row.cells.cell.errorMessage[errorMessage]
row.cells.cell.errorMessage.value[value]
row.record.index[index]
row.record.cells[cells]
row.record.fromRowIndex[fromRowIndex]
row.record.toRowIndex[toRowIndex]
row.record.rowCount[rowCount]
row --> row.index &amp; row.cells &amp; row.columnNames &amp; row.starred &amp; row.flagged &amp; row.record
row.cells --> row.cells.cell
row.cells.cell --> row.cells.cell.value &amp; row.cells.cell.recon &amp; row.cells.cell.errorMessage
row.cells.cell.errorMessage --> row.cells.cell.errorMessage.value
row.cells.cell.recon --> row.cells.cell.recon.properties
row.record --> row.record.index &amp; row.record.cells &amp; row.record.fromRowIndex &amp; row.record.toRowIndex &amp; row.record.rowCount
cell -.-> row.cells.cell
cells -.-> row.cells
rowIndex -.-> row.index
value -.-> row.cells.cell.value
&lt;/div>
&lt;p>Fortsetzung für Reconciliation Daten einer Zelle:&lt;/p>
&lt;div class="mermaid">---
title: Datenstruktur - recon
config:
look: handDrawn
theme: neutral
---
flowchart LR
row.cells.cell.recon[row.cells.cell....recon]
row.cells.cell.recon.judgment[judgment]
row.cells.cell.recon.judgmentAction[judgmentAction]
row.cells.cell.recon.judgmentHistory[judgmentHistory]
row.cells.cell.recon.matched[matched]
row.cells.cell.recon.match[match]
row.cells.cell.recon.best[best]
row.cells.cell.recon.features[features]
row.cells.cell.recon.candidates[candidates]
row.cells.cell.recon.features.typeMatch[typeMatch]
row.cells.cell.recon.features.nameMatch[nameMatch]
row.cells.cell.recon.features.nameLevenshtein[nameLevenshtein]
row.cells.cell.recon.features.nameWordDistance[nameWordDistance]
match.id[id]
match.name[name]
match.type[type]
match.score[score]
row.cells.cell.recon --> row.cells.cell.recon.judgment &amp; row.cells.cell.recon.judgmentAction &amp; row.cells.cell.recon.judgmentHistory &amp; row.cells.cell.recon.matched &amp; row.cells.cell.recon.match &amp; row.cells.cell.recon.best &amp; row.cells.cell.recon.features &amp; row.cells.cell.recon.candidates
row.cells.cell.recon.match --> match.id &amp; match.name &amp; match.type
row.cells.cell.recon.best --> match.id &amp; match.name &amp; match.type &amp; match.score
row.cells.cell.recon.candidates --> match.id &amp; match.name &amp; match.type &amp; match.score
row.cells.cell.recon.features --> row.cells.cell.recon.features.typeMatch &amp; row.cells.cell.recon.features.nameMatch &amp; row.cells.cell.recon.features.nameLevenshtein &amp; row.cells.cell.recon.features.nameWordDistance
&lt;/div>
&lt;p>Auf Attribute eines Objekts kann punktnotiert &lt;code>row.cells&lt;/code> oder via Klammern &lt;code>row[&amp;quot;cells&amp;quot;]&lt;/code> zugegriffen werden.
Prinzipiell empfiehlt es sich aus Gründen der Lesbarkeit die Punktnotation zu verwenden und die Variante mit Klammern nur, wenn der Name in einer Variablen steht &lt;code>cells[columnName]&lt;/code> oder wir mit Spaltennamen arbeiten, die potentiell &amp;ldquo;Sonderzeichen&amp;rdquo; beinhalten können &lt;code>cells[&amp;quot;GND-ID&amp;quot;]&lt;/code>.&lt;/p>
&lt;div class="alert alert-note">
&lt;div>
Aktuell kann nicht auf &lt;strong>alle&lt;/strong> Werte einer Spalte zugegriffen werden.
Man kann aber auf alle Werte einer Spalte eines Records zugreifen: &lt;code>row.record.cells[columnName]&lt;/code>.
&lt;/div>
&lt;/div>
&lt;h3 id="kontrollstrukturen">Kontrollstrukturen&lt;/h3>
&lt;p>Etwas verwirrend ist das Definieren von eigenen Variablen in GREL.
Das geschieht mit einer &lt;a href="https://docs.openrefine.org/manual/grel#withe1-variable-v-e2" target="_blank" rel="noopener">with&lt;/a> Anweisung.
Im folgenden Beispiel setzen wir die Variable &lt;code>jahr&lt;/code> auf den Wert &lt;code>1988&lt;/code> und berechnen dann den Ausdruck &lt;code>jahr &amp;lt; 2000&lt;/code>.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">with&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1988&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">jahr&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">jahr&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="mi">2000&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="kc">true&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
&lt;div class="alert alert-note">
&lt;div>
Wir geben das erwartete Ergebnis der GREL Ausdrücke in den Beispielen zusätzlich via &lt;code>== ...&lt;/code> an, so dass das Ergebnis auch abgelesen werden kann, ohne den Ausdruck in OpenRefine oder im Kopf auswerten zu müssen.
&lt;/div>
&lt;/div>
&lt;p>Eine der essentiellen Kontrollstrukturen in der Informatik ist die Verzweigung.
In GREL wird dies mit &lt;a href="https://docs.openrefine.org/manual/grel#ife-etrue-efalse" target="_blank" rel="noopener">if&lt;/a> realisiert.
Im folgenden Beispiel setzen wir wie zuvor die Variable &lt;code>jahr&lt;/code> auf den Wert &lt;code>1988&lt;/code>, geben aber abhängig von dem Ergebnis einer Prüfung unterschiedliche Werte zurück. Die if-Anweisungen können auch ineinander verschachtelt werden.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">with&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1988&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">jahr&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">if&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="nx">jahr&lt;/span> &lt;span class="o">&amp;gt;=&lt;/span> &lt;span class="mi">1980&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">and&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">jahr&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="mi">1990&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="s2">&amp;#34;80er&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;Sonstiges&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s2">&amp;#34;80er&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Anstatt einer Verzweigung ist es manchmal auch nötig eine komplette Liste (technisch Array) von Werten zu filtern.
Dafür gibt es in GREL eine &lt;a href="https://docs.openrefine.org/manual/grel#filtere1-v-e-test" target="_blank" rel="noopener">filter()&lt;/a> Funktion.
Im folgenden Beispiel erstellen wir einen Array aus einem Text indem wir ihn mit &lt;a href="https://docs.openrefine.org/manual/grelfunctions#splits-s-or-p-sep-b-preservetokens-optional" target="_blank" rel="noopener">split()&lt;/a> am Trennzeichen &lt;code>,&lt;/code> trennen.
Anschließend wandeln die einzelnen Elemente mit &lt;a href="https://docs.openrefine.org/manual/grelfunctions#tonumbers" target="_blank" rel="noopener">toNumber()&lt;/a> in Zahlen um und filtern anschließend nur die geraden Zahlen via &lt;code>mod(number, 2) == 2&lt;/code>.
Um das Ergebnis in OpenRefine ausgeben zu können, wandeln wir die Daten im Array anschließend mit &lt;a href="https://docs.openrefine.org/manual/grelfunctions#joina-sep" target="_blank" rel="noopener">join()&lt;/a> wieder in einen Text um, da OpenRefine in der GUI keine Arrays anzeigen kann.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">filter&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;1,2,3,4,5,6,7,8,9,10&amp;#34;&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">split&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;,&amp;#34;&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="nx">v&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">mod&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">v&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">toNumber&lt;/span>&lt;span class="p">(),&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">join&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;,&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s2">&amp;#34;2,4,6,8,10&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Um alle Werte einer Liste (technisch Array) durchzugehen, gibt es in GREL die
&lt;a href="https://docs.openrefine.org/manual/grel#foreache1-v-e2" target="_blank" rel="noopener">forEach&lt;/a> Anweisung.
Im folgenden Beispiel erstellen wir mit &lt;code>split()&lt;/code> einen Array aus einem Text und fügen mit &lt;code>+&lt;/code> zu jedem Element die Endung &amp;ldquo;er&amp;rdquo; hinzu.
Anschließend fügen wir die Daten im Array mit &lt;code>join()&lt;/code> wieder zu einen Text zusammen.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">forEach&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;70,80,90&amp;#34;&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">split&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;,&amp;#34;&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="nx">v&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">v&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="s2">&amp;#34;er&amp;#34;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">join&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;,&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s2">&amp;#34;70er,80er,90er&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Zusätzlich gibt es noch weitere Helfer. Um den Typ eines Wertes zu prüfen gibt es verschiedene Varianten von &lt;a href="https://docs.openrefine.org/manual/grel#isblanke-isnonblanke-isnulle-isnotnulle-isnumerice-iserrore" target="_blank" rel="noopener">is&amp;hellip;&lt;/a>, um einen bestimmten Bereich aufzuzählen &lt;a href="https://docs.openrefine.org/manual/grel#forrangen-from-n-to-n-step-v-e" target="_blank" rel="noopener">forRange&lt;/a> oder um eine Liste (technisch Array) aufzuzählen &lt;a href="https://docs.openrefine.org/manual/grel#foreachindexe1-i-v-e2" target="_blank" rel="noopener">forEachIndex&lt;/a>.&lt;/p>
&lt;details class="spoiler " id="spoiler-6">
&lt;summary>Extra: forNonBlank&lt;/summary>
&lt;p>&lt;p>Da sie etwas verwirrend ist, geben wir der Funktion &lt;a href="https://docs.openrefine.org/manual/grel#fornonblanke-v-enonblank-eblank" target="_blank" rel="noopener">forNonBlank&lt;/a> hier noch einen separaten Absatz.
Angenommen wir wollen eine Liste (technisch Array) von Werten durchgehen und dabei nur Werte bearbeiten, die einen Inhalt haben.
Also zum Beispiel die &lt;code>[&amp;quot;1&amp;quot;, &amp;quot;2&amp;quot;, &amp;quot;&amp;quot;, &amp;quot;4&amp;quot;, &amp;quot;5&amp;quot;]&lt;/code> umwandeln in &lt;code>1,2,0,4,5&lt;/code>.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">forNonBlank&lt;/span>&lt;span class="p">([&lt;/span>&lt;span class="s2">&amp;#34;1&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;2&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;4&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;5&amp;#34;&lt;/span>&lt;span class="p">],&lt;/span> &lt;span class="nx">v&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">v&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">join&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;,&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s2">&amp;#34;1,2,,4,5&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Das klappt so nicht, da &lt;code>forNonBlank(v, ...)&lt;/code> eine andere Schreibweise für &lt;code>if(isNonBlank(v), ...)&lt;/code> ist.
Der korrekte Weg ist also einer der beiden folgenden Ausdrücke.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">forEach&lt;/span>&lt;span class="p">([&lt;/span>&lt;span class="s2">&amp;#34;1&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;2&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;4&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;5&amp;#34;&lt;/span>&lt;span class="p">],&lt;/span> &lt;span class="nx">v&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">if&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">isNonBlank&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">v&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="nx">v&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)).&lt;/span>&lt;span class="nx">join&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;,&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s2">&amp;#34;1,2,0,4,5&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">forEach&lt;/span>&lt;span class="p">([&lt;/span>&lt;span class="s2">&amp;#34;1&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;2&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;4&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;5&amp;#34;&lt;/span>&lt;span class="p">],&lt;/span> &lt;span class="nx">v&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">forNonBlank&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">v&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">v&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">v&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)).&lt;/span>&lt;span class="nx">join&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;,&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s2">&amp;#34;1,2,0,4,5&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/p>
&lt;/details>
&lt;h3 id="standardbibliothek">Standardbibliothek&lt;/h3>
&lt;p>Üblicherweise haben Programmiersprachen eine Standardbibliothek von Funktionen zum Arbeiten mit den zur Verfügung gestellten Datentypen.&lt;/p>
&lt;p>Diese ist bei GREL im Vergleich zu anderen Programmiersprachen zwar recht übersichtlich, kann hier aber trotzdem nicht vollständig besprochen werden.
Wir empfehlen daher, die &lt;a href="https://docs.openrefine.org/manual/grelfunctions" target="_blank" rel="noopener">Liste der in OpenRefine verfügbaren Funktionen&lt;/a> als Referenz verfügbar zu halten.&lt;/p>
&lt;p>Wir gehen hier auf ein paar Besonderheiten von GREL ein.&lt;/p>
&lt;h4 id="syntax-für-funktionsaufrufe">Syntax für Funktionsaufrufe&lt;/h4>
&lt;p>Eine Besonderheit in GREL ist, dass Funktionen zur besseren Lesbarkeit auch in Punktnotation aufgerufen werden können.
Also &lt;code>length(value)&lt;/code> auch geschrieben werden kann als &lt;code>value.length()&lt;/code>.&lt;/p>
&lt;h4 id="aliase">Aliase&lt;/h4>
&lt;p>Es gibt Funktionen, die unter mehreren Namen verfügbar sind.&lt;/p>
&lt;p>Beispielsweise sind &lt;a href="https://docs.openrefine.org/manual/grelfunctions#trims" target="_blank" rel="noopener">trim()&lt;/a> und &lt;a href="https://docs.openrefine.org/manual/grelfunctions#strips" target="_blank" rel="noopener">strip()&lt;/a> bis auf ihre Namen identisch und wurden auch &lt;a href="https://github.com/OpenRefine/OpenRefine/commit/f1923758e771f1c3ce385859599cf2c06d6d9d71" target="_blank" rel="noopener">gleichzeitig&lt;/a> in OpenRefine eingeführt.
Vermutlich sollte das Quereinsteigern von anderen Programmiersprachen den Einstieg vereinfachen.&lt;/p>
&lt;h2 id="vorbereitung-projekt-erstellen">Vorbereitung: Projekt erstellen&lt;/h2>
&lt;p>Wir laden die folgende Datei in ein OpenRefine Projekt.&lt;/p>
&lt;blockquote>
&lt;p>💾 Wir benötigen die folgende Datei (Rechtsklick und &amp;ldquo;Ziel speichern unter&amp;hellip;&amp;rdquo;):&lt;/p>
&lt;/blockquote>
&lt;a href="https://fdmlab.landesarchiv-bw.de/data/openrefine-workshop/09_kretschmann-kabinett-iii-grel.csv" target="_blank">
&lt;i class="fas fa-file-csv pr-1 fa-fw">&lt;/i>Kretschmann Kabinett III&lt;/a>
&lt;p>Wie in Abbildung 2 beinhaltet die Datei das Kabinett Kretschmann III mit den Berufen.&lt;/p>
&lt;figure id="figure-bildschirmfoto-des-projektes-direkt-nach-dem-laden">
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="Bildschirmfoto des Projektes direkt nach dem Laden." srcset="
/workshop/openrefine-fortgeschrittene/09-arbeiten-mit-grel/screenshot-openrefine-grel-projekt-start_hucaa2914f8e6f784b182b02a52782faf6_32946_35fd9e933e599a3cfaf21fbfaa66933a.webp 400w,
/workshop/openrefine-fortgeschrittene/09-arbeiten-mit-grel/screenshot-openrefine-grel-projekt-start_hucaa2914f8e6f784b182b02a52782faf6_32946_97d584b4dd42d2964deb633925819e2a.webp 760w,
/workshop/openrefine-fortgeschrittene/09-arbeiten-mit-grel/screenshot-openrefine-grel-projekt-start_hucaa2914f8e6f784b182b02a52782faf6_32946_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://fdmlab.landesarchiv-bw.de/workshop/openrefine-fortgeschrittene/09-arbeiten-mit-grel/screenshot-openrefine-grel-projekt-start_hucaa2914f8e6f784b182b02a52782faf6_32946_35fd9e933e599a3cfaf21fbfaa66933a.webp"
width="760"
height="210"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;figcaption data-pre="Abbildung&amp;nbsp;" data-post=":&amp;nbsp;" class="numbered">
Bildschirmfoto des Projektes direkt nach dem Laden.
&lt;/figcaption>&lt;/figure>
&lt;h2 id="aufgabe-1-grel-variablen-nutzen">Aufgabe 1: GREL Variablen nutzen&lt;/h2>
&lt;p>Das Geburtsdatum ist in drei Spalten aufgeteilt, wir wollen das Geburtsdatum jedoch in einer Spalte haben und dabei den Monat auch noch ausgeschrieben haben.
Das haben wir in &lt;a href="https://fdmlab.landesarchiv-bw.de/workshop/openrefine-einsteiger/03-transformieren/#aufgabe-5-spalten-zusammenf%C3%BChren">03 Transformieren: Aufgabe 5&lt;/a> so ähnlich schon einmal mit Funktionen in der Benutzeroberfläche gelöst.
Hier wollen wir GREL Funktionen verwenden, um die Bearbeitung in einem Schritt durchzuführen.&lt;/p>
&lt;p>Dafür verwenden wir
&amp;ldquo;Geburtsjahr&amp;quot;
&lt;i class="far fa-caret-square-down pr-1 fa-fw">&lt;/i>&amp;quot;Edit column&amp;quot;
&lt;i class="fas fa-caret-right pr-1 fa-fw">&lt;/i>&amp;quot;Add column based on this column&amp;rdquo;
und entwickeln schrittweise den in Abbildung 3 gezeigten GREL Ausdruck.&lt;/p>
&lt;figure id="figure-bildschirmfoto-des-dialogs-zur-transformation">
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="Bildschirmfoto des Dialogs zur Transformation." srcset="
/workshop/openrefine-fortgeschrittene/09-arbeiten-mit-grel/screenshot-openrefine-grel-projekt-geburtsdatum_hu167ffd8fd3774eb2db5afd8a5930610c_27972_9d760af1f939e4e979b9eb59914f2681.webp 400w,
/workshop/openrefine-fortgeschrittene/09-arbeiten-mit-grel/screenshot-openrefine-grel-projekt-geburtsdatum_hu167ffd8fd3774eb2db5afd8a5930610c_27972_22b9fe69ce55c9872391a1afb88bdc25.webp 760w,
/workshop/openrefine-fortgeschrittene/09-arbeiten-mit-grel/screenshot-openrefine-grel-projekt-geburtsdatum_hu167ffd8fd3774eb2db5afd8a5930610c_27972_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://fdmlab.landesarchiv-bw.de/workshop/openrefine-fortgeschrittene/09-arbeiten-mit-grel/screenshot-openrefine-grel-projekt-geburtsdatum_hu167ffd8fd3774eb2db5afd8a5930610c_27972_9d760af1f939e4e979b9eb59914f2681.webp"
width="759"
height="596"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;figcaption data-pre="Abbildung&amp;nbsp;" data-post=":&amp;nbsp;" class="numbered">
Bildschirmfoto des Dialogs zur Transformation.
&lt;/figcaption>&lt;/figure>
&lt;h3 id="schritt-1-geburtsmonat-umwandeln">Schritt 1: Geburtsmonat umwandeln&lt;/h3>
&lt;p>Um die Spalte &amp;ldquo;Geburtsmonat&amp;rdquo; in einen Namen umzuwandeln verwenden wir den Zugriff über &lt;code>row.cells[&amp;quot;Geburtsjahr&amp;quot;]&lt;/code> und die Funktionen &lt;a href="https://docs.openrefine.org/manual/grelfunctions#todateo-b-monthfirst-s-format1-s-format2-" target="_blank" rel="noopener">toDate()&lt;/a> und &lt;a href="https://docs.openrefine.org/manual/grelfunctions#tostringo-string-format-optional" target="_blank" rel="noopener">toString()&lt;/a>.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">row&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">cells&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;Geburtsmonat&amp;#34;&lt;/span>&lt;span class="p">].&lt;/span>&lt;span class="nx">value&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">toDate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;M&amp;#34;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">toString&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;MMMM&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="schritt-2-spalten-zusammenfügen">Schritt 2: Spalten zusammenfügen&lt;/h3>
&lt;p>Um die Spalten zusammenzufügen verwenden wir den Operator &lt;code>+&lt;/code>.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">row&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">cells&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;Geburtstag&amp;#34;&lt;/span>&lt;span class="p">].&lt;/span>&lt;span class="nx">value&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="s2">&amp;#34;. &amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">+&lt;/span> &lt;span class="nx">row&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">cells&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;Geburtsmonat&amp;#34;&lt;/span>&lt;span class="p">].&lt;/span>&lt;span class="nx">value&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="s2">&amp;#34; &amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">+&lt;/span> &lt;span class="nx">row&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">cells&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;Geburtsjahr&amp;#34;&lt;/span>&lt;span class="p">].&lt;/span>&lt;span class="nx">value&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="schritt-3-verzweigung-einbauen">Schritt 3: Verzweigung einbauen&lt;/h3>
&lt;p>Für manche Zeilen wird entweder eine Fehlermeldung oder &lt;code>null&lt;/code> angezeigt.
Das sind die Zeilen, wo kein Geburtstag und kein Geburtsmonat vorhanden sind.&lt;/p>
&lt;p>Diese behandeln wir separat mit einer Verzweigung und passen dabei den Typ der Inhalte der Spalte &amp;ldquo;Geburtsjahr&amp;rdquo; an.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">isBlank&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">row&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">cells&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;Geburtstag&amp;#34;&lt;/span>&lt;span class="p">]),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">row&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">cells&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;Geburtsjahr&amp;#34;&lt;/span>&lt;span class="p">].&lt;/span>&lt;span class="nx">value&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">toString&lt;/span>&lt;span class="p">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="aufgabe-1">Aufgabe 1&lt;/h3>
&lt;p>Fügen Sie die drei obigen GREL Ausdrücke zu einem GREL Ausdruck zusammen.&lt;/p>
&lt;details class="spoiler " id="spoiler-13">
&lt;summary>&lt;strong>Lösung:&lt;/strong>&lt;/summary>
&lt;p>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">isBlank&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">row&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">cells&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;Geburtstag&amp;#34;&lt;/span>&lt;span class="p">]),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">row&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">cells&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;Geburtsjahr&amp;#34;&lt;/span>&lt;span class="p">].&lt;/span>&lt;span class="nx">value&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">toString&lt;/span>&lt;span class="p">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">row&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">cells&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;Geburtstag&amp;#34;&lt;/span>&lt;span class="p">].&lt;/span>&lt;span class="nx">value&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="s2">&amp;#34;. &amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">+&lt;/span> &lt;span class="nx">row&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">cells&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;Geburtsmonat&amp;#34;&lt;/span>&lt;span class="p">].&lt;/span>&lt;span class="nx">value&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">toDate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;M&amp;#34;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">toString&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;MMMM&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="s2">&amp;#34; &amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">+&lt;/span> &lt;span class="nx">row&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">cells&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;Geburtsjahr&amp;#34;&lt;/span>&lt;span class="p">].&lt;/span>&lt;span class="nx">value&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/p>
&lt;/details>
&lt;h2 id="aufgabe-2-werte-aufräumen-und-sortieren">Aufgabe 2: Werte aufräumen und sortieren&lt;/h2>
&lt;p>Die Werte in der Spalte &amp;ldquo;Beruf oder Beschäftigung&amp;rdquo; sind nicht sortiert, haben teilweise Duplikate und teilweise numerische Referenzen hinter den Berufsbezeichnungen.
Diese Spalte wollen wir daher in einem Schritt mit einem GREL Ausdruck aufräumen und dabei die numerischen Referenzen entfernen, Duplikate entfernen und die Werte alphabetisch sortieren.&lt;/p>
&lt;p>Dafür verwenden wir den Dialog
&amp;ldquo;Beruf oder Beschäftigung&amp;quot;
&lt;i class="far fa-caret-square-down pr-1 fa-fw">&lt;/i>&amp;quot;Edit cells&amp;quot;
&lt;i class="fas fa-caret-right pr-1 fa-fw">&lt;/i>&amp;quot;Transform&amp;rdquo;
und erarbeiten uns einen GREL Ausdruck.&lt;/p>
&lt;h3 id="schritt-1-text-in-liste-umwandeln">Schritt 1: Text in Liste umwandeln&lt;/h3>
&lt;p>Um die einzelnen Werte im Text in eine Liste (technisch Array) umzuwandeln, verwenden wir &lt;a href="https://openrefine.org/docs/manual/grelfunctions#splits-s-or-p-sep-b-preservetokens-optional" target="_blank" rel="noopener">split()&lt;/a> mit &lt;code>,&lt;/code> als Trennzeichen.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;Politiker, Abgeordneter, Lehrer, Regierungschef, Politiker (5), Lehrer (3)&amp;#34;&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">split&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;, &amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="schritt-2-numerische-referenzen-entfernen">Schritt 2: Numerische Referenzen entfernen&lt;/h3>
&lt;p>Um die numerischen Referenzen zu entfernen verwenden wir &lt;a href="https://docs.openrefine.org/manual/grelfunctions#replaces-s-or-p-find-s-replace" target="_blank" rel="noopener">replace()&lt;/a> mit einem regulären Ausdruck und entfernen zusätzliche Leerzeichen.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;Politiker (5)&amp;#34;&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">replace&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sr">/\(\d+\)/&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">trim&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="schritt-3-funktion-auf-alle-elemente-anwenden">Schritt 3: Funktion auf alle Elemente anwenden&lt;/h3>
&lt;p>Um die Ersetzung der numerischen Referenzen auf alle Elemente in der Liste (technisch Array) anzuwenden nutzen wir &lt;a href="https://docs.openrefine.org/manual/grel#foreache1-v-e2" target="_blank" rel="noopener">forEach&lt;/a>.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">forEach&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;Politiker, Abgeordneter, Lehrer, Regierungschef, Politiker (5), Lehrer (3)&amp;#34;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">v&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="schritt-4-werte-vereinheitlichen-und-sortieren">Schritt 4: Werte vereinheitlichen und sortieren&lt;/h3>
&lt;p>Um die Werte zu vereinheitlichen und zu sortieren nutzen wir &lt;a href="https://docs.openrefine.org/manual/grelfunctions#uniquesa" target="_blank" rel="noopener">uniques()&lt;/a> und &lt;a href="https://docs.openrefine.org/manual/grelfunctions#sorta" target="_blank" rel="noopener">sort()&lt;/a>.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;Politiker, Abgeordneter, Lehrer, Regierungschef, Politiker, Lehrer&amp;#34;&lt;/span>&lt;span class="p">].&lt;/span>&lt;span class="nx">uniques&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="nx">sort&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="schritt-5-text-aus-liste-erstellen">Schritt 5: Text aus Liste erstellen&lt;/h3>
&lt;p>Um aus der Liste (technisch Array) wieder einen Text zu erzeugen, verwenden wir &lt;a href="https://docs.openrefine.org/manual/grelfunctions#joina-sep" target="_blank" rel="noopener">join()&lt;/a> mit &lt;code>,&lt;/code> als Trennzeichen.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;Abgeordneter&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;Lehrer&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;Politiker&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;Regierungschef&amp;#34;&lt;/span>&lt;span class="p">].&lt;/span>&lt;span class="nx">join&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;, &amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="aufgabe-3">Aufgabe 3&lt;/h3>
&lt;p>Fügen Sie die fünf obigen GREL Ausdrücke zu einem GREL Ausdruck zusammen.
Das Ergebnis sollte in etwa wie in Abbildung 4 aussehen.&lt;/p>
&lt;figure id="figure-bildschirmfoto-des-projektes-nach-der-bearbeitung">
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="Bildschirmfoto des Projektes nach der Bearbeitung." srcset="
/workshop/openrefine-fortgeschrittene/09-arbeiten-mit-grel/screenshot-openrefine-grel-projekt-ziel_hu31884fd8468e9c60e0fcc9ebd508d4bd_29493_429f4d7120b8c3c72d377983cae502af.webp 400w,
/workshop/openrefine-fortgeschrittene/09-arbeiten-mit-grel/screenshot-openrefine-grel-projekt-ziel_hu31884fd8468e9c60e0fcc9ebd508d4bd_29493_79ac9b03c7a5805ff8e688c808b06fef.webp 760w,
/workshop/openrefine-fortgeschrittene/09-arbeiten-mit-grel/screenshot-openrefine-grel-projekt-ziel_hu31884fd8468e9c60e0fcc9ebd508d4bd_29493_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://fdmlab.landesarchiv-bw.de/workshop/openrefine-fortgeschrittene/09-arbeiten-mit-grel/screenshot-openrefine-grel-projekt-ziel_hu31884fd8468e9c60e0fcc9ebd508d4bd_29493_429f4d7120b8c3c72d377983cae502af.webp"
width="732"
height="322"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;figcaption data-pre="Abbildung&amp;nbsp;" data-post=":&amp;nbsp;" class="numbered">
Bildschirmfoto des Projektes nach der Bearbeitung.
&lt;/figcaption>&lt;/figure>
&lt;details class="spoiler " id="spoiler-17">
&lt;summary>&lt;strong>Lösung:&lt;/strong>&lt;/summary>
&lt;p>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">forEach&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">value&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">split&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;, &amp;#34;&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">v&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">v&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">replace&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sr">/\(\d+\)/&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">trim&lt;/span>&lt;span class="p">())&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">uniques&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">sort&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">join&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;, &amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/p>
&lt;/details>
&lt;h2 id="fazit">Fazit&lt;/h2>
&lt;p>Viele Umwandlungen lassen sich in Einzelschritten über die graphische Oberfläche von OpenRefine ansteuern und umsetzen.
So kommt man auch ohne in GREL zu programmieren in OpenRefine recht weit.
Das Wissen um den Zugriff auf die Umgebungsvariablen und nützliche Funktionen wie &lt;code>find()&lt;/code>, &lt;code>replace()&lt;/code> oder Datumsumwandlungen ersetzt viele manuelle Schritte.&lt;/p>
&lt;p>Das explizite Programmieren mit Verzweigungen und Schleifen ergänzt Arbeitsschritte oder reduziert mehrere Einzelschritte in der graphischen Oberfläche zu einem Schritt. Beim &lt;a href="https://fdmlab.landesarchiv-bw.de/workshop/openrefine-fortgeschrittene/14-xml-importieren-und-exportieren/">Export von XML in OpenRefine (Templating)&lt;/a> sind die Kontrollstrukturen noch einmal relevant.&lt;/p>
&lt;hr>
&lt;p>Im nächsten Teil behandeln wir das Konzept von &amp;ldquo;Records&amp;rdquo; in OpenRefine.&lt;/p>
&lt;ul class="cta-group">
&lt;li>
&lt;a href="https://fdmlab.landesarchiv-bw.de/workshop/openrefine-fortgeschrittene/10-arbeiten-mit-records" class="btn btn-primary px-3 py-3">10 Arbeiten mit Records&lt;/a>
&lt;/li>
&lt;/ul></description></item><item><title>Workshop für Fortgeschrittene - Warum OpenRefine?</title><link>https://fdmlab.landesarchiv-bw.de/workshop/openrefine-fortgeschrittene/warum-openrefine/</link><pubDate>Sun, 01 May 2022 00:00:00 +0000</pubDate><guid>https://fdmlab.landesarchiv-bw.de/workshop/openrefine-fortgeschrittene/warum-openrefine/</guid><description>&lt;p>In diesem Workshop für Fortgeschrittene gehen wir tiefer in die spezialisierten Funktionalitäten von OpenRefine und beschäftigen uns mit angrenzende Themen.
Unter anderem betrachten wir reguläre Ausdrücke und programmieren mit der &lt;em>General Refine Expression Language&lt;/em> (GREL).&lt;/p>
&lt;div class="alert alert-note">
Dieser Workshop wurde erstellt mit OpenRefine Version 3.5.0.&lt;br>
Dieser Workshop wurde zuletzt getestet mit OpenRefine Version &lt;strong>3.9.3&lt;/strong>.
&lt;/div>
&lt;h2 id="inhalte-des-workshops">Inhalte des Workshops&lt;/h2>
&lt;ul>
&lt;li>Motivation: Warum OpenRefine?&lt;/li>
&lt;li>&lt;a href="https://fdmlab.landesarchiv-bw.de/workshop/openrefine-fortgeschrittene/08-regulaere-ausdruecke/">08 Reguläre Ausdrücke&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://fdmlab.landesarchiv-bw.de/workshop/openrefine-fortgeschrittene/09-arbeiten-mit-grel/">09 Arbeiten mit GREL&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://fdmlab.landesarchiv-bw.de/workshop/openrefine-fortgeschrittene/10-arbeiten-mit-records/">10 Arbeiten mit Records&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://fdmlab.landesarchiv-bw.de/workshop/openrefine-fortgeschrittene/11-transponieren/">11 Transponieren&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://fdmlab.landesarchiv-bw.de/workshop/openrefine-fortgeschrittene/12-daten-zwischen-projekten-abgleichen/">12 Daten zwischen Projekten abgleichen&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://fdmlab.landesarchiv-bw.de/workshop/openrefine-fortgeschrittene/13-die-welt-der-facets/">13 Die Welt der Facets&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://fdmlab.landesarchiv-bw.de/workshop/openrefine-fortgeschrittene/14-xml-importieren-und-exportieren/">14 XML importieren und exportieren&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://fdmlab.landesarchiv-bw.de/workshop/openrefine-fortgeschrittene/15-erweiterter-gnd-abgleich-mit-lobid/">15 Erweiterter GND Abgleich mit lobid&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://fdmlab.landesarchiv-bw.de/workshop/openrefine-fortgeschrittene/16-erweiterter-abgleich-mit-wikidata/">16 Erweiterter Abgleich mit Wikidata&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://fdmlab.landesarchiv-bw.de/workshop/openrefine-fortgeschrittene/17-erweiterter-abgleich-mit-getty/">17 Erweiterter Abgleich mit Getty&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://fdmlab.landesarchiv-bw.de/workshop/openrefine-fortgeschrittene/18-nachladen-von-geokoordinaten/">18 Nachladen von Geokoordinaten&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://fdmlab.landesarchiv-bw.de/workshop/openrefine-fortgeschrittene/19-erweitertes-clustering/">19 Erweitertes Clustering&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://fdmlab.landesarchiv-bw.de/workshop/openrefine-fortgeschrittene/20-python-mit-openrefine/">20 Python mit OpenRefine&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="warum-eigentlich-openrefine-für-fortgeschrittene">Warum eigentlich OpenRefine für Fortgeschrittene?&lt;/h2>
&lt;blockquote>
&lt;p>OpenRefine is Excel on steroids!&lt;/p>
&lt;/blockquote>
&lt;p>Oder um einen bildlichen Vergleich eines Workshop Teilnehmers wiederzuverwenden:&lt;/p>
&lt;blockquote>
&lt;p>Mit Excel hatten wir bisher einen Trabi.&lt;br>
Mit OpenRefine haben wir einen Ferrari bekommen.&lt;/p>
&lt;/blockquote>
&lt;p>Der Umgang mit einem solchen Werkzeug sollte jedoch geübt und vertieft werden.
Viele Funktionalitäten von OpenRefine sind über die graphische Oberfläche bedienbar.
Jedoch können einige Bearbeitungsschritte vereinfacht oder automatisiert werden.
Im &lt;a href="https://fdmlab.landesarchiv-bw.de/workshop/openrefine-einsteiger/warum-openrefine/">Workshop für Einsteiger&lt;/a> haben wir uns auf grundlegende Funktionen von OpenRefine fokussiert. Diesmal vertiefen wir einige Konzepte oder ergänzen Sie mit weiteren Technologien.&lt;/p>
&lt;p>Wir werden dabei unter anderem die Programmiersprache GREL kennen lernen.
Diese ist bei weitem nicht so mächtig, wie zum Beispiel Java oder Python.
Jedoch können wir damit schon einige Dinge automatisieren und vereinfachen.&lt;/p>
&lt;p>Außerdem werden wir uns mit regulären Ausdrücken beschäftigen.
Diese helfen uns Daten zu finden, die bestimmten Mustern entsprechen.&lt;/p>
&lt;hr>
&lt;p>Im ersten Teil machen wir einen Exkurs zum Arbeiten mit regulären Ausdrücken zur Erkennung und Extraktion von Mustern.&lt;/p>
&lt;ul class="cta-group">
&lt;li>
&lt;a href="https://fdmlab.landesarchiv-bw.de/workshop/openrefine-fortgeschrittene/08-regulaere-ausdruecke" target="_blank" rel="noopener" class="btn btn-primary px-3 py-3">08 Arbeiten mit regulären Ausdrücken&lt;/a>
&lt;/li>
&lt;/ul></description></item></channel></rss>