Python電商數(shù)據(jù)清洗及分析

一、數(shù)據(jù)來源

本文使用python來分析一份電商數(shù)據(jù),源數(shù)據(jù)可在下方評(píng)論獲取。

二、分析思路

image

三、分析過程

3.1 讀取數(shù)據(jù)

首先導(dǎo)入后續(xù)分析需要的第三方庫及一些常用設(shè)置

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
# 設(shè)置風(fēng)格
sns.set(style='white', font_scale=1.2)
%matplotlib inline
plt.rcParams["font.sans-serif"] = "SimHei"
plt.rcParams['axes.titlesize'] = 20
plt.rcParams['axes.unicode_minus']=False
import warnings
warnings.filterwarnings("ignore")

通過pandas庫讀取數(shù)據(jù)

filename = "./電商數(shù)據(jù)源.csv"
data = pd.read_csv(filename,encoding="gbk")
data.head()
image

從讀取結(jié)果可以看到數(shù)據(jù)源包含24個(gè)字段,每個(gè)字段的含義如下:

3.2 數(shù)據(jù)清洗

數(shù)據(jù)清洗階段主要處理不符合業(yè)務(wù)邏輯的異常值,還有缺失值和重復(fù)值。

3.2.1 清洗發(fā)貨日期早于下單日期的異常值

# 根據(jù)業(yè)務(wù)需要提取數(shù)據(jù),發(fā)貨日期早于下單日期
# 1)轉(zhuǎn)換時(shí)間類型
data["OrderDate"] = pd.to_datetime(data["OrderDate"])
data["ShipDate"] = pd.to_datetime(data["ShipDate"])
# 2)計(jì)算時(shí)間差
data["interval"] = (data["ShipDate"]-data["OrderDate"]).dt.total_seconds()
# 3)找時(shí)間差為負(fù)的數(shù)據(jù)并刪除
data.drop(index=data[data["interval"]<0].index,axis=0,inplace=True)
print(data.shape)

采用如下代碼查看數(shù)據(jù),建立對(duì)數(shù)據(jù)的基本認(rèn)識(shí)

# 查看行列數(shù)量
data.shape
# 數(shù)據(jù)整體描述
data.describe()
# 數(shù)據(jù)信息
data.info()
# 統(tǒng)計(jì)NAN數(shù)據(jù)
data.isna().sum()
image

可以看到ShipMode(發(fā)貨模式)和PostalCode(郵編)兩列存在缺失值。

3.2.2 清洗RowID重復(fù)值

# 重復(fù)值
print(data["RowID"].unique().size)
data[data["RowID"].duplicated()]
data.drop(index=data[data["RowID"].duplicated()].index,axis=0,inplace=True)
print(data.shape)

3.2.3 處理ShipMode缺失值

data[data["ShipMode"].isnull()]
data["ShipMode"].unique()
# 眾數(shù)填充缺失值
# data["ShipMode"].mode()
data["ShipMode"].fillna(data["ShipMode"].mode()[0],inplace=True)

3.2.4 清洗Discount的異常值和缺失值

data[data["Discount"]>1]
data[data["Discount"]<0]
data["Discount"] = data["Discount"].mask(data["Discount"]>1,None)
# 處理缺失值
data["Discount"].isna().sum()
# 平均折扣
meanDiscount = data["Discount"].mean()
# data[data["Discount"].notnull()]["Discount"].sum()/data[data["Discount"].notnull()]["Discount"].size
data["Discount"].fillna(meanDiscount,inplace=True)

3.2.5 刪除PostalCode列

郵編對(duì)分析意義不大,這里直接刪除

data.drop(columns=["PostalCode"],inplace=True)

3.2.6 將訂單日期拆分為年、月、季度

data["Order-year"] = data["OrderDate"].dt.year
data["Order-month"] = data["OrderDate"].dt.month
data["quarter"] = data["OrderDate"].dt.to_period('Q')
data[["OrderDate","Order-year","Order-month","quarter"]].sample(5)
image

3.3 數(shù)據(jù)分析

3.3.1 銷售額與增長(zhǎng)率

sales_year = data.groupby(by='Order-year')['Sales'].sum()
# print(sales_year)

sales_rate_12 = sales_year[2012] / sales_year[2011] - 1
sales_rate_13 = sales_year[2013] / sales_year[2012] - 1
sales_rate_14 = sales_year[2014] / sales_year[2013] - 1
# print(sales_rate_12,sales_rate_13,sales_rate_14)

sales_rate_12_label = "%.2f%%" % (sales_rate_12 * 100)
sales_rate_13_label  = "%.2f%%" % (sales_rate_13 * 100)
sales_rate_14_label  = "%.2f%%" % (sales_rate_14 * 100)
# print(sales_rate_12,sales_rate_13,sales_rate_14)

sales_rate = pd.DataFrame(
    {'sales_all':sales_year,
     'sales_rate':[0,sales_rate_12,sales_rate_13,sales_rate_14],
     'sales_rate_label':['0.00%',sales_rate_12_label,sales_rate_13_label,sales_rate_14_label]
    })
# print(sales_rate)

sales_rate = pd.DataFrame(
    {'sales_all':sales_year,
     'sales_rate':[0,sales_rate_12,sales_rate_13,sales_rate_14]
    })
y1 = sales_rate['sales_all']
y2 = sales_rate['sales_rate']
x = [str(value) for value in sales_rate.index.tolist()]
# 新建figure對(duì)象
fig=plt.figure(figsize=(10,6)) 
# 新建子圖1
ax1=fig.add_subplot(1,1,1)
# ax2與ax1共享X軸
ax2 = ax1.twinx()
ax1.bar(x,y1,color = 'dodgerblue')
ax2.plot(x,y2,marker='o',color = 'r')
ax1.set_xlabel('年份')
ax1.set_ylabel('銷售額')
ax2.set_ylabel('增長(zhǎng)率')
ax1.set_title('銷售額與增長(zhǎng)率')
image

3.3.2 地區(qū)分析

sales_area = data.groupby(by="Market")["Sales"].sum()
# sales_area
pie_labels = sales_area.index.to_list()
f, ax = plt.subplots(figsize=(10,10))
pie_sales_area = plt.pie(sales_area,labels=pie_labels,autopct="%.1f%%",startangle=90)
plt.title('2011-2014年總銷售額占比')
image
# 各地區(qū)每一年的銷售額
sales_area = data.groupby(by=["Market","Order-year"])["Sales"].sum()
# 將多層索引設(shè)置為列,level這個(gè)參數(shù)的意思是要把哪些索引設(shè)置為列
sales_area = sales_area.reset_index(level=[0,1])
# pd.pivot_table(data=sales_area,values="Sales",index="Market",columns="Order-year",aggfunc="sum")
# 繪制柱形圖
fig = plt.figure(figsize=(10,6))
sns.barplot(x="Market",y="Sales",hue="Order-year",data=sales_area,estimator=np.sum)
plt.title('2011-2014年不同地區(qū)銷售額對(duì)比')
image
# 各地區(qū)不同產(chǎn)品的銷售額
sales_area = data.groupby(by=["Market","Category"])["Sales"].sum()
# 將多層索引設(shè)置為列,level這個(gè)參數(shù)的意思是要把哪些索引設(shè)置為列
sales_area = sales_area.reset_index(level=[0,1])
# pd.pivot_table(data=sales_area,values="Sales",index="Market",columns="Order-year",aggfunc="sum")
# 繪制柱形圖
fig = plt.figure(figsize=(10,6))
sns.barplot(x="Market",y="Sales",hue="Category",data=sales_area,estimator=np.sum)
plt.title('不同產(chǎn)品類型在不同地區(qū)的銷售額對(duì)比')
image

3.3.3 銷售淡旺季分析

sales_year_month = data.groupby(by=["Order-year","Order-month"])["Sales"].sum()
# 將多層索引設(shè)置為列,level這個(gè)參數(shù)的意思是要把哪些索引設(shè)置為列
sales_year_month = sales_year_month.reset_index(level=[0,1])
# 繪制折線圖
fig = plt.figure(figsize=(10,6))
sns.lineplot(x="Order-month",y="Sales",hue="Order-year",data=sales_year_month,estimator=np.sum)
plt.title('2011-2014年不同月份銷售額對(duì)比')
image

3.3.4 每月新增用戶

data_customer = data.copy()
data_customer = data_customer.drop_duplicates(subset=["CustomerID"])
new_customer = data_customer.groupby(["Order-year","Order-month"]).size()
new_customer = new_customer.reset_index(level=[0,1])
new_customer.columns = ["Order-year","Order-month","count"]
new_customer = pd.pivot_table(data=new_customer,values="count",index="Order-month",columns="Order-year",fill_value=0)
new_customer
image

3.3.5 用戶RFM模型
首先計(jì)算用戶的R、F和M值,然后根據(jù)均值算法或評(píng)分制算法來對(duì)用戶分類,均值算法就是直接與各自的平均值比較,高于平均值即為高,低于平均值即為低;評(píng)分制算法就是對(duì)R、F和M制定相應(yīng)的打分規(guī)則,一般采用5分制,計(jì)算得到R、F和M相應(yīng)的得分,最后與得分的平均值進(jìn)行比較。

# 獲取2014年數(shù)據(jù)
data_14 = data [data ['Order-year']==2014]
data_14 = data_14[['CustomerID','OrderDate','Sales']]
# print(data_14.shape)
customdf = data_14.copy() 
customdf.set_index('CustomerID',drop=True,inplace=True)  
customdf['orders'] = 1 
customdf
rfmdf = customdf.pivot_table(index=['CustomerID'],
                    values=['OrderDate','orders','Sales'],
                    aggfunc={'OrderDate':'max',
                            'orders':'sum',
                            'Sales':'sum'})

rfmdf['R'] = (rfmdf["OrderDate"].max()-rfmdf["OrderDate"]).dt.days
rfmdf.rename(columns={'Sales':'M','orders':'F'},inplace=True)
rfmdf

1)均值算法

def rfm_func(x):
    level = x.apply(lambda x: "1" if x >= 0 else '0')
    label = level.R + level.F + level.M
    d = {
        '011':'重要價(jià)值客戶',
        '111':'重要喚回客戶',
        '001':'重要深耕客戶',
        '101':'重要挽留客戶',
        '010':'潛力客戶',
        '110':'一般維持客戶',
        '000':'新客戶',
        '100':'流失客戶'
    }
    result = d[label]
    return result

rfmdf['label'] = rfmdf[['R','F','M']].apply(lambda x:x-x.mean()).apply(rfm_func,axis=1)
rfmdf
result = rfmdf.groupby('label')["OrderDate"].count()
result = result.reset_index()
result.columns = ["label","count"]
result
# 繪制柱形圖
fig = plt.figure(figsize=(10,6))
order = ['重要價(jià)值客戶', '重要喚回客戶', '重要深耕客戶', '重要挽留客戶', '潛力客戶', '一般維持客戶', '新客戶', '流失客戶']
sns.barplot(x="count",y="label",data=result,orient='h',order=order)
plt.title('RFM用戶數(shù)')
image

2)RFM模型除了使用上面的均值算法,還可以使用評(píng)分制算法來實(shí)現(xiàn),評(píng)分一般采用5分制,評(píng)分規(guī)則也要根據(jù)具體的業(yè)務(wù)情況來決定,這里我們采用五分位數(shù)來制定評(píng)分規(guī)則,python實(shí)現(xiàn)如下:

rfm_score_df = rfmdf[['R','F',"M"]]
# 修改describe區(qū)間范圍,得到五分位數(shù)
rfm_score_df.describe(percentiles=[0.2,0.4,0.6,0.8])
image.png

計(jì)算R、F和M的評(píng)分

# 區(qū)間( ],所以第一個(gè)設(shè)置為最小值-1
section_list_R = [-1,9,24,43,103,362]
grade_R = pd.cut(rfm_score_df['R'],bins=section_list_R,labels=[5,4,3,2,1])
rfm_score_df['R_S'] = grade_R.values.astype(int)

# 區(qū)間( ],所以第一個(gè)設(shè)置為最小值-1
section_list_F = [0,4,7,13,19,48] 
grade_F = pd.cut(rfm_score_df['F'],bins=section_list_F,labels=[1,2,3,4,5])
rfm_score_df['F_S'] = grade_F.values.astype(int)

# 區(qū)間( ],所以第一個(gè)設(shè)置為最小值-1
section_list_M = [0,365,1196,2855,4938,23296]
grade_M = pd.cut(rfm_score_df['M'],bins=section_list_M,labels=[1,2,3,4,5])
# 上一步的cut方法返回值是category類型,不能用戶后續(xù)計(jì)算,這里要轉(zhuǎn)為數(shù)值類型
rfm_score_df['M_S'] = grade_M.values.astype(int) 
rfm_score_df
image.png

用戶的R、F和M分值與平均分比較

def rfm_func(x):
    level = x.apply(lambda x: "1" if x >= 0 else '0')
    level
    label = level.R_S + level.F_S + level.M_S
    d = {
        '111':'重要價(jià)值客戶',
        '011':'重要喚回客戶',
        '101':'重要深耕客戶',
        '001':'重要挽留客戶',
        '110':'潛力客戶',
        '010':'一般維持客戶',
        '100':'新客戶',
        '000':'流失客戶'
    }
    result = d[label]
    return result

rfm_score_df["label"] = rfm_score_df[["R_S","F_S","M_S"]].apply(lambda x:x-x.mean()).apply(rfm_func,axis=1)
rfm_score_df
image.png
result = rfm_score_df.groupby('label')["R"].count()
result = result.reset_index()
result.columns = ["label","count"]
result
# 繪制柱形圖
fig = plt.figure(figsize=(10,6))
order = ['重要價(jià)值客戶', '重要喚回客戶', '重要深耕客戶', '重要挽留客戶', '潛力客戶', '一般維持客戶', '新客戶', '流失客戶']
sns.barplot(x="count",y="label",data=result,orient='h',order=order)
plt.title('RFM用戶數(shù)')
image.png

從均值算法和評(píng)分制算法的結(jié)果可以看出,兩種方法存在一定差異,具體使用哪種算法要根據(jù)實(shí)際業(yè)務(wù)情況決定。比如評(píng)分制算法中的R打分可以根據(jù)7日留存,30日留存,90日留存等來制定。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容