<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Projects on Benjamin Geer</title>
    <link>https://benjamingeer.eu/en/categories/projects/</link>
    <description>Recent content in Projects on Benjamin Geer</description>
    <generator>Hugo</generator>
    <language>en-GB</language>
    <lastBuildDate>Wed, 19 Aug 2026 15:06:58 +0000</lastBuildDate>
    <atom:link href="https://benjamingeer.eu/en/categories/projects/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Comparing the performance of SQL queries</title>
      <link>https://benjamingeer.eu/en/post/sqlstopwatch/</link>
      <pubDate>Thu, 29 Jan 2026 18:25:18 +0100</pubDate>
      <guid>https://benjamingeer.eu/en/post/sqlstopwatch/</guid>
      <description>&lt;p&gt;I wrote a little Rust program,&#xA;&lt;a href=&#34;https://codeberg.org/benjamingeer/sqlstopwatch&#34; target=&#34;_blank&#34;&gt;sqlstopwatch&lt;/a&gt;, to compare the&#xA;performance of different &lt;a href=&#34;https://en.wikipedia.org/wiki/SQL&#34; target=&#34;_blank&#34;&gt;SQL&lt;/a&gt; queries. I&#xA;was inspired by the article &lt;a href=&#34;https://www.jooq.org/benchmark&#34; target=&#34;_blank&#34;&gt;Benchmarking SQL&lt;/a&gt;&#xA;published by the jOOQ project, where they write the test code in SQL. I wanted&#xA;something similar, but with a few additional requirements:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;A command-line program with a simple&#xA;&lt;a href=&#34;https://en.wikipedia.org/wiki/Text-based_user_interface&#34; target=&#34;_blank&#34;&gt;TUI&lt;/a&gt; showing a&#xA;progress bar.&lt;/li&gt;&#xA;&lt;li&gt;The program should support &lt;a href=&#34;https://www.postgresql.org&#34; target=&#34;_blank&#34;&gt;PostgreSQL&lt;/a&gt;,&#xA;&lt;a href=&#34;https://www.mysql.com&#34; target=&#34;_blank&#34;&gt;MySQL&lt;/a&gt;, and &lt;a href=&#34;https://sqlite.org&#34; target=&#34;_blank&#34;&gt;SQLite&lt;/a&gt;.&lt;/li&gt;&#xA;&lt;li&gt;It should read the SQL queries and test parameters from a configuration file&#xA;specified on the command line.&lt;/li&gt;&#xA;&lt;li&gt;It should print the results as a table in the terminal or save them as a CSV&#xA;file that I can use to generate a chart.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;The TUI was easy to make with &lt;a href=&#34;https://ratatui.rs&#34; target=&#34;_blank&#34;&gt;Ratatui&lt;/a&gt;. To interact with&#xA;the database, I used the async database library&#xA;&lt;a href=&#34;https://crates.io/crates/sqlx&#34; target=&#34;_blank&#34;&gt;sqlx&lt;/a&gt;, which provides a bundled SQLite&#xA;library.&lt;/p&gt;&#xA;&lt;p&gt;I considered using &lt;a href=&#34;https://toml.io&#34; target=&#34;_blank&#34;&gt;TOML&lt;/a&gt; for the config file, but the file&#xA;has to specify the database credentials. In testing environments, credentials&#xA;may have to be loaded from environment variables or from files on a mounted file&#xA;system, but TOML doesn&amp;rsquo;t support carrying out those sorts of operations.&#xA;Moreover, you might want to generate a number of similar queries and compare&#xA;them, as I did in &lt;a href=&#34;https://benjamingeer.eu/en/post/bref&#34;&gt;this database optimisation project&lt;/a&gt;.&#xA;Therefore, it&amp;rsquo;s better if the config file is a program in a scripting language.&#xA;My &lt;a href=&#34;https://benjamingeer.eu/en/post/organiclox&#34;&gt;exercise in writing an interpreter for a scripting&#xA;language&lt;/a&gt; had given me a new appreciation for the&#xA;&lt;a href=&#34;https://www.lua.org&#34; target=&#34;_blank&#34;&gt;Lua&lt;/a&gt; virtual machine. So the config file is a Lua&#xA;script, which I find more convenient to write in&#xA;&lt;a href=&#34;https://fennel-lang.org&#34; target=&#34;_blank&#34;&gt;Fennel&lt;/a&gt;, a Lisp dialect that compiles to Lua. Thanks&#xA;to the &lt;a href=&#34;https://crates.io/crates/mlua&#34; target=&#34;_blank&#34;&gt;mlua&lt;/a&gt; library, it was very easy to&#xA;embed a Lua interpreter in the program, and to use &lt;a href=&#34;https://serde.rs&#34; target=&#34;_blank&#34;&gt;Serde&lt;/a&gt;&#xA;to convert Lua tables into Rust &lt;code&gt;struct&lt;/code&gt;s.&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Writing a Bittorrent client in Rust</title>
      <link>https://benjamingeer.eu/en/post/sayaca/</link>
      <pubDate>Thu, 15 Jan 2026 15:16:01 +0100</pubDate>
      <guid>https://benjamingeer.eu/en/post/sayaca/</guid>
      <description>&lt;h2 class=&#34;heading&#34; id=&#34;overview&#34;&gt;&#xA;  Overview&lt;span class=&#34;heading__anchor&#34;&gt; &lt;a href=&#34;#overview&#34;&gt;#&lt;/a&gt;&lt;/span&gt;&#xA;&lt;/h2&gt;&lt;p&gt;To continue learning &lt;a href=&#34;https://benjamingeer.eu/en/topics/rust/&#34;&gt;Rust&lt;/a&gt;, I decided to implement a client&#xA;for &lt;a href=&#34;https://en.wikipedia.org/wiki/BitTorrent&#34; target=&#34;_blank&#34;&gt;Bittorrent&lt;/a&gt;, the communication&#xA;protocol for peer-to-peer file sharing. The Bittorrent clients I&amp;rsquo;ve used have&#xA;complex GUIs that seem to be designed for users who download and upload torrents&#xA;all day, rather than for the occasional user like me, who just wants to get one&#xA;file. And like many GUI applications, Bittorrent clients tend to accumulate&#xA;files that take up disk space in obscure directories. I do many things in the&#xA;terminal, and when I need to download a file, my reflex is to use&#xA;&lt;a href=&#34;https://curl.se/&#34; target=&#34;_blank&#34;&gt;curl&lt;/a&gt;. So I thought, why not make something like curl for&#xA;Bittorrent? Thinking about curl&amp;rsquo;s &lt;a href=&#34;https://curl.se/docs/tutorial.html#:~:text=www.example.com-,Progress%20Meter,-The%20progress%20meter&#34; target=&#34;_blank&#34;&gt;progress&#xA;meter&lt;/a&gt;,&#xA;it occurred to me that I could do something similar with the TUI library&#xA;&lt;a href=&#34;https://ratatui.rs&#34; target=&#34;_blank&#34;&gt;Ratatui&lt;/a&gt;, which I&amp;rsquo;d been wanting to try.&lt;/p&gt;&#xA;&lt;p&gt;A Bittorrent client seemed like a good opportunity to learn about asynchronous&#xA;programming in Rust. I had read a lot of criticism of Rust&amp;rsquo;s &lt;code&gt;async&lt;/code&gt;, but I&#xA;found it pleasant to work with (using the &lt;a href=&#34;https://tokio.rs&#34; target=&#34;_blank&#34;&gt;Tokio&lt;/a&gt; runtime),&#xA;and certainly more comfortable than the comparable frameworks that I&amp;rsquo;d used in&#xA;Scala (&lt;a href=&#34;https://akka.io&#34; target=&#34;_blank&#34;&gt;Akka&lt;/a&gt; and &lt;a href=&#34;https://typelevel.org/cats-effect/&#34; target=&#34;_blank&#34;&gt;Cats&#xA;Effect&lt;/a&gt;). I particularly like that an&#xA;&lt;code&gt;async&lt;/code&gt; function can be run in an existing task or in its own task, unlike&#xA;Scala&amp;rsquo;s &lt;code&gt;Future&lt;/code&gt;, which is assigned a thread and starts running as soon as it&amp;rsquo;s&#xA;constructed. Rust&amp;rsquo;s approach seems more conducive to refactoring and&#xA;maintainability. At the same time, I&amp;rsquo;m glad not to have to deal with the&#xA;complexity of using monads as in Cats Effect.&lt;/p&gt;&#xA;&lt;p&gt;The result is &lt;a href=&#34;https://codeberg.org/benjamingeer/sayaca&#34; target=&#34;_blank&#34;&gt;Sayaca&lt;/a&gt;, named after&#xA;a South American bird that plays an important role in seed dispersal (in&#xA;Bittorrent terminology, &amp;lsquo;seeding&amp;rsquo; means uploading).&lt;/p&gt;&#xA;&lt;h2 class=&#34;heading&#34; id=&#34;design&#34;&gt;&#xA;  Design&lt;span class=&#34;heading__anchor&#34;&gt; &lt;a href=&#34;#design&#34;&gt;#&lt;/a&gt;&lt;/span&gt;&#xA;&lt;/h2&gt;&lt;p&gt;My first task was to implement a parser and formatter for&#xA;&lt;a href=&#34;https://en.wikipedia.org/wiki/Bencode&#34; target=&#34;_blank&#34;&gt;Bencode&lt;/a&gt;, the data format used in the&#xA;Bittorrent protocol. Conceptually it&amp;rsquo;s similar to&#xA;&lt;a href=&#34;https://en.wikipedia.org/wiki/JSON&#34; target=&#34;_blank&#34;&gt;JSON&lt;/a&gt;, but it&amp;rsquo;s designed for binary data&#xA;rather than text. I wanted to try using a parser combinator library, and had no&#xA;trouble implementing the parser using&#xA;&lt;a href=&#34;https://github.com/rust-bakery/nom&#34; target=&#34;_blank&#34;&gt;nom&lt;/a&gt;. Since I wanted to be able to&#xA;serialise and deserialise Rust &lt;code&gt;struct&lt;/code&gt;s as Bencode, I added&#xA;&lt;a href=&#34;https://serde.rs&#34; target=&#34;_blank&#34;&gt;Serde&lt;/a&gt; support. I&amp;rsquo;ve published the resulting crate as&#xA;&lt;a href=&#34;https://crates.io/crates/bside&#34; target=&#34;_blank&#34;&gt;bside&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;The main difficulty in designing the client was the messy state of the&#xA;Bittorrent specifications. There are ambiguities (the word &amp;lsquo;piece&amp;rsquo; means two&#xA;different things) and implementations have diverged from the specs (e.g. peer&#xA;IDs are not used to identify peers). A great deal of knowledge about the&#xA;protocol is not in the official specs, but in other documents scattered around&#xA;the Web. There is a &lt;a href=&#34;https://www.bittorrent.org/beps/bep_0000.html&#34; target=&#34;_blank&#34;&gt;long list&lt;/a&gt;&#xA;(not updated since 2018) of draft protocol extensions that are supported by many&#xA;clients. A so-called &lt;a href=&#34;https://en.wikipedia.org/wiki/BitTorrent_protocol_encryption&#34; target=&#34;_blank&#34;&gt;encryption&#xA;extension&lt;/a&gt; (which&#xA;actually seems to describe a type of obfuscation) seems to be widely&#xA;implemented, but as far as I can tell, since 2023, &lt;a href=&#34;https://web.archive.org/web/20230405235517/http://wiki.vuze.com/w/Message_Stream_Encryption&#34; target=&#34;_blank&#34;&gt;its&#xA;specification&lt;/a&gt;&#xA;has been preserved only in the Internet Archive&amp;rsquo;s Wayback Machine. There is a&#xA;draft &lt;a href=&#34;https://bittorrent.org/beps/bep_0052.html&#34; target=&#34;_blank&#34;&gt;BitTorrent Protocol Specification&#xA;v2&lt;/a&gt; (not updated since 2017), which&#xA;attempts to correct some of the original protocol&amp;rsquo;s flaws, such as its reliance&#xA;on the &lt;a href=&#34;https://en.wikipedia.org/wiki/SHA-1&#34; target=&#34;_blank&#34;&gt;SHA-1&lt;/a&gt; hash function (which has&#xA;not been considered secure since 2005). I could have wandered for a long time in&#xA;a labyrinth of documents and in the source code of existing clients. To keep&#xA;things simple, I decided to implement only the original &lt;a href=&#34;https://bittorrent.org/beps/bep_0003.html&#34; target=&#34;_blank&#34;&gt;BitTorrent Protocol&#xA;Specification&lt;/a&gt; and the &lt;a href=&#34;https://www.bittorrent.org/beps/bep_0007.html&#34; target=&#34;_blank&#34;&gt;IPv6&#xA;tracker extension&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;When saving data to disk, a Bittorrent client should perform well with large&#xA;files, be able to interrupt a download and resume it later (which means that it&#xA;has to note which pieces have been downloaded), and handle torrents that include&#xA;multiple files. To meet these requirements, and to keep things transparent for&#xA;the user, I chose to save downloaded data in an intermediate file (with the&#xA;extension &lt;code&gt;.sayaca&lt;/code&gt;), in the same directory where the completed file(s) will be&#xA;saved. The intermediate file contains a bit field indicating which pieces have&#xA;been downloaded (the same bit field that we have to send to peers anyway).&#xA;Sayaca uses memory mapping to read and write this file; this should provide good&#xA;I/O performance with large files. When the download is complete, it copies the&#xA;data from the intermediate file into the destination file(s), and the&#xA;intermediate file is deleted on exit if it is no longer needed.&lt;/p&gt;&#xA;&lt;p&gt;Since a Bittorrent client is I/O-bound, I decided to use &lt;a href=&#34;https://codeberg.org/benjamingeer/sayaca/src/branch/main/docs/design.md&#34; target=&#34;_blank&#34;&gt;an event-driven&#xA;architecture&lt;/a&gt;&#xA;in which one task does most of the work, as in &lt;a href=&#34;https://redis.io&#34; target=&#34;_blank&#34;&gt;Redis&lt;/a&gt;. This&#xA;main task delegates all the network I/O to other tasks, which it communicates&#xA;with via &lt;a href=&#34;https://docs.rs/tokio/latest/tokio/sync/&#34; target=&#34;_blank&#34;&gt;channels&lt;/a&gt;. This was simple&#xA;to implement, and had the advantage of making it easy to write unit tests for&#xA;the main task.&lt;/p&gt;&#xA;&lt;p&gt;Much of the work that a Bittorrent client does amounts to a sort of bookkeeping.&#xA;In Sayaca, this job is the responsibility of the&#xA;&lt;a href=&#34;https://codeberg.org/benjamingeer/sayaca/src/branch/main/src/client/bookkeeper.rs&#34; target=&#34;_blank&#34;&gt;&lt;code&gt;Bookkeeper&lt;/code&gt;&lt;/a&gt;.&#xA;For example, in the &lt;a href=&#34;https://wiki.theory.org/BitTorrentSpecification#Piece_downloading_strategy&#34; target=&#34;_blank&#34;&gt;recommended&#xA;algorithm&lt;/a&gt;&#xA;for selecting file pieces to request, the client tries to request the rarest&#xA;pieces first. This requires a data structure that keeps track of the pieces that&#xA;each peer has, as well has how rare each piece is, among other things. This can&#xA;be done in different ways; I chose to use an &lt;a href=&#34;https://doc.rust-lang.org/std/collections/struct.BTreeMap.html&#34; target=&#34;_blank&#34;&gt;ordered&#xA;map&lt;/a&gt;, ordered by&#xA;rarity, to store the IDs of pieces and the sets of peers that have them. The&#xA;&lt;code&gt;Bookkeeper&lt;/code&gt; can then easily find a piece, randomly chosen from among the rarest&#xA;pieces, along with a randomly chosen peer that has that piece and is accepting&#xA;requests from us. Thus, much of the client&amp;rsquo;s work involves updating the&#xA;&lt;code&gt;Bookkeeper&lt;/code&gt; when new information arrives and querying it to find out what to do&#xA;next.&lt;/p&gt;&#xA;&lt;p&gt;I like TUIs, and it was easy to make a simple one with Ratatui. But since the&#xA;client is a library that communicates with its UI via channels, it would be&#xA;straightforward to wrap the client in a different UI, e.g. a &lt;a href=&#34;https://xogium.me/the-text-mode-lie-why-modern-tuis-are-a-nightmare-for-accessibility&#34; target=&#34;_blank&#34;&gt;more&#xA;accessible&lt;/a&gt;&#xA;CLI.&lt;/p&gt;&#xA;&lt;h2 class=&#34;heading&#34; id=&#34;alternatives-to-this-approach&#34;&gt;&#xA;  Alternatives to this approach&lt;span class=&#34;heading__anchor&#34;&gt; &lt;a href=&#34;#alternatives-to-this-approach&#34;&gt;#&lt;/a&gt;&lt;/span&gt;&#xA;&lt;/h2&gt;&lt;p&gt;I chose to use Tokio because I wanted to learn about it and about &lt;code&gt;async&lt;/code&gt;, but I&#xA;think the program probably wouldn&amp;rsquo;t have been very different if I had used&#xA;threads instead, with the same communication via channels.&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Writing a virtual machine in Rust</title>
      <link>https://benjamingeer.eu/en/post/organiclox/</link>
      <pubDate>Sun, 12 Oct 2025 00:00:00 +0000</pubDate>
      <guid>https://benjamingeer.eu/en/post/organiclox/</guid>
      <description>&lt;h2 class=&#34;heading&#34; id=&#34;overview&#34;&gt;&#xA;  Overview&lt;span class=&#34;heading__anchor&#34;&gt; &lt;a href=&#34;#overview&#34;&gt;#&lt;/a&gt;&lt;/span&gt;&#xA;&lt;/h2&gt;&lt;p&gt;To help me learn Rust, I decided to implement an interpreter for Lox, the&#xA;scripting language in Robert Nystrom&amp;rsquo;s book &lt;a href=&#34;https://craftinginterpreters.com/&#34; target=&#34;_blank&#34;&gt;&lt;em&gt;Crafting&#xA;Interpreters&lt;/em&gt;&lt;/a&gt;. I&amp;rsquo;ve been interested in&#xA;scripting languages that are designed to be embedded in applications, and this&#xA;seemed like a good way to learn more about how such languages are made.&lt;/p&gt;&#xA;&lt;p&gt;Nystrom&amp;rsquo;s book walks you through two different implementations of the&#xA;interpreter: a tree-walk interpreter in Java, and a bytecode virtual machine in&#xA;C. I was more interested in the bytecode VM. Over the years, I&amp;rsquo;d written a&#xA;couple of tree-walk interpreters, starting with &lt;a href=&#34;https://freemarker.apache.org/&#34; target=&#34;_blank&#34;&gt;Apache&#xA;FreeMarker&lt;/a&gt;, as well as &lt;a href=&#34;https://doi.org/10.3233/sw-200386&#34; target=&#34;_blank&#34;&gt;an optimising&#xA;source-to-source compiler&lt;/a&gt; with type&#xA;inference. But I hadn&amp;rsquo;t implemented a bytecode VM yet. The idea of it brought&#xA;back good memories of doing assembly-language programming on the &lt;a href=&#34;https://en.wikipedia.org/wiki/Apple_II&#34; target=&#34;_blank&#34;&gt;Apple&#xA;II&lt;/a&gt; when I was fifteen years old. The&#xA;instruction set for Nystrom&amp;rsquo;s virtual machine is simpler than the instruction&#xA;set of the Apple II&amp;rsquo;s &lt;a href=&#34;https://en.wikipedia.org/wiki/MOS_Technology_6502&#34; target=&#34;_blank&#34;&gt;Motorola&#xA;6502&lt;/a&gt; CPU, but the basic idea&#xA;is the same. So I skipped the part of his book about the tree-walk interpreter&#xA;and went directly to the part about the bytecode VM.&lt;/p&gt;&#xA;&lt;p&gt;The result is &lt;a href=&#34;https://codeberg.org/benjamingeer/organiclox&#34; target=&#34;_blank&#34;&gt;OrganicLox&lt;/a&gt;. The&#xA;design is broadly similar to the one in the book, but I&amp;rsquo;ve tried to write&#xA;idiomatic Rust, with reasonable performance and no unsafe code. Functionally,&#xA;OrganicLox should be identical to Nystrom&amp;rsquo;s &lt;code&gt;clox&lt;/code&gt;: at least, it passes his&#xA;&lt;a href=&#34;https://github.com/munificent/craftinginterpreters/?tab=readme-ov-file#testing-your-implementation&#34; target=&#34;_blank&#34;&gt;246 automated&#xA;tests&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;I decided against writing my own garbage collector, because everyone seems to&#xA;agree that garbage collectors are &lt;a href=&#34;https://manishearth.github.io/blog/2021/04/05/a-tour-of-safe-tracing-gc-designs-in-rust/&#34; target=&#34;_blank&#34;&gt;particularly difficult to implement in&#xA;Rust&lt;/a&gt;.&#xA;To keep things simple, I used reference counting instead (via&#xA;&lt;a href=&#34;https://doc.rust-lang.org/std/rc/struct.Rc.html&#34; target=&#34;_blank&#34;&gt;Rc&lt;/a&gt;). On the&#xA;&lt;a href=&#34;https://codeberg.org/benjamingeer/organiclox/src/branch/main&#34; target=&#34;_blank&#34;&gt;&lt;code&gt;main&lt;/code&gt;&lt;/a&gt; branch,&#xA;this means that cycles of object properties won&amp;rsquo;t be collected. On the&#xA;&lt;a href=&#34;https://codeberg.org/benjamingeer/organiclox/src/branch/with_dumpster_gc&#34; target=&#34;_blank&#34;&gt;&lt;code&gt;with_dumpster_gc&lt;/code&gt;&lt;/a&gt;&#xA;branch, I&amp;rsquo;ve replaced &lt;code&gt;Rc&lt;/code&gt; with the&#xA;&lt;a href=&#34;https://codeberg.org/benjamingeer/organiclox/src/branch/main&#34; target=&#34;_blank&#34;&gt;dumpster&lt;/a&gt; crate,&#xA;which uses reference counting but also deals with cycles.&lt;/p&gt;&#xA;&lt;h2 class=&#34;heading&#34; id=&#34;my-favourite-parts&#34;&gt;&#xA;  My favourite parts&lt;span class=&#34;heading__anchor&#34;&gt; &lt;a href=&#34;#my-favourite-parts&#34;&gt;#&lt;/a&gt;&lt;/span&gt;&#xA;&lt;/h2&gt;&lt;p&gt;I liked this book very much: Nystrom&amp;rsquo;s clear explanations, useful illustrations,&#xA;easygoing writing style, and humour made the book a pleasure to read.&lt;/p&gt;&#xA;&lt;p&gt;For me, some of the most interesting parts of his design, which draws heavily on&#xA;&lt;a href=&#34;https://www.lua.org/doc/jucs05.pdf&#34; target=&#34;_blank&#34;&gt;the design of the Lua VM&lt;/a&gt;, involved the&#xA;implementation of local variables and closures in a stack-based VM. I enjoyed&#xA;learning that the compiler &lt;a href=&#34;https://craftinginterpreters.com/local-variables.html&#34; target=&#34;_blank&#34;&gt;associates local variables with specific positions&#xA;on the stack&lt;/a&gt;, so the VM&#xA;only needs to know about stack positions, not about the variable names they&amp;rsquo;re&#xA;associated with.&lt;/p&gt;&#xA;&lt;p&gt;The implementation of&#xA;&lt;a href=&#34;https://craftinginterpreters.com/closures.html#upvalues&#34; target=&#34;_blank&#34;&gt;upvalues&lt;/a&gt;, the&#xA;variables that are captured by closures (and may need to be heap-allocated as a&#xA;result), was tricky to get right. While fixing a bug, I was delighted to realise&#xA;that &lt;a href=&#34;https://github.com/munificent/craftinginterpreters/blob/master/test/function/local_recursion.lox&#34; target=&#34;_blank&#34;&gt;a recursive call to a function defined in a local&#xA;scope&lt;/a&gt;&#xA;requires the function&amp;rsquo;s closure to be one of its own upvalues.&lt;/p&gt;&#xA;&lt;h2 class=&#34;heading&#34; id=&#34;what-i-wished-for&#34;&gt;&#xA;  What I wished for&lt;span class=&#34;heading__anchor&#34;&gt; &lt;a href=&#34;#what-i-wished-for&#34;&gt;#&lt;/a&gt;&lt;/span&gt;&#xA;&lt;/h2&gt;&lt;p&gt;Nystrom alternates between top-down and bottom-up explanations. The bottom-up&#xA;explanations made it more difficult to translate his design into Rust, since I&#xA;had to guess where he was going. Several times I had to refactor a lot of code&#xA;because I had guessed incorrectly. Since implementing Lox in all sorts of&#xA;programming languages seems to be &lt;a href=&#34;https://github.com/munificent/craftinginterpreters/wiki/Lox-implementations&#34; target=&#34;_blank&#34;&gt;a popular&#xA;exercise&lt;/a&gt;,&#xA;it would have been helpful to have more high-level notes about the design.&lt;/p&gt;&#xA;&lt;p&gt;It was fun to learn about Vaughan Pratt&amp;rsquo;s &lt;a href=&#34;https://craftinginterpreters.com/compiling-expressions.html&#34; target=&#34;_blank&#34;&gt;top-down operator precedence parsing&#xA;technique&lt;/a&gt; and to&#xA;see that it&amp;rsquo;s actually possible to generate bytecode without first building an&#xA;AST. But perhaps it would have been more useful to produce an AST and learn&#xA;something about writing a second pass to optimise the bytecode.&lt;/p&gt;&#xA;&lt;h2 class=&#34;heading&#34; id=&#34;performance&#34;&gt;&#xA;  Performance&lt;span class=&#34;heading__anchor&#34;&gt; &lt;a href=&#34;#performance&#34;&gt;#&lt;/a&gt;&lt;/span&gt;&#xA;&lt;/h2&gt;&lt;p&gt;The main optimisation I did at the outset was to have the scanner intern all&#xA;identifiers (using&#xA;&lt;a href=&#34;https://docs.rs/string-interner/latest/string_interner/&#34; target=&#34;_blank&#34;&gt;string_interner&lt;/a&gt;),&#xA;so neither the compiler nor the VM need to do any string&#xA;comparisons on identifiers.&lt;/p&gt;&#xA;&lt;p&gt;After finishing &lt;a href=&#34;https://craftinginterpreters.com/calls-and-functions.html&#34; target=&#34;_blank&#34;&gt;Chapter&#xA;24&lt;/a&gt;, I spent a bit of&#xA;time looking at performance, following the advice in &lt;a href=&#34;https://nnethercote.github.io/perf-book&#34; target=&#34;_blank&#34;&gt;The Rust Performance&#xA;Book&lt;/a&gt;. I ran benchmarks with&#xA;&lt;a href=&#34;https://docs.rs/criterion/latest/criterion/&#34; target=&#34;_blank&#34;&gt;criterion&lt;/a&gt;, did some profiling&#xA;with &lt;a href=&#34;https://perfwiki.github.io/main/&#34; target=&#34;_blank&#34;&gt;perf&lt;/a&gt; and&#xA;&lt;a href=&#34;https://github.com/KDAB/hotspot&#34; target=&#34;_blank&#34;&gt;hotspot&lt;/a&gt;, &lt;a href=&#34;https://nnethercote.github.io/perf-book/build-configuration.html#maximizing-runtime-speed&#34; target=&#34;_blank&#34;&gt;adjusted the build&#xA;configuration&lt;/a&gt;,&#xA;and &lt;a href=&#34;https://nnethercote.github.io/perf-book/inlining.html&#34; target=&#34;_blank&#34;&gt;inlined some&#xA;functions&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;I then looked at some &lt;a href=&#34;https://github.com/munificent/craftinginterpreters/wiki/Lox-implementations#rust&#34; target=&#34;_blank&#34;&gt;other Rust implementations of&#xA;Lox&lt;/a&gt;,&#xA;and noticed that everyone seems to have found it difficult to approach the&#xA;performance of Nystrom&amp;rsquo;s &lt;code&gt;clox&lt;/code&gt;, and that the implementations that succeeded in&#xA;doing so contained a lot of unsafe code, which I wanted to avoid. I highly&#xA;recommend Manuel Cerón&amp;rsquo;s &lt;a href=&#34;https://ceronman.com/blog/my-experience-crafting-an-interpreter-with-rust/&#34; target=&#34;_blank&#34;&gt;blog&#xA;post&lt;/a&gt;&#xA;about his impressive, determined, and partly successful attempt to match the&#xA;speed of &lt;code&gt;clox&lt;/code&gt; in Rust by writing more and more unsafe code.&lt;/p&gt;&#xA;&lt;p&gt;For comparison, here are the results of some of &lt;a href=&#34;https://github.com/munificent/craftinginterpreters/tree/master/test/benchmark&#34; target=&#34;_blank&#34;&gt;Nystrom&amp;rsquo;s&#xA;benchmarks&lt;/a&gt;&#xA;run on these implementations:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Nystrom&amp;rsquo;s &lt;code&gt;clox&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;The safe version of Cerón&amp;rsquo;s &lt;code&gt;loxido&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;The unsafe version of &lt;code&gt;loxido&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;OrganicLox with &lt;code&gt;Rc&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;OrganicLox with the &lt;code&gt;dumpster&lt;/code&gt; garbage collector&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;figure&gt;&lt;img src=&#34;https://benjamingeer.eu/en/post/organiclox/lox-benchmarks-en.svg&#34;&#xA;&#x9;&#x9;&#x9;alt=&#34;a bar chart of benchmark results&#34; width=&#34;100%&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&#xA;&lt;p&gt;OrganicLox with &lt;code&gt;Rc&lt;/code&gt; is two to four times slower than &lt;code&gt;clox&lt;/code&gt;, which is perhaps&#xA;in the typical range for interpreters written in Rust &lt;a href=&#34;https://github.com/raviqqe/stak/blob/939f7bae9f188027d2f9e2cdc62c3baa76be36ed/README.md?plain=1#L174&#34; target=&#34;_blank&#34;&gt;compared to interpreters&#xA;written in&#xA;C&lt;/a&gt;.&#xA;The version with &lt;code&gt;dumpster&lt;/code&gt; is much slower. It seems clear that a fast garbage&#xA;collector is essential for a high-performance VM.&lt;/p&gt;&#xA;&lt;p&gt;How much do these differences matter? Benchmarks are designed to simulate&#xA;extreme scenarios, e.g. by using long loops or deep recursion, or by creating a&#xA;lot of work for the garbage collector, so they&amp;rsquo;re not necessarily representative&#xA;of real use cases. The scripts run by applications with an embedded interpreter&#xA;are often short and do very little work. We might get a more realistic idea of&#xA;how a VM performs in such a scenario by running Nystrom&amp;rsquo;s integration test&#xA;suite, which runs 246 short Lox scripts and checks their output. Here&amp;rsquo;s a&#xA;comparison:&lt;/p&gt;&#xA;&lt;figure&gt;&lt;img src=&#34;https://benjamingeer.eu/en/post/organiclox/lox-tests-en.svg&#34;&#xA;&#x9;&#x9;&#x9;alt=&#34;a bar chart of test suite running times&#34; width=&#34;100%&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&#xA;&lt;p&gt;In an application where every CPU cycle counts, the differences in performance&#xA;between these different implementations could be very significant. But in other&#xA;use cases, they might not be.&lt;/p&gt;&#xA;&lt;h2 class=&#34;heading&#34; id=&#34;alternatives-to-this-approach&#34;&gt;&#xA;  Alternatives to this approach&lt;span class=&#34;heading__anchor&#34;&gt; &lt;a href=&#34;#alternatives-to-this-approach&#34;&gt;#&lt;/a&gt;&lt;/span&gt;&#xA;&lt;/h2&gt;&lt;p&gt;It&amp;rsquo;s tempting to try adding a just-in-time compiler (JIT) to improve the&#xA;performance of the interpreter, either by writing one from scratch or by using&#xA;an existing backend like &lt;a href=&#34;https://llvm.org/&#34; target=&#34;_blank&#34;&gt;LLVM&lt;/a&gt; or&#xA;&lt;a href=&#34;https://cranelift.dev/&#34; target=&#34;_blank&#34;&gt;Cranelift&lt;/a&gt;. Examples in Rust include&#xA;&lt;a href=&#34;https://blog.nlnetlabs.nl/introducing-roto-a-compiled-scripting-language-for-rust&#34; target=&#34;_blank&#34;&gt;Roto&lt;/a&gt;&#xA;and&#xA;&lt;a href=&#34;https://blog.nlnetlabs.nl/introducing-roto-a-compiled-scripting-language-for-rust/&#34; target=&#34;_blank&#34;&gt;Dora&lt;/a&gt;.&#xA;But even if you do that, it&amp;rsquo;s not easy to achieve high performance. In &lt;a href=&#34;https://blog.miguelgrinberg.com/post/python-3-14-is-here-how-fast-is-it&#34; target=&#34;_blank&#34;&gt;these&#xA;benchmarks&lt;/a&gt;,&#xA;Python&amp;rsquo;s new JIT has no measurable effect on performance (see also &lt;a href=&#34;https://dinfuehr.com/blog/dora-implementing-a-jit-compiler-with-rust/#benchmarks&#34; target=&#34;_blank&#34;&gt;these Dora&#xA;benchmark&#xA;results&lt;/a&gt;).&lt;/p&gt;&#xA;&lt;p&gt;That&amp;rsquo;s one reason to consider using an existing interpreter that already has&#xA;years of engineering invested in it, instead of creating a new one.&#xA;&lt;a href=&#34;https://www.lua.org/&#34; target=&#34;_blank&#34;&gt;Lua&lt;/a&gt; is one popular option, but it isn&amp;rsquo;t fast enough for&#xA;some applications. The widely used &lt;a href=&#34;https://luajit.org/&#34; target=&#34;_blank&#34;&gt;LuaJIT&lt;/a&gt; VM is an order&#xA;of magnitude faster than the standard Lua VM for some use cases, but it&amp;rsquo;s a&#xA;&lt;a href=&#34;https://www.polarsignals.com/blog/posts/2024/11/13/lua-unwinding&#34; target=&#34;_blank&#34;&gt;tracing JIT&lt;/a&gt;,&#xA;which has advantages and disadvantages. The Lua developers have an interesting&#xA;&lt;a href=&#34;https://www.inf.puc-rio.br/~roberto/docs/pallene-sblp.pdf&#34; target=&#34;_blank&#34;&gt;paper&lt;/a&gt; in which they&#xA;discuss the limitations of this approach, and propose a different solution (an&#xA;additional statically typed language, a bit like &lt;a href=&#34;https://cython.org/&#34; target=&#34;_blank&#34;&gt;Cython&lt;/a&gt;).&lt;/p&gt;&#xA;&lt;p&gt;For some use cases, if embedded scripts don&amp;rsquo;t do much work, their performance&#xA;might not be critical. If they do so much work that their performance is&#xA;insufficient, another option is not to embed an interpreter at all. Cloudflare,&#xA;which had invested heavily for over 20 years in scripts using LuaJIT embedded in&#xA;&lt;a href=&#34;https://openresty.org/en/&#34; target=&#34;_blank&#34;&gt;OpenResty&lt;/a&gt;, recently &lt;a href=&#34;https://blog.cloudflare.com/20-percent-internet-upgrade/&#34; target=&#34;_blank&#34;&gt;replaced them with Rust&#xA;code&lt;/a&gt;.&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Calculating centrality indices with the Paris public transport network</title>
      <link>https://benjamingeer.eu/en/post/paris-rail-centrality/</link>
      <pubDate>Fri, 29 Aug 2025 00:00:00 +0000</pubDate>
      <guid>https://benjamingeer.eu/en/post/paris-rail-centrality/</guid>
      <description>&lt;p&gt;I thought it would be interesting to try some basic &lt;a href=&#34;https://en.wikipedia.org/wiki/Network_theory&#34; target=&#34;_blank&#34;&gt;network analysis&lt;/a&gt; of the &lt;a href=&#34;https://www.ratp.fr/en/plans&#34; target=&#34;_blank&#34;&gt;Paris métro (rapid transit) and RER (commuter rail) systems&lt;/a&gt;, starting with some &lt;a href=&#34;https://en.wikipedia.org/wiki/Centrality&#34; target=&#34;_blank&#34;&gt;centrality&lt;/a&gt; indices. These are metrics that rank the nodes in a graph according to their importance, for some definition of importance. For example, in a transport network, the centrality of a station could give an indication of the likelihood that passengers will pass through that station as part of their itinerary. A public transport authority could use this information to gauge how inconvenient it would be if particular stations were closed, e.g. because of flooding or maintenance, and to help it identify the most effective ways of improving the resilience of the network. Here I&amp;rsquo;ll use &lt;a href=&#34;https://www.python.org&#34; target=&#34;_blank&#34;&gt;Python&lt;/a&gt;, &lt;a href=&#34;https://networkx.org&#34; target=&#34;_blank&#34;&gt;networkx&lt;/a&gt;, and &lt;a href=&#34;https://sqlite.org&#34; target=&#34;_blank&#34;&gt;SQLite&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;h2 class=&#34;heading&#34; id=&#34;constructing-the-graph&#34;&gt;&#xA;  Constructing the graph&lt;span class=&#34;heading__anchor&#34;&gt; &lt;a href=&#34;#constructing-the-graph&#34;&gt;#&lt;/a&gt;&lt;/span&gt;&#xA;&lt;/h2&gt;&lt;p&gt;Here I&amp;rsquo;ve chosen to combine the métro and RER into a single graph. The first challenge is to construct this graph as an edge list and a node list. The agencies that manage the Paris public transport networks don&amp;rsquo;t provide such a dataset, but they do provide a &lt;a href=&#34;https://www.data.gouv.fr/datasets/horaires-prevus-sur-les-lignes-de-transport-en-commun-dile-de-france-gtfs-datahub/&#34; target=&#34;_blank&#34;&gt;GTFS dataset&lt;/a&gt; representing the train schedules for the next 30 days. (Thanks to &lt;a href=&#34;https://masto.ai/@bovine3dom&#34; target=&#34;_blank&#34;&gt;Oliver Blanthorn&lt;/a&gt; for pointing this out.) The GTFS data represents the trips (sequences of stops) that trains are scheduled to take along their routes (métro or RER lines). To construct the edge list for the network, we can traverse each trip, storing an edge between each stop and the next one. Some trips go in one direction, others go in the opposite direction, and many don&amp;rsquo;t cover the whole line, but by traversing them all, we should build up a complete directed graph of the network. We&amp;rsquo;ll have to traverse about 64,000 trips, containing a total of nearly 1.5 million stops, and a lot of the data will be redundant for our purposes, but this doesn&amp;rsquo;t matter as long as we can process it in a reasonable amount of time.&lt;/p&gt;&#xA;&lt;p&gt;We therefore have to implement an &lt;a href=&#34;https://en.wikipedia.org/wiki/Extract,_transform,_load&#34; target=&#34;_blank&#34;&gt;ETL (Extract, transform, load)&lt;/a&gt; process. I wrote &lt;a href=&#34;https://framagit.org/benjamingeer/benjamingeer/-/tree/main/notebook-data/paris-rail-data/download-data.sh&#34; target=&#34;_blank&#34;&gt;a shell script&lt;/a&gt; that downloads the GTFS data, as well as several files from a dataset called &lt;a href=&#34;https://data.iledefrance-mobilites.fr/explore/dataset/referentiel-des-lignes/information/?disjunctive.transportsubmode&amp;amp;disjunctive.operatorname&amp;amp;disjunctive.networkname&amp;amp;disjunctive.transportmode&#34; target=&#34;_blank&#34;&gt;Référentiel des lignes de transport en commun d’île-de-France&lt;/a&gt;, which we will also need. Since all these files are in CSV format, and use unique identifiers to link together the data in different files, it will be easier to work with this data if we use a relational database. In &lt;a href=&#34;https://framagit.org/benjamingeer/benjamingeer/-/tree/main/notebook-data/paris-rail-data/import-data.sh&#34; target=&#34;_blank&#34;&gt;this shell script&lt;/a&gt;, I import the necessary files into an intermediate SQLite database, and create some database indexes to speed up the queries in the transformation step. This takes about 21 seconds on my laptop, and the size of the resulting database is 2.1G.&lt;/p&gt;&#xA;&lt;p&gt;Finally, I&amp;rsquo;ve written &lt;a href=&#34;https://framagit.org/benjamingeer/benjamingeer/-/tree/main/notebook-data/paris-rail-data/build-graph.py&#34; target=&#34;_blank&#34;&gt;a Python script&lt;/a&gt; for the transform and load phases. It reads the intermediate database and builds the output database, which just contains a table of edges and a table of nodes. Since the output database is small (132K), the script creates it in memory for better performance, and writes it to disk when it&amp;rsquo;s complete. This script takes about 8 seconds to run.&lt;/p&gt;&#xA;&lt;p&gt;In the Référentiel, links between the métro and and RER networks are represented by &lt;em&gt;zones de correspondance&lt;/em&gt; and &lt;em&gt;pôles d&amp;rsquo;échange&lt;/em&gt;, which can include multiple stations connected by corridors. To include these relations in the graph, I&amp;rsquo;ve chosen to add edges between all the stations in the same group.&lt;/p&gt;&#xA;&lt;p&gt;In the output database, the graph is a directed multigraph, because two stations can be connected by more than one line. In such cases, we can see those two stations as having a stronger connection than two stations connected by only one line. Other things being equal, more lines mean more passengers travelling between those two stations. We&amp;rsquo;ll keep this information about mutiple edges when we do our calculations.&lt;/p&gt;&#xA;&lt;h2 class=&#34;heading&#34; id=&#34;drawing-parts-of-the-graph&#34;&gt;&#xA;  Drawing parts of the graph&lt;span class=&#34;heading__anchor&#34;&gt; &lt;a href=&#34;#drawing-parts-of-the-graph&#34;&gt;#&lt;/a&gt;&lt;/span&gt;&#xA;&lt;/h2&gt;&lt;p&gt;First let&amp;rsquo;s draw some of the data to see if it looks correct. We can write a function that draws a transport network graph. Here we don&amp;rsquo;t care about the geographical positions of the stations, we just want to see that we have the right stations with the right links between them. To help us see which line is which, we can use the colours defined for the &lt;a href=&#34;https://www.ratp.fr/en/plans/&#34; target=&#34;_blank&#34;&gt;transport network map&lt;/a&gt;, which are given in the GTFS dataset, plus black for the edges I added for the &lt;em&gt;zones de correspondance&lt;/em&gt; and the &lt;em&gt;pôles d&amp;rsquo;échange&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;&#xA;&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;31&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;32&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;33&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;34&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;35&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;36&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;37&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;38&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;39&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;40&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;41&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;42&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;43&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;44&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;45&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;46&#xA;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#xA;&lt;td class=&#34;lntd&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;%&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;config&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;InlineBackend&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;figure_formats&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;svg&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;sqlite3&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;pandas&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;as&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;pd&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;networkx&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;as&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;nx&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;numpy&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;as&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;np&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;matplotlib.pyplot&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;as&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;plt&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;itertools&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;as&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;it&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;draw_graph&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;G&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;title&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;height&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;label_x_offset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;plt&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;figure&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;figsize&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;12&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;height&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;plt&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;margins&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;0.18&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;y&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;plt&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;title&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;title&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;pos&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;nx&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;nx_agraph&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;graphviz_layout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;G&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;prog&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;dot&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# See https://networkx.org/documentation/stable/auto_examples/drawing/plot_multigraphs.html&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;connectionstyle&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;sa&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;arc3,rad=&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;r&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;it&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;accumulate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;([&lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;0.15&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;nx&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;draw_networkx_nodes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;G&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pos&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;node_size&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;30&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;node_color&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;#000000&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;edge_colors&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;sa&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;#&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;G&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;source_node_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;][&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;target_node_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;][&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;edge_key&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;][&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;route_color&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;source_node_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;target_node_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;edge_key&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;G&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;edges&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;nx&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;draw_networkx_edges&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;G&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pos&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;edge_color&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;edge_colors&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;connectionstyle&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;connectionstyle&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;label_pos&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;node_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;node_pos&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;label_x_offset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;node_pos&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;])&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;node_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;node_pos&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pos&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;items&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;text&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;nx&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;draw_networkx_labels&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;G&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;label_pos&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;labels&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;nx&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;get_node_attributes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;G&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;node_name&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;horizontalalignment&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;left&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;verticalalignment&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;bottom&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;font_size&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;6&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;_&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;t&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;text&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;items&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;():&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;t&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;set_rotation&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;15&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#xA;&lt;/div&gt;&#xA;&lt;/div&gt;&lt;p&gt;Let&amp;rsquo;s try it with the métro lines 1 and 4, and the RER A:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;&#xA;&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;31&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;32&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;33&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;34&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;35&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;36&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;37&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;38&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;39&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;40&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;41&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;42&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;43&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;44&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;45&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;46&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;47&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;48&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;49&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;50&#xA;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#xA;&lt;td class=&#34;lntd&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;DATABASE&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;../../../notebook-data/paris-rail-data/paris-graph.db&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;conn&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sqlite3&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;connect&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;sa&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;file:&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DATABASE&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;?mode=ro&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;nodes&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pd&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;read_sql_query&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&amp;#34;select distinct&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;        nodes.node_id, nodes.node_name&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;    from nodes&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;    inner join edges on&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;        nodes.node_id = edges.source_node_id or&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;        nodes.node_id = edges.target_node_id&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;    where&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;        (edges.route_type = 1 and edges.route_name = &amp;#39;1&amp;#39;) or&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;        (edges.route_type = 1 and edges.route_name = &amp;#39;4&amp;#39;) or&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;        (edges.route_type = 2 and edges.route_name = &amp;#39;A&amp;#39;) or&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;        (edges.route_type is null and&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;            (edges.route_name = &amp;#39;PdE Châtelet - Les Halles&amp;#39; or&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;            edges.route_name = &amp;#39;ZdC Gare de Lyon&amp;#39;))&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;conn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;index_col&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;node_id&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;edges&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pd&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;read_sql_query&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&amp;#34;select * from edges where&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;        (edges.route_type = 1 and edges.route_name = &amp;#39;1&amp;#39;) or&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;        (edges.route_type = 1 and edges.route_name = &amp;#39;4&amp;#39;) or&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;        (edges.route_type = 2 and edges.route_name = &amp;#39;A&amp;#39;) or&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;        (edges.route_type is null and&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;            (edges.route_name = &amp;#39;PdE Châtelet - Les Halles&amp;#39; or&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;            edges.route_name = &amp;#39;ZdC Gare de Lyon&amp;#39;))&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;conn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;replace&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;np&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;nan&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;None&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;G&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;nx&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;from_pandas_edgelist&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;edges&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;source&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;source_node_id&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;target&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;target_node_id&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;edge_attr&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;True&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;create_using&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;nx&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;MultiDiGraph&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(),&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;node_attributes&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;nodes&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;to_dict&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;orient&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;index&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;nx&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;set_node_attributes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;G&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;node_attributes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;draw_graph&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;G&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;title&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;Métro lines 1 and 4 and the RER A&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;height&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;20&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;label_x_offset&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;6&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#xA;&lt;/div&gt;&#xA;&lt;/div&gt;&lt;p&gt;&lt;img src=&#34;https://benjamingeer.eu/en/post/paris-rail-centrality/index_files/index_4_0.svg&#34; alt=&#34;svg&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;That looks correct.&lt;/p&gt;&#xA;&lt;h2 class=&#34;heading&#34; id=&#34;plotting-results&#34;&gt;&#xA;  Plotting results&lt;span class=&#34;heading__anchor&#34;&gt; &lt;a href=&#34;#plotting-results&#34;&gt;#&lt;/a&gt;&lt;/span&gt;&#xA;&lt;/h2&gt;&lt;p&gt;The centrality functions in &lt;code&gt;networkx&lt;/code&gt; return a dictionary whose keys are node IDs and whose values are centrality scores. Let&amp;rsquo;s define a function that takes such a dictionary and plots a bar chart of the top 25 stations in descending order of centrality.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;&#xA;&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15&#xA;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#xA;&lt;td class=&#34;lntd&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;centrality_chart&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;centrality_dict&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;title&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;names_and_scores&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;node_attributes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;item&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]][&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;node_name&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;item&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;])&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;item&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;centrality_dict&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;items&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;top_25&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;sorted&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;names_and_scores&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;key&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;lambda&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;t&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;t&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;reverse&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;True&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)[:&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;25&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;top_25_df&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pd&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DataFrame&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;top_25&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;columns&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;Station&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Centrality score&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;set_index&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;Station&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;ax&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;top_25_df&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;plot&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;barh&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;title&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;title&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;ax&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;invert_yaxis&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#xA;&lt;/div&gt;&#xA;&lt;/div&gt;&lt;h2 class=&#34;heading&#34; id=&#34;degree-centrality&#34;&gt;&#xA;  Degree centrality&lt;span class=&#34;heading__anchor&#34;&gt; &lt;a href=&#34;#degree-centrality&#34;&gt;#&lt;/a&gt;&lt;/span&gt;&#xA;&lt;/h2&gt;&lt;p&gt;First let&amp;rsquo;s read the whole graph:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;&#xA;&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24&#xA;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#xA;&lt;td class=&#34;lntd&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;nodes&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pd&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;read_sql_query&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s2&#34;&gt;&amp;#34;select * from nodes&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;conn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;index_col&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;node_id&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;edges&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pd&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;read_sql_query&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s2&#34;&gt;&amp;#34;select * from edges&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;conn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;replace&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;np&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;nan&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;None&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;conn&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;close&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;G&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;nx&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;from_pandas_edgelist&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;edges&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;source&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;source_node_id&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;target&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;target_node_id&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;edge_attr&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;True&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;create_using&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;nx&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;MultiDiGraph&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(),&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;node_attributes&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;nodes&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;to_dict&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;orient&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;index&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;nx&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;set_node_attributes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;G&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;node_attributes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;sa&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;Loaded &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;G&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;nodes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt; nodes and &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;G&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;edges&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt; edges.&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#xA;&lt;/div&gt;&#xA;&lt;/div&gt;&lt;pre&gt;&lt;code&gt;Loaded 563 nodes and 1363 edges.&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;The simplest centrality index is degree centrality: for each node, we can count its inbound links (for &lt;em&gt;in-degree centrality&lt;/em&gt;) or its outbound links (for &lt;em&gt;out-degree centrality&lt;/em&gt;), and normalise the result by dividing by $n - 1$, where $n$ is the number of nodes. In our case, the results are the same for in-degree and out-degree centrality.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;&#xA;&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2&#xA;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#xA;&lt;td class=&#34;lntd&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;in_degree_centrality_dict&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;nx&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;in_degree_centrality&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;G&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;centrality_chart&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;in_degree_centrality_dict&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;In-degree centrality&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#xA;&lt;/div&gt;&#xA;&lt;/div&gt;&lt;p&gt;&lt;img src=&#34;https://benjamingeer.eu/en/post/paris-rail-centrality/index_files/index_11_0.svg&#34; alt=&#34;svg&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;We can see immediately that the stations with the highest scores are mostly métro stations rather than RER stations. One possible explanation could be the apparently more radial structure of the RER network compared to the métro. Besides being located on more than one line, several of these stations (such as Havre-Caumartin) belong to a &lt;em&gt;pôle d&amp;rsquo;échange&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;h2 class=&#34;heading&#34; id=&#34;eigenvector-centrality&#34;&gt;&#xA;  Eigenvector centrality&lt;span class=&#34;heading__anchor&#34;&gt; &lt;a href=&#34;#eigenvector-centrality&#34;&gt;#&lt;/a&gt;&lt;/span&gt;&#xA;&lt;/h2&gt;&lt;p&gt;With &lt;a href=&#34;https://en.wikipedia.org/wiki/Eigenvector_centrality&#34; target=&#34;_blank&#34;&gt;eigenvector centrality&lt;/a&gt;, each node gets a score that is proportional to the sum of the scores of its neighbours. This way, a node can get a high score by having many neighbours and/or by having high-scoring neighbours. Given an undirected graph of $n$ nodes, for a node $i$ whose set of neighbours is $M(i)$, the eigenvector centrality $x_i$ is defined as&lt;/p&gt;&#xA;$$&#xA;x_i = \frac{1}{\lambda} \sum_{j \in M(i)} x_j&#xA;$$&lt;p&gt;where $\lambda$ is a constant. We can rewrite this equation using the network&amp;rsquo;s adjacency matrix $A$, in which $a_{ij}$ is 1 if there is an edge that links nodes $i$ and $j$, and 0 otherwise:&lt;/p&gt;&#xA;$$&#xA;x_i = \frac{1}{\lambda} \sum_{j = 1}^n a_{ij} x_j&#xA;$$&lt;p&gt;This is equivalent to&lt;/p&gt;&#xA;$$&#xA;\mathbf{x} = \frac{1}{\lambda} A\mathbf{x}&#xA;$$&lt;p&gt;where $\mathbf{x}$ is a vector whose elements are the centrality scores. We can write this as&lt;/p&gt;&#xA;$$&#xA;A\mathbf{x} = \lambda\mathbf{x}&#xA;$$&lt;p&gt;which means that $\lambda$ is an eigenvalue of $A$ and $\mathbf{x}$ is the corresponding eigenvector. Given the eigenvalues and eigenvectors of the matrix, choosing the appropriate eigenvector is simple, because (according to the &lt;a href=&#34;https://en.wikipedia.org/wiki/Perron%E2%80%93Frobenius_theorem&#34; target=&#34;_blank&#34;&gt;Perron–Frobenius theorem&lt;/a&gt;), for a matrix (like the adjacency matrix) that has no negative elements, only the eigenvector corresponding to the largest eigenvalue can be chosen to have no negative elements. With a directed graph like our transport network, we can choose a left eigenvector or a right eigenvector. Usually the left eigenvector is chosen (and this is what &lt;code&gt;networkx&lt;/code&gt; does), so that a node&amp;rsquo;s score is based on its inbound edges rather than its outbound edges. This doesn&amp;rsquo;t make much difference in our case, though, because nearly every inbound edge in our network has a matching outbound edge.&lt;/p&gt;&#xA;&lt;p&gt;When eigenvector centrality is used with a directed graph, the graph must be strongly connected. This is true of our transport network, because every station is reachable from every other station:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;&#xA;&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1&#xA;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#xA;&lt;td class=&#34;lntd&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;nx&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;is_strongly_connected&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;G&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#xA;&lt;/div&gt;&#xA;&lt;/div&gt;&lt;pre&gt;&lt;code&gt;True&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Here we use the &lt;code&gt;networkx.eigenvector_centrality_numpy&lt;/code&gt; function, which first constructs the adjacency matrix. In a directed multigraph such as this one, if there are $n$ edges in the same direction between two nodes, &lt;code&gt;networkx&lt;/code&gt; represents them as the value $n$ in the matrix, which is equivalent to a single edge with weight $n$. It then gets the relevant eigenvector, and returns a dictionary containing each node ID and the corresponding element of the eigenvector.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;&#xA;&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2&#xA;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#xA;&lt;td class=&#34;lntd&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;eigenvector_centrality_dict&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;nx&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;eigenvector_centrality_numpy&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;G&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;centrality_chart&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;eigenvector_centrality_dict&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Eigenvector centrality&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#xA;&lt;/div&gt;&#xA;&lt;/div&gt;&lt;p&gt;&lt;img src=&#34;https://benjamingeer.eu/en/post/paris-rail-centrality/index_files/index_16_0.svg&#34; alt=&#34;svg&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;If we compare these results to the degree centrality results, we can see that Havre-Caumartin, which is in the middle of the six-station Paris Saint-Lazare - Opéra &lt;em&gt;pôle d&amp;rsquo;échange&lt;/em&gt;, gets a higher score because it&amp;rsquo;s adjacent to the two highly-ranked stations Saint-Lazare and Opéra. Several other stations, such as Saint-Augustin, Auber, and Haussmann Saint-Lazare, also get higher scores for the same reason.&lt;/p&gt;&#xA;&lt;h2 class=&#34;heading&#34; id=&#34;betweenness-centrality&#34;&gt;&#xA;  Betweenness centrality&lt;span class=&#34;heading__anchor&#34;&gt; &lt;a href=&#34;#betweenness-centrality&#34;&gt;#&lt;/a&gt;&lt;/span&gt;&#xA;&lt;/h2&gt;&lt;p&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Betweenness_centrality&#34; target=&#34;_blank&#34;&gt;Betweenness centrality&lt;/a&gt; counts the number of times a node is on one of the shortest paths (i.e., those that pass through as few nodes as possible) between each pair of other nodes. For a node $v$, it is defined as&lt;/p&gt;&#xA;$$&#xA;c_B(v) =\sum_{s \neq v \neq t} \frac{\sigma(s, t|v)}{\sigma(s, t)}&#xA;$$&lt;p&gt;where $s$ and $t$ are any other nodes in the network, $\sigma(s, t)$ is the number of shortest paths from $s$ to $t$, and $\sigma(s, t|v)$ is the number of those paths that pass through $v$. In a transport network, a station with a high value for $c_B(v)$ is one that many passengers are likely to pass through if they choose one of the shortest paths to their destination.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;&#xA;&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2&#xA;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#xA;&lt;td class=&#34;lntd&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;betweenness_centrality_dict&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;nx&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;betweenness_centrality&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;G&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;centrality_chart&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;betweenness_centrality_dict&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Betweenness centrality&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#xA;&lt;/div&gt;&#xA;&lt;/div&gt;&lt;p&gt;&lt;img src=&#34;https://benjamingeer.eu/en/post/paris-rail-centrality/index_files/index_19_0.svg&#34; alt=&#34;svg&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;Most of the stations on this list are RER stations rather than métro stations, which makes sense, because the RER covers more distance between stops, so if the same journey can be done via métro or via RER, the shortest path will usually be via RER. It&amp;rsquo;s not surprising to see Châtelet les Halles at the top of the list, given its geographically central location and the large number of lines that pass through its &lt;em&gt;pôle d&amp;rsquo;échange&lt;/em&gt;. It also looks like there are other configurations that can give a station a relatively high rank. For example, Juvisy is on a long section of the RER C where few stations have connections to other lines, and it connects to a similar section of the RER D, so it seems likely that many shortest paths have to pass through it.&lt;/p&gt;&#xA;&lt;h2 class=&#34;heading&#34; id=&#34;conclusion&#34;&gt;&#xA;  Conclusion&lt;span class=&#34;heading__anchor&#34;&gt; &lt;a href=&#34;#conclusion&#34;&gt;#&lt;/a&gt;&lt;/span&gt;&#xA;&lt;/h2&gt;&lt;p&gt;This has just been a brief exploration of a few centrality indices for this network, with some examples of how different definitions of a node&amp;rsquo;s importance result in different rankings. All the scripts used here can be found &lt;a href=&#34;https://framagit.org/benjamingeer/benjamingeer/-/tree/main/notebook-data/paris-rail-data&#34; target=&#34;_blank&#34;&gt;in this blog&amp;rsquo;s Git repository&lt;/a&gt;, along with the &lt;a href=&#34;https://framagit.org/benjamingeer/benjamingeer/-/tree/main/notebooks/en/paris-rail-centrality/index.ipynb&#34; target=&#34;_blank&#34;&gt;Jupyter notebook&lt;/a&gt; from which I generated this page. Please &lt;a href=&#34;https://benjamingeer.eu/en/about/&#34;&gt;contact me&lt;/a&gt; if you have any questions or suggestions.&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>The Sound of Crying</title>
      <link>https://benjamingeer.eu/en/post/the-sound-of-crying/</link>
      <pubDate>Thu, 06 Mar 2025 00:00:00 +0000</pubDate>
      <guid>https://benjamingeer.eu/en/post/the-sound-of-crying/</guid>
      <description>&lt;p&gt;In 2024 I did the English subtitles for the short film &lt;a href=&#34;https://pasfeerique.com/mal-partum&#34; target=&#34;_blank&#34;&gt;&lt;em&gt;Mal partum&lt;/em&gt;&lt;/a&gt; (English title: &lt;em&gt;The Sound of Crying&lt;/em&gt;), a beautiful documentary about postnatal depression. Émilie D. used her personal experience to make a film that provides practical knowledge that we can all use.&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>كوكبنا</title>
      <link>https://benjamingeer.eu/en/post/kawkabna/</link>
      <pubDate>Fri, 20 Sep 2024 15:00:00 +0000</pubDate>
      <guid>https://benjamingeer.eu/en/post/kawkabna/</guid>
      <description>&lt;p&gt;An Arabic-language podcast that I started in 2021 on ecology, migration, and inequality, and would like to continue someday: &lt;a href=&#34;https://%d9%83%d9%88%d9%83%d8%a8%d9%86%d8%a7.%d8%b4%d8%a8%d9%83%d8%a9&#34; target=&#34;_blank&#34;&gt;كوكبنا.شبكة&lt;/a&gt;.&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Tondauer</title>
      <link>https://benjamingeer.eu/en/post/tondauer/</link>
      <pubDate>Fri, 20 Sep 2024 14:00:00 +0000</pubDate>
      <guid>https://benjamingeer.eu/en/post/tondauer/</guid>
      <description>&lt;p&gt;In 2020 I started a project called &lt;a href=&#34;https://tondauer.art/&#34; target=&#34;_blank&#34;&gt;Tondauer&lt;/a&gt;, to make Creative Commons licensed editions of sheet music. It happened because of the Covid-19 lockdown that year: I was learning to play &lt;a href=&#34;https://tondauer.art/2021/03/mendelssohn-prelude-mwv-u-123/&#34; target=&#34;_blank&#34;&gt;Felix Mendelssohn&amp;rsquo;s Prelude Op. 104a, No. 2 (MWV U 123)&lt;/a&gt;, because it suited my mood of restless frustration at the time.&lt;/p&gt;&#xA;&lt;p&gt;I started with a 19th-century edition that could be downloaded for free. Then I bought an edition with fingerings, and wondered why it had different notes. I looked in vain for the composer&amp;rsquo;s manuscript, but found only an earlier draft. I had been reading R. Larry Todd&amp;rsquo;s biography of Mendelssohn, so I emailed him to ask if he knew where the manuscript of the final version was, and he replied that it hadn&amp;rsquo;t been found. This meant that the only sources were the first editions, which were published after the composer&amp;rsquo;s death.&lt;/p&gt;&#xA;&lt;p&gt;I had learned something about how critical editions are made while working in the field of digital humanities. So I decided to make my own critical edition. Over several months, as libraries and archives gradually reopened, I got digital reproductions of the first German and British editions. The two editors had worked together to publish them simultaneously, but they didn&amp;rsquo;t have the same notes, either. In my edition, I tried to make a plausible explanation of what had happened, and reconstruct the composer&amp;rsquo;s intentions as well as possible. In the end I tried to make the edition I wished I had had, and added fingerings with the help of &lt;a href=&#34;https://www.roskellacademy.com/&#34; target=&#34;_blank&#34;&gt;Penelope Roskell&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;More recently I&amp;rsquo;ve been working with musicologist Florence Launay on editions of forgotten works by female composers, starting with Armande de Polignac&amp;rsquo;s &lt;a href=&#34;https://tondauer.art/2022/10/polignac-preludes/&#34; target=&#34;_blank&#34;&gt;Preludes for Piano&lt;/a&gt;, which had survived only in a single copy of the first edition, preserved by her descendants. Florence had photocopied it, and pianists had been recording the pieces using photocopies of that photocopy. Now that we&amp;rsquo;ve published a corrected edition with Florence&amp;rsquo;s preface, I hope more pianists will enjoy playing these Preludes.&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>SocioResources</title>
      <link>https://benjamingeer.eu/en/post/socioresources/</link>
      <pubDate>Fri, 20 Sep 2024 13:00:00 +0000</pubDate>
      <guid>https://benjamingeer.eu/en/post/socioresources/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://socioresources.net/&#34; target=&#34;_blank&#34;&gt;My old blog&lt;/a&gt; (2013-2021) on the sociology of autonomy and the autonomy of sociology.&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>أبحاث لفتت نظري</title>
      <link>https://benjamingeer.eu/en/post/abhath/</link>
      <pubDate>Fri, 20 Sep 2024 12:00:00 +0000</pubDate>
      <guid>https://benjamingeer.eu/en/post/abhath/</guid>
      <description>&lt;p&gt;My old Arabic blog (2009-2014) on which I summarised research that I thought was interesting: &lt;a href=&#34;https://benjamingeer.blogspot.com/&#34; target=&#34;_blank&#34;&gt;أبحاث لفتت نظري&lt;/a&gt;.&lt;/p&gt;&#xA;</description>
    </item>
  </channel>
</rss>
