當前位置:
首頁 > 知識 > 在瀏覽器中用Python做數據科學:Pyodide

在瀏覽器中用Python做數據科學:Pyodide

(此處已添加圈子卡片,請到今日頭條客戶端查看)

Pyodide 是Mozilla的一個實驗性項目,用於創建一個完全在瀏覽器中運行的完整的Python數據科學技術棧。

在瀏覽器中用Python做數據科學:Pyodide

Pyodide的動機來自於Mozilla的另一個項目 Iodide的工作,在早先的一個博客中我們介紹了它。Iodide是一個基於最新Web技術的用於數據科學實驗和交流的工具。值得注意的是,它被設計來在瀏覽器中而不是在遠程內核中執行數據科學計算。

不幸的是,我們在瀏覽器中所用的語言,JavaScript,沒有一個成熟的數據科學庫。並且它缺失了一些對於數值計算很有用的特性,比如操作符重載。我們仍然認為致力於改變改狀況並將JavaScript數據科學生態向前推進是值得的。同時,我們也在選取一個捷徑: 我們通過將流行的成熟的Python科學技術棧帶入瀏覽器領域來滿足數據科學家。

廣泛被爭論的就是Python不在瀏覽器中運行是對該語言的存在的威脅—現在如此多用戶交互發生在Web或者移動設備上,它需要也能在上面工作否則就會落後。因此,儘管Pyodide試圖要首先滿足Iodide,它仍然被設計為也可作為獨立體使用。

Pyodide帶給你一個完全運行在瀏覽器中的完整的,標準的Python解釋器,並具有完整的訪問瀏覽器的Web APIs的能力。在上面的例子中(50MB下載),對加利福利亞奧克蘭城當地信息服務中心「311」的訪問密度進行了3D作圖。數據載入和處理在Python中執行,然後將其遞交給Javascript和WebGL來繪圖。

另外一個快速示例是,下面有一個簡單的小腳本可以讓你在瀏覽器窗口中進行繪畫。

在瀏覽器中用Python做數據科學:Pyodide

下面就是它看起來的樣子:

在瀏覽器中用Python做數據科學:Pyodide

最好的了解Pyodide可以做什麼的方式就是直接去嘗試它!這裡有一個演示notebook (50MB 下載),其概括了一些高級特徵。這篇博客的剩餘部分將更多的是對Pyodide如何工作的深入的技術研究。

現有技術

當我們啟動Pyodide項目的時候,已經有一些令人印象深刻的項目將Python帶到瀏覽器的世界。不幸的是,還沒有任何一個項目致力於實現具有完整功能特性的主流數據科學技術棧(支持 NumPy,Pandas, Scipy, 和Matplotlib),而那正是我們的目標。

諸如 Transcrypt這樣的項目將Python轉換為JavaScript。由於轉換編譯這個步驟發生在Python上, 你要麼需要提前完成所有的轉譯工作要麼需要與一個伺服器交互來完成該工作。這不能真正地滿足我們讓用戶能夠在沒有外界幫助的情況下在瀏覽器中編寫Python並運行的目標。

像 Brython 和Skulpt 這樣的項目是用JavaScript重寫了標準的Python解釋器,因此,你能夠直接在瀏覽器中運行Python代碼。不幸的是,由於它們是由JavaScript啟動的完全的新的Python的實現,其不能夠與用C寫的Python擴展兼容,比如 NumPy 和Pandas。因此,沒有數據科學工具。

PyPyJs是在瀏覽器中JIT編譯Python的實現 PyPy的一個可替代品,其使用emscripten。它有潛力能夠非常迅速地運行Python代碼,原因和PyPy一樣。不幸的是,它也像PyPy一樣有無法執行C擴展 的問題。所有這些方式使得我們要去重寫科學計算工具來獲得足夠的性能。作為一個經常使用Matplotlib的人,我知道那將佔據多少數不清的個人時間:其它的項目已經進行過嘗試並停滯不前,而且肯定是會帶來許多我們這樣的雜湊的創業團隊無法處理的工作量。我們因此需要構建一個儘可能地接近標準Python實現的工具,以及大多數據科學家已經使用的科學計算技術棧。

在與Mozilla的WebAssembly 團隊交談之後,我們發現構建的關鍵是 emscripten 和WebAssembly: 用於將向瀏覽器傳送C代碼的技術。這導致我們發現了一個已存的但是沉寂的用於emscripten的Python構建, cpython-emscripten,其最終被用作Pyodide的基礎。

emscripten與WebAssembly

有許多方式來描述 emscripten為何物,但是對於我們來說最重要的是它提供了兩樣東西:

  • 一個將C/C++編譯到WebAssembly的編譯器
  • 一個使瀏覽器感覺就像是一個原生的計算環境的兼容層

WebAssembly是一個新的運行於現代Web瀏覽器中的語言,其是對JavaScript的補充。它是一個類似於彙編語言的運行時幾乎具有原生性能被設計作為如C和C++這樣的低級語言的編譯目標的低級語言。值得注意的是,Python的最流行的解釋器CPython就是用C實現的,因此這就是emscripten被創造出來的意義。

Pyodide通過以下過程組合:

  • 從主流的Python 解釋器(CPython)下載源碼,和科學計算包(NumPy等)
  • 引入很小的改變來使得它們能夠在新環境中工作
  • 使用emscripten的編譯器將它們編譯為WebAssembly

如果你直接將WebAssembly載入進入瀏覽器,你會發現事情與你直接在你的操作系統上運行Python解釋器有非常明顯的不同。例如,Web瀏覽器沒有一個文件系統(用於載入和存儲文件的地方)。幸運的是,emscripten提供了一個虛擬的文件系統,其使用JavaScript編寫並能夠被Python解釋器使用。這些虛擬的「文件」默認保存在瀏覽器標籤對應的內存上,當你導航離開該頁面時,它們就會消失。(emscripten也提供了讓該文件系統能夠將數據存儲在瀏覽器的持續性本地存儲上的方式,但是Pyodide沒有使用該功能。)

通過模擬出標準計算環境的文件系統以及其它一些特性,emscripten使得將現存的項目轉移到Web瀏覽器上成為可能而且令人驚喜的是只需要少量的修改。(某一天,我們也許會轉移到使用WASI來作為系統模擬層,但是目前來說emscripten是一個更成熟和完整的選擇)。

總的來說,為了能夠在你的瀏覽器中載入Pyodide,你需要下載:

  • 已經編譯到WebAssembly的Python解釋器
  • 一系列由emscripten提供的JavaScript用於提供系統模擬
  • 一個打包好含有Python解釋器所需要的所有的文件的文件系統,最重要的就是Python標準庫

這些文件可能會非常大: Python本身就是21MB,NumPy是7MB等等。幸運的是,這些包只需要被下載一次,之後他們會被存儲在瀏覽器的緩存中。

先後完成這些步驟以後,Python解釋器就能夠訪問它的標準庫中的文件,啟動,以及開始運行用戶代碼。

哪些能工作,哪些又不能

我們運行了CPython的單元測試作為Pyodide的持續性測試的一部分來理解哪些Python的特性可以工作哪些又不可以工作。一些功能,比如線程現在還不能工作,但是通過最新可獲取的WebAssembly 線程,我們應該在不遠的將來就能加入支持。

其它特性,例如 底層網路sockets由於瀏覽器的安全沙盒幾乎不可能實現。很抱歉需要告訴你,你希望在你的Web瀏覽器中運行一個Pythonminecraft 伺服器的希望還有很長一段路要走。然而,你可以通過瀏覽器的API來通過網路獲取數據(更多細節在後面的內容)。

它有多快?

在JavaScript虛擬機中運行Python解釋器具有性能損失,但是該損失實際上是令人吃驚的小—在我們的基準測試中,相比於原生的速度在Firefox上要慢1到12倍在Chrome上要慢1到16倍。經驗表明,這對於互動式開發來說已經足夠使用了。

值得注意的是,在Python中有大量的內部循環的代碼的速度趨向於以更大的係數慢於那些依賴於NumPy進行內部循環的代碼。下面是在同一個硬體上與原生運行相比,在Firefox和Chrome上運行各種純Python和Numpy基準測試的結果。

在瀏覽器中用Python做數據科學:Pyodide

在Python和JavaScript之間進行交互

如果所有Pyodide能做的就只是運行Python代碼並寫出到標準輸出上,它將會增長成為一個不錯的很酷的技巧,但是不會成為一個用於實際工作的實用工具。真正的力量源於它與瀏覽器API以及其它運行在瀏覽器中的JavaScript庫交互的能力。由於我們已經將Python解釋器編譯為了WebAssembly,它也與JavaScript端具有深度的交互。

Pyodide會在許多Python與JavaScript之間的內建數據類型之間進行隱式轉換。其中一些轉換時很直接明顯的,但如往常一樣,那就是很有趣的極端情況。

在瀏覽器中用Python做數據科學:Pyodide

Python將字典和對象實例作為不同的類型來對待。字典是從鍵到值得映射。另一方面,對象通常擁有一些能夠作用於該對象的方法。在JavaScript中,這兩個概念被合併到一個單一類型對象。(是的,這裡我進行了過度簡化來使描述易於理解)

沒有對開發者使用JavaScript對象的意圖進行真正的理解,要去有效的區分是否應該將其轉換為Python字典或者對象將會是不可能的。因此,我們不得不使用一個代理和讓」鴨子類型「來處理這個情形。

代理是一個圍繞在其它語言中的一個變數的包裝器。相比於簡單地在JavaScript中讀取變數並重寫為Python的數據結構(就如基本類型的做法一樣),代理堅持了原始的JavaScript變數,並在需要時對它調用方法。這意味著任何JavaScript變數,不管有多麼定製化,都是完全能夠從Python訪問的。代理在其它方面也能工作。

鴨子類型是一個原則,而不是詢問一個變數「你是一隻鴨子嗎?」你問它「你走路像鴨子嗎?」以及「你叫得像鴨子嗎?」並從中推斷它很有可能是一隻鴨子,或者至少會像鴨子一樣工作。這允許Pyodide來延遲做出如何轉換JavaScript對象的決定:它將其包裝在一個代理中並讓使用它的代碼來決定如何處理。當然,這並非總是能工作,鴨子可能實際上是一隻兔子。因此,Pyodide也提供了方法來顯式地處理這些轉換。

正是這樣級別的緊密集成允許了用戶能夠在Python中處理它們的數據,並將其發送給JavaScript來可視化。例如,在我們的Hipster Band Finder 展示中,我們表達了如何在Python的Pandas中載入和分析數據,然後將其發送給JavaScript的Plotly 來進行可視化。

訪問Web API和DOM

代理也被證明是訪問Web API以及瀏覽器所提供的使API工作的函數集的關鍵。例如,很大部分Web API是在document對象上。你可以通過如下方式從Python中獲取:

在瀏覽器中用Python做數據科學:Pyodide

這會將document對象作為一個代理從JavaScript端導入到Python端。你可以開始從Python中對其調用方法:

在瀏覽器中用Python做數據科學:Pyodide

所有這些都通過能夠動態查詢document對象的代理來完成。Pyodide不需要包含一個瀏覽器所有的全部API的完整列表。

當然,直接使用Web API並不總是最Python化或者用戶友好型的工作方式。更棒的方式要等到對Web API的用戶友好型的Python包裝器的出現,就像jQuery和其它的庫如何使得Web API能夠從JavaScript中使用一樣。 如果你感興趣於為這個想法來工作,請告知我們!

多維數組

有些數據類型是特定對數據科學有用的,而Pyodide對它們也有專門的支持。多維數組是具有相同類型的值(通常是數值)的集合。它們傾向於非常大,並且知道每個元素都是同樣的類型相對於能夠存儲任何類型的Python list和JavaScriptArrays來說具有實際上的性能優勢。

在Python中, NumPy 數組是最常用的多維數組的實現。JavaScript具有TypedArrays,其僅含有一個單一的數值類型,但是是一維的,因此需要在其之上構建多維索引。

由於實際上這些數組可能會非常大,我們不想在語言運行時間拷貝它們。那不僅僅會花相當長的時間,而且在內存中同時保留兩個拷貝將會加重瀏覽器所具有的被限制的內存的負擔。

幸運的是,我們可以不用拷貝來共享數據。多維數組通常是用少量用於描述值類型和數組形狀及內存分布的元數據來實現的。數據本身是從元數據中通過指針訪問的另一個內存區域。該內存處於一個叫作「WebAssembly堆」的區域,這帶來一個優勢,因為其可以從JavaScript和Python中同時訪問。我們可以簡單地在語言之間拷貝元數據(其本身非常小),並保持指針指向WebAssembly堆中的數據。

在瀏覽器中用Python做數據科學:Pyodide

這個想法目前對一維數組進行了實現,同時對於高維數組目前工作還處於次最優狀態。我們需要對JavaScript端不斷改善來獲得一個可工作的有用的對象。迄今為止還沒有一個用於JavaScript多維數組的很顯然的選擇。有前景的項目,比如Apache Arrow](https://arrow.apache.org/) 和xnd』s ndarray就正是致力於解決該問題,旨在使語言間運行時的內存結構化數據的傳遞更容易。對這些項目的構建的調查仍在進行當中以使得這類型的數據轉換更為強大。

實時交互可視化

在瀏覽器中進行數據科學計算相比於如Jupyter一樣在遠程內核中進行計算的一大優勢就是,互動式可視化不用通過網路來傳輸數據並重新處理和展示這些數據。這很大程度地減少了延遲—用戶移動滑鼠的時刻與屏幕更新並顯示圖案的時刻之間的間隔時間。

要使得其能工作需要上面描述到的所有的技術片段能夠很好地協同工作。我們使用matplotlib來看一下用於展示正態分布如何工作的交互性示例。首先,通過Python的Numpy產生隨機數據。接下來,Matplotlib接管該數據,並使用內建的軟體渲染器來將其繪出。它使用Pyodide對零拷貝共享數組的支持來將像素回饋給JavaScript端,在這裡數據最終被渲染為HTML的畫布。然後瀏覽器接管工作,將像素顯示到屏幕上。用來支持交互性操作的滑鼠和鍵盤事件通過從Web瀏覽器到Python的回調函數的調用來處理。

在瀏覽器中用Python做數據科學:Pyodide

軟體包

Python科學計算棧並不是單獨的一個龐然大物—它實際上是一個一系列相互之間松關聯的包在一起工作構造的環境。其中最流行的是用於數值數組和基本計算的 NumPy,用於更複雜和通用計算如線性代數的 Scipy ,用於數據可視化的Matplotlib,用於表格數據或者數據幀的的Pandas 。你可以在 這裡看到Pyodide為瀏覽器構建的完整的持續更新的包列表。

其中一些包是很容易直接被引入Pyodide的。通常來說,任何用純Python寫而沒有使用編譯語言擴展的部分都是很容易。而稍微有點兒困難的類型是像Matplotlib這樣的庫,其需要特定的代碼來在HTML畫布中展示繪圖。屬於極其困難的一端是Scipy庫,它一直是一個相當大的挑戰。

Roman Yurchak致力於使Scipy中大量的古老的Fortran被編譯到WebAssembly。Kirill Smelkov改善了emscripten以使得共享對象能夠被其它共享的對象使用,將Scipy控制到一個可管理的大小。(這些外部貢獻者的工作由 Nexedi提供支持)。如果你在將一個包轉為Pyodide的時候很費勁兒,可以在GitHub上聯繫我們: 很有可能我們已經之前就遇到過你的問題。

由於我們不能提前預測用戶最終需要用哪些庫,它們被按需單獨地下載到瀏覽器中。例如,當你導入NumPy時:

在瀏覽器中用Python做數據科學:Pyodide

Pyodide抓取NumPy庫(以及其所有的依賴)並將它們載入到瀏覽器。再一次,這些文件只需要被下載一次,然後就被存儲在瀏覽器緩存中。

將新的包添加到Pyodide目前是一個半手動的過程,涉及到添加文件到Pyodide構建中。我們長期以來更希望採用分發的方式來實現該過程,為了任何人都可以在不用遍歷單個項目的情況下能貢獻包到生態中。最棒的例子就是conda-forge。能將它們的工具擴展來支持WebAssembly作為一個平台將會非常棒,而不是花費大力氣重新打造一個。

此外,Pyodide將會很快支持 直接從PyPI(Python的主要社區包倉庫)中載入包,如果該包是純Python編寫而且以wheel格式發布。

除了Python以外

Pyodide相對早的成功已經啟發了其它語言社區的開發人員,包括Julia,R,OCaml,Lua,來使這些語言的運行時能夠在瀏覽器中很好地工作,並與如Iodide樣的web優先的工具相集成。我們已經定義了一系列的層級來鼓勵實現者創造與JavaScript運行時更緊密的集成。

  • 層級 1:僅包含字元串輸出,為了能夠對於基本操縱台REPL(read-eval-print-loop)有用
  • 層級 2:轉換基本數據類型(數值,字元串,數組和對象)到或從JavaScript
  • 層級 3:在客戶端語言與JavaScript之間共享類實例(有方法的對象)。這允許訪問Web API
  • 層級 4:在客戶端語言和JavaScript之間共享數據科學相關類型(n維數組和數據幀)

我們肯定想要鼓勵這個勇敢的新世界,並對能夠有可能讓更多的語言之間相互操作而感到興奮。讓我們知道你在做哪方面的努力!

結語

如果你還沒有在實踐中嘗試Pyodide,快去試一下吧(50MB下載)

能夠看見所有那些在Pyodide發布後段時間內就使用其創建的很酷的東西是非常讓人欣慰的。然而,仍然還有很多工作需要完成來將這個概念驗證性的實驗轉換為一個用於日常數據科學工作的專業性的工具。如果你感興趣於幫助我們構建未來,趕快來gitter, github 和 郵件列表找到我們吧。

英文原文:https://hacks.mozilla.org/2019/04/pyodide-bringing-the-scientific-python-stack-to-the-browser/

譯者:青蒿素

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 Python部落 的精彩文章:

互聯網巨頭們在測試介面中泄露的商業機密
在Python中使用PDF:閱讀和拆分

TAG:Python部落 |