EmacsからJupyter Notebookを触るための設定

はじめに

Emacsのeinというパッケージを用いることで、EmacsからJupyter Notebookを編集し、表示することができる。
実行イメージは以下の通りである。

Emacsから直接Jupyter Notebookが編集できるので、ブラウザにはない各種補完機能やカーソルジャンプ、undoなど充実したEmacsの編集機能を継承して使えるので便利ということである。

本記事ではその設定をメモしておく。

事前準備

1. jupyterをインストール

pip3 install jupyter

2. MELPAからeinをインストール(必須)

M-x package-install RET ein RET

設定

(eval-when-compile
  (require 'ein)
  (require 'ein-notebook)
  (require 'ein-notebooklist)
  (require 'ein-markdown-mode)
  (require 'smartrep))

;; (add-hook 'ein:notebook-mode-hook 'electric-pair-mode) ;; お好みで
;; (add-hook 'ein:notebook-mode-hook 'undo-tree-mode) ;; お好みで

;; undoを有効化 (customizeから設定しておいたほうが良さげ)
(setq ein:worksheet-enable-undo t)

;; 画像をインライン表示 (customizeから設定しておいたほうが良さげ)
(setq ein:output-area-inlined-images t)

;; markdownパーサー
;; M-x ein:markdown →HTMLに翻訳した結果を*markdown-output*バッファに出力
(require 'ein-markdown-mode)

;; pandocと markdownコマンドは入れておく
;; brew install pandoc
;; brew install markdown
(setq ein:markdown-command "pandoc --metadata pagetitle=\"markdown preview\" -f markdown -c ~/.pandoc/github-markdown.css -s --self-contained --mathjax=https://raw.githubusercontent.com/ustasb/dotfiles/b54b8f502eb94d6146c2a02bfc62ebda72b91035/pandoc/mathjax.js")

;; markdownをhtmlに出力してブラウザでプレビュー
(defun ein:markdown-preview ()
  (interactive)
  (ein:markdown-standalone)
  (browse-url-of-buffer ein:markdown-output-buffer-name))

;; smartrepを入れておく。
;; C-c C-n C-n C-n ... で下のセルに連続で移動、
;; その途中でC-p C-p C-pで上のセルに連続で移動など
;; セル間の移動がスムーズになってとても便利
(declare-function smartrep-define-key "smartrep")
(with-eval-after-load "ein-notebook"
  (smartrep-define-key ein:notebook-mode-map "C-c"
    '(("C-n" . 'ein:worksheet-goto-next-input-km)
      ("C-p" . 'ein:worksheet-goto-prev-input-km))))

実行手順

1. M-x ein:run を実行
Notebookを作成したいディレクトリを選択できるようになる。ディレクトリを決定するとJupyterがバックグラウンドで起動する。

2. カーネルを選択
要するにPythonのバージョン指定。

3. ファイル/ノートブック一覧から、ノートブックのところの[Open]をRET
既存のNotebookが起動する。

4. Notebook新規作成は[New Notebook]をRET

使い方

主なキーバインドとコマンド一覧を以下に示す。

とりあえず、セルに何か書いたらC-c C-cすれば、セルが評価される。

キーバインド/コマンド 説明
M-x ein:run jupyterを起動
C-c C-t 当該セルの評価モードをPython->MarkDown->rawの順に切り替える
C-c C-n 1つ下のセルに移動
C-c C-p 1つ上のセルに移動
C-c C-e 当該セルの表示をトグル
C-c C-c 当該セルを評価(Pythonのみ; Markdownは不可) →C-uをつけて実行するとすべてのセルを先頭から評価できる
C-c C-k 当該セルを削除
C-c C-a 新規セルを当該セルの上に作成 (C-uを先につけるとMarkdownで作成)
C-c C-b 新規セルを当該セルの下に作成 (C-uを先につけるとMarkdownで作成)
C-c C-s 当該セルを分割(split)
C-c C-m 当該セルをマージ(merge)
C-c C-l 当該セルの出力をクリア
C-c C-S-l 全てのセルの出力をクリア
C-x C-s 現在のnotebookを保存
C-x C-w 現在のnotebookを別名保存
M-x ein:notebook-save-to-command 当該ノートのコピーを、名前を付けて保存
C-c C-x C-r notebookのセッションを再起動する →番号をクリアしたいときに使う
M-x ein:stop jupyterを終了

Markdownのプレビューには、設定ファイルに書いた ein:markdown-preview を使うことでブラウザ上のプレビューが可能である。応用すればewwやemacs-w3m上で表示させ、Emacs内で完結させることもできるだろう。

おまけ:Einにおける数式表示

Markdown中にLatexで記述した数式をプレビューするための設定。インライン表示ではない。
実行イメージはこちら:


設定

M-x my-markdown-preview-latexとするとバッファがポップアップして、そこに数式が表示される。

(defun my-markdown-preview-latex ()
  "Preview LaTeX from the current cell in a separate buffer.

Handles only markdown and code cells, but both in a bit different
ways: on the former, its input is being rendered, while on the
latter - its output."
  (interactive)
  (let* ((cell (ein:worksheet-get-current-cell))
         (text-to-render
          (cond ((ein:markdowncell-p cell) (slot-value cell :input))
                ((ein:codecell-p cell)
                 (plist-get (car (cl-remove-if-not
                                  (lambda (e) (string= (plist-get e :name) "stdout"))
                                  (slot-value cell :outputs)))
                            :text))
                (t (error "Unsupported cell type"))))
         (buffer (get-buffer-create " *ein: LaTeX preview*")))
    (with-current-buffer buffer
      (when buffer-read-only
        (read-only-mode -1))
      (unless (= (point-min) (point-max))
        (delete-region (point-min) (point-max)))
      (insert text-to-render)
      (goto-char (point-min))
      (org-mode)
      (org-toggle-latex-fragment 16)
      (special-mode)
      (unless buffer-read-only
        (read-only-mode 1))
      (display-buffer
       buffer
       '((display-buffer-below-selected display-buffer-at-bottom)
         (inhibit-same-window . t)))
      (fit-window-to-buffer (window-in-direction 'below)))))

おわりに

EmacsからJupyter Notebook、一度試してみる価値はあると思う。