Einführung von Version 14.2 der Wolfram Language & Mathematica: Big Data trifft auf Berechnung & KI
Der Takt der Veröffentlichungen geht weiter…
Vor knapp sechs Monaten (genau vor 176 Tagen) haben wir Version 14.1 veröffentlicht. Heute freue ich mich, bekanntzugeben, dass wir Version 14.2 veröffentlichen – mit den neuesten Entwicklungen aus unserer F&E-Pipeline.
Das ist eine aufregende Zeit für unsere Technologie – sowohl in Bezug darauf, was wir jetzt umsetzen können, als auch darauf, wie unsere Technologie inzwischen weltweit eingesetzt wird. Besonders bemerkenswert ist die zunehmende Nutzung der Wolfram Language nicht nur durch Menschen, sondern auch durch KIs. Es ist sehr erfreulich zu sehen, dass sich all die Mühe, die wir über die Jahre in ein konsistentes Sprachdesign, die Implementierung und Dokumentation gesteckt haben, nun auszahlt: Die Wolfram Language wird zu einem einzigartig wertvollen Werkzeug für KIs – als Ergänzung ihrer eigenen Fähigkeiten.
Aber es gibt auch eine andere Perspektive auf KI. Mit unserem letzten Monat veröffentlichten Wolfram Notebook Assistant nutzen wir KI-Technologie (und vieles mehr), um eine Art konversationelle Schnittstelle zur Wolfram Language bereitzustellen. Wie ich bei der Veröffentlichung des Wolfram Notebook Assistant beschrieben habe, ist das etwas, das sowohl für Experten als auch für Anfänger äußerst nützlich ist. Letztendlich denke ich jedoch, dass die wichtigste Folge darin bestehen wird, den Übergang von jedem beliebigen Fachgebiet X zu „computational X“ zu beschleunigen – und dabei den gesamten Technologie-Turm zu nutzen, den wir rund um die Wolfram Language aufgebaut haben.
Also, was ist neu in Version 14.2? Unter der Haube gibt es Änderungen, die den Wolfram Notebook Assistant effizienter und schlanker machen. Aber es gibt auch viele sichtbare Erweiterungen und Verbesserungen an den für den Nutzer sichtbaren Teilen der Wolfram Language. Insgesamt gibt es 80 völlig neue Funktionen – sowie 177 Funktionen, die wesentlich aktualisiert wurden.
Es gibt Fortsetzungen langjähriger Forschungs- und Entwicklungsprojekte, wie zusätzliche Funktionen für Video und erweiterte Möglichkeiten im Umgang mit symbolischen Arrays. Außerdem gibt es völlig neue eingebaute Funktionalitätsbereiche, zum Beispiel Spieltheorie. Die bedeutendste Neuerung in Version 14.2 betrifft jedoch die Verarbeitung tabellarischer Daten – insbesondere großer tabellarischer Datenmengen. Es ist ein komplett neues Teilsystem für die Wolfram Language mit weitreichenden Auswirkungen im gesamten System. Wir arbeiten schon seit mehreren Jahren daran und freuen uns, es nun erstmals in Version 14.2 veröffentlichen zu können.
Apropos neue Funktionalitäten: Bereits vor mehr als sieben Jahren haben wir das Konzept des offenen Software-Designs eingeführt und unsere Software-Design-Meetings per Livestream übertragen. Seit der Veröffentlichung von Version 14.1 haben wir beispielsweise 43 Software-Design-Livestreams mit einer Gesamtdauer von 46 Stunden durchgeführt (zusätzlich habe ich in dieser Zeit noch 73 Stunden andere Livestreams gemacht). Einige der Funktionen, die jetzt in Version 14.2 enthalten sind, haben wir vor vielen Jahren begonnen zu entwickeln. Aber wir streamen schon so lange live, dass eigentlich alles, was jetzt in Version 14.2 ist, irgendwann einmal live und öffentlich in einem Livestream entworfen wurde. Software-Design ist harte Arbeit (das merkt man, wenn man die Livestreams anschaut). Aber es ist immer spannend zu sehen, wie die Früchte dieser Arbeit in dem System Gestalt annehmen, das wir über so lange Zeit hinweg kontinuierlich aufgebaut haben. Deshalb freue ich mich heute sehr, Version 14.2 veröffentlichen zu können und allen die Nutzung der Dinge zu ermöglichen, an denen wir so hart gearbeitet haben.
Notebook-Assistent: Chatten Sie in jedem Notebook
Letzten Monat haben wir den Wolfram Notebook Assistant veröffentlicht, um „Worte in Berechnungen zu verwandeln“ und sowohl Experten als auch Anfängern zu helfen, die Wolfram Language-Technologie breiter und tiefer zu nutzen. In Version 14.1 erfolgte die Hauptnutzung des Notebook Assistant über ein separates „Side-Chat“-Fenster. In Version 14.2 sind „Chat-Zellen“ jedoch zu einem Standard-Feature in jedem Notebook geworden, das allen mit einem Notebook Assistant-Abonnement zur Verfügung steht.
Tippen Sie einfach ein ‘ als erstes Zeichen in eine beliebige Zelle, und sie wird zu einer Chat-Zelle:
Jetzt können Sie anfangen, mit dem Notebook Assistant zu chatten:

Mit dem Side-Chat haben Sie einen „separaten Kanal“, um mit dem Notebook Assistant zu kommunizieren – der beispielsweise nicht zusammen mit Ihrem Notebook gespeichert wird. Mit Chat-Zellen hingegen wird Ihr Chat ein integraler Bestandteil des Notebooks.
Tatsächlich haben wir Chat Notebooks erstmals Mitte 2023 eingeführt – nur wenige Monate nach dem Erscheinen von ChatGPT. Chat-Notebooks definierten die Benutzeroberfläche, aber der eigentliche Inhalt der Chat-Zellen stammte damals ausschließlich von externen LLMs. Mit Version 14.2 sind Chat-Zellen nun nicht mehr auf separate Chat-Notebooks beschränkt, sondern in jedem beliebigen Notebook verfügbar. Standardmäßig nutzen sie den vollständigen Technologie-Stack des Notebook Assistant, der weit über ein reines LLM hinausgeht. Außerdem können Sie mit einem Abonnement für das Notebook Assistant + LLM Kit Chat-Zellen nahtlos verwenden – ganz ohne separates Konto bei externen LLM-Anbietern.
Die Chat-Zellen-Funktionalität in Version 14.2 übernimmt alle Features der Chat-Notebooks. Zum Beispiel erzeugt das Tippen von ~ in einer neuen Zelle einen Chat-Umbruch, mit dem Sie eine „neue Unterhaltung“ starten können. Und wenn Sie eine Chat-Zelle verwenden, kann diese alles in Ihrem Notebook bis zum letzten Chat-Umbruch sehen. (Übrigens: Wenn Sie den Notebook Assistant über den Side-Chat verwenden, kann dieser ebenfalls sehen, was Sie im aktuell „fokussierten“ Notebook ausgewählt haben.)
Standardmäßig „sprechen“ Chat-Zellen mit dem Notebook Assistant. Wenn Sie möchten, können Sie sie jedoch auch verwenden, um mit externen LLMs zu kommunizieren – genau wie in unserem ursprünglichen Chat-Notebook – und es gibt ein praktisches Menü, um das einzurichten. Natürlich steht Ihnen beim Einsatz eines externen LLMs nicht all die Technologie zur Verfügung, die jetzt im Notebook Assistant enthalten ist. Und sofern Sie nicht gerade LLM-Forschung betreiben, werden Sie es in der Regel als deutlich nützlicher und wertvoller empfinden, Chat-Zellen in ihrer Standardkonfiguration zu verwenden – also im Austausch mit dem Notebook Assistant.
Bringen Sie uns Ihre Gigabyte! Einführung in Tabular
Listen, Assoziationen, Datasets – das sind sehr flexible Möglichkeiten, strukturierte Datensammlungen in der Wolfram Language darzustellen. Aber jetzt, in Version 14.2, gibt es noch eine weitere: Tabular. Tabular bietet eine besonders schlanke und effiziente Methode zur Verarbeitung von Datentabellen in Zeilen- und Spaltenform. Und wenn wir „effizient“ sagen, meinen wir damit, dass damit routinemäßig Datenmengen im Gigabyte-Bereich – sowohl im Arbeitsspeicher als auch außerhalb – verarbeitet werden können.
Machen wir ein Beispiel. Beginnen wir damit, einige tabellarische Daten zu importieren:

Dies sind Daten über Bäume in New York City – 683.788 Stück, jeder mit 45 Eigenschaften (von denen einige fehlen können). Tabular führt eine Reihe neuer Konzepte ein. Eines davon ist die Behandlung von Tabellenspalten ähnlich wie Variablen. Hier nutzen wir das, um ein Histogramm der Werte in der Spalte „tree_dbh“ dieses Tabular-Objekts zu erstellen:

Man kann sich ein Tabular als eine optimierte Form einer Liste von Assoziationen vorstellen, bei der jede Zeile aus einer Assoziation besteht, deren Schlüssel die Spaltennamen sind. Funktionen wie Select funktionieren dann direkt auf einem Tabular:

Length gibt die Anzahl der Zeilen zurück:

CountsBy behandelt das Tabular wie eine Liste von Assoziationen, extrahiert den Wert, der mit dem Schlüssel „spc_latin“ („lateinische Art“) in jeder Assoziation verknüpft ist, und zählt, wie oft dieser Wert vorkommt ("spc_latin" steht hier für #"spc_latin"&):

Um die Namen der Spalten zu erhalten, können wir die neue Funktion ColumnKeys verwenden:

Wenn man ein Tabular wie eine Liste von Assoziationen betrachtet, kann man Teile daraus extrahieren – indem man zuerst eine Zeilenauswahl und dann eine Spaltenauswahl angibt:

Es gibt viele neue Operationen, die wir jetzt mit Tabular einführen konnten. Ein Beispiel ist AggregrateRows, das aus einem gegebenen Tabular ein neues Tabular erstellt, indem es Gruppen von Zeilen zusammenfasst – in diesem Fall solche mit dem gleichen Wert in „spc_latin“ – und dann eine Funktion auf diese Zeilengruppen anwendet, hier zum Beispiel den Mittelwert von „tree_dbh“ berechnet:

Eine Operation wie ReverseSortBy funktioniert dann „einfach so“ auf dieser Tabelle, hier beispielsweise eine absteigende Sortierung nach dem Wert von „meandbh“:

Hier erstellen wir aus einem kleinen Ausschnitt unserer Tabular-Daten eine gewöhnliche Matrix:

Und jetzt können wir das Ergebnis plotten, das die Positionen der Virginia-Kiefern in New York City zeigt:

Wann sollte man ein Tabular statt beispielsweise ein Dataset verwenden? Tabular ist speziell für Daten ausgelegt, die in Zeilen und Spalten angeordnet sind – und unterstützt viele leistungsstarke Operationen, die für Daten in dieser „rechteckigen“ Form sinnvoll sind. Dataset ist allgemeiner und kann eine beliebige Hierarchie von Datenebenen haben, weshalb es im Allgemeinen nicht alle „rechteckigen“ Datenoperationen von Tabular unterstützen kann. Zudem ist Tabular durch die Spezialisierung auf „rechteckige“ Daten deutlich effizienter und nutzt die neuesten typenspezifischen Methoden für die Verarbeitung großer Datenmengen.
Wenn Sie TabularStructure verwenden, können Sie einiges davon sehen, was Tabular so effizient macht. Jede Spalte wird als Daten eines spezifischen Typs behandelt (und ja, diese Typen stimmen mit denen im Wolfram Language Compiler überein). Außerdem gibt es eine optimierte Behandlung von fehlenden Daten, wobei mehrere neue Funktionen speziell für diesen Zweck hinzugefügt wurden:

Was wir bisher gesehen haben, ist Tabular, das mit „In-Core“-Daten arbeitet. Aber Sie können Tabular auch ganz transparent auf „Out-of-Core“-Daten anwenden, zum Beispiel auf Daten, die in einer relationalen Datenbank gespeichert sind.
Hier ist ein Beispiel, wie das aussieht:

Es handelt sich um ein Tabular, das auf eine Tabelle in einer relationalen Datenbank verweist. Standardmäßig zeigt es die Daten im Tabular nicht explizit an (und lädt sie tatsächlich auch nicht in den Arbeitsspeicher – da sie sehr groß sein könnten und sich außerdem schnell ändern können). Trotzdem können Sie Operationen darauf ausführen, genau wie bei jedem anderen Tabular. So ermittelt man beispielsweise, welche Spalten vorhanden sind:

Und dies definiert eine Operation, die das Ergebnis als symbolisches, out-of-core Tabular-Objekt zurückgibt:

Sie können dies „auflösen“ und mit ToMemory ein explizites, im Arbeitsspeicher liegendes Tabular-Objekt erhalten:

Bearbeiten von Daten in Tabellenform
Angenommen, Sie haben ein Tabular – wie dieses hier, das auf Pinguindaten basiert:

Es gibt viele Operationen, mit denen Sie die Daten in einem Tabular strukturiert bearbeiten können – wobei jeweils ein neues Tabular zurückgegeben wird. Zum Beispiel können Sie einfach die letzten 2 Zeilen des Tabular nehmen:

Oder Sie können 3 zufällige Zeilen entnehmen:

Andere Operationen hängen vom tatsächlichen Inhalt des Tabular ab. Und da jede Zeile wie eine Assoziation behandelt werden kann, können Sie Funktionen definieren, die effektiv über die Spaltennamen auf einzelne Elemente zugreifen:

Beachten Sie, dass man jederzeit #[name] verwenden kann, um auf Elemente in einer Spalte zuzugreifen. Wenn name ein alphanumerischer String ist, kann man auch die Kurzschreibweise #name verwenden. Und für andere Strings kann man #"name" nutzen. Manche Funktionen erlauben es sogar, einfach „name“ zu schreiben, was dann automatisch als Funktion #["name"] interpretiert wird:

Bisher haben wir nur darüber gesprochen, wie man Zeilen in einem Tabular anordnet oder auswählt. Aber was ist mit den Spalten? Hier sehen Sie, wie man ein Tabular erstellt, das nur zwei Spalten aus dem ursprünglichen Tabular enthält:

Was ist, wenn wir nicht nur vorhandene Spalten möchten, sondern neue Spalten, die aus diesen berechnet werden? Mit ConstructColumns können wir neue Spalten definieren, indem wir deren Namen und die Funktionen angeben, mit denen die Werte berechnet werden sollen:

(Beachten Sie den Trick, Function auszuschreiben, um keine Klammern setzen zu müssen – wie etwa bei
"species"(StringTake[#species,1]&).)
ConstructColumns ermöglicht es, aus einem bestehenden Tabular ein neues zu erstellen. TransformColumns hingegen erlaubt es, Spalten direkt innerhalb eines bestehenden Tabular zu transformieren – hier zum Beispiel, indem die Artnamen durch ihre Anfangsbuchstaben ersetzt werden:

TransformColumns erlaubt es ebenfalls, neue Spalten hinzuzufügen – und zwar auf dieselbe Weise wie bei ConstructColumns, indem der Inhalt der Spalten definiert wird. Aber wo werden diese neuen Spalten eingefügt? Standardmäßig kommen sie am Ende – also hinter alle bestehenden Spalten. Wenn Sie jedoch eine bestehende Spalte explizit angeben, wird diese als Marker verwendet, um festzulegen, wo die neue Spalte eingefügt werden soll.(„name“ → Nothing entfernt übrigens eine Spalte.)

Alles, was wir bisher gesehen haben, arbeitet zeilenweise auf einzelnen Zeilen eines Tabular. Aber was, wenn wir eine ganze Spalte „aufschlucken“ und für eine Berechnung verwenden möchten – zum Beispiel, um den Mittelwert einer gesamten Spalte zu berechnen und ihn dann von jedem Wert dieser Spalte abzuziehen?
ColumnwiseValue ermöglicht genau das, indem es der Funktion (hier Mean) eine Liste aller Werte der angegebenen Spalte(n) übergibt:

ColumnwiseValue ermöglicht es Ihnen effektiv, einen Skalarwert zu berechnen, indem eine Funktion auf eine gesamte Spalte angewendet wird. Es gibt auch ColumnwiseThread, mit dem Sie eine Liste von Werten berechnen können, die quasi „in eine Spalte eingefädelt“ werden. Hier erzeugen wir eine Spalte aus einer Liste von aufsummierten Werten:

Übrigens, wie wir weiter unten noch besprechen werden: Wenn Sie eine Liste von Werten (mit der passenden Länge) extern erzeugt haben und diese als Spalte verwenden möchten, können Sie das direkt mit InsertColumns tun.
Ein weiteres Konzept, das in der Praxis bei der Arbeit mit tabellarischen Daten sehr nützlich ist, ist das Gruppieren. In unseren Pinguindaten haben wir für jeden einzelnen Pinguin jeder Art eine eigene Zeile. Aber was, wenn wir stattdessen alle Pinguine einer bestimmten Art zusammenfassen wollen, zum Beispiel, um deren durchschnittliche Körpermasse zu berechnen?
Das können wir mit AggregateRows machen. AggregateRows funktioniert ähnlich wie ConstructColumns, indem Sie Spalten und deren Inhalte angeben. Im Unterschied zu ConstructColumns erzeugt es jedoch neue „aggregierte“ Zeilen:

Was ist diese erste Spalte hier? Die graue Hintergrundfarbe ihrer Einträge zeigt an, dass es sich um eine sogenannte „Schlüsselspalte“ handelt: eine Spalte, deren Einträge (gegebenenfalls zusammen mit anderen Schlüsselspalten) zur eindeutigen Identifikation von Zeilen verwendet werden können.
Später werden wir sehen, wie Sie mit RowKey eine Zeile angeben können, indem Sie einen Wert aus einer Schlüsselspalte verwenden:

Aber machen wir mit unseren Aggregationen weiter. Angenommen, wir wollen nicht nur nach Art, sondern auch nach Insel gruppieren. So geht das mit AggregateRows:

Im Grunde haben wir hier eine Tabelle, deren Zeilen durch Wertepaare (hier „species“ und „island“) definiert sind. Es ist aber oft praktisch, die Daten so „zu drehen“ (pivotieren), dass diese Werte jeweils für Zeilen und Spalten verwendet werden. Das geht mit PivotTable:

Beachten Sie die – (Bindestriche), die fehlende Werte anzeigen; offenbar gibt es keine Gentoo-Pinguine auf der Insel Dream, usw.
PivotTable liefert normalerweise genau dieselben Daten wie AggregateRows, jedoch in einer umgeordneten Form. Eine zusätzliche Funktion von PivotTable ist die Option IncludeGroupAggregates, die „Alle“-Einträge einschließt, welche die Aggregation über jede Art von Gruppe darstellen:

Wenn Sie mehrere Funktionen berechnen, gibt AggregateRows diese einfach als separate Spalten zurück:

PivotTable kann auch mit mehreren Funktionen umgehen – indem es Spalten mit „erweiterten Schlüsseln“ erzeugt:

Und jetzt können Sie RowKey und ExtendedKey verwenden, um auf Elemente des resultierenden Tabular zuzugreifen:

Daten in Tabular importieren
Wir haben bereits einige Möglichkeiten gesehen, was man mit Daten im Tabular-Format tun kann. Aber wie bekommt man überhaupt Daten in ein Tabular? Dafür gibt es mehrere Wege:
Der erste ist die Umwandlung aus bestehenden Strukturen wie Listen und Assoziationen.
Der zweite besteht im Import aus einer Datei, z. B. einer CSV- oder XLSX-Datei (oder – bei größeren Datenmengen – im Parquet-Format), oder aus einem externen Datenspeicher wie S3, Dropbox usw.
Der dritte Weg ist die Verbindung mit einer Datenbank.
Außerdem können Daten für Tabular direkt aus der Wolfram Knowledgebase oder aus dem Wolfram Data Repository bezogen werden.
So können Sie eine Liste von Listen in ein Tabular umwandeln:

Und so können Sie die Umwandlung wieder rückgängig machen:

Das funktioniert auch mit Sparse Arrays – hier wird in Sekundenbruchteilen ein Tabular mit einer Million Zeilen erstellt:

…das nur 80 MB Speicherplatz benötigt:

So verhält es sich bei einer Liste von Assoziationen:

Sie können dasselbe Tabular erhalten, indem Sie die Daten und die Spaltennamen separat eingeben:

Übrigens können Sie ein Tabular auch in ein Dataset umwandeln.

Und in diesem einfachen Fall können Sie es auch wieder zurück in ein Tabular konvertieren:

Im Allgemeinen gibt es viele verschiedene Optionen, wie man Listen, Datasets usw. in Tabular-Objekte umwandeln kann – und ToTabular ist so gestaltet, dass Sie diese steuern können. Zum Beispiel können Sie mit ToTabular ein Tabular aus Spalten statt aus Zeilen erstellen:

Wie sieht es mit externen Daten aus? In Version 14.2 unterstützt Import jetzt ein „Tabular“-Element für tabellarische Datenformate. Zum Beispiel – bei einer CSV-Datei – funktioniert das so:
Import kann die Datei sofort als Tabular importieren:

Das funktioniert sehr effizient – selbst bei riesigen CSV-Dateien mit Millionen von Einträgen. Dabei erkennt Import auch automatisch Spaltennamen und Kopfzeilen.
Dasselbe Prinzip funktioniert auch mit stärker strukturierten Dateien, wie sie etwa aus Tabellenkalkulationen oder statistischen Datenformaten stammen. Und es funktioniert ebenfalls mit modernen spaltenbasierten Speicherformaten wie Parquet, ORC und Arrow.
Import verarbeitet sowohl normale Dateien als auch URLs (und URIs) transparent und fordert bei Bedarf eine Authentifizierung an.
In Version 14.2 führen wir das neue Konzept des DataConnectionObject ein – eine symbolische Repräsentation entfernter Daten, die im Wesentlichen alle Details darüber kapselt, wie auf die Daten zugegriffen wird.
Zum Beispiel hier ein DataConnectionObject für einen S3-Bucket, dessen Inhalte wir sofort importieren können:

(In Version 14.2 unterstützen wir Amazon S3, Azure Blob Storage, Dropbox und IPFS – mit vielen weiteren Anbindungen in Vorbereitung. Außerdem planen wir die Unterstützung für Data-Warehouse-Verbindungen, APIs usw.)
Aber was ist mit Daten, die zu gross oder zu schnelllebig sind, um sie sinnvoll explizit zu importieren? Ein wichtiges Merkmal von Tabular (wie oben erwaehnt) ist, dass es externe Daten – zum Beispiel aus relationalen Datenbanken – transparent verarbeiten kann.
Hier ist ein Verweis auf eine grosse externe Datenbank:
Das definiert ein Tabular, das auf eine Tabelle in der externen Datenbank verweist:
Wir können die Dimensionen des Tabular abfragen – und sehen, dass es 158 Millionen Zeilen hat:
Die Tabelle, die wir uns ansehen, enthält sämtliche zeilenorientierten Daten aus OpenStreetMap. Hier sind die ersten 3 Zeilen und 10 Spalten:
Die meisten Operationen auf dem Tabular werden jetzt tatsächlich in der externen Datenbank ausgeführt. Hier fordern wir die Auswahl von Zeilen an, deren „name“-Feld „Wolfram“ enthält:
Die eigentliche Berechnung wird erst ausgeführt, wenn wir ToMemory verwenden, und in diesem Fall (weil sich viele Daten in der Datenbank befinden) dauert das ein wenig. Aber bald erhalten wir das Ergebnis als Tabular:
Und wir erfahren, dass es 58 Einträge mit dem Namen „Wolfram“ in der Datenbank gibt:
Eine weitere Datenquelle für Tabular ist die eingebaute Wolfram Knowledgebase. In Version 14.2 unterstützt EntityValue die direkte Ausgabe im Tabular-Format:

Eine weitere Datenquelle für Tabular ist die eingebaute Wolfram Knowledgebase. In Version 14.2 unterstützt EntityValue die direkte Ausgabe im Tabular-Format:

Datenbereinigung für Tabular
In vielerlei Hinsicht ist es der Fluch der Data Science: Ja, die Daten liegen in digitaler Form vor. Aber sie sind nicht sauber, nicht direkt berechenbar. Die Wolfram Language ist seit Langem ein einzigartig leistungsfaehiges Werkzeug zur flexiblen Datenbereinigung (und zum Beispiel auch zur Umsetzung der zehn Stufen der Datenverarbeitbarkeit, die ich vor einigen Jahren definiert habe).
Aber jetzt, in Version 14.2, verfuegen wir mit Tabular ueber eine ganz neue Sammlung schlanker Funktionen zur Datenbereinigung. Beginnen wir mit dem Import einiger „wilder“ Daten (dieses Beispiel ist tatsaechlich sauberer als viele andere):

(Uebrigens: Wenn sich wirklich wirres Zeug in der Datei befunden haette, haetten wir die Option MissingValuePattern verwenden koennen, um ein Muster anzugeben, das solches Zeug sofort durch Missing[…] ersetzt.)
OK, aber schauen wir uns zunaechst mit TabularStructure an, was aus unserer Datei importiert wurde:

Wir sehen, dass Import in den meisten Spalten erfolgreich den grundlegenden Datentyp erkannt hat – obwohl es zum Beispiel nicht unterscheiden kann, ob Zahlen nur Zahlen sind oder ob sie Mengen mit Einheiten darstellen usw. Zudem erkennt es, dass in einigen Spalten eine bestimmte Anzahl von Eintraegen fehlend ist.
Als ersten Schritt der Datenbereinigung entfernen wir die offenbar irrelevante Spalte "id":

Als naechstes sehen wir, dass die Elemente in der ersten Spalte zwar als Strings erkannt wurden – es handelt sich aber in Wirklichkeit um Datumsangaben, die mit den Uhrzeiten in der zweiten Spalte kombiniert werden sollten. Das koennen wir mit TransformColumns erledigen, wobei wir die nun ueberfluessige Spalte durch Nothing entfernen:

Wenn wir uns die verschiedenen numerischen Spalten anschauen, sehen wir, dass es sich in Wirklichkeit um Groessen mit Einheiten handelt. Aber zuerst benennen wir der Uebersichtlichkeit halber die letzten beiden Spalten um:

Jetzt wandeln wir die numerischen Spalten in Spalten mit Einheiten um – und konvertieren dabei auch gleich von °C in °F:

So koennen wir nun die Temperatur in Abhaengigkeit von der Zeit plotten:

Da gibt es viele Schwankungen. Und wenn wir uns die Daten anschauen, sehen wir, dass die Temperaturwerte von mehreren verschiedenen Wetterstationen stammen. Hier waehlen wir die Daten von nur einer Station aus:

Woher kommt der Bruch in der Kurve? Wenn wir einfach zu diesem Abschnitt im Tabular scrollen, sehen wir, dass er durch fehlende Daten verursacht wird:

Was koennen wir also dagegen tun? Es gibt eine leistungsstarke Funktion namens TransformMissing, die viele Moeglichkeiten bietet. Hier verwenden wir sie, um fehlende Temperaturwerte durch Interpolation zu ersetzen:

Und jetzt gibt es keine Lücken mehr – aber etwas mysteriös wirkt es, dass sich der gesamte Plot weiter ausdehnt:

Der Grund ist, dass interpoliert wird, selbst wenn eigentlich keine Messwerte vorliegen. Diese Zeilen koennen wir mit Discard entfernen:

Und jetzt haben wir diesen „Überhang“ am Ende nicht mehr:

Manchmal fehlen Daten explizit; manchmal sind die Daten (auf heimtückischere Weise) einfach falsch. Schauen wir uns das Histogramm der Druckwerte in unseren Daten an:

Ups. Was sind das für diese kleinen Werte? Vermutlich sind sie fehlerhaft (vielleicht Tippfehler?). Solche „anomalien“ können wir mit TransformAnomalies entfernen. Hier sagen wir der Funktion, dass sie einfach jede Zeile komplett entfernt, in der der Druck „anomal“ war:

Wir können TransformAnomalies auch verwenden, um die Daten zu „korrigieren“. Hier ersetzen wir jeden anomalen Druckwert einfach durch den vorherigen Druckwert, der im Tabular aufgeführt ist:

Sie können TransformAnomalies auch anweisen, anomale Werte zu „markieren“ und als „fehlend“ zu kennzeichnen. Aber was passiert dann, wenn wir mit fehlenden Werten rechnen wollen? Hier kommt MissingFallback ins Spiel.
Es ist im Grunde eine sehr einfache Funktion, die einfach ihr erstes nicht fehlendes Argument zurückgibt:

Auch wenn sie einfach ist, ist sie wichtig, um den Umgang mit fehlenden Werten zu erleichtern. Zum Beispiel berechnet diese Funktion eine „northspeed“ und greift dabei auf 0 zurück, falls dafür benötigte Daten fehlen:

Die Struktur von Tabular
Wir haben gesagt, dass ein Tabular „wie“ eine Liste von Assoziationen ist. Und tatsächlich – wenn Sie Normal darauf anwenden, erhalten Sie genau das:

Aber intern wird ein Tabular auf eine viel kompaktere und effizientere Weise gespeichert. Es ist hilfreich, das zu wissen – denn so koennen Sie Tabular-Objekte direkt manipulieren, ohne sie erst in Dinge wie Listen oder Assoziationen „zerlegen“ zu muessen.
Hier ist unser einfaches Beispiel-Tabular:

Was passiert, wenn wir eine Zeile extrahieren? Dann erhalten wir ein TabularRow-Objekt:

Wenn wir Normal anwenden, erhalten wir eine Assoziation:

So sieht es aus, wenn wir stattdessen eine Spalte extrahieren:

Jetzt liefert Normal eine Liste:

Wir koennen eine TabularColumn aus einer Liste erstellen:

Jetzt koennen wir mit InsertColumns eine solche symbolische Spalte in ein bestehendes Tabular einfuegen (das „b“ gibt an, dass die neue Spalte nach der Spalte „b“ eingefuegt werden soll):

Aber was genau ist ein Tabular eigentlich intern? Schauen wir uns das am Beispiel an:

TabularStructure gibt uns eine Uebersicht ueber die interne Struktur:

Das Erste, was auffällt, ist, dass alles in Spaltenform dargestellt wird – was zeigt, dass Tabular ein grundsaetzlich spaltenorientiertes Konstrukt ist.
Ein wesentlicher Grund fuer die hohe Effizienz von Tabular ist, dass innerhalb einer Spalte alle Werte einheitlich sind, also denselben Datentyp haben.
Zusaetzlich werden bei Elementen wie Groessen oder Datumsangaben die Daten „faktorisiert“: Intern wird in der Spalte nur eine Liste von Zahlen gespeichert, zusammen mit einer einzigen Kopie von „Metadaten“, die angeben, wie diese Zahlen zu interpretieren sind.
Und ja, all das hat eine grosse Auswirkung. Hier zum Beispiel sehen wir die Groesse in Bytes unseres oben verwendeten Tabular mit den New Yorker Baeumen:

Aber wenn wir es mit Normal in eine Liste von Assoziationen umwandeln, ist das Ergebnis etwa 14-mal groesser:

Okay, aber was sind eigentlich diese „Spaltentypen“ in der Tabular-Struktur? ColumnTypes liefert eine Liste davon:

Hierbei handelt es sich um niedrigstufige Typen, wie sie im Wolfram Language Compiler verwendet werden. Ein Teil des Wissens darüber besteht darin, dass es uns sofort sagt, welche Operationen wir an einer bestimmten Spalte durchführen können. Das ist sowohl bei der niedrigstufigen Verarbeitung als auch bei Dingen wie der Einschätzung, welche Art von Visualisierung möglich sein könnte, nützlich.
Wenn Import Daten aus etwas wie einer CSV-Datei lädt, versucht es, den Typ jeder Spalte zu erkennen. Aber manchmal (wie oben erwähnt) möchte man eine Spalte in einen anderen Typ „casten“ und dabei den „Zieltyp“ mithilfe der Wolfram Language-Typbeschreibung angeben. Zum Beispiel wird hier die Spalte „b“ in eine 32-Bit-Gleitkommazahl und die Spalte „c“ in Einheiten von Metern umgewandelt:

Wenn ein Tabular in einem Notebook angezeigt wird, zeigen die Spaltenüberschriften die Datentypen der jeweiligen Spalten an. In diesem Fall steht zum Beispiel ein kleines in der ersten Spalte, um anzuzeigen, dass sie Zeichenketten enthält. Zahlen und Daten zeigen im Grunde einfach „was sie sind“. Größen haben ihre Einheiten angegeben. Und allgemeine symbolische Ausdrücke (wie hier die Spalte „f“) werden mit
gekennzeichnet. (Wenn man mit der Maus über eine Spaltenüberschrift fährt, erhält man weitere Details zu den Typen.)
Das nächste Thema ist fehlende Daten. Tabular behandelt Spalten immer als einheitlichen Typ, behält jedoch eine Gesamtübersicht darüber, wo Werte fehlen. Wenn man die Spalte extrahiert, sieht man ein symbolisches Missing:

Wenn du direkt mit der Tabular-Spalte arbeitest, verhält sie sich einfach so, als ob die fehlenden Daten tatsächlich fehlen würden:

Übrigens, wenn du Daten „aus der freien Wildbahn“ importierst, versucht Import automatisch, den richtigen Typ für jede Spalte zu erkennen. Es weiß, wie es mit gängigen Anomalien in den Eingabedaten umgehen kann, wie zum Beispiel NaN oder null in einer Spalte mit Zahlen. Aber wenn es andere ungewöhnliche Werte gibt – zum Beispiel notfound mitten in einer Zahlenspalte –, kannst du Import anweisen, solche Werte als gewöhnliche fehlende Daten zu behandeln, indem du sie als Einstellungen für die Option MissingValuePattern angibst.
Es gibt noch ein paar weitere Feinheiten im Zusammenhang mit der Struktur von Tabular-Objekten zu besprechen. Die erste ist das Konzept der erweiterten Schlüssel (extended keys). Nehmen wir an, wir haben das folgende Tabular:

Wir können dies „in Spalten drehen“ (pivot to columns), sodass die Werte x und y zu Spaltenüberschriften werden, aber „unter“ der übergeordneten Spaltenüberschrift „value“:

Aber wie ist die Struktur dieses Tabular? Wir können ColumnKeys verwenden, um das herauszufinden:

Du kannst diese erweiterten Schlüssel nun als Indizes für das Tabular verwenden:

In diesem speziellen Fall können wir, da die „Subschlüssel“ „x“ und „y“ einzigartig sind, einfach diese verwenden, ohne den anderen Teil des erweiterten Schlüssels mit einzubeziehen:

Unsere letzte Feinheit (vorerst) hängt damit zusammen und betrifft Schlüsselspalten. Normalerweise geben wir eine Zeile in einem Tabular-Objekt einfach durch ihre Position an. Aber wenn die Werte einer bestimmten Spalte eindeutig sind, können wir stattdessen diese Werte verwenden, um eine Zeile zu spezifizieren. Betrachte dieses Tabular:

Die Spalte „fruit“ hat die Eigenschaft, dass jeder Eintrag nur einmal vorkommt – daher können wir ein Tabular erstellen, das diese Spalte als Schlüsselspalte verwendet:

Beachte, dass die Zeilennummern jetzt verschwunden sind und die Schlüsselspalte mit einem grauen Hintergrund hervorgehoben wird. In diesem Tabular kannst du dann eine bestimmte Zeile zum Beispiel mit RowKey referenzieren:

Ebenso kannst du auch eine Assoziation mit dem Spaltennamen verwenden:

Was ist, wenn die Werte in einer einzelnen Spalte nicht ausreichen, um eine Zeile eindeutig zu spezifizieren, aber mehrere Spalten zusammen das können? (Zum Beispiel in der Praxis: eine Spalte enthält Vornamen, eine andere Nachnamen und eine weitere Geburtsdaten.) In diesem Fall kannst du alle diese Spalten als Schlüsselspalten festlegen:

Und sobald du das getan hast, kannst du eine Zeile referenzieren, indem du die Werte aller Schlüsselspalten angibst:

Überall tabellarisch
Tabular bietet eine wichtige neue Möglichkeit, strukturierte Daten in der Wolfram Language darzustellen. Es ist für sich genommen bereits leistungsstark, aber noch mächtiger wird es durch die nahtlose Integration mit allen anderen Funktionen der Wolfram Language. Viele Funktionen arbeiten direkt mit Tabular. In Version 14.2 wurden jedoch Hunderte von Funktionen erweitert, um die speziellen Eigenschaften von Tabular zu nutzen.
Meistens geht es darum, direkt auf Spalten in einem Tabular operieren zu können. Zum Beispiel, gegeben das folgende Tabular:

können wir sofort eine Visualisierung basierend auf zwei der Spalten erstellen:

Wenn eine der Spalten kategoriale Daten enthält, wird das erkannt und entsprechend in der Darstellung berücksichtigt:

Ein weiteres Einsatzgebiet, in dem Tabular sofort verwendet werden kann, ist das maschinelle Lernen. Zum Beispiel wird hier eine Klassifikatorfunktion erstellt, die versucht, anhand anderer Daten die Pinguinart zu bestimmen:

Nun können wir diese Klassifikatorfunktion verwenden, um die Art eines Pinguins anhand anderer Daten vorherzusagen:

Wir können auch das gesamte Tabular verwenden und ein Feature-Space-Plot erstellen, bei dem nach Art beschriftet wird:

Oder wir könnten die „Verteilung möglicher Pinguine lernen“:

und zufällig 3 „fiktive Pinguine“ aus dieser Verteilung erzeugen:

Algebra mit symbolischen Arrays
Eine der großen Neuerungen in Version 14.1 war die Einführung symbolischer arrays – und die Möglichkeit, Ausdrücke mit Vektor-, Matrix- und Array-Variablen zu erstellen und davon Ableitungen zu nehmen. In Version 14.2 führen wir dieses Konzept weiter: Zum ersten Mal wird der bisher manuelle Prozess der Algebra mit symbolischen Arrays systematisch automatisiert, und Ausdrücke mit symbolischen Arrays werden vereinfacht.
Fangen wir mit ArrayExpand an. Unsere bewährte Funktion Expand erweitert nur gewöhnliche Multiplikationen, also im Grunde von Skalaren – in diesem Fall macht sie also nichts:

In Version 14.2 gibt es auch ArrayExpand, das die Expansion durchführt:

ArrayExpand behandelt viele Verallgemeinerungen der Multiplikation, die nicht kommutativ sind:

In einem Beispiel wie diesem müssen wir wirklich nichts über a und b wissen. Aber manchmal können wir die Expansion nicht durchführen, ohne zum Beispiel ihre Dimensionen zu kennen. Eine Möglichkeit, diese Dimensionen anzugeben, ist als Bedingung in ArrayExpand:

Eine Alternative ist die Verwendung einer expliziten symbolischen Array-Variable:

Zusätzlich zur Erweiterung verallgemeinerter Produkte mit ArrayExpand unterstützt Version 14.2 auch die allgemeine Vereinfachung von Ausdrücken mit symbolischen Arrays:

Die Funktion ArraySimplify führt speziell Vereinfachungen bei symbolischen Arrays durch, während andere Teile des Ausdrucks unverändert bleiben. Version 14.2 unterstützt viele Arten von Vereinfachungen für Arrays:


Diese Vereinfachungen könnten wir durchführen, ohne etwas über die Dimensionen von a und b zu wissen. Aber manchmal stoßen wir ohne dieses Wissen an Grenzen. Zum Beispiel erhalten wir ohne Dimensionsangaben:

Aber mit den Dimensionsangaben können wir diesen Ausdruck explizit zu einer n×n Einheitsmatrix vereinfachen:

ArraySimplify kann auch die Symmetrien von Arrays berücksichtigen. Zum Beispiel richten wir eine symbolische symmetrische Matrix ein:

Und jetzt kann ArraySimplify das sofort auflösen:

Die Fähigkeit, algebraische Operationen an vollständigen Arrays in symbolischer Form durchzuführen, ist sehr mächtig. Aber manchmal ist es auch wichtig, einzelne Komponenten von Arrays zu betrachten. In Version 14.2 haben wir daher ComponentExpand hinzugefügt, mit dem du Komponenten von Arrays in symbolischer Form erhalten kannst.
Zum Beispiel nimmt dies einen 2-Komponenten-Vektor und schreibt ihn als explizite Liste mit zwei symbolischen Komponenten:

Darunter werden diese Komponenten mithilfe von Indexed dargestellt:

Hier ist die Determinante einer 3×3-Matrix, ausgeschrieben in symbolischen Komponenten:

Und hier ist eine Matrixpotenz:

Gegeben die 3D-Vektoren und
, können wir zum Beispiel auch das Kreuzprodukt bilden:

Und wir können es dann in eine inverse Matrix hineinmultiplizieren (dotten):

Sprachoptimierungen
Als täglicher Nutzer der Wolfram Language bin ich sehr zufrieden damit, wie reibungslos ich rechnerische Ideen in Code umsetzen kann. Doch je einfacher wir das machen, desto mehr entdecken wir neue Bereiche, in denen die Sprache weiter verfeinert werden kann. Und in Version 14.2 – wie in jeder vorherigen Version auch – haben wir eine Reihe von „Sprachoptimierungen“ hinzugefügt.
Eine einfache Neuerung – deren Nutzen besonders bei Tabular deutlich wird – ist Discard. Man kann es sich als Ergänzung zu Select vorstellen: Es verwirft Elemente entsprechend dem von dir angegebenen Kriterium:

Und neben der Einführung von Discard haben wir auch Select verbessert. Normalerweise liefert Select nur die Liste der ausgewählten Elemente zurück. Aber in Version 14.2 kannst du auch andere Ergebnisse anfordern. Hier fragen wir nach dem „Index“ (also der Position) der Elemente, die NumberQ auswählt:

Etwas, das bei der Verarbeitung sehr großer Datenmengen hilfreich sein kann, ist, von Select (und Discard) eine Bitvektor-Datenstruktur zu erhalten, die eine Bitmaske darüber liefert, welche Elemente ausgewählt sind und welche nicht:

Übrigens, so kannst du mehrere Ergebnisse von Select und Discard anfordern:

Beim Thema Tabular haben wir bereits MissingFallback erwähnt. Eine weitere Funktion im Zusammenhang mit robusterem Code und Fehlerbehandlung ist die neue Funktion Failsafe. Angenommen, du hast eine Liste, die einige „fehlerhafte“ Elemente enthält. Wenn du eine Funktion f auf diese Liste anwendest, wird sie auch auf die fehlerhaften Elemente angewendet, genau wie auf alle anderen:

Aber sehr wahrscheinlich war f nicht dafür ausgelegt, mit solchen fehlerhaften Eingaben umzugehen. Genau hier kommt Failsafe ins Spiel. Denn Failsafe[f][x] ist so definiert, dass es f[x] ausführt, wenn x kein Fehler ist, und im Fehlerfall einfach den Fehler zurückgibt. So können wir f ohne Bedenken auf unsere Liste anwenden, in dem Wissen, dass niemals fehlerhafte Eingaben verarbeitet werden:

Wenn wir schon von kniffligen Fehlerfällen sprechen: Eine weitere neue Funktion in Version 14.2 ist HoldCompleteForm. HoldForm ermöglicht es, einen Ausdruck anzuzeigen, ohne ihn normal auszuwerten. Aber – ähnlich wie Hold – erlaubt es dennoch bestimmte Transformationen. HoldCompleteForm – wie HoldComplete – verhindert all diese Transformationen vollständig. Während sich HoldForm hier also etwas verwirrt, wenn die Sequenz „aufgelöst“ wird, verhindert HoldCompleteForm genau das:

HoldCompleteForm hält die Sequenz vollständig fest und zeigt sie genau so an:

Eine weitere Verbesserung in Version 14.2 betrifft Counts. Häufig möchte ich Elemente in einer Liste zählen und dabei auch eine 0 erhalten, wenn ein bestimmtes Element fehlt. Standardmäßig zählt Counts nur die tatsächlich vorhandenen Elemente:

In Version 14.2 haben wir ein zweites Argument hinzugefügt, mit dem du eine vollständige Liste aller Elemente angeben kannst, die gezählt werden sollen – selbst wenn sie in der Liste nicht vorkommen:

Als letztes Beispiel für eine Sprachoptimierung in Version 14.2 möchte ich AssociationComap erwähnen. In Version 14.0 haben wir Comap als „Co-“ (wie in „Co-Funktor“ usw.) Analogon zu Map eingeführt:

In Version 14.2 führen wir AssociationComap ein – die „Co-“-Variante von AssociationMap:

Du kannst dir das vorstellen als eine elegante Methode, beschriftete Tabellen von Dingen zu erstellen, zum Beispiel so:

Unsere Farben aufhellen; Aufgepeppt für 2025
Im Jahr 2014 – mit Version 10.0 – haben wir eine umfassende Überarbeitung der Standardfarben für alle unsere Grafik- und Visualisierungsfunktionen durchgeführt und eine Lösung gefunden, die uns gut erschien. (Und, wie wir gerade festgestellt haben, auf etwas bizarre Weise, haben viele Grafik- und Visualisierungsbibliotheken in den folgenden Jahren offenbar unsere Farbauswahl übernommen!) Nun ist ein Jahrzehnt vergangen, die visuellen Erwartungen (und Display-Technologien) haben sich verändert, und wir haben beschlossen, unsere Farben für 2025 aufzufrischen.
So sah ein typisches Diagramm in den Versionen 10.0 bis 14.1 aus:

Und hier ist dasselbe Diagramm in Version 14.2:

Das Design ist immer noch vollständig wiedererkennbar, aber es hat jetzt einen kleinen zusätzlichen Kick.
Mit mehr Kurven gibt es auch mehr Farben. Hier ist die alte Version:

Und hier ist die neue Version:

Auch Histogramme sind jetzt heller. Die alte Version:

Und die neue Version:

Hier ist der Vergleich zwischen den alten („2014“) und neuen („2025“) Farben:

Es ist subtil, aber es macht einen Unterschied. Ich muss sagen, dass ich in den letzten Jahren immer öfter das Bedürfnis hatte, die Farben in fast jedem Wolfram Language-Bild, das ich veröffentlicht habe, anzupassen. Aber ich freue mich, sagen zu können, dass dieses Bedürfnis mit den neuen Farben verschwunden ist – und ich jetzt einfach wieder unsere Standardfarben verwenden kann!
LLM-Streamlining und Streaming
Wir haben im Jahr 2023 erstmals den programmgesteuerten Zugriff auf LLMs in der Wolfram Language eingeführt – mit Funktionen wie LLMFunction und LLMSynthesize. Damals benötigten diese Funktionen noch Zugriff auf externe LLM-Dienste. Aber mit der Veröffentlichung des LLM Kit (im letzten Monat) zusammen mit dem Wolfram Notebook Assistant haben wir diese Funktionen nun nahtlos für alle mit einem Notebook Assistant + LLM Kit-Abonnement verfügbar gemacht. Sobald du dein Abonnement hast, kannst du die programmgesteuerten LLM-Funktionen überall in Version 14.2 verwenden – ganz ohne zusätzliche Einrichtung.
Es gibt ausserdem zwei neue Funktionen: LLMSynthesizeSubmit und ChatSubmit. Beide dienen dazu, schrittweise Ergebnisse von LLMs zu erhalten (was – zumindest im Moment – wichtig ist, da LLMs mitunter recht langsam sein koennen). Aehnlich wie CloudSubmit und URLSubmit sind LLMSynthesizeSubmit und ChatSubmit asynchrone Funktionen: Du rufst sie auf, um einen Prozess zu starten, der bei einem bestimmten Ereignis automatisch eine passende Handler-Funktion aufruft.
Sowohl LLMSynthesizeSubmit als auch ChatSubmit unterstuetzen eine ganze Reihe von Ereignissen. Ein Beispiel ist „ContentChunkReceived“ – ein Ereignis, das auftritt, wenn ein Inhaltsabschnitt vom LLM empfangen wurde.
So kann man das verwenden:

LLMSynthesizeSubmit gibt ein TaskObject zurück, beginnt aber sofort damit, Text als Antwort auf den angegebenen Prompt zu generieren. Dabei wird die von dir angegebene Handler-Funktion jedes Mal aufgerufen, wenn ein Textabschnitt eintrifft. Nach einigen Momenten hat das LLM den Vorgang der Texterzeugung abgeschlossen – und wenn du dann den Wert von c abfragst, siehst du die einzelnen Textabschnitte, die erzeugt wurden:

Versuchen wir das erneut – aber dieses Mal richten wir eine dynamische Anzeige für den String s ein und lassen dann LLMSynthesizeSubmit laufen, um den erzeugten Text in diesem String zu sammeln:
ChatSubmit ist das asynchrone Gegenstück zu ChatEvaluate – und du kannst es verwenden, um ein vollständiges Chat-Erlebnis zu erstellen, bei dem Inhalte direkt in dein Notebook gestreamt werden, sobald das LLM (oder von ihm aufgerufene Tools) etwas generieren.
Optimierung der parallelen Berechnung: Starten Sie alle Maschinen!
Seit fast 20 Jahren gibt es in der Wolfram Language eine einfache und leistungsstarke Möglichkeit zur parallelen Berechnung – mit Funktionen wie ParallelMap, ParallelTable und Parallelize. Die parallele Ausführung kann entweder auf mehreren Kernen eines einzelnen Rechners oder verteilt über viele Maschinen im Netzwerk erfolgen. (Zum Beispiel habe ich in meinem aktuellen Setup 7 Maschinen mit insgesamt 204 Kernen.)
In den letzten Jahren haben wir, teilweise als Reaktion auf die steigende Anzahl an Kernen auf einzelnen Maschinen, die Art und Weise, wie parallele Berechnung bereitgestellt wird, Schritt für Schritt vereinfacht. Und in Version 14.2 haben wir genau diesen Bereitstellungsprozess selbst parallelisiert. Das bedeutet zum Beispiel, dass meine 7 Maschinen jetzt alle ihre parallelen Kernel gleichzeitig starten – sodass der gesamte Vorgang nun in wenigen Sekunden abgeschlossen ist, statt wie früher möglicherweise mehrere Minuten zu dauern:

Eine weitere Neuerung für parallele Berechnungen in Version 14.2 ist die Möglichkeit, automatisch über mehrere Variablen in ParallelTable zu parallelisieren. ParallelTable hat schon immer verschiedene Algorithmen genutzt, um die Aufteilung der Berechnungen auf unterschiedliche Kernel zu optimieren. Jetzt wurde das erweitert, sodass auch mehrere Variablen gleichzeitig berücksichtigt werden können:

Als jemand, der regelmäßig groß angelegte Berechnungen mit der Wolfram Language durchführt, kann ich gar nicht genug betonen, wie nahtlos und wichtig die parallelen Berechnungsfunktionen für mich sind. Meistens entwickle ich eine Berechnung zuerst mit Map, Table usw. Wenn alles passt, ersetze ich dann einfach durch ParallelMap, ParallelTable usw. Und es ist beeindruckend, wie viel eine 200-fache Beschleunigung ausmacht – vorausgesetzt, die Berechnung hat nicht zu viel Kommunikationsaufwand.
(Apropos Kommunikationsaufwand: Zwei neue Funktionen in Version 14.2 sind ParallelSelect und ParallelCases. Damit kannst du Elemente aus Listen parallel auswählen oder Fälle finden, wobei nur die Endergebnisse an den Master-Kernel zurückgeschickt werden – das spart erheblich Kommunikationsaufwand. Diese Funktionalität gab es zwar schon länger über Konstruktionen wie Parallelize [… Select […]], aber in Version 14.2 ist sie deutlich effizienter und benutzerfreundlicher geworden.)
Folge dem ____! Tracking im Video
Angenommen, wir haben ein Video, zum Beispiel von Personen, die durch einen Bahnhof gehen. Schon seit einiger Zeit können wir aus einem einzelnen Frame eines solchen Videos Personen erkennen. Aber in Version 14.2 gibt es etwas Neues: die Fähigkeit, Objekte zu verfolgen, die sich zwischen den Frames des Videos bewegen.
Lass uns mit einem Video beginnen:

Wir können einzelne Frames nehmen und Bildbegrenzungsrahmen (ImageBoundingBoxes) finden. Aber seit Version 14.2 können wir ImageBoundingBoxes einfach auf das gesamte Video gleichzeitig anwenden:

Dann können wir die Daten zu den Begrenzungsrahmen verwenden, um Personen im Video hervorzuheben – mit der neuen Funktion HighlightVideo:

Aber das zeigt nur separat an, wo sich Personen in jedem einzelnen Frame befinden; es verbindet sie nicht von einem Frame zum nächsten. In Version 14.2 haben wir VideoObjectTracking hinzugefügt, um Objekte zwischen den Frames zu verfolgen:

Wenn wir nun HighlightVideo verwenden, werden verschiedene Objekte mit unterschiedlichen Farben markiert:

Das identifiziert alle einzigartigen Objekte, die im Verlauf des Videos erkannt wurden, und zählt sie:

„Wo ist der Hund?“, fragst du dich vielleicht. Er ist definitiv nicht lange zu sehen:

Und wenn wir den ersten Frame finden, in dem das Objekt erscheinen soll, sieht es tatsächlich so aus, als wäre das, was vermutlich eine Person unten rechts ist, fälschlicherweise als Hund erkannt worden:

Ja, genau — das hat das System als Hund erkannt:

Spieltheorie
„Wie sieht es mit Spieltheorie aus?“, wurde schon lange gefragt. Und ja, es wurde viel Spieltheorie mit der Wolfram Language gemacht, und es gibt viele Pakete, die sich mit speziellen Aspekten beschäftigen. Aber in Version 14.2 führen wir endlich integrierte Systemfunktionen für Spieltheorie ein – sowohl für Matrixspiele als auch für Baumspiele.
So definieren wir ein (nullsummiges) 2-Spieler-Matrixspiel:

Das definiert die Auszahlungen, wenn jeder Spieler jede Aktion wählt. Wir können das mit einem Dataset darstellen:

Eine Alternative ist, das Spiel mit MatrixGamePlot zu visualisieren:

Okay, wie können wir dieses Spiel „lösen“? Mit anderen Worten: Welche Aktion sollte jeder Spieler mit welcher Wahrscheinlichkeit wählen, um seinen durchschnittlichen Gewinn über viele Spielrunden zu maximieren? (Es wird angenommen, dass die Spieler in jeder Runde gleichzeitig und unabhängig voneinander ihre Aktionen wählen.) Eine „Lösung“, die die erwarteten Gewinne aller Spieler maximiert, nennt man Nash-Gleichgewicht. (Kleiner historischer Hinweis: John Nash war ein langjähriger Nutzer von Mathematica und der heutigen Wolfram Language – allerdings viele Jahre nach der Entwicklung des Nash-Gleichgewichts.) Nun, in Version 14.2 berechnet FindMatrixGameStrategies optimale Strategien (auch bekannt als Nash-Gleichgewichte) für Matrixspiele:

Dieses Ergebnis bedeutet, dass Spieler 1 Aktion 1 mit der Wahrscheinlichkeit und Aktion 2 mit
spielen sollte, und Spieler 2 mit den Wahrscheinlichkeiten
bzw.
. Aber wie hoch sind ihre erwarteten Auszahlungen? MatrixGamePayoff berechnet diese:

Es kann ziemlich schwierig werden, den Überblick über die verschiedenen Fälle in einem Spiel zu behalten. Deshalb ermöglicht dir MatrixGame, beliebige Bezeichnungen für Spieler und Aktionen zu vergeben:

Diese Bezeichnungen werden dann in den Visualisierungen verwendet:

Was wir gerade gezeigt haben, ist tatsächlich ein klassisches Beispielspiel – das „Gefangenendilemma“. In der Wolfram Language gibt es jetzt GameTheoryData als Sammlung von etwa 50 Standardspielen. Hier ist eines, das für 4 Spieler spezifiziert ist:

Und dieses Spiel ist deutlich komplexer zu lösen, aber hier ist das Ergebnis – mit 27 verschiedenen Lösungen:

Und ja, die Visualisierungen funktionieren weiterhin, auch bei mehr Spielern (hier zeigen wir den Fall mit 5 Spielern und markieren die 50. Lösung des Spiels):

Es ist vielleicht erwähnenswert, dass wir diese Art von Spielen mithilfe unserer neuesten Fähigkeiten zur Lösung polynomialer Gleichungssysteme lösen. Dabei können wir routinemäßig alle möglichen Nash-Gleichgewichte finden – nicht nur einen einzelnen Fixpunkt – und zudem exakte Ergebnisse liefern:

Neben Matrixspielen, bei denen die Spieler ihre Aktionen gleichzeitig genau einmal auswählen, unterstützen wir nun auch Baumspiele. Dabei wechseln sich die Spieler ab, was zu einem Baum möglicher Spielverläufe führt, der in einem bestimmten Auszahlungsergebnis für jeden Spieler endet. Hier ist ein Beispiel für ein sehr einfaches Baumspiel:

Wir können mindestens eine Lösung für dieses Spiel finden – beschrieben durch eine verschachtelte Struktur, die die optimalen Wahrscheinlichkeiten für jede Aktion jedes Spielers in jedem Zug angibt:

Bei Baumspielen können die Dinge deutlich komplexer werden. Hier ist ein Beispiel, bei dem bestimmte Spieler nicht wissen, welcher Pfad tatsächlich eingeschlagen wurde – dargestellt durch Zustände, die mit gestrichelten Linien verbunden sind:

Was wir in Version 14.2 bereitstellen, deckt die grundlegenden Konzepte eines typischen einführenden Spieltheorie-Kurses ziemlich vollständig ab. Aber jetzt – ganz im Stil der Wolfram Language – ist das Ganze rechnerisch zugänglich und erweiterbar. So kannst du realistischere Spiele untersuchen und schnell viele Beispiele durchspielen, um ein besseres Verständnis und Intuition dafür zu entwickeln.
Bisher haben wir uns auf die „klassische Spieltheorie“ konzentriert – insbesondere mit dem Merkmal (relevant für viele aktuelle Anwendungen), dass alle Aktionsknoten das Ergebnis unterschiedlicher Aktionsfolgen sind. Spiele wie Tic-Tac-Toe (das ich kürzlich mithilfe von Multiway-Graphen untersucht habe) lassen sich jedoch vereinfachen, indem äquivalente Aktionsknoten zusammengeführt werden. Mehrere Aktionsfolgen können nämlich zum exakt gleichen Spielzustand führen – wie es häufig bei iterierten Spielen der Fall ist.
Solche Graphstrukturen passen nicht in die klassische Spielbaum-Darstellung, die wir in Version 14.2 eingeführt haben – obwohl sie (wie meine eigenen Untersuchungen zeigen) sich auf einzigartige Weise mit der Wolfram Language analysieren lassen.
Berechnung der Syzygien und andere Fortschritte in der Astronomie
In der Astronomie gibt es viele „Zufälle“ – also Konstellationen, bei denen Himmelskörper in einer bestimmten Weise zueinander stehen. Eine Sonnenfinsternis ist ein bekanntes Beispiel. Aber es gibt noch viele weitere solcher Ereignisse. In Version 14.2 gibt es jetzt eine allgemeine Funktion namens FindAstroEvent, mit der man genau solche „Zufälle“ finden kann – technisch auch Syzygien („sizz-ee-gees“) genannt – sowie andere „besondere Konfigurationen“ von astronomischen Objekten.
Ein einfaches Beispiel dafür ist die September-Tagundnachtgleiche (Herbst-Äquinoktium):

Grob gesagt ist das der Zeitpunkt, an dem Tag und Nacht gleich lang sind. Genauer gesagt ist es der Moment, in dem die Sonne eine der beiden Positionen am Himmel erreicht, an denen die Ekliptik (also die Umlaufbahn der Erde um die Sonne) den Himmelsäquator (die Projektion des Erdäquators) kreuzt – wie wir hier sehen können (die Ekliptik ist die gelbe Linie, der Himmelsäquator die blaue):

Ein weiteres Beispiel: Lassen wir uns den nächsten Zeitpunkt innerhalb des nächsten Jahrhunderts berechnen, an dem Jupiter und Saturn sich am nächsten am Himmel befinden.

Sie werden sich so nahe kommen, dass man ihre Monde gemeinsam am Himmel beobachten kann:

Es gibt eine erstaunliche Anzahl astronomischer Konstellationen, die im Laufe der Geschichte spezielle Namen erhalten haben: Tagundnachtgleichen (Äquinoktien), Sonnenwenden, Equiluxes, Kulminationen, Konjunktionen, Oppositionen, Quadraturen – sowie Periapsen und Apoapsen (spezialisiert zu Perigäum, Perihel, Periareion, Perijovium, Perikron, Periuranion, Periposeideum usw.). In Version 14.2 unterstützen wir all diese Konfigurationen.
Zum Beispiel liefert dieser Befehl den nächsten Zeitpunkt, zu dem Triton Neptun am nächsten ist:

Ein berühmtes Beispiel betrifft das Perihel (den sonnennächsten Punkt) des Merkur. Berechnen wir also die Position des Merkurs (vom Standpunkt der Sonne aus gesehen) zu all seinen Periheldurchgängen in den ersten Jahrzehnten des 19. Jahrhunderts:

Wir sehen, dass es eine systematische „Verschiebung“ gibt (zusammen mit etwas Hin- und Her):

Nun berechnen wir diesen Fortschritt quantitativ. Zuerst ermitteln wir die Zeiten der ersten Periheldurchgänge in den Jahren 1800 und 1900:

Jetzt berechnen wir den Winkelabstand zwischen den Positionen des Merkurs zu diesen Zeitpunkten:

Dann teilen wir diesen Winkelabstand durch die Zeitdifferenz:

und wandeln die Einheiten um:

Berühmt ist, dass 43 Bogensekunden pro Jahrhundert auf Abweichungen vom Gravitationsgesetz im Sinne des Invers-Quadratgesetzes zurückzuführen sind, die durch die Allgemeine Relativitätstheorie erklärt werden – und natürlich von unserem astronomischen Berechnungssystem berücksichtigt werden. (Der übrige Teil der Verschiebung resultiert aus den klassischen Gravitationswirkungen von Venus, Jupiter, Erde usw.)
PDEs jetzt auch für magnetische Systeme
Vor über anderthalb Jahrzehnten haben wir uns verpflichtet, die Wolfram Language zu einer vollwertigen Umgebung für die Modellierung von partiellen Differentialgleichungen (PDEs) zu machen. Dabei war es natürlich hilfreich, dass wir auf die vielfältigen anderen Funktionen der Wolfram Language zurückgreifen konnten – und das, was wir geschaffen haben, ist dank der Synergie mit dem gesamten System unbezahlbar wertvoll geworden. Über die Jahre hinweg haben wir mit großem Aufwand kontinuierlich symbolische PDE-Modellierungsfunktionen für alle gängigen Anwendungsbereiche aufgebaut. Heute kann man mit Fug und Recht sagen, dass wir einen großen Teil der in der Praxis auftretenden PDE-Modelle in industriellem Maßstab handhaben können.
Dennoch gibt es immer wieder neue Anwendungsfälle, für die wir zusätzliche Funktionen schaffen können. In Version 14.2 fügen wir daher eingebaute Modellierungsprimitive für statische und quasistatische Magnetfelder hinzu. So können wir zum Beispiel jetzt ein sanduhrförmiges Magnetmodell erstellen. Dabei definieren wir die Randbedingungen und lösen dann die Gleichungen für das magnetische Skalarpotenzial:

Anschliessend koennen wir dieses Ergebnis nehmen und zum Beispiel sofort die Magnetfeldlinien zeichnen, die sich daraus ergeben:

Version 14.2 fuegt zudem die Grundbausteine hinzu, um mit langsam variierenden elektrischen Stroemen und den von ihnen erzeugten magnetischen Feldern umzugehen. All das laesst sich sofort nahtlos mit unseren anderen Modellierungsbereichen wie Waermetransport, Stroemungsdynamik, Akustik usw. integrieren.
Zum Thema PDE-Modellierung und deren Anwendungen gibt es viel zu berichten. In Version 14.2 haben wir über 200 Seiten zusätzlicher, lehrbuchähnlicher Dokumentation zum Thema PDE-Modellierung hinzugefügt, inklusive einiger Beispiele auf Forschungsniveau.
Neue Funktionen in den Bereichen Grafik, Geometrie und Diagramme
Grafik war schon immer eine Stärke der Wolfram Language, und im letzten Jahrzehnt haben wir auch sehr leistungsfähige Funktionen für rechnerische Geometrie aufgebaut. Version 14.2 legt nun noch etwas „Zuckerguss obendrauf“, insbesondere durch bessere Verbindungen zwischen Grafik und Geometrie sowie zwischen Geometrie und anderen Teilen des Systems.
Zum Beispiel erweitert Version 14.2 die Geometrie-Funktionen auf viele Objekte, die zuvor nur als Grafikprimitive galten. So ist hier etwa eine geometrische Region zu sehen, die durch das Ausfüllen einer Bezier-Kurve entsteht:

Und wir können jetzt alle unsere üblichen rechnerischen Geometrieoperationen darauf anwenden:

So etwas funktioniert jetzt auch:

Eine weitere Neuerung in Version 14.2 ist MoleculeMesh, mit dem du aus molekularen Strukturen berechenbare Geometrien erstellen kannst. Hier siehst du eine grafische Darstellung eines Moleküls:

Und hier ist nun ein geometrisches Netz (Mesh), das dem Molekül entspricht:

Wir können dann eine rechnerische Geometrie für dieses Netz erstellen:


Eine weitere neue Funktion in Version 14.2 ist eine zusätzliche Methode zum Zeichnen von Diagrammen, die Symmetrien nutzen kann. Wenn Sie ein geschichtetes Diagramm aus einem symmetrischen Gitter erstellen, wird es nicht sofort symmetrisch gerendert:

Aber mit dem neuen Graph-Layout „SymmetricLayeredEmbedding“ funktioniert es:

Optimierungen der Benutzeroberfläche
Bei der Entwicklung einer großartigen Benutzeroberfläche geht es immer darum, kontinuierlich zu feilen, und das tun wir nun schon seit fast vier Jahrzehnten für die Benutzeroberfläche von Notebooks. In Version 14.2 wurden mehrere bemerkenswerte Verbesserungen hinzugefügt. Eine davon betrifft die automatische Vervollständigung für Optionswerte.
Wir haben schon lange automatische Vervollständigungen für Optionen angeboten, die eine diskrete Menge von festen, häufig verwendeten Einstellungen haben (wie zum Beispiel All, Automatic usw.). In Version 14.2 fügen wir sogenannte „Template Completions“ hinzu, die die Struktur der Einstellungen anzeigen und es dir ermöglichen, per Tab-Taste gezielt Werte auszufüllen. In all den Jahren ist einer der Bereiche, in die ich in der Dokumentation fast immer schaue, die Einstellungen für FrameLabel. Jetzt zeigt mir die Autovervollständigung sofort die Struktur dieser Einstellungen an:
Außerdem haben wir in der Autovervollständigung die Möglichkeit hinzugefügt, Kontextnamen, Kontext-Aliasnamen und Symbole, die Kontexte enthalten, automatisch zu vervollständigen. Dabei ist die Autovervollständigung „fuzzy“ – das heißt, sie reagiert nicht nur auf Zeichen am Anfang eines Namens, sondern auf Zeichen an beliebiger Stelle im Namen. So kannst du einfach einzelne Zeichen eines Symbols eingeben, und passende Kontexte werden als Vorschläge angezeigt.
Eine weitere kleine Neuerung in Version 14.2 ist, dass du Bilder jetzt von einem Notebook in ein anderes ziehen kannst – oder auch in jede andere Anwendung, die das Einfügen von Bildern per Drag & Drop unterstützt. Bisher war es nur möglich, Bilder aus anderen Anwendungen in Notebooks zu ziehen, jetzt geht es auch andersherum.
Etwas, das momentan nur für macOS gilt, ist die verbesserte Unterstützung für Icon-Vorschauen (sowie Quick Look). Wenn du jetzt zum Beispiel in einem Ordner mit vielen Notebooks die Symbolansicht (Icon View) wählst, siehst du für jedes Notebook ein kleines Vorschaubild, das den Inhalt repräsentiert:
Unter der Haube gibt es in Version 14.2 auch einige grundlegende Infrastrukturentwicklungen, die in zukünftigen Versionen bedeutende neue Funktionen ermöglichen werden. Einige davon betreffen die allgemeine Unterstützung für den Dunkelmodus (Dark Mode). (Ja, man könnte zunächst denken, Dunkelmodus sei trivial, aber wenn man alle Grafiken und Bedienelemente mit Farben betrachtet, merkt man schnell, dass dem nicht so ist. Zum Beispiel haben wir nach erheblichem Aufwand kürzlich den Dunkelmodus für Wolfram|Alpha veröffentlicht.)
So gibt es in Version 14.2 das neue Symbol LightDarkSwitched, das Teil des Mechanismus ist, mit dem Stile definiert werden können, die sich automatisch für Hell- und Dunkelmodus umschalten. Außerdem gibt es die Stiloption LightDark, mit der man den Modus für Notebooks umschalten kann – diese Option wird zumindest experimentell unterstützt.
Eng verbunden mit Hell-/Dunkelmodus ist auch das Konzept der Theme Colors: Farben, die symbolisch definiert sind und gemeinsam umgeschaltet werden können. Dafür gibt es in Version 14.2 das experimentelle Symbol ThemeColor. Die vollständige Umsetzung dieses Mechanismus wird allerdings erst in der nächsten Version erfolgen.
Die Anfänge der nativen GPU-Nutzung
Viele wichtige Funktionen der Wolfram Language nutzen automatisch GPUs, wenn diese verfügbar sind. Bereits vor 15 Jahren haben wir grundlegende Bausteine für GPU-Programmierung eingeführt. Mit Version 14.2 beginnen wir nun, die GPU-Fähigkeiten leichter zugänglich zu machen, um die allgemeine Nutzung der Wolfram Language zu optimieren. Das zentrale neue Konstrukt ist GPUArray, das ein Datenarray repräsentiert, das – wenn möglich – so gespeichert wird, dass es sofort und direkt von der GPU genutzt werden kann. (Auf manchen Systemen wird es im separaten „GPU-Speicher“ abgelegt, auf anderen, wie modernen Macs, im gemeinsamen Speicher, sodass die GPU direkt darauf zugreifen kann.)
In Version 14.2 unterstützen wir eine erste Auswahl von Operationen, die direkt auf GPU-Arrays ausgeführt werden können. Die verfügbaren Operationen variieren je nach GPU-Typ etwas. Im Laufe der Zeit erwarten wir, viele weitere GPU-Bibliotheken zu verwenden oder zu entwickeln, die die Menge der auf GPU-Arrays möglichen Operationen erweitern.
Hier ein zufälliger Vektor mit zehn Millionen Elementen, gespeichert als GPU-Array:

Die GPU des Macs, auf dem ich dies schreibe, unterstützt die erforderlichen Operationen, sodass diese Berechnung vollständig auf der GPU ausgeführt wird und als Ergebnis ein GPUArray zurückgegeben wird:

Hier ist die Zeitmessung:

Und hier ist das entsprechende normale Ergebnis, das auf der CPU berechnet wurde:

In diesem Fall ist das Ergebnis mit GPUArray etwa doppelt so schnell wie die herkömmliche CPU-Berechnung. Wie viel schneller es genau ist, hängt von den ausgeführten Operationen und der verwendeten Hardware ab. Die größten Beschleunigungen, die ich bisher gesehen habe, liegen bei etwa dem Zehnfachen. Mit dem Ausbau weiterer GPU-Bibliotheken erwarte ich, dass diese Faktoren noch steigen – besonders wenn viele Berechnungen „direkt auf der GPU“ stattfinden und nicht zu viel Speicherzugriff erforderlich ist.
Übrigens: Wenn du GPUArray in deinem Code verwendest, beeinflusst das normalerweise nicht das Ergebnis, da Operationen standardmäßig auf der CPU ausgeführt werden, falls sie auf der GPU nicht unterstützt werden. (In der Regel beschleunigt GPUArray die Berechnungen, aber wenn zu viele „GPU-Misses“ auftreten, kann der Datenübertragungsaufwand sogar zu einer Verlangsamung führen.) Es ist wichtig zu wissen, dass GPU-Computing noch nicht einheitlich standardisiert ist. Manchmal werden nur Vektoren unterstützt, manchmal auch Matrizen – und verschiedene Datentypen mit unterschiedlichen numerischen Genauigkeiten können je nach Fall unterschiedlich unterstützt sein.
Und noch mehr…
Neben all den bisher besprochenen Neuerungen gibt es in Version 14.2 auch eine Vielzahl weiterer „kleiner“ neuer Funktionen. Auch wenn sie im Vergleich zu den anderen Features klein erscheinen mögen, können sie sehr nützlich sein, wenn genau diese Funktionalität benötigt wird.
Ein Beispiel dafür ist MidDate – eine Funktion, die den Mittelpunkt zwischen zwei Daten berechnet:

Und wie bei fast allem, was mit Daten zu tun hat, steckt auch in MidDate so manches Detail. Hier berechnet es die Woche, die zwei Drittel durch das Jahr 2025 liegt:

In der Mathematik können Funktionen wie DSolve und SurfaceIntegrate jetzt mit symbolischen Array-Variablen umgehen:

SumConvergence ermöglicht jetzt, den Summationsbereich anzugeben und Bedingungen zu definieren, die von diesem Bereich abhängen:

Eine kleine, aber praktische Neuerung, die ich tatsächlich angefragt habe: DigitCount erlaubt jetzt, anzugeben, wie viele Stellen eine Zahl insgesamt haben soll, sodass auch führende Nullen korrekt mitgezählt werden:

Apropos Komfortfunktionen: Für Funktionen wie MaximalBy und TakeLargest haben wir ein neues Argument hinzugefügt, mit dem festgelegt werden kann, wie Elemente sortiert werden, um „das Grösste“ zu bestimmen. Hier ist die standardmässige numerische Reihenfolge:

Und so sieht das Ergebnis aus, wenn wir stattdessen die „symbolische Ordnung“ verwenden:

Es gibt immer viele Details, die man noch verfeinern kann. So gibt es in Version 14.2 ein Update für MoonPhase und verwandte Funktionen – mit neuen Abfragemöglichkeiten und neuen Berechnungsmethoden:


In einem anderen Bereich gibt es neben wichtigen neuen Import-/Exportformaten (insbesondere zur Unterstützung von Tabular) ein Update für den "Markdown"-Import, das Ergebnisse als reinen Text liefert, sowie ein Update für den "PDF"-Import, das eine gemischte Liste aus Text und Bildern zurückgibt.
Und es gibt noch viele weitere Neuerungen, wie du im Abschnitt „Zusammenfassung der neuen und verbesserten Funktionen in 14.2“ nachlesen kannst. Übrigens: Wenn du dir eine bestimmte Dokumentationsseite zu einer Funktion anschaust, kannst du mit einem Klick auf „Änderungen anzeigen“ immer sehen, was in dieser Version neu dazugekommen ist.