當我做 hackathon 時我在做什麼 (2)

2021-02-21 程序人生

書接上文:當我做 hackathon 時我在做什麼(1)。

前文中提到,我做的第二個項目是個可視化的項目,名字叫 deneb。deneb 是天鵝座的一等星,也是夏季大三角和北十字兩個星群的端點之一。deneb 是對 vega-lite 的封裝,受 同樣封裝了 vega-ltie,深得我喜愛的 Python 的庫 altair 的啟發。嗯,deneb - vega - altair,聰明的你一定想到了我為什麼起這樣一個名字:

為什麼是 vega-lite?

在數據可視化這塊,我自己走了不少彎路。我最早的啟蒙工具是 matplotlib [1],它很容易上手,照著例子很快就能做出還算不錯的圖表。後來我發現了基於 matplotlib 的 seaborn [2],提供了對統計相關的圖表一個高階的抽象,很多在 matplotlib 下很多行代碼才能表達出來的圖表,seaborn 一兩行就搞定,非常給力。之後,因為希望做出來的圖表可以有更多的交互,我又轉向了 plotly [3]。plotly 使用起來更加簡單,但其背後的思路和 matplotlib 一脈相承:你需要定義 fig,描述你需要繪製哪種類型的圖表,x 軸,y 軸數據等信息。plotly 之所以能夠交互,是因為其背後是一套 javascript 庫,最終渲染出來的是一段 html 代碼。如果你需要能夠對可視化的圖表做簡單的動畫,plotly 也能勝任。

我一度以為 plotly 是我的真命天子,直到有一天我敲開了 altair [4] 這個潘多拉魔盒。

altair 讓我了解到其背後的 vega-lite [5],以及 vega-lite 背後的那本被稱作 GG(The Grammar of Graphics)的曠世奇書。這本書的作者是 Leland Wilkinson,是數據可視化領域的大牛,他的著作影響了一代人。如果你對 GG 感興趣,可以 youtube 裡搜索 Leland 的大名,看看他對自己思想的解讀。

為啥我說 GG 是曠世奇書呢?因為僅僅看了一些介紹,以及書中思想的一些片段,我就受益匪淺,感覺對數據可視化的認知提升了一個級別。比如 GG 裡提到,「餅圖是極坐標下的柱狀圖」。你品,你仔細品。

我們平時做可視化,首先接觸的是各種圖表的分類,但 Leland 認為:

Taxonomies of charts are harmful, just like goto in programming languages.

他覺得我們在做數據分析的時候,更多是一種探索,而分類是反探索的,因為當你用某種類型的圖表來表達數據的時候,你已經對如何分析數據有了先入為主的看法。

那麼什麼是圖表呢?Leland 認為:函數(Graph)在有限的的作用域下(Frame)通過美感(Aesthetic)表達出來,就是圖表(Graphic)。

具體如何表達呢?通過組合坐標系,方面,統計方式,形狀,標度,美感,再加上數據本身,共同作用出一個合適的圖表:

這種方式打破了傳統圖表的分類法,更貼近如何去探索數據本身。

我很喜歡這裡的 Aesthetics。圖表是數據的視覺編碼,好的視覺編碼一定是要具備美感。美感可以通過大小,顏色等方面表達出來,其中最重要的表達手段,或者說視覺通道就是顏色。顏色可以描述變量的模式/規律,可以做類別標註,也可以起高亮和強調的作用。

GG 這本書除了把這些概念介紹地很透徹,還對圖形的表達做了完整的形式化表述,也正因為如此,很多工具直接在 GG 的基礎上進行開發,比如 R 裡的 ggplot。vega 受 GG 和 ggplot2 的啟發誕生,隨後更加精簡,更受大家歡迎的 vega-lite 又在 vega 的基礎上產生。受 vega-lite 的影響,altair 開始崛起,而我受 altair 的影響,萌發了在 Elixir 下復刻 altair 的想法。

好了,關於 GG 的故事就先講這麼多,等我通讀完這本大部頭後,有空可以單開一文講講我對可視化的認知。

如何在 Elixir 上「復刻」一個 Altair

在做這次 hackathon 之前,我已經有了還算豐富的 altair 的使用經驗,但我並未太多研究 vega-lite 本身。所以在做 deneb 的過程,其實就是我自己學習 vega-lite,然後把 vega-lite 的代碼用 Elixir 封裝起來的一個過程。vega-lite 主要有這樣幾種對象:

下面是一個最簡單的 vega-lite 的代碼,完全由 JSON 表述:

{  "$schema": "https://vega.github.io/schema/vega-lite/v4.json",  "description": "A simple bar chart with embedded data.",  "data": {    "values": [      {"a": "A", "b": 28}, {"a": "B", "b": 55}, {"a": "C", "b": 43},      {"a": "D", "b": 91}, {"a": "E", "b": 81}, {"a": "F", "b": 53},      {"a": "G", "b": 19}, {"a": "H", "b": 87}, {"a": "I", "b": 52}    ]  },  "mark": "bar",  "encoding": {    "x": {"field": "a", "type": "nominal", "axis": {"labelAngle": 0}},    "y": {"field": "b", "type": "quantitative"}  }}

所以,對於 deneb 來說,就是提供優雅的接口把 Elixir struct 翻譯成 vega-lite 裡的 JSON object。為了達到這個目標,我們需要提供對 vega-lite 語法在 Elixir 上的封裝。我認為封裝有幾層:

傳遞給 deneb 要繪製的數據,和繪製這個數據所用的 vega-lite 表達,deneb 將其組合成一個可以展示的 JSON 數據。

傳遞給 deneb 要繪製的數據,和繪製這個數據所用的 elixir structs,deneb 將其組合併翻譯成一個可以展示的 JSON 數據。

在 2 的基礎上進一步封裝,讓每個域都有其 Elixir 語法。

在 3 的基礎上提供數據校驗和足夠清晰的出錯信息。

在 altair 接口中,已經完全沒有 vega-lite 的表達式了,取而代之是對應的 Python 表達式,如果用戶撰寫的代碼有誤,Altair 能夠清晰地展示錯誤,幫你定位問題。所以altair 實現到了第四級。然而 altair 付出的代價是四萬七千行 Python 代碼。就算我腦子裡有個 Python-to-Elixir 的代碼轉換器可以逐行翻譯,讓我抄四萬多行代碼一天也抄不完。

所以,我打算一步步來。先實現第一層,讓 deneb 用最小的代價跑起來。比如上面的那段代碼,對應的 Elixir 代碼如下:

%{  mark: "bar",  encoding: {    x: %{field: "a", type: "nominal", axis: %{labelAngle: 0}},    y: %{field: "b", type: "quantitative"}  }}|> Chart.new()|> Deneb.to_json(data)

有了這個基礎,我再一步步把幾個主要對象映射到 Elixir,最終形成這樣的代碼:

:bar|> Mark.new()|> Chart.new(Encoding.new(%{    x: %{field: "a", type: "nominal", axis: %{labelAngle: 0}},    y: %{field: "b", type: "quantitative"}}))|> Deneb.to_json(data)

是不是感覺兩個變化並不大?但這些對象內部有一些校驗,保證輸入的正確性。

我雖然很喜歡使用 altair,但學會了 altair 並不能保證我同時會寫 vega-lite 語法,因為 altair 自己已經成為一個厚重的 DSL,完全包裹住了 vega-lite。這其實對學習 vega-lite 不夠友好。

所以,我認為 deneb 實現到第 2 層至第 3 層的封裝和抽象就足夠了。一來是留給我的時間不多了,二來我覺得過於厚重的封裝不是那麼有必要,vega-lite 自己的語法表現力足夠且並不複雜。三來對於使用者而言,了解 vega-lite 的語法對他們非常有必要。因為最終 altair / deneb 這樣的工具是趕不上 vega-lite 的發展的,總會有滯後(比如現在 altair 還不支持 vega-lite 4.9 的新功能),所以用戶在極端情況下還是需要掌握 vega-lite。

有了基礎的 deneb 的實現,接下來就是如何把生成的 vega-lite JSON 展示成圖表。我需要定義一個 Viewer,用於將 JSON 數據放入一段 javascript 中,然後加載到 html 頁面中。我參考了 altair_viewer,實現得不費吹灰之力。至此,用戶想生成一個複雜的圖形,比如證券分析裡經常使用的蠟燭圖,可以用幾行代碼輕鬆表述:

難道就這麼簡單?

當然,事情絕對不會那麼簡單,brick wall 總是會不期而至的。

第五次撞牆:IElixir 和 jupyter notebook

完成 ex_polars 就像打完我自己的淮海戰役一樣,做 deneb 的過程是摧枯拉朽,幾乎不費太大的力氣。一切開發妥當後,我在 Jupyter notebook 上運行我心心念念的第一個最簡單的柱狀圖,結果,jupyter notebook 沒有任何輸出。我查看 chrome 的 console error,沒有任何報錯,這下麻煩了,如果在這裡卡住,那真的就是功虧一簣啦。畢竟,一個無法支持 notebook 的可視化庫,還好意思說自己為 data science 所生?

Jupyter Notebook 本不支持 Elixir,但它充分考慮了語言級別的擴展性,提供了一個 ZeroMQ 接口和 kernel 交互消息,因此,其它語言可以實現對應的 ZMQ 接口,和 Jupyter 通信。下圖展示了 IPython Kernel 如何跟 Jupyter 通訊的(這圖的審美,唉,要不是沒時間自己畫,我真不好意思放上來):

好在 Elixir 生態圈裡有個 IElixir,仿照 IPython,做了對 Jupyter 的支持。我在實現 ExPolars 時,使用的就是 IElixir + Jupyter Notebook 來展示功能。

然而,IElixir 實現了基本的消息通訊,但有些細節似乎沒有測試過。比如對 html 片段的支持。這也是為什麼我在做 ExPolars 時, 在 Jupyter notebook 裡,一切操作都正常,因為那些輸出都是簡單的 text;而當我想輸出 deneb 生成的包含 vega-lite spec 的 html 片段時,IElixir 就無法正常工作了。

既然我定位到問題可能出在 html 上,那麼,問題的解決並不麻煩。我只需在合適的地方加入列印,看 IElixir 的輸出,一步步縮小問題的範圍即可。最後,我成功解決了問題,並給 IElixir 的作者提交了一個 PR(還有什麼比一個對已有開源項目的 PR 更能彰顯 OSS-a-thon 的意義的?):

享受勝利的喜悅

當第一張圖表輸出到 Jupyter notebook 的輸出框裡時,我激動地跳了起來。一旁搭樂高的小貝茫然地看著我,不知所措中就被我掄起來往空中拋了三次。然後我又趴在地上示意她騎大馬,待她坐定繞著三樓的空地蜿蜿蜒蜒走了一圈才心滿意足。

隨後的幾個小時,就是查漏補缺,即興發揮的時刻。我為 ExPolars 提供了 plot_single,plot_repeat 和 plot_by_type 幾個快速生成圖表的功能,對標 pandas 的 df.plot 功能。比如,一行代碼實現下面的可視化:

以及,一行代碼實現上文中的 candlestick:

注意看這幅圖,它是兩個 chart 組合而成的,還使用了 selection 來提供交互。用戶在選擇小圖的時候,大圖會隨之而動。

嗯。開森。

參考資料

我的 hackathon 項目:

tyrchen/ex_polars

tyrchen/deneb

感興趣的同學可以關注。本文中提到的其它項目:

[1] matplotlib: matplotlib.org

[2] seaborn: seaborn.pydata.org

[3] plotly: plotly.com

[4] altair: altair-viz.github.io

[5] vega-lite: vega.github.io/vega-lite

賢者時刻

四天的 hackathon 結束後,我無比滿意四天前的我的選擇。因為這個選擇,讓我一次又一次遇見新鮮。董卿說世間一切,都是遇見,就像冷遇見暖,就有了雨,春遇到冬,有了歲月;天遇見地,有了永恆;人遇見了人,有了生命。

獻上一曲小寶最近彈的 Arabesque:

相關焦點

  • 李奧納多·達·文西讓你想到了什麼?丨 SegmentFault Hackathon 文藝復興深圳站獲獎作品簡介
    和平年代,看似平靜,但是遮掩不了內心的波瀾的想法,為大家帶來了作品——蒙娜麗莎的喜怒哀樂:當你參與到聊天中,作品就可以檢測你的面部,為你戴上蒙娜麗莎的面具,但是沒有人可以將心藏起,一旦你的情緒波動較大時,系統就會自動向對方發送一條消息,告訴他/她你的心情。
  • 我是誰?我能做什麼?我該做什麼?猶太人是這樣回答的
    大家好,我是小評歷史。我是誰?我能做什麼?我應該做什麼?這三個問題是德國哲學家康德提出來的,每個人都給出了不同的答案,那麼,猶太人是否也對此做出了解答呢?哪怕在歐洲的猶太人中也有一部分人認為,之所以會發現那麼多的悲劇,是因為猶太人拒絕融入當地環境,如果有必要,應該放棄猶太教,放棄猶太文化,放棄所有跟猶太歷史有關的東西,讓自己徹底變成當地人,這樣就不會再有歧視,不會再有仇恨,到了那時候,猶太人在被問及「我是誰」時,可以平靜地回答「我是X國人」,然而直到20世紀六、七十年代,美國猶太人都不敢在找工作時,直接表明自己是猶太人
  • 讓我做你的式神是什麼意思?讓我做你的式神是什麼電影
    讓我做你的式神是什麼意思?讓我做你的式神是什麼電影時間:2020-12-28 23:38   來源:今日頭條   責任編輯:毛青青 川北在線核心提示:原標題:讓我做你的式神是什麼意思?讓我做你的式神是什麼電影 讓我做你的式神是什麼意思?
  • 秦牛正威:如果我做什麼都會被攻擊,那也意味著我什麼都可以做
    但可能這樣的表演看下來確實會覺得我態度有問題,因為大家或許認為我準備了很久,不應該是這個樣子。」 既然對舞臺不擅長,不「感冒」,甚至需要臨時抱佛腳。難道只是因朋友攛掇如此簡單的理由便願意「拋頭露面」?這顯然不是能被很好理解的因果關係。「也是因為我不太想給自己設限。對現在的我來說,並沒有很具象的,一定要做、不做什麼方向。不管哪方面,只有多去嘗試,才會知道自己更適合什麼。
  • 當我做蛋糕的時候,我在聽什麼
    「當我在做蛋糕的時候,我在聽什麼」的啟發來自於村上春樹《當我談跑步時,我談些什麼》。
  • 我在美國做Product Designer,到底做什麼?
    Product Designer(產品設計師)又在做著怎樣的工作?今天,本文作者為我們分享了他作為PD的日常工作內容,幫助我們了解美國的PD。Hello大家好,我是Teresa,這是我第一篇文章。最近和國內的朋友們交流的時候,他們總是很好奇我作為PD到底做什麼?今天就用這篇文章回答一下這個問題。
  • 我有2萬塊,想做個小本生意,做什麼好呢?
    我現在有幾萬元錢想去做個小本生意,可又不知道做什麼才好,做生意沒有百分之百的賺錢,都是要靠自己去經營的,同樣的生意,有的人賺得滿盆金箔,有的人虧得血本無歸,都是經營方式不一樣導致的。首先我可以明確告訴大家,在農村做小酒坊是賺錢的,因為農村人在購買白酒的時候,多是幾斤幾斤購買的,第一價格實惠,第二,純糧放心看的到喝得到。假如每天就按照50斤銷量來算吧,那麼一天下來就是1000塊錢的營業額了,600元利潤,一個月18000元。收入還是十分可觀的。
  • 做時博的這些年 什麼是我可以分享給你的?
    最近,白哥在「VOGUE FACE訓練營」和很多想要做時博、以及想要從事時尚工作的孩子們來了一次深度對話。
  • 我有什麼 我是怎樣的人 我能做什麼
    公式講座開始先做遊戲。孫同學填寫的「3I」圖表。周耀飛以做遊戲開場,讓大家跟著視頻《花生寶寶拍拍拍》一起拍手。孩子們馬上嗨了起來,整個狀態也隨之放鬆。隨後,周耀飛以一段小視頻《生命是怎樣誕生的》展示了一隻小雞的孵化過程,「21天,一個胚胎發育成了小雞,從雞蛋裡破殼而出。那你們出生前,在媽媽的肚子裡要待多久呢?」
  • 你想我做你的什麼?請做我的奴隸吧!
    「你還喜歡現在的我嗎?
  • 明明沒有人在等我,我頻頻回頭做什麼
    明明沒有人在等我,我頻頻回頭做什麼。1:人的命,天註定,富貴由天不由你,生死由命不由人,人活一世,草木一秋,半點不由人。2:也許你明白,放棄是最好的選擇,選擇放棄才能真正讓自己從痛苦裡,解脫出來,只是愛的心不聽控制,放不下在每個夜裡默默想起,然後輾轉反則,思念到天明。3:孤獨是一個人吃飯,就算桌上有很多美味,也要自己去品嘗它的酸甜苦辣,不管它好吃不好吃,都是一個人也很快的吃完。
  • 「許仙」葉童上位史:導演叫我做什麼就做什麼
    關注我,帶你看娛圈秘聞,吃最熟的瓜!「導演讓我做什麼就做什麼。」她也是幸運的,拍攝《烈火青春》時搭檔張國榮,雖然有不少的大尺度戲份,但在張國榮的照料下也是完成的非常順利。在她的演藝道路上,拍攝了眾多的作品,也貫徹著她那句「導演讓我做什麼我就做什麼」。
  • 朱珠 :我喜歡做任何事情時的自己
    朱珠 :(笑)我喜歡唱歌時的自己,喜歡健身時的自己,喜歡主持時的自己,喜歡做畫廊做藝術品的自己。總之我喜歡做任何事情時的自己。《雲端》: 對於多次入選「全球最美面孔」你自己怎麼看?是否會擔心人們只關注你的外表?
  • 最近做了幾個幣圈視頻,我做了什麼準備?
    在 2018 年 EOS 主網啟動前,那段時間我就有做視頻的想法,所以零零碎碎做了一些小視頻,買了一個不錯的麥克(已經在柜子裡吃灰),後續就沒有繼續花時間弄了。 直到 2019 年年底,在我日更了快 400 天的時候,我覺得該做點視頻玩一玩了,畢竟做視頻比寫文章好玩多了。
  • 女星葉童上位史:導演叫我做什麼就做什麼
    她到底靠的是什麼,她又是如何一步步上位的呢?就讓我們一起來一探究竟吧!多年後葉童談起自己剛出道時談裸戲的心得和原因,她脫口而出:「導演叫我做什麼就做什麼。」1983年葉童出演電影《表情七日情》,她再次貫徹了「導演叫我做什麼就做什麼」的態度,與鍾鎮濤上演極其香豔的激情戲碼,哪怕露點也無半句怨言。
  • 同事給我介紹的妹子,吃飯時聊的挺開心的,接下來該做什麼呢?
    2、大哥好膽量,不怕被這魚給吃了嗎3、高手是怎麼煉成的4、這麼賢惠的媳婦你也忍心惡搞?5、這做的什麼肉,比看那些吃播都要爽6、兔子這表情吃得很開心啊7、據說婆婆給女婿買了一輛車,但是開走真的不容易。8、小孩:我輸了,輸得徹徹底底,我姑姑嫁人了9、這樣的老婆你敢娶嗎?
  • 我長大後要做什麼呢?
    本文轉載自【微信公眾號:吳春隨筆,ID:gh_5d115dd7553f】經微信公眾號授權轉載,如需轉載與原文作者聯繫睡前,兒子忽然煞有介事地問我:爸爸,你說我長大後要做什麼呢?我一愣,隨即問他:那你想做什麼呢?兒子認真地想了想說:我想做警察、醫生和消防員。如果只能從這三個裡選一個,你最喜歡做什麼呢?我笑著問。這三個我都要做。我可以的,爸爸!都這麼信心滿滿了,我還能說什麼。
  • 我為七哥做過什麼?
    我為七哥做過什麼?當我自己寫出這個問題的時候,我也懵了一下。一直以來,不管是在生活還是感情上,都是七哥在照顧我,寵著我,即便是在文章中,表述的也是七哥對我的濃濃的愛,那作為七哥的愛人,我為他做過什麼呢?今天,就來簡單說說我為七哥做過的那些小事。時間對於我而言,我為七哥做的最多的應該就是時間,我願意花自己的時間去陪伴他。
  • 香港阿婆一人拆路障舌戰暴徒:我做不了什麼,但我要做!
    最近香港有一個阿婆火了,她在灣仔清理暴徒設下的路障時大聲斥責暴徒的行為,而且每一句到懟到了點子上!阿婆先是上來大聲質問設置路障的暴徒,為什麼這麼多人不做一點好事,專做阻礙別人通行的事。但是暴徒不答,並用鐳射筆照射阿婆的眼睛。
  • 知道嗎 | 「作為貓,我在做什麼,我自己心裡有數」
    野外生活中簡單幸福到,除了日常吃點什麼,要等球球回家,和跟路遇的動物們交朋友、以及怎麼把這些有趣的事表達和分享出來之外,就沒什麼大事了。】1 跟石榴談心**有一次小貓石榴在路上閒逛時,有幾個路人經過:是一個年輕的媽媽抱著一個嬰兒,領著一個七八歲的小男孩。石榴就跑上前試圖親近他們,還在地上打滾給他們看。