Box and Whisker

Optimize the World

IPython Notebook으로 블로깅하기

IPython Notebook과 Jekyll을 연동하여 블로깅을 하는 방법을 소개한다.

IPython Notebook

IPython Notebook을 사용하면 웹에서 코드와 수식과 그래프가 어우러진 문서를 작성할 수 있다. 이를테면 LaTeX을 써서 $c = \sqrt{a^2 + b^2}$ 이런 수식을 만들거나, 코드를 실행하여 0부터 9까지의 누적 합계(accumulative sum)를 구할 수 있다:

In [5]:
a = np.arange(10)
np.cumsum(a)
Out[5]:
array([ 0,  1,  3,  6, 10, 15, 21, 28, 36, 45])

물론 그래프도 그릴 수 있다:

In [6]:
x = np.linspace(0, 20, 100)
fig, axes = subplots(nrows=2)
for i in range(5):
    axes[0].plot(x, i * (x - 10)**2, 'o-')
for i in range(5):
    axes[1].plot(x, i * np.cos(x), '-')
show()

Jekyll

Jekyll은 블로그나 간단한 웹사이트를 만들 수 있는 웹 사이트 생성기이다. 마크다운 문서나 HTML 문서 파일들을 적절한 디렉토리 구조에 맞춰서 넣어주면 웹사이트가 만들어진다. 블로그나 회사 홈페이지처럼 별다른 기능이 없는 문서 위주의 사이트를 가볍게 운영하는 경우라면 Jekyll을 추천한다. Box and Whisker의 공식 웹사이트도 Jekyll을 이용하여 운영하고 있다.

다만 Jekyll은 IPython Notebook을 지원하지 않기 때문에 이 둘을 엮으려면 간단한 추가 작업이 필요하다.

IPython Notebook + Jekyll

IPython Notebook에서 작성된 파일은 ipynb 확장자로 저장되지만, 다음 명령을 실행하면 HTML 문서로 변경할 수 있다:

> ipython nbconvert --to html test.ipynb

하지만 내가 원하는 것은 전체 HTML 문서가 아니라 웹사이트 중간에 삽입할 수 있는 HTML 조각(fragment)이다. 다음 명령을 실행하면 단일한 <div> 루트 요소를 갖는 HTML 조각을 얻어낼 수 있다:

> ipython nbconvert --to html --template basic test.ipynb

하나 더. 파일을 생성하는게 아니라 결과를 표준 출력(Standard Output)으로 내보내려면 아래와 같은 옵션을 주자:

> ipython nbconvert --to html --template basic --stdout test.ipynb

이제 준비는 끝났고, Jekyll 플러그인을 만들어서 위 코드가 자동으로 실행되게 하면 된다.

플러그인 개발 문서를 참고해서 아래와 같이 간단한 코드를 만들었다:

module Jekyll
  class IPythonNotebook < Converter
    safe true
    priority :low

    def matches(ext)
      ext =~ /^\.ipynbref$/i
    end

    def output_ext(ext)
      ".html"
    end

    def convert(content)
      `ipython nbconvert --to html --template basic --stdout \`pwd\`/_ipynbs/#{content}`
    end
  end
end

ipynbref 파일을 찾아서, 그 파일에 담겨있는 ipynb 파일을 HTML로 변환해주는 코드이다. 이제 _ipynbs 디랙토리를 만들고 ipynb 파일을 저장한 다음, _posts 디렉터리에 markdown 파일 대신 ipynbref 파일을 만들어주면 된다. 예를 들어 지금 읽고 있는 문서의 ipynbref 파일은 아래와 같다:

---
layout: post
title: IPython Notebook으로 블로깅하기
author: 강규영
date: 2015-02-10 14:00:00
---
2015-02-10-blogging_with_python.ipynb

이제 다음 명령으로 Jekyll을 실행하면 끝:

> jekyll serve --watch

몇 가지 예시

일단 히스토그램:

In [38]:
data = np.random.normal(size=10000).reshape(-1, 2)
data[:, 1] = data[:, 1] * 0.5 + 1
_ = hist(data, bins=30, edgecolor='#FFFFFF')

다음엔 산포도(scatter plot):

In [50]:
a = np.linspace(0, 1, 100)
b = a + np.random.uniform(size=len(a))
_ = scatter(a, b, c=rcParams['lines.color'], edgecolor='none', alpha=0.7)

이번에는 sympy 예시:

In [3]:
from sympy import *
from sympy.interactive import printing
printing.init_printing(use_latex='mathjax')

x, y, z = symbols("x y z")
equation = ((x+y)**2 * (x+1))
equation
Out[3]:
$$\left(x + 1\right) \left(x + y\right)^{2}$$

전개하기:

In [4]:
expand(equation)
Out[4]:
$$x^{3} + 2 x^{2} y + x^{2} + x y^{2} + 2 x y + y^{2}$$

물론 R도 빼놓을 수 없다. rpy2가 설치되어 있다면 R의 수많은 패키지들을 쉽게 활용할 수 있다.

In [11]:
from rpy2.robjects import FloatVector
from rpy2.robjects.packages import importr
stats = importr('stats')
base = importr('base')

ctl = FloatVector([4.17,5.58,5.18,6.11,4.50,4.61,5.17,4.53,5.33,5.14])
trt = FloatVector([4.81,4.17,4.41,3.59,5.87,3.83,6.03,4.89,4.32,4.69])
group = base.gl(2, 10, 20, labels = ["Ctl","Trt"])
weight = ctl + trt

robjects.globalenv["weight"] = weight
robjects.globalenv["group"] = group
lm_D9 = stats.lm("weight ~ group")
print(stats.anova(lm_D9))
Analysis of Variance Table

Response: weight
          Df Sum Sq Mean Sq F value Pr(>F)
group      1 0.6882 0.68820  1.4191  0.249
Residuals 18 8.7292 0.48496               


마지막은 당연히 박스앤위스커 플롯(box and whisker plot) :-)

In [51]:
labels = list('ABCDEFGHIJKLMN')
data = np.random.lognormal(size=(20, len(labels)), mean=1.5, sigma=1.5)
yscale('log')
_ = bnw_boxplot(data, labels=labels)

훌륭하지 아니한가.