用Python分析用戶消費行為
本文是《如何七周成為數據分析師》的第二十三篇教程,如果想要了解寫作初衷,可以先行閱讀七周指南。溫馨提示:如果您已經熟悉Python,大可不必再看這篇文章,或只挑選部分。
今天用Python分析用戶消費行為,內容會更接地氣,難度會稍稍上升一點。它也是七周成為數據分析師的最後一篇教程了。這裡會串聯以前的幾個知識點。
數據來源CDNow網站的用戶購買明細。一共有用戶ID,購買日期,購買數量,購買金額四個欄位。我們通過案例數據完成一份基礎的數據分析報告。
載入包和數據,文件是txt,用read_table方法打開,因為原始數據不包含表頭,所以需要賦予。字元串是空格分割,用s+表示匹配任意空白符。
觀察數據,order_dt表示時間,但現在它只是年月日組合的一串數字,沒有時間含義。購買金額是小數。值得注意的是,一個用戶在一天內可能購買多次,用戶ID為2的用戶就在1月12日買了兩次,這個細節不要遺漏。
用戶平均每筆訂單購買2.4個商品,標準差在2.3,稍稍具有波動性。中位數在2個商品,75分位數在3個商品,說明絕大部分訂單的購買量都不多。最大值在99個,數字比較高。購買金額的情況差不多,大部分訂單都集中在小額。
一般而言,消費類的數據分布,都是長尾形態。大部分用戶都是小額,然而小部分用戶貢獻了收入的大頭,俗稱二八。
沒有空值,很乾凈的數據。接下來我們要將時間的數據類型轉換。
pd.to_datetime可以將特定的字元串或者數字轉換成時間格式,其中的format參數用於匹配。例如19970101,%Y匹配前四位數字1997,如果y小寫只匹配兩位數字97,%m匹配01,%d匹配01。
另外,小時是%h,分鐘是%M,注意和月的大小寫不一致,秒是%s。若是1997-01-01這形式,則是%Y-%m-%d,以此類推。
astype也可以將時間格式進行轉換,比如[M]轉化成月份。我們將月份作為消費行為的主要事件窗口,選擇哪種時間窗口取決於消費頻率。
上圖是轉化後的格式。月份依舊顯示日,只是變為月初的形式。
pandas中有專門的時間序列方法tseries,它可以用來進行時間偏移,也是處理時間類型的好方法。時間格式也能作為索引,在金融、財務等領域使用較多,這裡不再多敘述了。
上面的消費行為數據粒度是每筆訂單,我們轉換成每位用戶看一下。
用groupby創建一個新對象。
從用戶角度看,每位用戶平均購買7張CD,最多的用戶購買了1033張,屬於狂熱用戶了。用戶的平均消費金額(客單價)100元,標準差是240,結合分位數和最大值看,平均值才和75分位接近,肯定存在小部分的高額消費用戶。
接下來按月的維度分析。
按月統計每個月的CD銷量。從圖中可以看到,前幾個月的銷量非常高漲。數據比較異常。而後期的銷量則很平穩。
金額一樣呈現早期銷售額多,後期平穩下降的趨勢。為什麼會呈現這個原因呢?我們假設是用戶身上出了問題,早期時間段的用戶中有異常值,第二假設是各類促銷營銷,但這裡只有消費數據,所以無法判斷。
繪製每筆訂單的散點圖。從圖中觀察,訂單消費金額和訂單商品量呈規律性,每個商品十元左右。訂單的極值較少,超出1000的就幾個。顯然不是異常波動的罪魁禍首。
繪製用戶的散點圖,用戶也比較健康,而且規律性比訂單更強。因為這是CD網站的銷售數據,商品比較單一,金額和商品量的關係也因此呈線性,沒幾個離群點。
消費能力特彆強的用戶有,但是數量不多。為了更好的觀察,用直方圖。
plt.subplot用於繪製子圖,子圖用數字參數表示。121表示分成1*2個圖片區域,佔用第一個,即第一行第一列,122表示佔用第二個。figure是尺寸函數,為了容納兩張子圖,寬設置的大一點即可。
從直方圖看,大部分用戶的消費能力確實不高,高消費用戶在圖上幾乎看不到。這也確實符合消費行為的行業規律。
觀察完用戶消費的金額和購買量,接下來看消費的時間節點。
用groupby函數將用戶分組,並且求月份的最小值,最小值即用戶消費行為中的第一次消費時間。ok,結果出來了,所有用戶的第一次消費都集中在前三個月。我們可以這樣認為,案例中的訂單數據,只是選擇了某個時間段消費的用戶在18個月內的消費行為。
觀察用戶的最後一次消費時間。絕大部分數據依然集中在前三個月。後續的時間段內,依然有用戶在消費,但是緩慢減少。
異常趨勢的原因獲得了解釋,現在針對消費數據進一步細分。我們要明確,這只是部分用戶的訂單數據,所以有一定局限性。在這裡,我們統一將數據上消費的用戶定義為新客。
接下來分析消費中的復購率和回購率。首先將用戶消費數據進行數據透視。
在pandas中,數據透視有專門的函數pivot_table,功能非常強大。pivot_table參數中,index是設置數據透視後的索引,column是設置數據透視後的列,簡而言之,index是你想要的行,column是想要的列。案例中,我希望統計每個用戶在每月的訂單量,所以user_id是index,month是column。
values是將哪個值進行計算,aggfunc是用哪種方法。於是這裡用values=order_dt和aggfunc=count,統計里order_dt出現的次數,即多少筆訂單。
使用數據透視表,需要明確獲得什麼結果。有些用戶在某月沒有進行過消費,會用NaN表示,這裡用fillna填充。
生成的數據透視,月份是1997-01-01 00:00:00表示,比較丑。將其優化成標準格式。
首先求復購率,復購率的定義是在某時間窗口內消費兩次及以上的用戶在總消費用戶中佔比。這裡的時間窗口是月,如果一個用戶在同一天下了兩筆訂單,這裡也將他算作復購用戶。
將數據轉換一下,消費兩次及以上記為1,消費一次記為0,沒有消費記為NaN。
applymap針對DataFrame里的所有數據。用lambda進行判斷,因為這裡涉及了多個結果,所以要兩個if else,記住,lambda沒有elif的用法
用sum和count相除即可計算出復購率。因為這兩個函數都會忽略NaN,而NaN是沒有消費的用戶,count不論0還是1都會統計,所以是總的消費用戶數,而sum求和計算了兩次以上的消費用戶。這裡用了比較巧妙的替代法計算復購率,SQL中也可以用。
圖上可以看出復購率在早期,因為大量新用戶加入的關係,新客的復購率並不高,譬如1月新客們的復購率只有6%左右。而在後期,這時的用戶都是大浪淘沙剩下的老客,復購率比較穩定,在20%左右。
單看新客和老客,復購率有三倍左右的差距。
接下來計算回購率。回購率是某一個時間窗口內消費的用戶,在下一個時間窗口仍舊消費的佔比。我1月消費用戶1000,他們中有300個2月依然消費,回購率是30%。
回購率的計算比較難,因為它設計了橫向跨時間窗口的對比。
將消費金額進行數據透視,這裡作為練習,使用了平均值。
再次用applymap+lambda轉換數據,只要有過購買,記為1,反之為0。
新建一個判斷函數。data是輸入的數據,即用戶在18個月內是否消費的記錄,status是空列表,後續用來保存用戶是否回購的欄位。
因為有18個月,所以每個月都要進行一次判斷,需要用到循環。if的主要邏輯是,如果用戶本月進行過消費,且下月消費過,記為1,沒有消費過是0。本月若沒有進行過消費,為NaN,後續的統計中進行排除。
用apply函數應用在所有行上,獲得想要的結果。
最後的計算和復購率大同小異,用count和sum求出。從圖中可以看出,用戶的回購率高於復購,約在30%左右,波動性也較強。新用戶的回購率在15%左右,和老客差異不大。
將回購率和復購率綜合分析,可以得出,新客的整體質量低於老客,老客的忠誠度(回購率)表現較好,消費頻次稍次,這是CDNow網站的用戶消費特徵。
接下來進行用戶分層,我們按照用戶的消費行為,簡單劃分成幾個維度:新用戶、活躍用戶、不活躍用戶、迴流用戶。
新用戶的定義是第一次消費。活躍用戶即老客,在某一個時間窗口內有過消費。不活躍用戶則是時間窗口內沒有消費過的老客。迴流用戶是在上一個窗口中沒有消費,而在當前時間窗口內有過消費。以上的時間窗口都是按月統計。
比如某用戶在1月第一次消費,那麼他在1月的分層就是新用戶;他在2月消費國,則是活躍用戶;3月沒有消費,此時是不活躍用戶;4月再次消費,此時是迴流用戶,5月還是消費,是活躍用戶。
分層會涉及到比較複雜的邏輯判斷。
函數寫得比較複雜,主要分為兩部分的判斷,以本月是否消費為界。本月沒有消費,還要額外判斷他是不是新客,因為部分用戶是3月份才消費成為新客,那麼在1、2月份他應該連新客都不是,用unreg表示。如果是老客,則為unactive。
本月若有消費,需要判斷是不是第一次消費,上一個時間窗口有沒有消費。大家可以多調試幾次理順裡面的邏輯關係,對用戶進行分層,邏輯確實不會簡單,而且這裡只是簡化版本的。
從結果看,用戶每個月的分層狀態以及變化已經被我們計算出來。我是根據透視出的寬表計算,其實還有一種另外一種寫法,只提取時間窗口內的數據和上個窗口對比判斷,封裝成函數做循環,它適合ETL的增量更新。
unreg狀態排除掉,它是「未來」才作為新客,這麼能計數呢。換算成不同分層每月的統計量。
生成面積圖,比較丑。因為它只是某時間段消費過的用戶的後續行為,藍色和灰色區域都可以不看。只看紫色迴流和紅色活躍這兩個分層,用戶數比較穩定。這兩個分層相加,就是消費用戶佔比(後期沒新客)。
用戶迴流佔比在5%~8%,有下降趨勢。所謂迴流佔比,就是迴流用戶在總用戶中的佔比。另外一種指標叫迴流率,指上個月多少不活躍/消費用戶在本月活躍/消費。因為不活躍的用戶總量近似不變,所以這裡的迴流率也近似迴流佔比。
活躍用戶的下降趨勢更明顯,佔比在3%~5%間。這裡用戶活躍可以看作連續消費用戶,質量在一定程度上高於迴流用戶。
結合迴流用戶和活躍用戶看,在後期的消費用戶中,60%是迴流用戶,40%是活躍用戶/連續消費用戶,整體質量還好,但是針對這兩個分層依舊有改進的空間,可以繼續細化數據。
接下來分析用戶質量,因為消費行為有明顯的二八傾向,我們需要知道高質量用戶為消費貢獻了多少份額。
新建一個對象,按用戶的消費金額生序。使用cumsum,它是累加函數。逐行計算累計的金額,最後的2500315便是總消費額。
轉換成百分比。
繪製趨勢圖,橫坐標是按貢獻金額大小排序而成,縱坐標則是用戶累計貢獻。可以很清楚的看到,前20000個用戶貢獻了40%的消費。後面4000位用戶貢獻了60%,確實呈現28傾向。
統計一下銷量,前兩萬個用戶貢獻了45%的銷量,高消費用戶貢獻了55%的銷量。在消費領域中,狠抓高質量用戶是萬古不變的道理。
接下來計算用戶生命周期,這裡定義第一次消費至最後一次消費為整個用戶生命。
統計出用戶第一次消費和最後一次消費的時間,相減,得出每一位用戶的生命周期。因為數據中的用戶都是前三個月第一次消費,所以這裡的生命周期代表的是1月~3月用戶的生命周期。因為用戶會持續消費,所以理論上,隨著後續的消費,用戶的平均生命周期會增長。
求一下平均,所有用戶的平均生命周期是134天,比預想的高,但是平均數不靠譜,還是看一下分布吧,大家有興趣可以用describe,更詳細。
因為這裡的數據類型是timedelta時間,它無法直接作出直方圖,所以先換算成數值。換算的方式直接除timedelta函數即可,這裡的np.timedelta64(1, "D"),D表示天,1表示1天,作為單位使用的。因為max-min已經表示為天了,兩者相除就是周期的天數。
看到了沒有,大部分用戶只消費了一次,所有生命周期的大頭都集中在了0天。但這不是我們想要的答案,不妨將只消費了一次的新客排除,來計算所有消費過兩次以上的老客的生命周期。
轉換成DataFrame。
篩選出lifetime>0,即排除了僅消費了一次的那些人。做直方圖。
這個圖比上面的靠譜多了,雖然仍舊有不少用戶生命周期靠攏在0天。這是雙峰趨勢圖。部分質量差的用戶,雖然消費了兩次,但是仍舊無法持續,在用戶首次消費30天內應該盡量引導。少部分用戶集中在50天~300天,屬於普通型的生命周期,高質量用戶的生命周期,集中在400天以後,這已經屬於忠誠用戶了,大家有興趣可以跑一下400天+的用戶占老客比多少,佔總量多少。
消費兩次以上的用戶生命周期是276天,遠高於總體。從策略看,用戶首次消費後應該花費更多的引導其進行多次消費,提供生命周期,這會帶來2.5倍的增量。
再來計算留存率,留存率也是消費分析領域的經典應用。它指用戶在第一次消費後,有多少比率進行第二次消費。和迴流率的區別是留存傾向於計算第一次消費,並且有多個時間窗口。
這裡用到merge函數,它和SQL中的join差不多,用來將兩個DataFrame進行合併。我們選擇了inner 的方式,對標inner join。即只合併能對應得上的數據。這裡以on=user_id為對應標準。這裡merge的目的是將用戶消費行為和第一次消費時間對應上,形成一個新的DataFrame。suffxes參數是如果合併的內容中有重名column,加上後綴。除了merge,還有join,concat,用戶接近,查看文檔即可。
這裡將order_date和order_date_min相減。獲得一個新的列,為用戶每一次消費距第一次消費的時間差值。
日期轉換成時間。
將時間差值分桶。我這裡分成0~3天內,3~7天內,7~15天等,代表用戶當前消費時間距第一次消費屬於哪個時間段呢。這裡date_diff=0並沒有被劃分入0~3天,因為計算的是留存率,如果用戶僅消費了一次,留存率應該是0。另外一方面,如果用戶第一天內消費了多次,但是往後沒有消費,也算作留存率0。
用pivot_table數據透視,獲得的結果是用戶在第一次消費之後,在後續各時間段內的消費總額。
計算一下用戶在後續各時間段的平均消費額,這裡只統計有消費的平均值。雖然後面時間段的金額高,但是它的時間範圍也寬廣。從平均效果看,用戶第一次消費後的0~3天內,更可能消費更多。
但消費更多是一個相對的概念,我們還要看整體中有多少用戶在0~3天消費。
依舊將數據轉換成是否,1代表在該時間段內有後續消費,0代表沒有。
只有2.5%的用戶在第一次消費的次日至3天內有過消費,3%的用戶在3~7天內有過消費。數字並不好看,CD購買確實不是高頻消費行為。時間範圍放寬後數字好看了不少,有20%的用戶在第一次消費後的三個月到半年之間有過購買,27%的用戶在半年後至1年內有過購買。從運營角度看,CD機營銷在教育新用戶的同時,應該注重用戶忠誠度的培養,放長線掉大魚,在一定時間內召回用戶購買。
怎麼算放長線掉大魚呢?我們計算出用戶的平均購買周期。
我們將用戶分組,groupby分組後的數據,也是能用for進行循環和迭代的。第一個循環對象user,是分組的對象,即user_id;第二個循環對象group,是分組聚合後的結果。為了舉例我用了print,它依次輸出了user_id=1,user_id=2時的用戶消費數據,是一組切割後的DataFrame。
大家應該了解分組循環的用法,但是網不建議大家用for循環,它的效率非常慢。要計算用戶的消費間隔,確實需要用戶分組,但是用apply效率更快。
定義一個計算間隔的函數diff,輸入的是group,通過上面的演示,大家也應該知道分組後的數據依舊是DataFrame。我們將用戶上下兩次消費時間相減將能求出消費間隔了。shift函數是一個偏移函數,和excel上的offset差不多。
x.shift()是往上偏移一個位置,x.shift(-1)是往下偏移一個位置,加參數axis=1則是左右偏移。當我想將求用戶下一次距本次消費的時間間隔,用shift(-1)減當前值即可。案例用的diff函數便藉助shift方法,巧妙的求出了每位用戶的兩次消費間隔,若為NaN,則沒有下一次。
然後就簡單了,用mean函數即可求出用戶的平均消費間隔時間是68天。想要召回用戶,在60天左右的消費間隔是比較好的。
看一下直方圖,典型的長尾分布,大部分用戶的消費間隔確實比較短。不妨將時間召回點設為消費後立即贈送優惠券,消費後10天詢問用戶CD怎麼樣,消費後30天提醒優惠券到期,消費後60天簡訊推送。這便是數據的應用了。
假若大家有興趣,不妨多做幾個分析假設,看能不能用Python挖掘出更有意思的數據,1月、2月、3月的新用戶在留存率有沒有差異?不同生命周期的用戶,他們的消費累加圖是什麼樣的?消費留存,劃分其他時間段怎麼樣?
你若想要追求更好的Python技術,可以把上述的分析過程都封裝成函數。當下次想要再次分析的時候,怎麼樣只用幾個函數就搞定,而不是繼續重複碼代碼。
這裡的數據只是用戶ID,消費時間,購買量和消費金額。如果換成用戶ID,瀏覽時間,瀏覽量,能不能直接套用?瀏覽變成評論、點贊又行不行?消費行為變成用戶其他行為呢?我可以明確地告訴你,大部分代碼只要替換部分就能直接用了。把所有的結果分析出來需要花費多少時間呢?
Python的優勢就在於快速和靈活,遠比Excel和SQL快。這次是CD網站的消費行為,下次換成電商,換成O2O,一樣可以在幾分鐘內計算出用戶生命周期,用戶購買頻次,留存率復購率回購率等等。這對你的效率提升有多大幫助?
經過一系列的講解,你是否掌握了Python的數據分析姿勢?這裡的很多技巧,都和我以前的文章相關,有excel的函數影子,有數據可視化的用法,有分析思路,有描述統計的知識,有各種業務指標,有接近SQL的數據規整,雖然是Python進行數據分析,也是整個數據分析的總結。
所以,所有的課程都結束啦。七周成為數據分析師,告一段落。
——————
因為知乎關閉了外鏈,數據可以去我的公眾號(ID:tracykanc),後台發送「CD數據」獲得地址。歡迎關注。
推薦閱讀:
※自媒體人,有一天會跳樓嗎?
※揭秘:圈內人眼中的Google和Facebook
※另一種單純
※如何看待酒仙網強制不讓員工春節提前回家?
※「Growth Hacker」 - 增長黑客