天天看點

python中unique_财險資料互動式可視化——運用Python的Bokeh包

python中unique_财險資料互動式可視化——運用Python的Bokeh包
本文由珂珂撰寫。

導引

繼 Alonso 上篇

Li2O:用Python分析财險資料——菜鳥向​zhuanlan.zhihu.com

python中unique_财險資料互動式可視化——運用Python的Bokeh包

,我對同樣的資料用 Bokeh server 進行了可視化。

Bokeh簡單介紹:Bokeh 是 Python 的一個制作互動式可視化工具的包,R 中也有相應的包叫做 shiny (https://shiny.rstudio.com/)。Boken 目前對中文的支援不太友好,但本文我們将用 JS 将網頁語言改變為中文。Boken 有兩種用法:

  • 第一種是不利用 Bokeh server,這種情況下能做出好看的互動圖,實作拖曳,放大,滑鼠懸浮标簽等功能。最後能夠生成靜态的HTML檔案。
  • 第二種是利用 Bokeh server,做一個 Web application。這種情況下能實作資料篩選調用等更多功能。一般使用 Flask + Bokeh,把 Bokeh 放置于 Flask application 裡面。我們的這個例子中沒有使用 FLask,而用了一個預設的 HTML 模闆,叫做 Jinja,很多可以修改的功能被限制了。

本文介紹的是第二種,Bokeh server 的 Web application 應用示例,代碼基于 Bokeh Gallery 裡面的兩個 sample。一個是 movie,一個是 crossfilter,連結見文末的參考文獻。

先來示範一下效果:

  • 篩選資料功能:
python中unique_财險資料互動式可視化——運用Python的Bokeh包
  • 拖曳,選擇,資料标簽功能:
python中unique_财險資料互動式可視化——運用Python的Bokeh包
  • 通過拖曳點的方式修改資料的功能:
python中unique_财險資料互動式可視化——運用Python的Bokeh包

該互動式圖表目前 host 于http://49.234.103.189:5006/test 這個網頁中。

步驟

安裝 Bokeh

pure python 使用者打開指令行:

pip install bokeh
           

conda 使用者:

conda install bokeh
           

檔案樹

我們需要的檔案樹大概是這樣的結構:

python中unique_财險資料互動式可視化——運用Python的Bokeh包

app 檔案夾下有三個檔案:一個是 main.py,是我們的 python 主檔案;另一個是 templates 檔案夾,裡面放 index.html,是我們對于基本 html 架構的補充;還有一個是 lidata.csv,是我們的資料源檔案。

分析資料

我們要根據公司,險種,險别來進行資料篩選,是以,我們首先要得到這幾列有哪些情況。

# lidata就是Alonso的資料集
df_all = pd.read_csv(r'./app/data/lidata.CSV', header = 0)
# 計算ULR
df_all['ULR'] = df_all['UL'] / df_all['EP']
# 加入all是為了能夠選擇所有情況
unique_company = ["All"] + df_all['公司'].unique().tolist()
unique_business = ["All"] + df_all['險種'].unique().tolist() 
unique_product = ["All"] + df_all['險别'].unique().tolist() 
           

需要對不同險别展示不同顔色,代碼如下

color = pl.mpl['Plasma'][len(unique_product)]
#這裡Plasma是一個Bokeh自帶的調色盤,幫助我們找到好看的配色
df_all["color"] = [color[unique_product.index(pro)] for pro in df_all["險别"].values]
           

我們還要篩選展示的事故年,是以,我們需要讀取最小的事故年和最大的事故年。

year_start = df_all['事故年'].min()
year_end =  df_all['事故年'].max()
           

最後一個要準備的是要展示的資料y列是什麼。這裡需要做一個字典用來對應選項和資料列名的關系。

axis_map = {
    "ULR": "ULR",
    "ULAE": "EP",
    "DAC":'DAC'
}
           

接下來就是作圖啦。圖分為左右兩邊。左邊的部分叫做 control,右邊的部分叫做 plot。

制作control

# year_range: 展示的事故年範圍
year_range = RangeSlider(start=year_start, end=year_end, value=(year_start,year_end), step=1,
                       title="展示年")
Slider(title="開始展示年", start=year_start, end=year_end, value=year_start, step=1)
max_year = Slider(title="結束展示年", start=year_start, end=year_end, value=year_end, step=1)
# 選擇的公司,險别,險種
company = Select(title="公司選擇", value="All",
               options=unique_company)
business = Select(title="險别選擇", value="All",
               options=unique_business)
product = Select(title="險種選擇", value="All",
               options=unique_product)
y_axis = Select(title="展示值", options=sorted(axis_map.keys()), value="ULR")

controls = [company, business, product, year_range,  y_axis]
           

制作plot

# Tooltips用來制作滑鼠懸浮于資料時的資料标簽
TOOLTIPS=[
    ("公司為", "@com"),
    ("年:", "@year"),
    ("險别為", "@business"),
    ("險種為", "@pro")
]
# TOOLS規定了哪些工具要顯示出來,比如拖曳等
TOOLS="pan,wheel_zoom,box_select,lasso_select,reset"
p = figure(tools=TOOLS,plot_height=100, plot_width=200, title="", toolbar_location="above", tooltips=TOOLTIPS, sizing_mode="scale_both")
r = p.circle(x="x",y="y" ,source=source, size=10, color = 'color', alpha=0.6, hover_color='white', hover_alpha=0.5)
# PointDrawTool這個工具需要單獨放入其中
draw_tool = PointDrawTool(renderers=[r], empty_value='black')
p.add_tools(draw_tool)
p.toolbar.active_tap = draw_tool

           

更新資料

def select_products():
    # strip可以去除資料前面或者後面的空格
    company_val = company.value.strip()
    business_val = business.value.strip()
    product_val = product.value.strip()
    # 選擇事故年
    selected = df_all[
        (df_all.事故年 >= year_range.value[0]) &
        (df_all.事故年 <= year_range.value[1]) 
    ]
    # 選擇公司,險種,險别等
    if (company_val != "All"):
        selected = selected[selected.公司.str.contains(company_val)==True]
    if (business_val != "All"):
        selected = selected[selected.險種.str.contains(business_val)==True]
    if (product_val != "All"):
        selected = selected[selected.險别.str.contains(product_val)==True]
    return selected

# 這個函數用來更新資料源
def update():
    df = select_products()
    x_name = "事故年"
    y_name = axis_map[y_axis.value]
    p.title.text = "%d points selected" % len(df)
    source.data = dict(
        x=df[x_name],
        y=df[y_name],
        com=df["公司"].values,
        year=df["事故年"].values,
        business=df["險别"].values,
        pro=df["險種"].values,
        color = df["color"]
    )
# control中的每一個元素改變後,都需要運作update()
for control in controls:
    control.on_change('value', lambda attr, old, new: update())
           

生成圖

# input就是圖左邊的control
inputs = column(*controls, width=320, height=1000)
inputs.sizing_mode = "fixed"

l = layout([
    [inputs, p],
], sizing_mode="scale_both")

update()
curdoc().add_root(l, p)

           

templates檔案夾:利用Bokeh自帶Jinja模闆對網頁更改基本樣式

這個時候就要用到templates檔案夾啦!它裡面的index.html是對于jinja模闆的補充。

Jinja模闆如下,這個我們沒有辦法改,想要改的話隻能用JS在後面改。

<!DOCTYPE html>
<html >
{% block head %}
<head>
    {% block inner_head %}
    <meta charset="utf-8">
    <title>{% block title %}{{ title | e if title else "Bokeh Plot" }}{% endblock %}</title>
    {% block preamble %}{% endblock %}
    {% block resources %}
        {% block css_resources %}
        {{ bokeh_css | indent(8) if bokeh_css }}
        {% endblock %}
        {% block js_resources %}
        {{ bokeh_js | indent(8) if bokeh_js }}
        {% endblock %}
    {% endblock %}
    {% block postamble %}{% endblock %}
    {% endblock %}
</head>
{% endblock %}
{% block body %}
<body>
    {% block inner_body %}
    {% block contents %}
        {% for doc in docs %}
        {{ embed(doc) if doc.elementid }}
        {% for root in doc.roots %}
            {{ embed(root) | indent(10) }}
        {% endfor %}
        {% endfor %}
    {% endblock %}
    {{ plot_script | indent(8) }}
    {% endblock %}
</body>
{% endblock %}
</html>
           

index.html 的基本格式如下:

{% extends base %}

<!-- goes in head -->
{% block preamble %}
<link href="app/static/css/custom.min.css" target="_blank" rel="external nofollow"  rel="stylesheet">
{% endblock %}

<!-- goes in body -->
{% block contents %}
<div> {{ embed(roots.scatter) }} </div>
<div> {{ embed(roots.line) }} </div>
{% endblock %}
           

我在标準模闆中加了一些代碼,來保證 html 的語言選項是 zh,也就是中文,加以對CSS檔案的修改,就大功告成啦!

window.onload = function() {
  document.querySelector("html").lang = "zh";
};
           

運作

在指令行中先 cd 到 app 所在檔案夾,并輸入:

bokeh serve app
           

或者

bokeh serve --show app
           

或在 Debug 模式運作

bokeh serve --log-level=debug app
           

當 python 由于版本不同可能有沖突時,可以使用:

python3 -m bokeh serve app
           

完整代碼可以點選

閱讀原文

在珂珂的github下載下傳: https://github.com/Mengkee/bokeh_example

參考文獻

Bokeh Gallery Bokeh Sample: movies Bokeh Sample: crossfilter

http://weixin.qq.com/r/CTtkfKvEi-b0re8f924b (二維碼自動識别)

  • 微信公衆号& B 站 & 知乎 :精算後花園
  • 電子郵箱:[email protected]
  • 精算部落格: actuarygarden.cn
  • 精算論壇: actuarygarden.com
  • QQ 群: 544736656
專輯|英國精算師考試導引篇 專輯|CM1:利息理論&壽險精算 專輯|北美精算師考試導引篇 專輯|寓教于樂 專輯|調侃精算 專輯|精算考研申研和競賽 專輯|資料科學