Jupyter Notebook Enhancements, Tips And Tricks

(Stas Bekman) #8

Control notebook resource allocation

Sometimes notebooks take a way more resources than available, causing undesired behavior, like a hanging system.

There are various ways to limit system resource usage, I decided to try the modern cgroups to limit memory usage in this case.

sudo cgcreate -a stas:stas -t stas:stas -g memory:JupyterGroup

replace stas with your setup’s user:group (usually yourusername:yourusername).

Then I decided to give each notebook max of 15GB RAM and 25GB Total (RAM+SWAP):

sudo echo $(( 15000 * 1024 * 1024 )) > /sys/fs/cgroup/memory/JupyterGroup/memory.limit_in_bytes
sudo echo $(( 10000 * 1024 * 1024 )) > /sys/fs/cgroup/memory/JupyterGroup/memory.kmem.max_usage_in_bytes

Now I start the notebook as:

cgexec -g memory:JupyterGroup jupyter notebook

a greedy kernel now gets terminated if it consumes more than what I allocated, without crashing/overloading my system.

Note that filenames under /sys/fs/cgroup/memory/JupyterGroup/ might be slightly different on your system. You may have to check the documentation.

I also needed to install the following packages, before I was able to run this:

sudo apt-get install cgroup-bin cgroup-lite cgroup-tools cgroupfs-mount libcgroup1

5 Likes

(Stas Bekman) #9

Following the currently executing cell mode

Won’t it be nice to be able to watch the progress of the notebook run hands off? Now you can:

Add the following in ~/.jupyter/custom/custom.js and reload the notebooks you’re running:

/*
 In Command mode Meta-[ toggles Follow Exec Cell mode, Meta-] turns it off.

 To adjust the behavior you can adjust the arguments:
 * behavior: One of "auto", "instant", or "smooth". Defaults to "auto". Defines the transition animation.
 * block:    One of "start", "center", "end", or "nearest". Defaults to "center".
 * inline:   One of "start", "center", "end", or "nearest". Defaults to "nearest".
 https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView
*/
function scrollIntoRunningCell(evt, data) {
    $('.running')[0].scrollIntoView({behavior: 'smooth', inline: 'center'});
}

Jupyter.keyboard_manager.command_shortcuts.add_shortcut('Meta-[', {
    help: 'Follow Executing Cell On',
    help_index: 'zz',
    handler: function (event) {
        Jupyter.notebook.events.on('finished_execute.CodeCell', scrollIntoRunningCell);
        //console.log("Follow Executing Cell On")
        return false;
    }
});

Jupyter.keyboard_manager.command_shortcuts.add_shortcut('Meta-]', {
    help: 'Follow Executing Cell Off',
    help_index: 'zz',
    handler: function (event) {
        Jupyter.notebook.events.off('finished_execute.CodeCell', scrollIntoRunningCell);
        //console.log("Follow Executing Cell Off")
        return false;
    }
});

Now in Command Mode (when cell in focus has a blue box around it and not green, or hit Esc to toggle mode), hit Meta-[ to get the currently run cell stay in the middle of the screen, hit Meta-] to return to normal behavior.

If this is not working, debug this setup by uncommenting console.log() calls and watch your browser Developer Tools’ Console to check that custom.js got loaded without errors and that the shortcuts got registered and the handler is activated. Sometimes you need to restart jupyter notebook, but most of the time tab-reload works.

If you just want to jump once to the current executing cell use Alt-I after you add the following to ~/.jupyter/custom/custom.js and reload the notebooks you’re running:

// Alt-I: Go to Running cell shortcut [Command mode]
Jupyter.keyboard_manager.command_shortcuts.add_shortcut('Alt-I', {
    help : 'Go to Running cell',
    help_index : 'zz',
    handler : function (event) {
        setTimeout(function() {
            // Find running cell and click the first one
            if ($('.running').length > 0) {
                //alert("found running cell");
                $('.running')[0].scrollIntoView();
            }}, 250);
        return false;
    }
});

Caveat: for it to work - the sections should all be uncollapsed - otherwise it won’t know to go into a collapsed section.

You can adjust the activation shortcut keys to your liking.

Remember that all 3 shortcuts will only work in the Command mode (see above for figuring that out).

This has been tested to work with jupyter notebook 5.6.0 with python 3.6.6.

2 Likes

Jupyter Notebook Enhancements [Discussion]
Developer chat
(Stas Bekman) #16

nbdime: Selective Diff/Merge Tool for jupyter notebooks

I also need to share very useful tips about nbdime which is very useful for working with jupyter notebooks.

Install it first:

pip install -U nbdime

it should automatically configure it for jupyter notebook. If something doesn’t work, see installation.

Then put the following into ~/.jupyter/nbdime_config.json:

{

  "Extension": {
    "source": true,
    "details": false,
    "outputs": false,
    "metadata": false
  },

  "NbDiff": {
    "source": true,
    "details": false,
    "outputs": false,
    "metadata": false
  },

  "NbDiffDriver": {
    "source": true,
    "details": false,
    "outputs": false,
    "metadata": false
  },

  "NbMergeDriver": {
    "source": true,
    "details": false,
    "outputs": false,
    "metadata": false
  },

  "dummy": {}
}

Change outputs value to true if you care to see outputs diffs too.

Now when you do:

git diff

Instead of getting a very noisy and hard to parse normal diff:

--- a/examples/tabular.ipynb
+++ b/examples/tabular.ipynb
@@ -2,12 +2,12 @@
  "cells": [
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 1,
    "metadata": {},
    "outputs": [],
    "source": [
-    "from fastai import *          # Quick access to most common functionality\n",
-    "from fastai.tabular import *  # Quick access to tabular functionality\n",
+    "from fastai import *          # Very Quick access to most common functionality\n",
+    "from fastai.tabular import *  # Very Quick access to tabular functionality\n",
     "from fastai.docs import *     # Access to example data provided with fastai"
    ]
   },

You will get this sweetness:

snap5

The second feature I like is that now your notebook has a button: [nbdiff] along all the tools and it’ll show you the diff right in your browser!

snap6

And did I say that it does notebook merging too! Whoah! Use the setting above and it’ll ignore any noise when merging (i.e. metadata, execution_count, etc.), and only merge code cells! Of course you can adjust the configuration to suite your needs.

For the full docs see its website.

5 Likes

(nirant) #17

Hey @stas, thanks for this. This is amazing!

I took what I learned here, combined with what I learnt through fast ai and daily use of Jupyter notebook into this one place: https://github.com/NirantK/best-of-jupyter

Hope you find this useful!

2 Likes

(nirant) #18

For those coming here for the first time, here are some direct links to what you do better in your Jupyter usage:

Contents

13 Likes

(Stas Bekman) #19

Tell the notebook to save itself now

juputer notebook by default autosaves itself every 5 min or so if you haven’t changed the defaults.

But if you want to make sure the notebook is saved at the end of the run, you can just insert a new cell at the end of your notebook and make sure you run it:

%%javascript
IPython.notebook.save_notebook()

now your notebook will be always saved as soon as it’s done running.

This is useful if you’re then immediately needing to commit the change to git.

credit: the idea came from here.

3 Likes

(Haider Alwasiti) #21

Python (not Jupyter) trick, but still useful for our long running Jupyter Notebooks:

When the code takes extremely long to run and I don’t want to be staring at it all the time but want to know when it is done.

How can I make the (Python) code sort of sound an “alarm” when it is done?

The following answer works only on server side. So it is useful only when you have a local server:

On Windows

import winsound
duration = 1000  # millisecond
freq = 440  # Hz
winsound.Beep(freq, duration)

Where freq is the frequency in Hz and the duration is in milliseconds.

On Linux (and Mac)

import os
duration = 1  # second
freq = 440  # Hz
os.system('play --no-show-progress --null --channels 1 synth %s sine %f' % (duration, freq))

In order to use this example, you must install sox .

On Debian/Ubuntu/LinuxMint you need to run in your terminal:

sudo apt install sox

Here is the macports way of doing that…run this is your terminal:

sudo port install sox

Speech on Mac

And something really cool if you’re using a mac in terminal, maybe can do the same in windows, but I only know for sure for mac, this will tell you it’s done:

import os
os.system('say "your program has finished"')

Speech on Linux

import os
os.system('spd-say "your program has finished"')

You need to install the speech-dispatcher package in Ubuntu (or the corresponding package on other distributions):

sudo apt install speech-dispatcher

Source:
https://stackoverflow.com/questions/16573051/sound-alarm-when-code-finishes/16573339#16573339

2 Likes

(Stas Bekman) #22

Make Esc always switch to Command mode

update: this problem should be fixed in the recent version of navigation-hotkeys.

If you use a very handy jupyter_contrib_nbextensions/nbextensions/navigation-hotkeys it introduces a very bad idea of changing the behavior of Esc to always switch to the opposite mode, making Esc unreliable as a precursor for a command mode shortcut call. Which leads to a lot of unpredictable behavior. So the following takes the goodness of the navigation-hotkeys extension and avoids the undesirable.

  1. disable navigation-hotkeys
  2. add to ~/.jupyter/custom/custom.js the following:
/* start a custom version of jupyter_contrib_nbextensions/nbextensions/navigation-hotkeys to remove Esc=> Edit mode toggle as it interferes with the workflow - Esc needs to be deterministic - switches to Command mode */

var add_command_shortcuts = {
        'home' : {
            help    : 'go to top',
            help_index : 'ga',
            handler : function() {
                IPython.notebook.select(0);
                IPython.notebook.scroll_to_top();
                return false;
            }
        },

        'end' : {
            help    : 'go to bottom',
            help_index : 'ga',
            handler : function() {
                var ncells = IPython.notebook.ncells();
                IPython.notebook.select(ncells-1);
                IPython.notebook.scroll_to_bottom();
                return false;
            }
        },

        'pageup' : {
            help    : 'page up',
            help_index : 'aa',
            handler : function() {
            var wh = 0.6 * $(window).height();
            var cell = IPython.notebook.get_selected_cell();
            var h = 0;
            /* loop until we have enough cells to span the size of the notebook window (= one page) */
            do {
                h += cell.element.height();
                IPython.notebook.select_prev();
                cell = IPython.notebook.get_selected_cell();
            } while ( h < wh );
            var cp = cell.element.position();
            var sp = $('body').scrollTop();
            if ( cp.top < sp) {
                IPython.notebook.scroll_to_cell(IPython.notebook.get_selected_index(), 0);
            }
            cell.focus_cell();
            return false;
            }
        },

        'pagedown' : {
            help    : 'page down',
            help_index : 'aa',
            handler : function() {

            /* jump to bottom if we are already in the last cell */
            var ncells = IPython.notebook.ncells();
            if ( IPython.notebook.get_selected_index()+1 == ncells) {
                IPython.notebook.scroll_to_bottom();
                return false;
            }

            var wh = 0.6*$(window).height();
            var cell = IPython.notebook.get_selected_cell();
            var h = 0;

            /* loop until we have enough cells to span the size of the notebook window (= one page) */
            do {
                h += cell.element.height();
                IPython.notebook.select_next();
                cell = IPython.notebook.get_selected_cell();
            } while ( h < wh );
            cell.focus_cell();
            return false;
            }
        }

    };

var add_edit_shortcuts = {
        'alt-add' : {
            help    : 'split cell',
            help_index : 'eb',
            handler : function() {
                IPython.notebook.split_cell();
                IPython.notebook.edit_mode();
                return false;
            }
        },
        'alt-subtract' : {
            help    : 'merge cell',
            help_index : 'eb',
            handler : function() {
                var i = IPython.notebook.get_selected_index();
                if (i > 0) {
                    var l = IPython.notebook.get_cell(i-1).code_mirror.lineCount();
                    IPython.notebook.merge_cell_above();
                    IPython.notebook.get_selected_cell().code_mirror.setCursor(l,0);
                    }
            }
        },
        'shift-enter' : {
            help    : 'run cell, select next codecell',
            help_index : 'bb',
            handler : function() {
                IPython.notebook.execute_cell_and_select_below();
                var rendered = IPython.notebook.get_selected_cell().rendered;
                var ccell = IPython.notebook.get_selected_cell().cell_type;
                if (rendered === false || ccell === 'code') IPython.notebook.edit_mode();
                return false;
            }
        },
        'ctrl-enter' : {
            help    : 'run cell',
            help_index : 'bb',
            handler : function() {
                var cell = IPython.notebook.get_selected_cell();
                var mode = cell.mode;
                cell.execute();
                if (mode === "edit") IPython.notebook.edit_mode();
                return false;
            }
        },
        'alt-n' : {
            help    : 'toggle line numbers',
            help_index : 'xy',
            handler : function() {
                var cell = IPython.notebook.get_selected_cell();
                cell.toggle_line_numbers();
                return false;
            }
        },
        'pagedown' : {
            help    : 'page down',
            help_index : 'xy',
            handler : function() {

                var ic = IPython.notebook.get_selected_index();
                var cells = IPython.notebook.get_cells();
                var i, h=0;
                for (i=0; i < ic; i ++) {
                    h += cells[i].element.height();
                    }
                var cur = cells[ic].code_mirror.getCursor();
                h += cells[ic].code_mirror.defaultTextHeight() * cur.line;
                IPython.notebook.element.animate({scrollTop:h}, 0);
                return false;
            }
        },
        'pageup' : {
            help    : 'page down',
            help_index : 'xy',
            handler : function() {

                var ic = IPython.notebook.get_selected_index();
                var cells = IPython.notebook.get_cells();
                var i, h=0;
                for (i=0; i < ic; i ++) {
                    h += cells[i].element.height();
                    }
                var cur =cells[ic].code_mirror.getCursor();
                h += cells[ic].code_mirror.defaultTextHeight() * cur.line;
                IPython.notebook.element.animate({scrollTop:h}, 0);
                return false;
            }
        },
        'ctrl-y' : {
            help : 'toggle markdown/code',
            handler : function() {
                var cell = IPython.notebook.get_selected_cell();
                var cur = cell.code_mirror.getCursor();
                if (cell.cell_type == 'code') {
                    IPython.notebook.command_mode();
                    IPython.notebook.to_markdown();
                    IPython.notebook.edit_mode();
                    cell = IPython.notebook.get_selected_cell();
                    cell.code_mirror.setCursor(cur);
                } else if (cell.cell_type == 'markdown') {
                    IPython.notebook.command_mode();
                    IPython.notebook.to_code();
                    IPython.notebook.edit_mode();
                    cell = IPython.notebook.get_selected_cell();
                    cell.code_mirror.setCursor(cur);
                }
                return false;
            }
        }
    };

Jupyter.keyboard_manager.edit_shortcuts.add_shortcuts(add_edit_shortcuts);
Jupyter.keyboard_manager.command_shortcuts.add_shortcuts(add_command_shortcuts);

/* end a custom version of jupyter_contrib_nbextensions/nbextensions/navigation-hotkeys */
1 Like

(Haider Alwasiti) #23

Restoring Jupyter client’s GUI (scores and graphs…etc) after client disconnects for long time

Requirements:

  1. we should use tmux to keep the jupyter server running, even after ssh disconnects. Without tmux, jupyter server will be killed after ssh connection disconnects.
  2. The client (e.g., your pc/laptop showing the jupyter notebook) may go to hibernation or sleep without problem, but the tab of the browser showing the jupyter clients should not be closed. Only that tab that began running the notebook will be able to restore the whole running GUI elements of the notebook.
  3. Increase 2 parameters in the jupyter config file:

nano /home/<username>/.jupyter/jupyter_notebook_config.py

if config file is not available, we should generate it by:

jupyter notebook --generate-config

ctrl+w: search for c.NotebookApp.iopub_data_rate_limit

Uncomment and change these values into something very large:

c.NotebookApp.iopub_msg_rate_limit = 100000000

c.NotebookApp.iopub_data_rate_limit = 10000000000

1 Like

(Stas Bekman) #24

Automatic Notebook Halt / Restart cell

When included at the bottom of a notebook, it will stop the notebook at the end of Run All, and pop up a modal dialog letting the user know that the notebook was auto-stopped at the end of the run. The dialog offers a “Kernel Restart” button which restarts the notebook.

%%javascript
require(
    ["base/js/dialog"], 
    function(dialog) {
        dialog.modal({

                title: 'Notebook Halted',
                body: 'This notebook is no longer running; the kernel has been halted. Close the browser tab, or, to continue working, restart the kernel.',
                buttons: {
                    'Kernel restart': { click: function(){ Jupyter.notebook.session.restart(); } }
                }
        });
    }
);
Jupyter.notebook.session.delete();

source.

0 Likes

(Haider Alwasiti) #25

Use plotly instead of matplotlib to get better-looking and interactive chart

For those used to matplotlib , all we have to do is add one more letter ( iplot instead of plot ) and we get a much better-looking and interactive chart! We can click on the data to get more details, zoom into sections of the plot, and as we’ll see later, select different categories to highlight.

  1. One-line charts for rapid exploration
  2. Interactive elements for subsetting/investigating data
  3. Option to dig into details as needed
  4. Easy customization for final presentation

That’s just as simple:

df['claps'].iplot(kind='hist', xTitle='claps', yTitle='count', title='Claps Distribution')

Try it now with nbviewer live demo:

The plotly Python package is an open-source library built on plotly.js which in turn is built on d3.js . This code is using a wrapper on plotly called cufflinks designed to work with Pandas dataframes. So, our entire stack is cufflinks > plotly > plotly.js > d3.js which means we get the efficiency of coding in Python with the incredible interactive graphics capabilities of d3.

source:

P.S.: For jupyter Lab you should install also:
jupyter labextension install @jupyterlab/plotly-extension

2 Likes

(Shital Shah) #26
  • Shift + Tab cycles through help info like docstring
  • Ctrl + Shift + - splits the current cell from cursor
  • Ctrl + / comments out selected lines in the cell
1 Like

(深度碎片) #27

How to add a keyboard shortcut to insert a code snippet

Inspired by https://stackoverflow.com/a/51719689/9201239
and some help from @stas

If you first enter a cell with either code mode or markdown mode, like the following

Then you press Ctrl + Shift + M, you will get the following

To make this happening, you just need to

  1. go to custom.js whose path can be found by running
echo $(jupyter --config-dir)/custom/custom.js
  1. add the following codes into custom.js and save.
Jupyter.keyboard_manager.edit_shortcuts.add_shortcut('Ctrl-Shift-M', {
                              help : 'add details drop',
                              help_index : 'zz',
                              handler : function (event) {
                              var target = Jupyter.notebook.get_selected_cell()
                              var cursor = target.code_mirror.getCursor()
                              var before = target.get_pre_cursor()
                              var after = target.get_post_cursor()
                              target.set_text(before + '[/details][details=""]' + after)
                              cursor.ch += 20 // where to put your cursor
                              target.code_mirror.setCursor(cursor)
                              return false;
                              }}
                                                        );

  1. finally, go back to Jupyter Notebook and refresh your page, and you are ready to go.
1 Like

Fast.ai v3 2019课程中文版笔记
Jupyter Notebook Enhancements [Discussion]
(深度碎片) #28

How to create a multi-line code snippet for Jupyter notebook

First, install nbextensions

conda install jupyter_contrib_nbextensions
jupyter contrib nbextension install --user

Second, go to your jupyter notebook and open nbextensions settings to add features


Third, open custom.js file and add the following codes

require(["nbextensions/snippets_menu/main"], function (snippets_menu) {
        console.log('Loading `snippets_menu` customizations from `custom.js`');
        var horizontal_line = '---';
        var my_favorites = {
        'name' : 'My favorites',
        'sub-menu' : [
                      {
                      'name' : 'most_jupyter_magics',
                      'snippet' : ['%reload_ext autoreload',
                                   '%autoreload 2',
                                   '%matplotlib inline',
                                   'from IPython.core.interactiveshell import InteractiveShell',
                                   'InteractiveShell.ast_node_interactivity = "all"',],
                      },
                      {
                      'name' : 'kaggle download links',
                      'snippet' : ['from IPython.display import FileLinks',
                                   'FileLinks(\'.\')',],
                      },
                      {
                      'name' : 'details_drop',
                      'snippet' : ['[/details][details=""]',],
                      },
                      {
                      'name' : 'find snippet custom js',
                      'snippet' : ['echo $(jupyter --config-dir)/custom/custom.js',],
                      },
                      ],
        };
        snippets_menu.options['menus'].push(snippets_menu.default_menus[0]);
        snippets_menu.options['menus'][0]['sub-menu'].push(horizontal_line);
        snippets_menu.options['menus'][0]['sub-menu'].push(my_favorites);
        console.log('Loaded `snippets_menu` customizations from `custom.js`');
        });

Finally, refresh your notebook page, and you shall see the following


reference:
Jupyter docs on snippet

2 Likes

Fast.ai v3 2019课程中文版笔记
(Stas Bekman) #29

Getting the notebook name automatically

# cell 1
%%javascript
IPython.notebook.kernel.execute('nb_name = ' + '"' + IPython.notebook.notebook_name + '"')
# cell 2
nb_name

Same without js magic:

# cell 1
from IPython.display import display, Javascript
Javascript("IPython.notebook.kernel.execute('nb_name = ' + '\"' + IPython.notebook.notebook_name + '\"')")
# cell 2
nb_name

In either case, JS is async - so it may not run right away and therefore it’s not guaranteed nb_name will be set right away.

I was looking for this feature, since I wanted to replace hardcoded cells in each dev_nbs such as:

!./notebook2script.py 02_fully_connected.ipynb

with something similar that will extract the nb name automatically.

So I wrote a little helper function:

from IPython.display import display, Javascript
def nb_auto_export():
    display(Javascript("if (IPython.notebook.kernel) {IPython.notebook.kernel.execute('!./notebook2script.py ' + IPython.notebook.notebook_name )}"))

(it looks like JS is the only way to get the nb name in jupyter :frowning:)

notes:

  • it assumes the nb name has no spaces in it, add quotes if it does
  • had to bracket the code with if (IPython.notebook.kernel) {} since the browser will attempt to run this js code automatically on jupyter nb load and fail, since IPython.notebook.kernel hasn’t been defined yet. It only happens if there is ! in the js code - odd.

and now there is no need to hardcode the nb name, just have the last cell say:

nb_auto_export()

that is if you imported nb_auto_export from somewhere you saved it.

Or alternatively without needing to import anything, you could just have the last cell of each notebook:

%%javascript 
if (IPython.notebook.kernel) {
    IPython.notebook.kernel.execute('!./notebook2script.py ' + IPython.notebook.notebook_name)
}

Also note that it’s async, so it usually takes a sec or so to start running once the nb finished running.

Or the messier approach w/o js magic:

from IPython.display import display, Javascript
display(Javascript("if (IPython.notebook.kernel) {IPython.notebook.kernel.execute('!./notebook2script.py ' + IPython.notebook.notebook_name)}"))

You might be able to drop display from it, but it seems not to work w/o it for me.

Finally, you shouldn’t rely on auto-save, since you’re likely to miss recent changes, so why not tell this code to save the notebook first, resulting in this code:

from IPython.display import display, Javascript
def nb_auto_export():
    display(Javascript("if (IPython.notebook) { IPython.notebook.save_notebook() }; if (IPython.notebook.kernel) { IPython.notebook.kernel.execute('!./notebook2script.py  ' + IPython.notebook.notebook_name )}"))

Ideas for this solution came from SO: 1, 2

2 Likes

(Thomas) #30

With a bit of Python import glue, you could also skip the export step and just import the notebooks (I used a %lib magic to indicate the things actually I want to show up in the library, but you could remove that and take it all). Maybe that can be even more user friendly than having to export things.

Best regards

Thomas

2 Likes

(Bart Fish) #31

Interactive Labeling:

I have some python code that I would like to use for interactive labeling of sections in a matplotlib plot using Jupyter Notebook. The code seems to work in python 2.7 under GUI, but will not run properly in a notebook. The problem is that the matplotlib close_event never leaves the cell containing the plot.

I’ve put together an example to demonstrate. If anyone has some working Jupyter Notebook code that can be used to label sections of a plot interactively please let me know.

Here is the sample code:

The notebook is here

0 Likes

(Stas Bekman) #32

Please do not use this thread for questions or problem reports, instead post them here:


as explained in the first post. Thank you.

0 Likes

(nok) #33

AutoScroll and go to current cell function are now included in the extension. The extension is in master branch but a new pip version is not released yet.

For now, you can install through

pip install https://github.com/ipython-contrib/jupyter_contrib_nbextensions/tarball/master

The PR for go to current cell

You can customize the shortkey yourself if needed.

1 Like

(Stas Bekman) #34

Undo cell delete

1 Like