跳轉中,訪問新網域網站 https://samiliu.xyz

logo头像
Snippet 博客主题

Python 實現桑基圖(Sankey)

透過上次 RFM 用戶分析模型將訪客分群,用 Python繪出桑基圖(Sankey)將用戶不同時間點分群的變化,用來觀察整體訪客經營是否符合預期中的變化。

桑基圖 (Sankey Diagram)


桑基圖是一種流程圖,其資訊包含著流量的大小、方向及變化狀況,套用在不同階段的客戶分群上,可以簡單看出訪客的變化狀況。

客戶分群意義


接續 RFM 用戶分析模型的分組,避免維度太多只使用 Frequency 與 Monetary 分成 4 組。

Frequency 頻率 Monetary 金額 客戶分群
Best Customers
Value Customers
Loyal Customers
Cheap Customers

實作


分群步驟參照前一篇 RFM 用戶分析模型

資料準備

Sankey 圖基本上由 source, target, value 組成,其資訊包含著流動方向與流量值。下圖為這次準備的數據集,其包含客戶每半年的分群與數量。

繪圖

函數

def gen_sankey(df, cat_cols=[], value_cols='', title='Sankey Diagram'):
    """ 生成 Sankey fig 物件

    :param df: 資料 DataFrame
    :param cat_cols: 流程中的級別欄位名稱
    :param value_cols: 每個流程的數量欄位名稱
    :param title: Sankey 標題名稱
    :return: dict() sankey data
    """
    # 漸層色
    colorPalette = ['#3f51b1', '#5a55ae', '#7b5fac', '#8f6aae', '#a86aa4', '#cc6b8e', '#f18271', '#f3a469', '#f7c978']
    labelList = []
    colorNumList = []
    for catCol in cat_cols:
        labelListTemp = list(df[catCol].unique())
        colorNumList.append(len(labelListTemp))
        labelList.extend(labelListTemp)

    # labelList 去除重複
    labelList = list(dict.fromkeys(labelList))

    # 根據層級定義顏色
    colorList = []
    for idx, colorNum in enumerate(colorNumList):
        colorList = colorList + [colorPalette[idx]]*colorNum

    # DataFrame 轉換成 source -> target
    sourceTargetDf = pd.DataFrame(columns=['source', 'target', 'count'])
    for i in range(len(cat_cols)-1):
        tempDf = df[[cat_cols[i], cat_cols[i+1], value_cols]]
        tempDf.columns = ['source', 'target', 'count']
        sourceTargetDf = pd.concat([sourceTargetDf, tempDf])
        sourceTargetDf = sourceTargetDf.groupby(['source', 'target']).agg({'count': 'sum'}).reset_index()

    # 對 source-target 編譯成 index
    sourceTargetDf['sourceID'] = sourceTargetDf['source'].apply(lambda x: labelList.index(x))
    sourceTargetDf['targetID'] = sourceTargetDf['target'].apply(lambda x: labelList.index(x))

    # 建立 Sankey 圖
    data = dict(
        type='sankey',
        node = dict(
          pad = 15,
          thickness = 20,
          line = dict(
            color = "black",
            width = 0.5
          ),
          label = labelList,
          color = colorList
        ),
        link = dict(
          source = sourceTargetDf['sourceID'],
          target = sourceTargetDf['targetID'],
          value = sourceTargetDf['count']
        )
      )

    layout =  dict(
        title = title,
        font = dict(
          size = 10
        )
    )

    fig = dict(data=[data], layout=layout)
    return fig

函式使用

import plotly
import plotly.graph_objects as go

fig = genSankey(rfm_variety, cat_cols=[
    'level_201701', 'level_201706', 'level_201801', 
    'level_201806', 'level_201901', 'level_201906'
    ], value_cols='count', title='訪客生命週期可視化')

# plotly.offline.plot(fig, validate=False) ## 生成 html
go.Figure(fig) ## 繪圖