用 IPython notebook + nvd3 實現(xiàn)交互式調試
發(fā)布于:2014/08/31 18:22:52
IPython(http://ipython.org/) 是一款 Python 的增強IDE史辙,而 IPython notebook 則是在網頁上實現(xiàn)了更為強大的交互功能的 IPython
本文的內容旨在簡要的介紹使用 IPython notebook 的方法,并重點介紹利用 IPython notebook + nvd3 圖形庫進行交互式的調試
一佩伤、準備
# 安裝 IPython 及 nvd3
$ sudo pip install ipython python-nvd3
# 安裝 numpy聊倔、scipy、matplotlib
$ sudo pip install numpy scipy matplotlib
# 打開 IPython
$mkdir~/notebook
$ ipython notebook ~/notebook
點擊New Notebook打開一個新頁面生巡,就算準備完成了
在Untitled0那一欄可以自由設定文件名耙蔑,按command + s就可以保存
二、為數(shù)值運算做準備
數(shù)值運算會需要用到 numpy障斋、scipy纵潦、matplotlib 這些常用庫,直接在 IPython notebook 里寫一句%pylab inline就自動的導入了這些庫
在 IPython 里垃环,代碼被分為一個一個的 cell 邀层,按shift + ENTER可以執(zhí)行當前選定的 cell, 也可以在菜單里點選 CELL -> Run ALL 從上至下運行所有的 cell遂庄,在運行大段的代碼的時候寥院,當你改變了某個上游的代碼塊,可能不僅僅需要重新 Run 這個或全部的 cell涛目,還需要點擊 Kernel Restart 來重啟后臺秸谢,來清理掉命名空間中之前留下的各種變量或導入的模塊
三、簡單的繪圖
因為之前使用 %pylab inline 已經自動引入了 numpy 和 matplotlib 庫霹肝,現(xiàn)在可以直接在 notebook 里繪圖了
Ipython %pylab 會自動引入 numpy估蹄,相當于執(zhí)行了 from numpy import *; import numpy as np ,所以你既可以直接用 numpy 里的內容沫换,也可以按照習慣用 np.xxx 的方式來使用
# 在 IPython notebook 里繪圖x = np.linspace(0,10,50)y = np.sin(x)z = np.cos(x)plot(x, y)scatter(x, z)
plot 和 scatter 都是 matplotlib 中的接口臭蚁,你可以在下面的地址中獲取到更多的信息
本文的目的并不是介紹 matplotlib 繪圖,所以先跳過了
四讯赏、使用 IPython 的交互式功能
IPython 提供了功能強大的交互式操作 iteract 接口垮兑,使用戶可以通過交互式的操作來調節(jié)參數(shù)
# 引入 interact 模塊fromIPython.html.widgetsimportinteract, IntSliderWidget# 把之前的畫圖函數(shù)改寫成依賴于參數(shù)的函數(shù)deftest_interact(min_, max_, steps_):x = np.linspace(min_, max_, steps_)? ? y = np.sin(x)? ? z = np.cos(x)? ? plot(x, y)? ? scatter(x, y)# 使用 interact 來交互式的調試參數(shù)interact(test_interact,# 為每一個參數(shù)設定一個 interact 控件min_=IntSliderWidget(min=1,# 最小值max=10,# 最大值step=1,# 每次調節(jié)的步長value=1),# 初始值max_=IntSliderWidget(min=10, max=20, step=1, value=10),? ? ? ? steps_=IntSliderWidget(min=10, max=100, step=10, value=50))
我們現(xiàn)在可以用鼠標拖動來簡單的調節(jié)效果了
IPython 的 widgets 還有
Widget, DOMWidget, CallbackDispatcher
CheckboxWidget, ToggleButtonWidget
ButtonWidget
ContainerWidget, PopupWidget
FloatTextWidget, BoundedFloatTextWidget, FloatSliderWidget, FloatProgressWidget
ImageWidget
IntTextWidget, BoundedIntTextWidget, IntSliderWidget, IntProgressWidget
RadioButtonsWidget, ToggleButtonsWidget, DropdownWidget, SelectWidget
TabWidget, AccordionWidget
HTMLWidget, LatexWidget, TextWidget, TextareaWidget
interact, interactive, fixed
官方文檔(http://ipython.org/ipython-doc/dev/api/index.html)的資料少的可憐,我一般都是上這里http://nullege.com/codes/search/IPython.html.widgets.DropdownWidget來查看接口的用法漱挎,其實大部分的用法都比較一致系枪,就是傳遞一個 min, max, step 值, dropdown 傳的是 dict 或 list磕谅,每個接口都有一個 value 參數(shù)來設定初始值
其實常用的就是 IntSliderWidget私爷、FloatSliderWidget雾棺、DropdownWidget、RadioButtonsWidget 当犯,用法也不難垢村,試一試就出來了,這里也就不詳細介紹了
五嚎卫、使用 nvd3
現(xiàn)在我們已經會用 matplotlib 畫圖嘉栓,而且還會使用 active 來進行交互式的調試,其實已經能解決日常中的絕大部分需求了拓诸。不過在 IPython notebook 中使用 matplotlib 有一個美中不足的地方就是不能對圖形進行交互式操作侵佃,當我們繪制一個三維圖的時候,可能需要拖動才能看清楚圖形的結構奠支,而 IPython 中的 matplotlib 對此就無能為力了馋辈。不過好在 IPython 強大到可以近乎完美的支持 HTML,也就是說你可以隨意的去挑選你喜歡的 JavaScript 圖形庫來進行繪圖倍谜。這里我們選擇在科學數(shù)據(jù)繪制上表現(xiàn)出眾的 D3 (http://d3js.org/)
nvd3(http://nvd3.org/)是對 d3 的封裝迈螟,而 python-nvd3 則是一個能夠生成 nvd3 網頁代碼(html & javascript)的 python 模塊,我們用 python-nvd3 來生成 HTML 代碼尔崔,然后用 IPython 的 HTML 模塊提供的功能來顯示
OK答毫,圖形按照我們所想的畫出來了~
和 matplotlib 相比,圖形更加美觀季春,而且更重要的是有了交互功能
*六洗搂、nvd3 的高級使用
python-nvd3 提供的接口實在少的可憐。比如如果我想改變 tooltip 的內容讓它顯示我希望的內容载弄,或者我希望在一個數(shù)據(jù)集內也有不同的顯示方式(比如不同 size 或顏色等等)耘拇,這些在 nvd3 中都是可以實現(xiàn)的功能,但是 python-nvd3 卻沒有提供相應的接口
好在 python-nvd3 返回了 HTML 代碼宇攻,我的解決辦法是直接用正則去匹配 HTML 代碼惫叛,然后插入我想要實現(xiàn)功能的 javascript 代碼
我也在探索更好的實現(xiàn)辦法,所以這段就不詳細介紹了逞刷,如果你只是迫切的需要類似的功能挣棕,可以考慮使用我目前的解決辦法
1、替換 tooltip
# 這個函數(shù)的功能是替換 python-nvd3 生成的 HTML代碼# 參數(shù) nodes 是一個形如 [{‘id’: name, ‘x’: 1, 'y': 2, 'value': 23}, {}, ...] 的數(shù)據(jù)塊# 讓 tooltip 去遍歷 nodes亲桥,找出 x 和 y 匹配的 node, 然后顯示其 id 值defreplace_tooltip(html, nodes):"""Change the D3 chart's tooltip
"""tooltip_content ="""
chart.tooltipContent(function(key, y, e, graph) {{
var nodes = {0};
var content = '';
var group = Number(key.split('_')[1]);
var x = graph.point.x;
var y = graph.point.y;
for(var i in nodes){{
var point = nodes[i];
if(point['x'] == x && point['y'] == y){{
content += '' + point['id'] + '';
break;
}}
}}
content += '
' + x.toString() + ' ' + y.toString();
return content;
}});
""".format(str(nodes))? ? p = re.compile(r'\bchart.tooltipContent[.\S\s]*''return tooltip_str;\s*\}\);')? ? html = p.sub(tooltip_content, html)returnhtml
2固耘、讓同組數(shù)據(jù)有不同的顯示效果
# 這個函數(shù)也是替換 python-nvd3 生成的 HTML# nodes 的結構同上# anomaly 是一個形如 [id, id] 的列表# 其實原理和上面的一樣题篷,都是根據(jù) x 和 y 匹配,然后修改 nvd3 的數(shù)據(jù)集 DATA_TCS (TCS是我的畫布名厅目,這個的命名規(guī)則是 DATA_番枚,你可以去看自己的 python-nvd3 生成的 HTML 代碼)defmark_anomalies_axis(nodes, anomalies, html):"""Mark the anomalies points
"""anoma_ls = []foranomalyinanomalies:fornodeinnodes:ifnode['id'] == anomaly:? ? ? ? ? ? ? ? anoma_ls.append({'x': node['x'],'y': node['y']})? ? _mark_anoma_js ="""
anomalies = {0};
// mark anomalies
for(var ia in anomalies) {{
anoma = anomalies[ia];
for(var ig in data_TCS) {{
var group = data_TCS[ig];
for(var ip in group['values']) {{
var p = group['values'][ip];
if(p['x']==anoma['x'] && p['y']==anoma['y']) {{
p['size'] = 0.9;
break;
}}
}}
}}
}}
nv.addGraph(function() {{
""".format(str(anoma_ls))? ? p = re.compile(r'\bnv\.addGraph\(function\(\) {')? ? html = p.sub(_mark_anoma_js, html)returnhtml
效果圖