#!/usr/bin/env python # coding: utf-8 # # IPython的魔法符号-Magics # #### [openthings@163.com](http://my.oschina.net/u/2306127/blog?catalog=2527511) # ### 最新的Jupyter Notebook可以混合执行Shell、Python以及Ruby、R等代码! # 这一功能将解释型语言的特点发挥到了极致,从而打破了传统语言`"运行时"`的边界。 # # **IPython是一个非常好用Python控制台,极大地扩展了Python的能力。** # 因为它不仅是一种语言的运行环境,而且是一个高效率的分析工具。 # * 之前任何语言和IDE都是相互独立的,导致工作时需要在不同的系统间切换和拷贝/粘贴数据。 # * Magic操作符可以在HTML页面中输入shell脚本以及Ruby等其它语言并混合执行,极大地提升了传统的“控制台”的生产效率。 # * Magics是一个单行的标签式“命令行”系统,指示后续的代码将如何、以及被何种解释器去处理。 # * Magisc与传统的shell脚本几乎没有什么区别,但是可以将多种指令混合在一起。 # # Magics 主要有两种语法: # # * Line magics: 以 `%` 字符开始,该行后面都为指令代码,参数用空格隔开,不需要加引号。 # * Cell magics: 使用两个百分号 (`%%`)开始, 后面的整个单元(Cell)都是指令代码。 # 注意,`%%`魔法操作符只在Cell的第一行使用,而且不能嵌套、重复(一个Cell只有一个)。极个别的情况,可以堆叠,但是只用于个别情况。 # # #### 输入 [`%lsmagic`] 可以获得Magic操作符的列表。 # 如下所示(在Jupyter Notebook环境下,按[`shift+enter`]可以运行。): # In[ ]: get_ipython().run_line_magic('lsmagic', '') # ** 缺省情况下,`Automagic`开关打开,不需要输入`%`符号,将会自动识别。** # 注意,这有可能与其它的操作引起冲突,需要注意避免。如果有混淆情况,加上`%`符号即可。 # # 下面显示运行一段代码所消耗的时间。 # In[26]: time print("hi") # In[27]: get_ipython().run_line_magic('time', '') # #### 执行Shell脚本。 # In[9]: ls -l -h # In[10]: get_ipython().system('ls -l -h') # In[16]: files = get_ipython().getoutput('ls -l -h') files # #### 执行多行shell脚本。 # In[25]: get_ipython().run_cell_magic('!', '', 'ls -l\npwd\nwho\n') # ### 下面开始体验一下魔法操作符的威力。 # # 载入matplotlib和numpy,后面的数值计算和绘图将会使用。 # In[49]: get_ipython().run_line_magic('matplotlib', 'inline') import numpy as np import matplotlib.pyplot as plt # ## cell magics的简单例子 # #### `%timeit` 魔法,计量代码的执行时间, 适用于单行和cell: # In[50]: get_ipython().run_line_magic('timeit', 'np.linalg.eigvals(np.random.rand(100,100))') # In[4]: get_ipython().run_cell_magic('timeit', 'a = np.random.rand(100, 100)', 'np.linalg.eigvals(a)\n') # #### `%%capture` 魔法,用于捕获stdout/err, 可以直接显示,也可以存到变量里备用: # In[4]: get_ipython().run_cell_magic('capture', 'capt', "from __future__ import print_function\nimport sys\nprint('Hello stdout')\nprint('and stderr', file=sys.stderr)\n") # In[5]: capt.stdout, capt.stderr # In[53]: capt.show() # #### `%%writefile` 魔法,将后续的语句写入文件中: # In[8]: get_ipython().run_cell_magic('writefile', 'foo.py', "print('Hello world')\n") # In[9]: get_ipython().run_line_magic('run', 'foo') # ## Magics 运行其它的解释器。 # ##### IPython 有一个 `%%script` 魔法操作符, 可以在一个子进程中运行其它语言的解释器,包括: bash, ruby, perl, zsh, R, 等等. # # 执行时从stdin取得输入,就像你自己在键入一样。 # #### 直接在`%%script` 行后传入指令即可使用。 # 后续的cell中的内容将按照指示符运行,子进程的信息通过stdout/err捕获和显示。 # In[1]: get_ipython().run_cell_magic('script', 'python', "import sys\nprint('hello from Python %s' % sys.version)\n") # In[2]: get_ipython().run_cell_magic('script', 'python3', "import sys\nprint('hello from Python: %s' % sys.version)\n") # IPython对通用的解释器创建了别名,可以直接使用, 譬如:bash, ruby, perl, etc. # **等价于这个操作符: `%%script ` ** # In[3]: get_ipython().run_cell_magic('ruby', '', 'puts "Hello from Ruby #{RUBY_VERSION}"\n') # In[4]: get_ipython().run_cell_magic('bash', '', 'echo "hello from $BASH"\n') # ## 高级练习: 写一个自己的脚本文件。 # 写一个脚本文件,名为 `lnum.py`, 然后执行: # In[6]: get_ipython().run_cell_magic('writefile', './lnum.py', 'print(\'my first line.\')\nprint("my second line.")\nprint("Finished.")\n') # In[33]: get_ipython().run_cell_magic('script', 'python ./lnum.py', '#\n') # ## 捕获输出。 # 可以直接从子进程中捕获stdout/err到Python变量中, 替代直接进入stdout/err。 # In[14]: get_ipython().run_cell_magic('bash', '', 'echo "hi, stdout"\necho "hello, stderr" >&2\n') # In[15]: get_ipython().run_cell_magic('bash', '--out output --err error', 'echo "hi, stdout"\necho "hello, stderr" >&2\n') # 可以直接访问变量名了。 # In[17]: print(error) print(output) # ## 后台运行 Scripts # 只需添加 **`--bg`** ,即可让脚本在后台运行。 # 在此情况下, 输出将被丢弃,除非使用 `--out/err` 捕获输出。 # In[34]: get_ipython().run_cell_magic('ruby', '--bg --out ruby_lines', 'for n in 1...10\n sleep 1\n puts "line #{n}"\n STDOUT.flush\nend\n') # #### 当后台线程保存输出时,有一个stdout/err *pipes*, 而不是输出的文本形式。 # In[35]: ruby_lines # In[36]: print(ruby_lines.read()) # # Cython Magic 函数扩展 # ## 载入 extension # IPtyhon 包含 `cythonmagic` extension,提供了几个与Cython代码工作的魔法函数。使用 `%load_ext` 载入,如下: # In[37]: get_ipython().run_line_magic('load_ext', 'cythonmagic') # ** `%%cython_pyximport`** magic函数允许你在Cell中使用任意的Cython代码。Cython代码被写入`.pyx` 文件,保存在当前工作目录,然后使用`pyximport` 引用进来。需要指定一个模块的名称,所有的符号将被自动import。 # In[38]: get_ipython().run_cell_magic('cython_pyximport', 'foo', 'def f(x):\n return 4.0*x\n') # In[6]: f(10) # ## The %cython magic # `%cython` magic类似于 `%%cython_pyximport` magic, 但不需要指定一个模块名称. `%%cython` magic 使用 `~/.cython/magic`目录中的临时文件来管理模块,所有符号会被自动引用。 # # 下面是一个使用Cython的例子,Black-Scholes options pricing algorithm: # In[39]: get_ipython().run_cell_magic('cython', '', 'cimport cython\nfrom libc.math cimport exp, sqrt, pow, log, erf\n\n@cython.cdivision(True)\ncdef double std_norm_cdf(double x) nogil:\n return 0.5*(1+erf(x/sqrt(2.0)))\n\n@cython.cdivision(True)\ndef black_scholes(double s, double k, double t, double v,\n double rf, double div, double cp):\n """Price an option using the Black-Scholes model.\n \n s : initial stock price\n k : strike price\n t : expiration time\n v : volatility\n rf : risk-free rate\n div : dividend\n cp : +1/-1 for call/put\n """\n cdef double d1, d2, optprice\n with nogil:\n d1 = (log(s/k)+(rf-div+0.5*pow(v,2))*t)/(v*sqrt(t))\n d2 = d1 - v*sqrt(t)\n optprice = cp*s*exp(-div*t)*std_norm_cdf(cp*d1) - \\\n cp*k*exp(-rf*t)*std_norm_cdf(cp*d2)\n return optprice\n') # In[40]: black_scholes(100.0, 100.0, 1.0, 0.3, 0.03, 0.0, -1) # #### 测量一下运行时间。 # In[45]: #%timeit black_scholes(100.0, 100.0, 1.0, 0.3, 0.03, 0.0, -1) # Cython 允许使用额外的库与你的扩展进行链接,采用 `-l` 选项 (或者 `--lib`)。 注意,该选项可以使用多次,libraries, such as `-lm -llib2 --lib lib3`. 这里是使用 system math library的例子: # In[10]: get_ipython().run_cell_magic('cython', '-lm', "from libc.math cimport sin\nprint 'sin(1)=', sin(1)\n") # 同样,可以使用 `-I/--include` 来指定包含头文件的目录, 以及使用 `-c/--compile-args` 编译选项,以及 `extra_compile_args` of the distutils `Extension` class. 请参考 [the Cython docs on C library usage](http://docs.cython.org/src/tutorial/clibraries.html) 获得更详细的说明。 # # Rmagic 函数扩展 # IPython 通过 `rmagic` 扩展来调用R模块,是通过rpy2来实现的(安装:`conda install rpy2`)。 # rpy2的文档:http://rpy2.readthedocs.io/en/version_2.7.x/ # #### 首先使用 `%load_ext` 载入该模块: # **注意:新的rpy2已改动,不能运行**。参考:http://rpy2.readthedocs.io/en/version_2.7.x/interactive.html?highlight=rmagic # In[58]: get_ipython().run_line_magic('reload_ext', 'rmagic') # 典型的用法是使用R来计算numpy的Array的统计指标。我们试一下简单线性模型,输出scatterplot。 # In[62]: get_ipython().run_line_magic('matplotlib', 'inline') import numpy as np import matplotlib.pyplot as plt # In[63]: X = np.array([0,1,2,3,4]) Y = np.array([3,5,4,6,7]) plt.scatter(X, Y) # 首先把变量赋给 R, 拟合模型并返回结果。 %Rpush 拷贝 rpy2中的变量. %R 对 rpy2 中的字符串求值,然后返回结果。在这里是线性模型的协方差-coefficients。 # In[64]: get_ipython().run_line_magic('Rpush', 'X Y') get_ipython().run_line_magic('R', 'lm(Y~X)$coef') # %R可以返回多个值。 # In[65]: get_ipython().run_line_magic('R', 'resid(lm(Y~X)); coef(lm(X~Y))') # 可以将 %R 结果传回 python objects. 返回值是一个“;”隔开的多行表达式,*coef(lm(X~Y))*. # 拉取R的其它变量, 采用 %Rpull 和 %Rget. 在 R code 执行后,在 rpy2 namespace 有变量需要获取。 # 主要区别是: # (%Rget)返回值, 而(%Rpull)从 self.shell.user_ns 拉取。想象一下,我们计算得到变量 "a" 在rpy2's namespace. 使用 %R magic, 我们得到结果并存储到 b。可以从 user_ns 使用 %Rpull得到。返回的是同一个数据。 # In[6]: b = get_ipython().run_line_magic('R', 'a=resid(lm(Y~X))') get_ipython().run_line_magic('Rpull', 'a') print(a) assert id(b.data) == id(a.data) get_ipython().run_line_magic('R', '-o a') # ## Plotting and capturing output # R的控制台stdout()被ipython捕获。 # In[7]: from __future__ import print_function v1 = get_ipython().run_line_magic('R', 'plot(X,Y); print(summary(lm(Y~X))); vv=mean(X)*mean(Y)') print('v1 is:', v1) v2 = get_ipython().run_line_magic('R', 'mean(X)*mean(Y)') print('v2 is:', v2) # ## Cell 级别的 magic # 我们希望用R在cell级别。而且numpy最好不要转换,参考R: rnumpy ( http://bitbucket.org/njs/rnumpy/wiki/API ) 。 # In[8]: get_ipython().run_cell_magic('R', '-i X,Y -o XYcoef', 'XYlm = lm(Y~X)\nXYcoef = coef(XYlm)\nprint(summary(XYlm))\npar(mfrow=c(2,2))\nplot(XYlm)\n') # # octavemagic: Octave inside IPython # **`octavemagic`** 提供与Octave交互的能力。依赖 `oct2py` 和 `h5py` 软件包。 # 载入扩展包: %load_ext octavemagic # ## 概览 # 载入这个扩展包,启用了三个magic functions: `%octave`, `%octave_push`, 和 `%octave_pull`。 # 第一个执行一行或多行Octave, 后面两个执行 Octave 和 Python 的变量交换。 # In[110]: x = get_ipython().run_line_magic('octave', '[1 2; 3 4];') x # In[111]: a = [1, 2, 3] get_ipython().run_line_magic('octave_push', 'a') get_ipython().run_line_magic('octave', 'a = a * 2;') get_ipython().run_line_magic('octave_pull', 'a') a # **`%%octave`** : 多行 Octave 被执行。但与单行不同, 没有值被返回, 我们使用`-i` 和 `-o` 指定输入和输出变量。 # In[116]: get_ipython().run_cell_magic('octave', '-i x -o y', 'y = x + 3;\n') # In[117]: y # ## Plotting # Plot输出自动被捕获和显示,使用 `-f` 参数选择输出的格式 (目前支持 `png` 和 `svg`)。 # In[118]: get_ipython().run_cell_magic('octave', '-f svg', "\np = [12 -2.5 -8 -0.1 8];\nx = 0:0.01:1;\n\npolyout(p, 'x')\nplot(x, polyval(p, x));\n") # 使用 `-s` 参数调整大小: # In[119]: get_ipython().run_cell_magic('octave', '-s 500,500', '\n# butterworth filter, order 2, cutoff pi/2 radians\nb = [0.292893218813452 0.585786437626905 0.292893218813452];\na = [1 0 0.171572875253810];\nfreqz(b, a, 32);\n') # In[120]: get_ipython().run_cell_magic('octave', '-s 600,200 -f png', '\nsubplot(121);\n[x, y] = meshgrid(0:0.1:3);\nr = sin(x - 0.5).^2 + cos(y - 0.5).^2;\nsurf(x, y, r);\n\nsubplot(122);\nsombrero()\n')