用戶路徑分析之利器“?;鶊D”
大家好,我是寶器!
本文約4千字,讀完需要20分鐘,后半部分有實(shí)操,可以拿出小本本練習(xí)。
引言
作為一名產(chǎn)品經(jīng)理,我們經(jīng)常會(huì)聽(tīng)到這樣的描述:
“用戶進(jìn)入xx頁(yè)面后,點(diǎn)擊這里,跳轉(zhuǎn)到xx頁(yè)面,然后再點(diǎn)擊xxx 跳轉(zhuǎn)到xx頁(yè)面?!?/span>
產(chǎn)品是這樣設(shè)計(jì)的,但是用戶是否真如你設(shè)計(jì)的那樣走?
未必。
那么用戶到底是怎么使用產(chǎn)品的,真實(shí)世界中的用戶的旅程是什么樣子,你需要一張?;鶊D。名字聽(tīng)起來(lái)有些陌生? 沒(méi)關(guān)系,本文就帶你走一遭,講講桑基圖的前世今生,桑基圖在互聯(lián)網(wǎng)產(chǎn)品分析領(lǐng)域的應(yīng)用, 以及如何用python將一個(gè)常見(jiàn)埋點(diǎn)數(shù)據(jù)CSV文件做出漂亮的?;鶊D。
?;鶊D的前世
提起桑基圖,要感謝以下這兩個(gè)人,可以說(shuō)是?;鶊D的爺爺和爸爸。左圖是:查爾斯.約瑟夫.米納德,法國(guó)工程師,數(shù)據(jù)可視化大師;右圖是: 馬修·亨利·菲尼亞斯·里亞爾·?;瑦?ài)爾蘭人,蒸汽機(jī)引擎設(shè)計(jì)工程師。

(圖片來(lái)源于網(wǎng)絡(luò))
1869年,查爾斯·米納德(Charles Minard)繪制了《1812年拿破侖東征圖》。這張圖形象的描繪了拿破侖在1812到1813年進(jìn)攻俄國(guó)時(shí)所遭受的災(zāi)難性損失。

圖中黃色為進(jìn)軍路線,黑色為撤退路線,線條的寬度代表拿破侖的軍隊(duì)人數(shù)變化,從圖中可以清楚的看到,在深入寒冷的俄國(guó)腹地時(shí),拿破侖軍隊(duì)的人數(shù)在逐漸的減少,到黑色線條撤退返回時(shí),線條細(xì)的都快看不見(jiàn)了(活著返回法國(guó)的只有1萬(wàn)余人)。這張圖也被認(rèn)為是“數(shù)據(jù)可視化”的經(jīng)典之作。
然而,讓?;鶊D廣泛應(yīng)用于科學(xué)工程領(lǐng)域的,還是要感謝馬修·亨利·菲尼亞斯·里亞爾·?;∕atthew Henry Phineas Riall Sankey)。?;鶊D(Sankey chart)也是以此人命名。1898年,桑基在一篇描述能量效率的文章中畫了這樣一張圖。

在文中,他對(duì)這張圖是這樣解釋的:當(dāng)能量在蒸汽機(jī)的各個(gè)部件中傳輸時(shí),都會(huì)有能量損失,要提高能量傳遞效率,需要知道哪些步驟中流失比較嚴(yán)重。熱量傳遞如下圖箭頭所示,其中箭頭的寬度代表能量的大小,可以從圖中看到每個(gè)步驟中能量損失。
從此以后?;鶊D被應(yīng)用于各個(gè)領(lǐng)域,比如農(nóng)業(yè)領(lǐng)域中追溯農(nóng)產(chǎn)品的走向,社會(huì)學(xué)領(lǐng)域研究人口的流向,醫(yī)學(xué)領(lǐng)域研究病例發(fā)展的流向。 而在互聯(lián)網(wǎng)產(chǎn)品中,桑基圖也被廣泛采納,主要用于用戶路徑分析。比如,用戶在首頁(yè)開(kāi)始,分別流向了哪些頁(yè)面,之后又流向了哪里。以下圖為例,非常直觀的表現(xiàn)了用戶從Play song or video開(kāi)始向其他頁(yè)面的流轉(zhuǎn)以及過(guò)程中的跳失量(跳失量由黑色表示)。

自然而然,我們不由的要提一個(gè)問(wèn)題,有時(shí)候我們也想知道,達(dá)到某頁(yè)面或者執(zhí)行某行為的用戶都從哪里來(lái),于是就有了?;鶊D的變種,可以理解為“逆向桑基圖”。 即設(shè)定一個(gè)終點(diǎn),看看用戶從哪里來(lái),如下圖所示。

可以看到,?;鶊D能非常直觀的展現(xiàn)用戶旅程,尤其是用戶旅程紛繁復(fù)雜的時(shí)候,桑基圖能很直觀的表現(xiàn)出用戶的使用習(xí)慣,幫助我們了解用戶行為,從而進(jìn)一步提高產(chǎn)品體驗(yàn)。根據(jù)個(gè)人經(jīng)驗(yàn),?;鶊D可以在以下幾個(gè)方面提高產(chǎn)品和用戶的契合度:
1. 找到主流流程,幫助確定轉(zhuǎn)化漏斗中的關(guān)鍵步驟。
2. 看用戶主要流向了哪里,發(fā)現(xiàn)用戶的興趣點(diǎn),尋找新的機(jī)會(huì)。
3. 發(fā)現(xiàn)被用戶忽略的產(chǎn)品價(jià)值點(diǎn),修正價(jià)值點(diǎn)曝光方式。
4. 發(fā)現(xiàn)用戶的流失點(diǎn)。
5. 找到有價(jià)值的用戶群體。
2.1. 找到主流流程,幫助確定轉(zhuǎn)化漏斗中的關(guān)鍵步驟。
比如下圖(仔細(xì)看圖)中,我們將每一步占比最高的流程摘出來(lái),得到最最主流的步驟,即Play Song or Video -> Favorite Song or Video -> Share Song or Video -> Search Song or Video -> Select Song or Video。

2.2.發(fā)現(xiàn)被用戶忽略的產(chǎn)品價(jià)值點(diǎn),修正價(jià)值點(diǎn)曝光方式。
在上圖中,我們發(fā)現(xiàn)執(zhí)行了Seach song 的用戶持續(xù)走到下一步的可能性會(huì)更大,然而在第二步并沒(méi)有search song 操作,在第三步,也只有9.48%的用戶選擇了search song,是不是可以考慮加強(qiáng)Search song功能的曝光。

2.3. 看用戶主要流向了哪里,發(fā)現(xiàn)用戶的興趣點(diǎn),尋找新的機(jī)會(huì)。

比如上圖中,我們發(fā)現(xiàn)Concert landing Screen中執(zhí)行Purchase ticket動(dòng)作的比例高達(dá)75.13%, 可以看出用戶是對(duì)Concert landing Screen到Purchase ticket的轉(zhuǎn)化率是極高的,可以發(fā)現(xiàn)用戶對(duì)Purchase Concert ticket的興趣是很高的,后續(xù)產(chǎn)品可以考慮增強(qiáng)這一塊的投入。
2.4. 發(fā)現(xiàn)用戶的流失點(diǎn)
上圖可以看出,每一步用戶的累計(jì)跳失率是:15.82%, 29.61%, 41.64%, 52.19%, 每一步的凈跳失率就是:
15.82%,13.79%(29.61%-15.82%),12.03%(41.64%-29.61%),10.55%(52.19%-41.64%)
第一步的的跳失率是最高的,結(jié)合之前的分析,產(chǎn)品側(cè)可以考慮通過(guò)search song來(lái)降低跳失率。

2.5. 尋找新的價(jià)值潛力點(diǎn)
share song是app實(shí)現(xiàn)裂變拉新的一個(gè)渠道。我們可以看到share song之后的群體一大部分去了Search song,但是search song之后,卻沒(méi)有share song,到底是因?yàn)閟earch song 沒(méi)有快捷分享通道,還是因?yàn)橛脩舨辉敢夥窒恚鸵Y(jié)合具體情況分析了。是不是可以在search song后鼓勵(lì)用戶分享,達(dá)到拉新的目的。

當(dāng)然,?;鶊D主要適用于用戶路徑相對(duì)復(fù)雜的應(yīng)用,如果只是簡(jiǎn)單路徑的分析,則有點(diǎn)大炮打蒼蠅的感覺(jué)了。
桑基圖這么好,那么?;鶊D怎么做呢?首先來(lái)剖析一下?;鶊D的組成,?;鶊D想要表達(dá)的是流向問(wèn)題,那么就需要知道從哪里(起點(diǎn))到哪里(終點(diǎn))---流了多少(流量),這句話中有三個(gè)要素,我個(gè)人稱之為點(diǎn)、線、面:
1. 點(diǎn):即流向的起點(diǎn)和終點(diǎn)。
2. 線:即哪些起點(diǎn)和終點(diǎn)間有流量。
3. 面:這些的量有多大(用面寬表示)。
以我的知乎文章頁(yè)面為例,我想知道進(jìn)入到文章頁(yè)面的人都流向了哪里, 這里我把知乎頁(yè)面做了簡(jiǎn)化,假設(shè)該頁(yè)面只有以下三個(gè)鏈接。

那么這里
點(diǎn)就是:
起點(diǎn):知乎個(gè)人主頁(yè)文章
終點(diǎn):是A文章, B 文章, C文章。
線就是:
主頁(yè) --->A文章
主頁(yè) --->A文章
主頁(yè) --->A文章
假設(shè)到A、B、C文章的人數(shù)分別是100、200、300,那么面(寬)就是100、200、300。所以要做出桑基圖,就是尋找點(diǎn)、線、面的問(wèn)題。
目前市面上有很成熟的工具做出?;鶊D,比如神策數(shù)據(jù)的用戶路徑分析就可以完成。但如果沒(méi)有成熟的工具支持,我們只能自己動(dòng)手、豐衣足食了,不過(guò)前提是需要有完備的埋點(diǎn)數(shù)據(jù),如果連埋點(diǎn)都沒(méi)有,只能是巧婦難為無(wú)米之炊,快快把自己項(xiàng)目的埋點(diǎn)體系建立起來(lái)[參考文章:數(shù)據(jù)人該知道的埋點(diǎn)體系]。接下來(lái)我們就看一下如何從最原始的埋點(diǎn)數(shù)據(jù)中自己動(dòng)手造出?;鶊D。
以下內(nèi)容比較枯燥,需要大家仔細(xì)看圖,也可以準(zhǔn)備好小本本寫一寫、畫一畫。
如下圖,我們的PV(Page visits)埋點(diǎn)原始數(shù)據(jù)(已做脫敏處理)有三列(其他不相關(guān)的列已隱藏):

表格中的幾列分別是:
當(dāng)前頁(yè)面的URL: 即有哪些起點(diǎn)或者終點(diǎn)。
session_id: 用于確認(rèn)屬于同一會(huì)話的PV頁(yè)面訪問(wèn)。關(guān)于session(會(huì)話)的定義自行百度
訪問(wèn)順序:即同一會(huì)話中頁(yè)面訪問(wèn)的順序。
舉個(gè)例子,表格前兩行的意思是某次會(huì)話中(16094237690031612632975|64)用戶第一次訪問(wèn)的頁(yè)面是xxx.com/home,第二次訪問(wèn)的頁(yè)面是:xxx.com/company/home。
我們來(lái)從這個(gè)表中找到點(diǎn)、線、面
點(diǎn):
以起點(diǎn)為例,我們需要找到用戶第一次訪問(wèn)的頁(yè)面都有哪些?,那么用excel過(guò)濾出訪問(wèn)順序==1的頁(yè)面,去重,就得到第一次被訪問(wèn)的頁(yè)面的集合。

同理得出第二次被訪問(wèn)的頁(yè)面的集合。這樣就得到了頭兩次被訪問(wèn)的頁(yè)面節(jié)點(diǎn):

線&面:
接下來(lái)我們需要知道兩個(gè)步驟節(jié)點(diǎn)之間是否有聯(lián)系。以第一個(gè)頁(yè)面xxx.com/home為例,需要知道第一次訪問(wèn)頁(yè)面xxx.com/home,并且第二次訪問(wèn)xxx.com/company/home有幾個(gè)session。這其實(shí)就是一個(gè)數(shù)學(xué)集合問(wèn)題, 先找到以xxx.com/home為第一次訪問(wèn)頁(yè)面的sessionID集合A, 再找到以xxx.com/company/home為第二次訪問(wèn)頁(yè)面的sessionID集合B, 取集合A和集合B的交集中元素的個(gè)數(shù),就得到這個(gè)“面”的寬度,即流量。照此,我們可以得出第一次訪問(wèn)頁(yè)面xxx.com/home,并且第二次訪問(wèn)xxx.com/company/home有3個(gè)session, 如下圖:

那么這兩個(gè)頁(yè)面之間的連接就是3。

以此類推,得到以xxx.com/home為起點(diǎn)的session,對(duì)這些session中,第二次被訪問(wèn)的頁(yè)面進(jìn)行計(jì)數(shù),就可以得到對(duì)應(yīng)的訪問(wèn)流量分布,如下圖:

同理,再分別計(jì)算出以xxx.com/order/manage/list, xxx.com/traffic/serving, xxx.com/fund/fund-withdraw 為起點(diǎn)的流量分布,從而得出一張不怎么好看的?;鶊D:

而這張圖的背后就是如下的數(shù)據(jù):其中traffic就是對(duì)應(yīng)的流量大小,也就是?;鶊D中線的寬度。
這樣,意味著我們要實(shí)現(xiàn)源數(shù)據(jù)到桑基圖數(shù)據(jù)的轉(zhuǎn)化:

接下來(lái)我們就用python 代碼實(shí)現(xiàn)這一轉(zhuǎn)變。
用Python實(shí)現(xiàn)?;鶊D
1. 點(diǎn):
#讀取PV數(shù)據(jù)filepath = '/Users/jigege/Desktop/sankey/PV_data.csv'pvData = pd.read_csv(filepath)#獲取前兩步桑基圖的節(jié)點(diǎn),首先定義一個(gè)數(shù)組,數(shù)組元素是每一步對(duì)應(yīng)的節(jié)點(diǎn)數(shù)組SankeyNodes = []#作為示意,我們僅考慮訪問(wèn)順序==1和2的數(shù)據(jù)for i in range(2):#過(guò)濾出訪問(wèn)順序?yàn)閕的頁(yè)面,用drop_duplicates()去重得到節(jié)點(diǎn)SankeyNodes.append(pvData[pvData['Sequence'] == i+1]['CurrentPage'].drop_duplicates().values)SankeyNodes
得到?;鶊D點(diǎn)的數(shù)組如下:
[array(['xxx.com/home', 'xxx.com/order/manage/list','xxx.com/traffic/serving', 'xxx.com/fund/fund-withdraw'],dtype=object),array(['xxx.com/company/home', 'xxx.com/product/manage/list','xxx.com/traffic/serving', 'xxx.com/order/manage/list','xxx.com/fund/account-statement', 'xxx.com/home','xxx.com/fund/fund-home'], dtype=object)]
2. 獲取線&面
#獲取?;鶊D的線&面#初始化?;鶊D數(shù)據(jù),列名分別為'source':流量起點(diǎn),'target':流量終點(diǎn),'traffic':流量大小sankeyTraffic =pd.DataFrame(columns = ['source','target','traffic'])#遍歷第一步的節(jié)點(diǎn)for i in range(len(SankeyNodes[0])):#得出第一步中各個(gè)節(jié)點(diǎn)對(duì)應(yīng)的session_id列表sourceSessionList = pvData[(pvData['CurrentPage']==SankeyNodes[0][i])&(pvData['Sequence']==1)]['session_id']#遍歷第二步的節(jié)點(diǎn)for j in range(len(SankeyNodes[1])):#得出第二步中各個(gè)節(jié)點(diǎn)對(duì)應(yīng)的session_id列表targetSessionList = pvData[(pvData['CurrentPage']==SankeyNodes[1][j])&(pvData['Sequence']==2)]['session_id']#算出同時(shí)訪問(wèn)過(guò)第一個(gè)頁(yè)面和第二個(gè)頁(yè)面的session個(gè)數(shù),即為流量;用isin函數(shù)判斷第二個(gè)頁(yè)面的session列表是否在第一個(gè)頁(yè)面的session列表中Traffic = targetSessionList.isin(sourceSessionList)[lambda x: x==True].count()#用append函數(shù)將算出的'source':流量起點(diǎn),'target':流量終點(diǎn),'traffic':流量大小添加到?;鶊D數(shù)據(jù)中sankeyTraffic=sankeyTraffic.append({'source':SankeyNodes[0][i],'target':SankeyNodes[1][j],'traffic':Traffic}, ignore_index=True)
3. 繪制?;鶊D
#繪制?;鶊D有兩個(gè)包,一個(gè)是pyecharts.charts, 另外一個(gè)是holoviews,我們選擇了holoviews。#但holoviews不允許source 和target當(dāng)中有重復(fù)項(xiàng),所以將source和target分別加上后綴,避免兩列中的重復(fù)項(xiàng)sankeyTraffic['source'] = sankeyTraffic['source'].apply(lambda x: x+'_source')sankeyTraffic['target'] = sankeyTraffic['target'].apply(lambda x: x+'_target')#引入相關(guān)的python 包,需要預(yù)先安裝holoviews,plotly等packageimport holoviews as hvimport plotly.graph_objects as goimport plotly.express as pex#導(dǎo)入對(duì)應(yīng)的擴(kuò)展組件hv.extension('bokeh')#繪制桑基圖,一步完成!hv.Sankey(sankeyTraffic,kdims=["source", "target"], vdims=["traffic"] )

當(dāng)然,如果你有興趣,在一個(gè)最簡(jiǎn)單的只描述的一步的?;鶊D完成之后,可以探尋更加完整復(fù)雜的多步?;鶊D。其實(shí)原理都是一樣的,本文就不再贅述,大家也可以參考文章:How to Plot Sankey Diagram in Python Jupyter Notebook [holoviews & plotly]? by Sunny Solanki(https://coderzcolumn.com/tutorials/data-science/how-to-plot-sankey-diagram-in-python-jupyter-notebook-holoviews-and-plotly)
總結(jié)
今天我們從?;鶊D的前世今生講起,聊了聊?;鶊D在互聯(lián)網(wǎng)中研究用戶路徑的應(yīng)用,以及業(yè)務(wù)價(jià)值,最后講了?;鶊D的繪制原理和python實(shí)操。在?;鶊D的繪制原理和python實(shí)操部分,是有點(diǎn)枯燥的,甚至需要大家拿出小本本在紙上畫一畫,寫一寫,仔細(xì)看看所附的圖片,然后上手練習(xí)一下。如果你們的工作中有現(xiàn)成的?;鶊D工具,本文可以幫你知道?;鶊D是怎么來(lái)的,如果沒(méi)有,試試今天的方法,做出一個(gè)酷酷的?;鶊D來(lái)擴(kuò)大你的影響力吧!
參考文章
1. 維基百科:http://en.wikipedia.org/wiki/Sankey_diagram
2.google charts: https://developers.google.com/chart/interactive/docs/gallery/sankey
3. How to Plot Sankey Diagram in Python Jupyter Notebook [holoviews & plotly]
4. 利用Python繪制誘人的?;鶊D_數(shù)據(jù)森麟-CSDN博客
5. Amplitude help center:Get the most out of Amplitude's Funnel Analysis chart
6. the art of consequences

推薦閱讀
歡迎長(zhǎng)按掃碼關(guān)注「數(shù)據(jù)管道」

