pandas增強版pyecharts

研究量化策略總少不了要可視化展示一些東西,對於固定模式,之前一直用matplotlib,一些臨時的圖表可能就用Excel了。但總是不太理想,要修改也挺麻煩,另外用matplotlib畫動圖以及一些互動式的圖不是太方便。無意中看到 @chenjiandongx 的pyecharts,隨即試用了一下,感覺很靈活,而且近期又加入了notebook中顯示的功能,更是方便了不少。

但目前pyecharts主要支持的數據結構是list,對於np.array, pd.Series, pd.DataFrame, 0.1.9中通過加入npcast和pdcast兩個方法進行轉換。這只是提供了一個轉換工具,個人更喜歡直接add(pd.Series, **kwargs),豈不更靈活,這對於經常用pandas處理時間序列數據的人來說,應該還是蠻實用的。其實pd.Series本也有plot方法直接調用,也挺方便,只是沒法像echarts那樣動態炫酷。當然pd.DataFrame也有plot方法,一次畫多條線,這個其實也很方便。

以上都是基於自己的需要而想到的,相信也會有其朋友可能需要,尤其是經常用pandas的朋友。有了這個想法,初略想了一些實現也不太複雜,就自己動手了。下面開始分析具體需求:

  1. 目前pyecharts支持的數據類型主要是list,如果不是可能會報異常。其實在Python中有各種container, iterable. 只要不是無限長的generator,都可以直接list化,自然想到把這些類型的支持也加進去;
  2. 對於二元數據構成的list,希望通過add(name, data, **kwargs)直接繪圖。pyecharts提供了方法cast,該方法主要實現:
    1. 將dict的keys和values分別轉為attr和data,也就是x數據和y數據;
    2. 將形如[{k1: v1}, {k2: v2}]的數據轉為兩個list;
    3. 將形如[(k1, v1), (k2, v2)]的由二元tuple構成的list轉為兩個list。
  3. 對於pd.Series,其name, index, values 三個屬性正好對應一般繪製所需要的參數,當然pyecharts也提供了pdcast方法來轉換,個人希望直接能支持,而且pdcast也並不識別name;
  4. 對於pd.DataFrame,其實每個column都對應一個pd.Series,很多時候每列直接都是可比的數據,需要繪製在同一張圖中,就像pd.DataFrame.plot那樣。pyecharts的pdcast會將pd.DataFram 轉為二維list,不識別相應的index和columns,可能對Radar, Parallel,這類圖有用,但個人以為可能更多人需要用該數據繪製多條線。對於更高維的數據,在pd中一般會用多維index或columns來表示,個性化太強,暫就不考慮了。

在不改變pyecharts的基礎上,實現上述4條功能,對於前3條,比較簡單,只需要在重載add方法即可實現。對於4,由於個人Python經驗不足,要在add中直接實現麻煩些,就添加了add_df方法,通過add_df(pd.DataFrame, **kwargs)調用。

之前學習fluentPython時,了解到Python類中方法的特點,這裡也通過直接註冊的方式動態實現重載及添加add_df方法,具體見__init__.py文件。這裡對pyecharts中所有首字母大寫的屬性模塊都重載add並添加add_df方法,由於沒有完全測試,可能部分類型不合適,可以有選擇的重載。

實現如下:

# __init__.pynimport pyecharts as penfrom .charts import add, add_dfnnnfor __name in dir(pe):n if __name[0].isupper():n locals()[__name] = type(__name, (getattr(pe, __name), ), {add: add, add_df: add_df})n

# charts.pynnfrom __future__ import division, print_functionnimport numpy as npnimport pandas as pdnnndef formatter(t):n if isinstance(t, pd.Timestamp):n return t.strftime(%Y-%m-%d)n try:n t = float(t)n except:n try:n t = str(t)n except:n raisen return tnnndef tolist(*args):n if len(args) == 1 and isinstance(args[0], pd.Series):n ss = args[0]n args = [ss.name, ss.index, ss.values]nn if len(args) == 2:n arg1 = args[1]n args = [args[0]]n try:n if isinstance(arg1, dict):n args.extend([list(arg1.keys()), list(arg1.values())])n else:n args.extend([[k[0] for k in arg1], [k[1] for k in arg1]])n except:n raisenn try:n name = str(args[0])n except UnicodeEncodeError:n name = args[0]n args_ = [name]n for arg in args[1:]:n if isinstance(arg, np.ndarray):n try:n arg = arg.astype(float).tolist()n except:n arg = arg.astype(str).tolist()n else:n arg = list(map(formatter, arg))n args_.append(arg)n return args_nnndef add(self, *args, **kwargs):n args = tolist(*args)n super(self.__class__, self).add(*args, **kwargs)n returnnnndef add_df(self, df, **kwargs):n for col in df.columns:n self.add(df[col], **kwargs)n

import numpy as npnimport pandas as pdnimport myecharts.myecharts as mennndef test_add(*args, **kwargs):n line = me.Line(Test Line add +kwargs.get(data_type, ))n line.add(*args)n line.show_config()n line.render(./test_line_add_{}.html.format(kwargs.get(data_type, )))nn bar = me.Bar(Test Bar add +kwargs.get(data_type, ))n bar.add(*args)n bar.show_config()n bar.render(./test_bar_add_{}.html.format(kwargs.get(data_type, )))nnndef test_add_df(df):n line = me.Line(Test Line add_df)n line.add_df(df)n line.show_config()n line.render(./test_line_add_df.html)nn bar = me.Bar(Test Bar add_df)n bar.add_df(df)n bar.show_config()n bar.render(./test_bar_add_df.html)nnndef main():n n = 100n ss = pd.Series(np.random.randn(n).cumsum(), index=pd.date_range(20150101, periods=n), name=SeriesData)n test_add(ss, data_type=series)nn dict_data = dict(ss)n test_add(dict_data, dict_data, data_type=dict)nn list_tuple = [(k, v) for k, v in dict_data.items()]n test_add(list_tuple, list_tuple, data_type=list_tuple)nn df = pd.DataFrame(np.random.randn(50, 5), index=pd.date_range(20150101, periods=50), columns=list(ABCDE))n test_add_df(df)nnnif __name__ == __main__:n try:n main()n except Exception as err:n print(repr(err))n

測試只測試了Line和Bar,其他大部分只要參數結構一致,應該問題不大,如有誰測試失敗,請告訴我一下^_^

詳見我的第一個github項目 myecharts

不是計算機專業,更不是專業程序員,以前沒有用過git,更沒有在github上創建項目,臨時看 @廖雪峰 的git教程弄上去的。參與重寫了pyecharts中npcast 和 pdcast 兩個方法,使得安裝pyecharts 不依賴numpy和pandas.


推薦閱讀:

Power BI儀錶板可視化部件使用參考
R語言可視化——地圖填充與散點圖圖層疊加
你不曾見過的酷炫地圖可視化作品(二)

TAG:Python | ECharts | 数据可视化 |