I love just about anything that is logical, well-organized, and flows. And I get somewhat (or quite, depending on the level of the offense) frustrated with just about anything that is lazily arranged, illogically planned, or otherwise leaves me feeling like "you had ONE job!" While I know not everyone appreciates a beautifully orchestrated layout like I do, I still aim to always execute my projects in such a way that they are easy to read and navigate so a reader has to make no effort, left free to enjoy the content and work.
Challenges with Jupyter
My life is all about data science. As such, I spend pretty much all my time on Jupyter. Making my notebooks as organized and easy-to-navigate as possible is my own little version of AMSR. One thing I have been dabbling in a lot lately is figuing out what is easily doable and what is not easily doable in Jupyter to the extent that it functions across all platforms, as well as every export type, while still retaining formatting. When I say "easy", all I mean is that the result is worth the time and pain it takes to get there. For me, organization is almost always worth it. We live in a world that is drowning in data, which I love. Because I get to organize it! With great data comes great responsibility.
It is not an easy task to come up with a standard style of organizing and presenting my work when the current situation is such that what works in Jupyter and looks amazing on my server looks ridiculous when I commit it to GitHub, and when exported to PDF, forgetaboutit! So I have been focusing on a somewhat bare-bones approach that is still navigationally pleasing.
I love writing Python functions that format my output data in such a way that the eyes are drawn to what is important and the mind can almost subconsciously take it all in. I also like for the reader to be able to quickly scroll through a notebook and find anything they are looking for. The challenge I have recently run into is that while it is easy to do this in Python for outputs of my code, it is not NEARLY as fun trying to get the same effect with markdown. And I found myself repeating the same silliness over and over. That's when things can get really messy. And as you might have noticed, that is not something I am ok with.
Python Does Markdown
So I decided to write Python functions to make markdown code for me! The following covers two main functions for producing navigational markdown code. One is called
table_of_contents(), which as you may have already guessed, produces the markdown code for a clean and concise table of contents. The second is
link_menu(), which produces the markdown code for a nice little navigation link menu, which I like to include at the beginning of every main section in my notebooks to make it easy to jump around. So let's look at the code!
This function takes a title, two headings, and two lists of topics, one for each heading. It is easily customizable for other configurations as well.
It prints the markdown code for a nice and neat little table of contents containing all the topics passed to it as the two lists. This code can then be pasted into a markdown cell wherever the table of contents is desired.
I am not much of a fan of making tables in markdown, especially when they contain links. They become a real mess really fast. This way, it is never a problem. I barely have to acknowledge the table exists. I just plug in my topics, run, and cut and paste the code. Beautiful!
It also returns a string containing all the tags for embedding in the appropriate cells matching the table of contents. This way, Python makes it as easy as listing the topics covered and returns all you need for a streamlined way of navigating a project. When notebooks get over 30 or 50 pages, this becomes extremely important.
This function is used inside of
table_of_contents(), as well as the link menu function below. Its job is to take the list of topics passed and create the blocks of markdown code that make up the table of content links and the tags that goes in the cell the link navigates to:
[Topic Name](#topic_link) and
<a name="topic_name"></a>. This saves so much time and effort and is easily customizable when changes need to be made.
This function creates a small navigation bar that can be used throughout a document, as I mentioned earlier, at the beginnings of sections or wherever it is called for. It uses
parse_links() just like
table_of_contents() does, but arranges the markdown code into a simple navigation link list rather than an entire table.
I personally like to have the one table of contents at the top of a notebook and navigation / link bars throughout. While it is always possible to use the tables of contents within Jupyter, I personally do not like having to open it repeatedly and would rather have navigation in links this way.
This function is also very customizable, even after the mardown code has been created. The nice part is that all you have to give it is a list of topics and a navigation bar heading, and tada! So Python does all the redundant markdown creation for you.
Thanks for coming to my little Python celebration. Any time Python makes my life easier, I find it necessary to once again sing its praises, which keeps me singing a lot! Happy coding and happy data wrangling!!
You can see the entire notebook of code below.