精华内容
下载资源
问答
  • python数据分析入门必备,包括:数据分析思维书籍,python3数据分析实战视频,kaggle竞赛数据分析实战视频
  • Kaggle奋斗 Kaggle数据分析
  • Kaggle数据分析入门之--酒店预订需求

    千次阅读 多人点赞 2020-04-23 08:27:01
    1.需求分析 ...我们使用pandas来查看数据文件,数据文件在https://www.kaggle.com/jessemostipak/hotel-booking-demand,下载一个csv文件。 import pandas as pd data = pd.read_csv('C:\\Users\\Administ...

    1.需求分析

    是否可以根据之前取消的预订情况来预测酒店预订的可能性?

    2.数据信息查看和数据清洗

    我们使用pandas来查看数据文件,数据文件在https://www.kaggle.com/jessemostipak/hotel-booking-demand,下载一个csv文件。

    import pandas as pd
    data = pd.read_csv('C:\\Users\\Administrator\\Desktop\\kaggle\\hotel-booking-demand\\hotel_bookings.csv')
    data
    

    通过jupyter notebook查看数据信息
    在这里插入图片描述

    data.info()
    
    <class 'pandas.core.frame.DataFrame'>
    RangeIndex: 119390 entries, 0 to 119389
    Data columns (total 32 columns):
     #   Column                          Non-Null Count   Dtype  
    ---  ------                          --------------   -----  
     0   hotel                           119390 non-null  object 
     1   is_canceled                     119390 non-null  int64  
     2   lead_time                       119390 non-null  int64  
     3   arrival_date_year               119390 non-null  int64  
     4   arrival_date_month              119390 non-null  object 
     5   arrival_date_week_number        119390 non-null  int64  
     6   arrival_date_day_of_month       119390 non-null  int64  
     7   stays_in_weekend_nights         119390 non-null  int64  
     8   stays_in_week_nights            119390 non-null  int64  
     9   adults                          119390 non-null  int64  
     10  children                        119386 non-null  float64
     11  babies                          119390 non-null  int64  
     12  meal                            119390 non-null  object 
     13  country                         118902 non-null  object 
     14  market_segment                  119390 non-null  object 
     15  distribution_channel            119390 non-null  object 
     16  is_repeated_guest               119390 non-null  int64  
     17  previous_cancellations          119390 non-null  int64  
     18  previous_bookings_not_canceled  119390 non-null  int64  
     19  reserved_room_type              119390 non-null  object 
     20  assigned_room_type              119390 non-null  object 
     21  booking_changes                 119390 non-null  int64  
     22  deposit_type                    119390 non-null  object 
     23  agent                           103050 non-null  float64
     24  company                         6797 non-null    float64
     25  days_in_waiting_list            119390 non-null  int64  
     26  customer_type                   119390 non-null  object 
     27  adr                             119390 non-null  float64
     28  required_car_parking_spaces     119390 non-null  int64  
     29  total_of_special_requests       119390 non-null  int64  
     30  reservation_status              119390 non-null  object 
     31  reservation_status_date         119390 non-null  object 
    dtypes: float64(4), int64(16), object(12)
    memory usage: 29.1+ MB
    

    初步分析有32列数据,其中存在有缺失值,有contryagent等。
    接下来对缺失数据进行查看:

    data.isnull().sum()[data.isnull().sum()!=0]
    
    children         4
    country        488
    agent        16340
    company     112593
    dtype: int64
    

    其中有四项信息存在缺失值,company缺失较多,可以考虑删除,children和country、agent较少,可以考虑填充。

    处理方法:

    • 假设agent中缺失值代表未指定任何机构,即nan=0
    • country则直接使用其字段内众数填充
    • childred使用其字段内众数填充
    • company因缺失数值过大,且其信息较杂(单个值分布太多),所以直接删除

    首先删除company列:

    data_new = data.copy(deep = True)
    data_new.drop("company", axis=1, inplace=True)
    

    然后对children和country、agent进行填充。
    查看children和country、agent的信息

    data[['children','agent','country']]
    

    在这里插入图片描述
    数据插入:

    data_new["agent"].fillna(0, inplace=True)
    data_new["children"].fillna(data_new["children"].mode()[0], inplace=True)
    data_new["country"].fillna(data_new["country"].mode()[0], inplace=True)
    

    再次查看信息:data_new.info()
    在这里插入图片描述

    这里还需要数据异常值的处理:为什么知道这个异常值呢,可以通过后面的计算错误得到这个东西。在后面计算人均价格的时候,如果总人数和为0的情况,则会有异常,所以需要处理异常值
    需要对此数据集中异常值为那些总人数(adults+children+babies)为0的记录,同时,因为先前已指名“meal”中“SC”和“Undefined”为同一类别,因此也需要处理一下。

    data_new["children"] = data_new["children"].astype(int)
    data_new["agent"] = data_new["agent"].astype(int)
    
    data_new["meal"].replace("Undefined", "SC", inplace=True)
    # 处理异常值
    # 将 变量 adults + children + babies == 0 的数据删除
    zero_guests = list(data_new["adults"] +
                      data_new["children"] +
                      data_new["babies"] == 0)
    # hb_new.info()
    data_new.drop(data_new.index[zero_guests], inplace=True)
    

    3.数据分析(数据可视化)

    因为是酒店的需求分析,那我们需要去寻找各个属性之间的关系,以及与结果之间(是否取消)的关系。
    我们首先看一下入住率和取消数。

    3.1入住率和取消数

    fig = plt.figure()
    fig.set(alpha=0.2)  # 设定图表颜色alpha参数
    data_new.is_canceled.value_counts().plot(kind='bar')# 柱状图 
    plt.title(u"取消预订情况 (1为取消预订)") # 标题
    plt.ylabel(u"酒店数")  
    cancel = data_new.is_canceled.value_counts()
    Sum=cancel.sum()
    count=0
    for i in cancel:   # 显示百分比
        plt.text(count,i+0.5, str('{:.2f}'.format(cancel[count]/Sum *100)) +'%', \
        ha='center') #位置,高度,内容,居中
        count= count + 1
    plt.show()
    

    在这里插入图片描述
    可以看出取消率为37%,入住率为63%左右。
    这只是一个基本分析,然后查看不同酒店的入住率与取消率。

    rh_iscancel_count = data_new[data_new["hotel"]=="Resort Hotel"].groupby(["is_canceled"])["is_canceled"].count()
    ch_iscancel_count = data_new[data_new["hotel"]=="City Hotel"].groupby(["is_canceled"])["is_canceled"].count()
    
    rh_cancel_data = pd.DataFrame({"hotel": "度假酒店",
                                  "is_canceled": rh_iscancel_count.index,
                                  "count": rh_iscancel_count.values})
    
    ch_cancel_data = pd.DataFrame({"hotel": "城市酒店",
                                  "is_canceled": ch_iscancel_count.index,
                                  "count": ch_iscancel_count.values})
    iscancel_data = pd.concat([rh_cancel_data, ch_cancel_data], ignore_index=True)
    plt.figure(figsize=(12, 8))
    
    cmap = plt.get_cmap("tab20c")
    outer_colors = cmap(np.arange(2)*4)
    inner_colors = cmap(np.array([1, 2, 5, 6]))
    
    w , t, at = plt.pie(data_new["is_canceled"].value_counts(), autopct="%.2f%%",textprops={"fontsize":18},
                       radius=0.7, wedgeprops=dict(width=0.3), pctdistance=0.75, colors=outer_colors)
    plt.legend(w, ["未取消预定", "取消预定"], loc="upper right", bbox_to_anchor=(0, 0, 0.2, 1), fontsize=12)
    
    
    val_array = np.array((iscancel_data.loc[(iscancel_data.hotel=="城市酒店")&(iscancel_data.is_canceled==0), "count"].values,
           iscancel_data.loc[(iscancel_data.hotel=="度假酒店")&(iscancel_data.is_canceled==0), "count"].values,
           iscancel_data.loc[(iscancel_data.hotel=="城市酒店")&(iscancel_data.is_canceled==1), "count"].values,
           iscancel_data.loc[(iscancel_data.hotel=="度假酒店")&(iscancel_data.is_canceled==1), "count"].values))
    
    w2, t2, at2 = plt.pie(val_array, autopct="%.2f%%",textprops={"fontsize":16}, radius=1,
           wedgeprops=dict(width=0.3), pctdistance=0.85, colors=inner_colors)
    plt.title("不同酒店预定情况", fontsize=16)
    
    bbox_props = dict(boxstyle="square,pad=0.3", fc="w", ec="k", lw=0.72)
    kw = dict(arrowprops=dict(arrowstyle="-", color="k"), bbox=bbox_props, zorder=3, va="center")
    for i, p in enumerate(w2):
    #     print(i, p, sep="---")
        text = ["城市酒店", "度假酒店", "城市酒店", "度假酒店"]
        ang = (p.theta2 - p.theta1) / 2. + p.theta1
        y = np.sin(np.deg2rad(ang))
        x = np.cos(np.deg2rad(ang))
        horizontalalignment = {-1: "right", 1: "left"}[int(np.sign(x))]
        connectionstyle = "angle, angleA=0, angleB={}".format(ang)
        kw["arrowprops"].update({"connectionstyle": connectionstyle})
        plt.annotate(text[i], xy=(x, y), xytext=(1.15*np.sign(x), 1.2*y),
                    horizontalalignment=horizontalalignment, **kw, fontsize=18)
    

    在这里插入图片描述

    3.2 酒店人均价格

    接下来可以从人均价格入手,看看两家酒店的运营情况。

    因为babies年龄过小,所以人均价格中未将babies带入计算。
    人 均 价 格 / 晚 = a d r a d u l t s + c h i l d r e n 人均价格/晚 = \frac{adr}{adults+children} /=adults+childrenadr

    此时来查看不同月份下的平均酒店价格,代码如下:

    data_new["adr_pp"] = data_new["adr"] / (data_new["adults"] + data_new["children"])
    full_data_guests = data_new.loc[data_new["is_canceled"] == 0] # only actual gusts
    room_prices = full_data_guests[["hotel", "reserved_room_type", "adr_pp"]].sort_values("reserved_room_type")
    room_price_monthly = full_data_guests[["hotel", "arrival_date_month", "adr_pp"]].sort_values("arrival_date_month")
    
    ordered_months = ["January", "February", "March", "April", "May", "June", "July", "August",
                     "September", "October", "November", "December"]
    month_che = ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月", ]
    
    for en, che in zip(ordered_months, month_che):
        room_price_monthly["arrival_date_month"].replace(en, che, inplace=True)
    room_price_monthly["arrival_date_month"] = pd.Categorical(room_price_monthly["arrival_date_month"],
                                                             categories=month_che, ordered=True)
    room_price_monthly["hotel"].replace("City Hotel", "城市酒店", inplace=True)
    room_price_monthly["hotel"].replace("Resort Hotel", "度假酒店", inplace=True)
    room_price_monthly.head(15)
    
    plt.figure(figsize=(12, 8))
    sns.lineplot(x="arrival_date_month", y="adr_pp", hue="hotel", data=room_price_monthly,
                hue_order=["城市酒店", "度假酒店"], ci="sd", size="hotel", sizes=(2.5, 2.5))
    plt.title("不同月份人均居住价格/晚", fontsize=16)
    plt.xlabel("月份", fontsize=16)
    plt.ylabel("人均居住价格/晚", fontsize=16)
    # plt.savefig("F:/文章/不同月份人均居住价格每晚")
    

    在这里插入图片描述
    这里可以看到处理异常值的必要性,否则会出现错误。

    3.3查看月度人流量

    # 查看月度人流量
    rh_bookings_monthly = full_data_guests[full_data_guests.hotel=="Resort Hotel"].groupby("arrival_date_month")["hotel"].count()
    ch_bookings_monthly = full_data_guests[full_data_guests.hotel=="City Hotel"].groupby("arrival_date_month")["hotel"].count()
    
    rh_bookings_data = pd.DataFrame({"arrival_date_month": list(rh_bookings_monthly.index),
                                    "hotel": "度假酒店",
                                    "guests": list(rh_bookings_monthly.values)})
    ch_bookings_data = pd.DataFrame({"arrival_date_month": list(ch_bookings_monthly.index),
                                    "hotel": "城市酒店",
                                    "guests": list(ch_bookings_monthly.values)})
    full_booking_monthly_data = pd.concat([rh_bookings_data, ch_bookings_data], ignore_index=True)
    
    ordered_months = ["January", "February", "March", "April", "May", "June", "July", "August",
                     "September", "October", "November", "December"]
    month_che = ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"]
    
    for en, che in zip(ordered_months, month_che):
        full_booking_monthly_data["arrival_date_month"].replace(en, che, inplace=True)
    
    full_booking_monthly_data["arrival_date_month"] = pd.Categorical(full_booking_monthly_data["arrival_date_month"],
                                                          categories=month_che, ordered=True)
    
    full_booking_monthly_data.loc[(full_booking_monthly_data["arrival_date_month"]=="七月")|\
                                 (full_booking_monthly_data["arrival_date_month"]=="八月"), "guests"] /= 3
    full_booking_monthly_data.loc[~((full_booking_monthly_data["arrival_date_month"]=="七月")|\
                                 (full_booking_monthly_data["arrival_date_month"]=="八月")), "guests"] /= 2
    plt.figure(figsize=(12, 8))
    sns.lineplot(x="arrival_date_month",
                y="guests",
                hue="hotel", hue_order=["城市酒店", "度假酒店"],
                data=full_booking_monthly_data, size="hotel", sizes=(2.5, 2.5))
    plt.title("不同月份平均旅客数", fontsize=16)
    plt.xlabel("月份", fontsize=16)
    plt.ylabel("旅客数", fontsize=16)
    # plt.savefig("F:/文章/不同月份平均旅客数")
    

    在这里插入图片描述

    结合上述两幅图可以了解到:

    在春秋两季城市酒店价格虽然高,但其入住人数一点也没降低,反而处于旺季;
    而度假酒店在6-9月份游客数本身就偏低,可这个时间段内的价格却在持续上升,远高于其他月份;
    不论是城市酒店还是度假酒店,冬季的生意都不是特别好。

    3.4餐食选择

    meal_data = data_new[["hotel", "is_canceled", "meal"]]
    # meal_data
    
    plt.figure(figsize=(12, 8))
    plt.subplot(121)
    plt.pie(meal_data.loc[meal_data["is_canceled"]==0, "meal"].value_counts(), 
            labels=meal_data.loc[meal_data["is_canceled"]==0, "meal"].value_counts().index, 
           autopct="%.2f%%")
    plt.title("未取消预订旅客餐食选择", fontsize=16)
    plt.legend(loc="upper right")
    
    plt.subplot(122)
    plt.pie(meal_data.loc[meal_data["is_canceled"]==1, "meal"].value_counts(), 
            labels=meal_data.loc[meal_data["is_canceled"]==1, "meal"].value_counts().index, 
           autopct="%.2f%%")
    plt.title("取消预订旅客餐食选择", fontsize=16)
    plt.legend(loc="upper right")
    

    在这里插入图片描述

    很明显,取消预订旅客和未取消预订旅客有基本相同的餐食选择,所以此特征在后面可以删掉。

    3.5居住时长

    那么在不同酒店居住的旅客通常会选择住几天呢?我们可以使用柱形图来看一下其时长的不同分布;

    首先计算出总时长:总时长=周末停留夜晚数+工作日停留夜晚数

    full_data_guests["total_nights"] = full_data_guests["stays_in_weekend_nights"] + full_data_guests["stays_in_week_nights"]
    
    # 新建字段:total_nights_bin——居住时长区间
    full_data_guests["total_nights_bin"] = "住1晚"
    full_data_guests.loc[(full_data_guests["total_nights"]>1)&(full_data_guests["total_nights"]<=5), "total_nights_bin"] = "2-5晚"
    full_data_guests.loc[(full_data_guests["total_nights"]>5)&(full_data_guests["total_nights"]<=10), "total_nights_bin"] = "6-10晚"
    full_data_guests.loc[(full_data_guests["total_nights"]>10), "total_nights_bin"] = "11晚以上"
    
    ch_nights_count = full_data_guests["total_nights_bin"][full_data_guests.hotel=="City Hotel"].value_counts()
    rh_nights_count = full_data_guests["total_nights_bin"][full_data_guests.hotel=="Resort Hotel"].value_counts()
    
    ch_nights_index = full_data_guests["total_nights_bin"][full_data_guests.hotel=="City Hotel"].value_counts().index
    rh_nights_index = full_data_guests["total_nights_bin"][full_data_guests.hotel=="Resort Hotel"].value_counts().index
    
    ch_nights_data = pd.DataFrame({"hotel": "城市酒店",
                                   "nights": ch_nights_index,
                                  "guests": ch_nights_count})
    rh_nights_data = pd.DataFrame({"hotel": "度假酒店",
                                   "nights": rh_nights_index,
                                  "guests": rh_nights_count})
    # 绘图数据
    nights_data = pd.concat([ch_nights_data, rh_nights_data], ignore_index=True)
    order = ["住1晚", "2-5晚", "6-10晚", "11晚以上"]
    nights_data["nights"] = pd.Categorical(nights_data["nights"], categories=order, ordered=True)
    
    plt.figure(figsize=(12, 8))
    sns.barplot(x="nights", y="guests", hue="hotel", data=nights_data)
    plt.title("旅客居住时长分布", fontsize=16)
    plt.xlabel("居住时长", fontsize=16)
    plt.ylabel("旅客数", fontsize=16)
    
    plt.legend()
    

    在这里插入图片描述

    3.6提前预定时长

    提前预定期对旅客是否选择取消预订也有很大影响,因为lead_time字段中的值分布多且散乱,所以使用散点图比较合适,同时还可以绘制一条回归线。

    lead_cancel_data = pd.DataFrame(data_new.groupby("lead_time")["is_canceled"].describe())
    # lead_cancel_data
    # 因为lead_time中值范围大且数量分布不匀,所以选取lead_time>10次的数据(<10的数据不具代表性)
    lead_cancel_data_10 = lead_cancel_data[lead_cancel_data["count"]>10]
    
    y = list(round(lead_cancel_data_10["mean"], 4) * 100)
    
    plt.figure(figsize=(12, 8))
    sns.regplot(x=list(lead_cancel_data_10.index),
               y=y)
    plt.title("提前预定时长对取消的影响", fontsize=16)
    plt.xlabel("提前预定时长", fontsize=16)
    plt.ylabel("取消数 [%]", fontsize=16)
    # plt.savefig("F:/文章/提前预定时长对取消的影响")
    

    在这里插入图片描述

    可以明显看到:不同的提前预定时长确定对旅客是否取消预定有一定影响;

    通常,越早预订,越容易取消酒店房间预定。

    4.进行各属性的分辨,哪个更重要

    可以利用data.corr()进行相关性的判断 #相关系数矩阵,即给出了任意两个变量之间的相关系数

    cancel_corr = data_new.corr()["is_canceled"]
    cancel_corr.abs().sort_values(ascending=False)[1:]
    
    lead_time                         0.292876
    total_of_special_requests         0.234877
    required_car_parking_spaces       0.195701
    booking_changes                   0.144832
    previous_cancellations            0.110139
    is_repeated_guest                 0.083745
    adults                            0.058182
    previous_bookings_not_canceled    0.057365
    days_in_waiting_list              0.054301
    agent                             0.046770
    adr                               0.046492
    babies                            0.032569
    stays_in_week_nights              0.025542
    adr_pp                            0.017808
    arrival_date_year                 0.016622
    arrival_date_week_number          0.008315
    arrival_date_day_of_month         0.005948
    children                          0.004851
    stays_in_weekend_nights           0.001323
    

    从上表中可以看到lead_time、total_of_special_requests 、required_car_parking_spaces、booking_changes 、previous_cancellations这五个特征影响最大。
    这里需要对特征进行判断,哪些不必要,哪些是必要的。还有哪些特征我们没有包含,因为部分特征并不是以数值方式显示,所以在进行相关计算时,不能计算,这时我们也要考虑这些特征,比如"reservation_status"(预订状态),这个我们应当考虑。
    来查看一下这个特征:

    data_new.groupby("is_canceled")["reservation_status"].value_counts()
    
    is_canceled  reservation_status
    0            Check-Out             75011
    1            Canceled              42993
                 No-Show                1206
    

    可以看到退房和取消的数目,还有没有展示的少数。

    5.特征模型训练

    好了,那我们接下来就用以下特征作为模型数据:
    当然,你可以选择其他的特征,或者少一部分特征,这个是可以的,因为模型的最优都要经过调试和试验,没有第一次就最好的。按照吴恩达老师,首先弄一个base model,看一下效果如何。
    先用python的各个机器学习算法进行试验一下准确率。
    比如决策树、随机森林、逻辑回归、XGBC分类器等
    首先导入需要的机器学习的包:

    # for ML:
    from sklearn.model_selection import train_test_split, KFold, cross_validate, cross_val_score
    from sklearn.pipeline import Pipeline
    from sklearn.compose import ColumnTransformer
    from sklearn.preprocessing import LabelEncoder, OneHotEncoder
    from sklearn.impute import SimpleImputer
    from sklearn.ensemble import RandomForestClassifier  # 随机森林
    from xgboost import XGBClassifier 
    from sklearn.linear_model import LogisticRegression
    from sklearn.tree import DecisionTreeClassifier
    from sklearn.metrics import accuracy_score
    import eli5 # Feature importance evaluation
    
    #手动选择要包括的列
    #为了使模型更通用并防止泄漏,排除了一些列
    #(到达日期、年份、指定房间类型、预订更改、预订状态、国家/地区,
    #等待日列表)
    #包括国家将提高准确性,但它也可能使模型不那么通用
    num_features = ["lead_time","arrival_date_week_number","arrival_date_day_of_month",
                    "stays_in_weekend_nights","stays_in_week_nights","adults","children",
                    "babies","is_repeated_guest", "previous_cancellations",
                    "previous_bookings_not_canceled","agent",
                    "required_car_parking_spaces", "total_of_special_requests", "adr"]
    
    cat_features = ["hotel","arrival_date_month","meal","market_segment",
                    "distribution_channel","reserved_room_type","deposit_type","customer_type"]
    #分离特征和预测值
    features = num_features + cat_features
    X = data_new.drop(["is_canceled"], axis=1)[features]
    y = data_new["is_canceled"]
    
    #预处理数值特征:
    #对于大多数num cols,除了日期,0是最符合逻辑的填充值
    #这里没有日期遗漏。
    num_transformer = SimpleImputer(strategy="constant")
    
    # 分类特征的预处理:
    cat_transformer = Pipeline(steps=[
        ("imputer", SimpleImputer(strategy="constant", fill_value="Unknown")),
        ("onehot", OneHotEncoder(handle_unknown='ignore'))])
    
    # 数值和分类特征的束预处理:
    preprocessor = ColumnTransformer(transformers=[("num", num_transformer, num_features),
                                                   ("cat", cat_transformer, cat_features)])
    
    # 定义要测试的模型:
    base_models = [("DT_model", DecisionTreeClassifier(random_state=42)),
                   ("RF_model", RandomForestClassifier(random_state=42,n_jobs=-1)),
                   ("LR_model", LogisticRegression(random_state=42,n_jobs=-1)),
                   ("XGB_model", XGBClassifier(random_state=42, n_jobs=-1))]
    
    #将数据分成“kfold”部分进行交叉验证,
    #使用shuffle确保数据的随机分布:
    kfolds = 4 # 4 = 75% train, 25% validation
    split = KFold(n_splits=kfolds, shuffle=True, random_state=42)
    
    #对每个模型进行预处理、拟合、预测和评分:
    for name, model in base_models:
        #将数据和模型的预处理打包到管道中:
        model_steps = Pipeline(steps=[('preprocessor', preprocessor),
                                  ('model', model)])
        
        #获取每个模型的交叉验证分数:
        cv_results = cross_val_score(model_steps, 
                                     X, y, 
                                     cv=split,
                                     scoring="accuracy",
                                     n_jobs=-1)
        # output:
        min_score = round(min(cv_results), 4)
        max_score = round(max(cv_results), 4)
        mean_score = round(np.mean(cv_results), 4)
        std_dev = round(np.std(cv_results), 4)
        print(f"{name} cross validation accuarcy score: {mean_score} +/- {std_dev} (std) min: {min_score}, max: {max_score}")
    

    结果:

    DT_model cross validation accuarcy score: 0.8255 +/- 0.0012 (std) min: 0.8241, max: 0.827
    RF_model cross validation accuarcy score: 0.8663 +/- 0.0005 (std) min: 0.8653, max: 0.8667
    LR_model cross validation accuarcy score: 0.7956 +/- 0.0017 (std) min: 0.7941, max: 0.7983
    XGB_model cross validation accuarcy score: 0.8465 +/- 0.0008 (std) min: 0.8452, max: 0.8474
    

    可以看到采用随机森林RF_model的效果最好。
    你可以继续对其进行一些超参数的优化。

    # Enhanced RF model with the best parameters I found:
    rf_model_enh = RandomForestClassifier(n_estimators=160,
                                   max_features=0.4,
                                   min_samples_split=2,
                                   n_jobs=-1,
                                   random_state=0)
    
    split = KFold(n_splits=kfolds, shuffle=True, random_state=42)
    model_pipe = Pipeline(steps=[('preprocessor', preprocessor),
                                  ('model', rf_model_enh)])
    cv_results = cross_val_score(model_pipe, 
                                     X, y, 
                                     cv=split,
                                     scoring="accuracy",
                                     n_jobs=-1)
    # output:
    min_score = round(min(cv_results), 4)
    max_score = round(max(cv_results), 4)
    mean_score = round(np.mean(cv_results), 4)
    std_dev = round(np.std(cv_results), 4)
    print(f"Enhanced RF model cross validation accuarcy score: {mean_score} +/- {std_dev} (std) min: {min_score}, max: {max_score}")
    

    Enhanced RF model cross validation accuarcy score: 0.8677 +/- 0.002 (std) min: 0.8644, max: 0.8694
    可以看到精度有适当提高。

    6.评价特征的重要性

    #拟合模型,以便可以访问值:
    model_pipe.fit(X,y)
    
    #需要所有(编码)功能的名称。
    #从一个热编码中获取列的名称:
    onehot_columns = list(model_pipe.named_steps['preprocessor'].
                          named_transformers_['cat'].
                          named_steps['onehot'].
                          get_feature_names(input_features=cat_features))
    
    #为完整列表添加num_功能。
    #顺序必须与X的定义相同,其中num_特征是第一个:
    feat_imp_list = num_features + onehot_columns
    
    #显示10个最重要的功能,提供功能名称:
    feat_imp_df = eli5.formatters.as_dataframe.explain_weights_df(
        model_pipe.named_steps['model'],
        feature_names=feat_imp_list)
    feat_imp_df.head(10)
    

    在这里插入图片描述
    查看三个最重要的功能:

    • lead_time
    • deposit_type
    • adr

    lead_time的功能

    # group data for lead_time:
    lead_cancel_data = data_new.groupby("lead_time")["is_canceled"].describe()
    # use only lead_times wih more than 10 bookings for graph:
    lead_cancel_data_10 = lead_cancel_data.loc[lead_cancel_data["count"] >= 10]
    
    #show figure:
    plt.figure(figsize=(12, 8))
    sns.regplot(x=lead_cancel_data_10.index, y=lead_cancel_data_10["mean"].values * 100)
    plt.title("Effect of lead time on cancelation", fontsize=16)
    plt.xlabel("Lead time", fontsize=16)
    plt.ylabel("Cancelations [%]", fontsize=16)
    # plt.xlim(0,365)
    plt.show()
    

    在这里插入图片描述

    在到达日期前几天进行的预订很少被取消,而提前一年以上的预订则经常被取消。

    存款类型:

    # group data for deposit_type:
    deposit_cancel_data = data_new.groupby("deposit_type")["is_canceled"].describe()
    
    #show figure:
    plt.figure(figsize=(12, 8))
    sns.barplot(x=deposit_cancel_data.index, y=deposit_cancel_data["mean"] * 100)
    plt.title("Effect of deposit_type on cancelation", fontsize=16)
    plt.xlabel("Deposit type", fontsize=16)
    plt.ylabel("Cancelations [%]", fontsize=16)
    plt.show()
    

    在这里插入图片描述

    正如Susmit Vengurlekar在数据集的讨论部分已经指出的那样,存款类型“不退款”和“取消”列以一种反直觉的方式关联起来。

    超过99%的预付款的人取消了。这就提出了一个问题:数据(或描述)是否有问题。

    还有什么是不退款的存款?

    以下是按存款类型分组的所有数据平均值表:

    deposit_mean_data = data_new.groupby("deposit_type").mean()
    deposit_mean_data
    

    在这里插入图片描述
    将不退款和不存款的平均值进行比较,结果如下:

    • 不退还押金的特点是提前期延长2倍以上
    • 重复的客人是~1/10
    • 以前的取消次数是以前的10倍
    • 以前的预订没有取消是1/15
    • 所需的停车位几乎为零
    • 特殊要求非常罕见

    根据这些调查结果,似乎特别是那些没有预先参观过其中一家酒店的人,预订、付款并多次取消。。。真奇怪!
    为了解决这个问题,接下来制作一个没有这个功能下面的模型。
    ADR
    在这里插入图片描述

    ADR越低取消的就越集中

    RF model without deposit type

    cat_features_non_dep = ["hotel","arrival_date_month","meal","market_segment",
                    "distribution_channel","reserved_room_type","customer_type"]
    
    features_non_dep = num_features + cat_features_non_dep
    X_non_dep = data_new.drop(["is_canceled"], axis=1)[features_non_dep]
    
    
    # Bundle preprocessing for numerical and categorical features:
    preprocessor_non_dep = ColumnTransformer(transformers=[("num", num_transformer, num_features),
                                                   ("cat", cat_transformer, cat_features_non_dep)])
    
    # Define dataset:
    X_non_dep = data_new.drop(["is_canceled"], axis=1)[features_non_dep]
    # Define model
    rf_model_non_dep = RandomForestClassifier(random_state=42) # basic model for this purpose
    
    kfolds=4
    split = KFold(n_splits=kfolds, shuffle=True, random_state=42)
    model_pipe = Pipeline(steps=[('preprocessor', preprocessor_non_dep),
                                  ('model', rf_model_non_dep)])
    cv_results = cross_val_score(model_pipe, 
                                     X_non_dep, y, 
                                     cv=split,
                                     scoring="accuracy",
                                     n_jobs=-1)
    # output:
    min_score = round(min(cv_results), 4)
    max_score = round(max(cv_results), 4)
    mean_score = round(np.mean(cv_results), 4)
    std_dev = round(np.std(cv_results), 4)
    print(f"RF model without deposit_type feature cross validation accuarcy score: {mean_score} +/- {std_dev} (std) min: {min_score}, max: {max_score}")
    

    结果:RF model without deposit_type feature cross validation accuarcy score: 0.8657 +/- 0.0003 (std) min: 0.8653, max: 0.8662

    我们看到结果和之前的相差并不远,还是很有意义。
    我们可以在新模型上增加前置时间、adr、特殊请求的总数量等来弥补这一点。

    这个分析暂告一段落,当然后期可以针对模型进行优化和完善。

    代码参考:https://www.kaggle.com/marcuswingen/eda-of-bookings-and-ml-to-predict-cancelations
    不能上去的也可以在我的云盘下载代码:链接经常失效,可以私发我要代码
    链接:https://pan.baidu.com/s/1gIl0ICKPVoB7yX_0aemCCw
    提取码:ffjm

    展开全文
  • 学习完《利用python进行数据分析》之后,学习了Numpy,pandas,matplotlib几个包的使用,于是着手准备在kaggle上找数据集进行练习。 在kaggle中找到了人力资源分析项目,看到不少人拿这个项目练手,本文将会分析...

    学习完《利用python进行数据分析》之后,学习了Numpy,pandas,matplotlib几个包的使用,于是着手准备在kaggle上找数据集进行练习。

    在kaggle中找到了人力资源分析项目,看到不少人拿这个项目练手,本文将会分析为何公司的好员工过早离职?

    我要做的是,哪些因素对员工的离职产生了大的影响。

    数据来源:Human Resources Analytics | Kaggle

    1、导入数据

    ##首先导入整理数据和数据可视化的包

    import pandas as pd
    import numpy as np
    import seaborn as sns
    import matplotlib.pyplot as plt
    %matplotlib inline

    ##导入数据到python中

    df=pd.read_csv('I:\HR_comma_sep.csv')

    2、检查和理解数据

    通常,清理数据需要大量的工作,并且可能是一个非常繁琐的过程。这个数据集从是Kaggle上下载的数据,相对而言很干净,不含缺失值。但是,我仍然需要检查数据集,以确保所有其他内容都是可读的,并且观察值与特征名称适当地匹配。

    ##检查是否有缺失值

    df.isnull().any()
    satisfaction_level       False
    last_evaluation          False
    number_project           False
    average_montly_hours     False
    time_spend_company       False
    Work_accident            False
    left                     False
    promotion_last_5years    False
    sales                    False
    salary                   False
    dtype: bool

    ##适当改名,方便选取列

    df = df.rename(columns={'satisfaction_level': 'satisfaction_level', 
                            'last_evaluation': 'last_evaluation',
                            'number_project': 'number_project',
                            'average_montly_hours': 'average_montly_hours',
                            'time_spend_company': 'time_spend_company',
                            'Work_accident': 'Work_accident',
                            'promotion_last_5years': 'promotion',
                            'sales' : 'department',
                            'left' : 'left'
                            })

    ##由于“部门”和“薪金”的功能是明确的,我将把它转换为数值,以便更好地分析。

    ##分别查看department列和salary列唯一值有多少个
    df1=pd.Series(df['department']).unique()
    df2=pd.Series(df['salary']).unique()

    ##把两列的值转化为数值

    df['department'].replace(list(pd.Series(df['department']).unique()),np.arange(10),inplace=True)
    df['salary'].replace(list(pd.Series(df['salary']).unique()),[0,1,2],inplace=True)

    ##把left列移到表的前面,方便分析

    front=df['left']
    df.drop(labels='left',axis=1,inplace=True)
    df.insert(0,'left',front)
    df.head()

    ##查看数据形状和结构

    df.shape
    (14999, 10)
    df.dtypes
    left                      int64
    satisfaction_level      float64
    last_evaluation         float64
    number_project            int64
    average_montly_hours      int64
    time_spend_company        int64
    Work_accident             int64
    promotion                 int64
    department                int64
    salary                    int64
    dtype: object

    整理好后,首先对数据变量进行说明:

    • left:是否离职
    • satisfaction_level:满意度
    • last_evaluation:绩效评估
    • number_project:完成项目数
    • average_montly_hours:平均每月工作时间
    • time_spend_company:为公司服务的年限
    • work_accident:是否有工作事故
    • promotion:过去5 年是否有升职
    • salary:薪资水平

    ##看一下整体数据,大概有76%的人留下了,24%的人离开了

    left_rate=df.left.value_counts()/14999
    0    0.761917
    1    0.238083
    Name: left, dtype: float64

    3、对数据进行探索分析

    1)、初步分析

    ##对left列进行聚合运算,描述性分析

    left_summary=df.groupby('left')
    left_summary.mean()
    format=lambda x: '%.2f'%x
    df.describe().applymap(format)

    2)、相关性分析

    (+)number_project&average_montly_hours&last_evaluation

    (-)left&satisfaction_level&salary

    sns.heatmap(corr,xticklabels=corr.columns.values,yticklabels=corr.columns.values);
    sns.plt.title('Heatmap of Correlation Matrix')

    从热图上看,有大的正(+)相关性的有,完成项目数(number_project)和平均月度工作时间(average_montly_hours),它们分别和绩效评估有较大的正相关,这可能意味着花了更多时间和做了更多项目的员工得到了高度评价。但是,绩效评估与响应变量转换之间几乎没有相关关系,也就是说绩效评估的高度评价没有转换到薪资水平和升职上来,只是得到了好的评价而已,对于负(-)关系,离职率、满意度和薪水是高度相关的。我们假设员工在不太满意投入产出比的情况下往往会离开公司。

    3)、变量分析

    ##department  vs  left
    depart_left_table=pd.crosstab(index=df['department'],columns=df['left'])
    ##职位:'sales', 'accounting', 'hr', 'technical', 'support', 'management','IT', 'product_mng', 'marketing', 'RandD'
    depart_left_table.plot(kind='bar',figsize=(5,5),stacked=True)
    ##department  vs  salary
    depart_salary_table=pd.crosstab(index=df['department'],columns=df['salary'])
    depart_salary_table.plot(kind="bar",figsize=(5,5),stacked=True)
    ##salary  vs left
    salary_left_table=pd.crosstab(index=df['salary'],columns=df['left'])
    salary_left_table.plot(kind='bar',figsize=(5,5),stacked=True)
    ##promotion  vs  left
    promotion_left_table=pd.crosstab(index=df['promotion'],columns=df['left'])
    promotion_left_table.plot(kind='bar',figsize=(5,5),stacked=True)
    ##number_project  vs  left
    project_left_table=pd.crosstab(index=df['number_project'],columns=df['left'])
    project_left_table.plot(kind='bar',figsize=(5,5),stacked=True)
    df.loc[(df['left']==1),'number_project'].plot(kind='hist',normed=1,bins=15,stacked=False,alpha=1)
    ##time_spend_company  vs  left
    company_left_table=pd.crosstab(index=df['time_spend_company'],columns=df['left'])
    company_left_table.plot(kind='bar',figsize=(5,5),stacked=True)
    df.loc[(df['left']==1),'time_spend_company'].plot(kind='hist',normed=1,bins=10,stacked=False,alpha=1)
    ##average_montly_hours  vs  left
    hours_left_table=pd.crosstab(index=df['average_montly_hours'],columns=df['left'])
    fig=plt.figure(figsize=(10,5))
    letf=sns.kdeplot(df.loc[(df['left']==0),'average_montly_hours'],color='b',shade=True,label='no left')
    left=sns.kdeplot(df.loc[(df['left']==1),'average_montly_hours'],color='r',shade=True,label='left')
    ##last_evaluation  vs  left
    evaluation_left_table=pd.crosstab(index=df['last_evaluation'],columns=df['left'])
    fig=plt.figure(figsize=(10,5))
    left=sns.kdeplot(df.loc[(df['left']==0),'last_evaluation'],color='b',shade=True,label='no left')
    left=sns.kdeplot(df.loc[(df['left']==1),'last_evaluation'],color='r',shade=True,label='left')
    ##satisfaction_level  vs  left  
    satis_left_table=pd.crosstab(index=df['satisfaction_level'],columns=df['left'])
    fig=plt.figure(figsize=(10,5))
    left=sns.kdeplot(df.loc[(df['left']==0),'satisfaction_level'],color='b',shade=True,label='no left')
    left=sns.kdeplot(df.loc[(df['left']==1),'satisfaction_level'],color='r',shade=True,label='left')
    ##last_evaluation  vs  satisfaction_level
    df1=df[df['left']==1]
    fig, ax = plt.subplots(figsize=(10,10))
    pd.scatter_matrix(df1[['satisfaction_level','last_evaluation']],color='k',ax=ax)
    plt.savefig('scatter.png',dpi=1000,bbox_inches='tight')
    • department vs left

    职位分别是:'sales', 'accounting', 'hr', 'technical', 'support', 'management','IT', 'product_mng', 'marketing', 'RandD'

    公司职位基本对应业务支持,技术,和销售。大多数部门的离职率相似,也就管理层和研发低一些,但管理层离职率最低。这可能意味着地位较高的人倾向于不离开。

    • department vs salary

    可以看出管理层的薪资最高,离职率最低

    • salary vs left

    很形象的看出,离职的员工薪资几乎都在低到中等水平,很少有高新的员工离开公司。

    • promotion vs left

    在离职的员工中几乎都没有得到升职。

    • number_project vs left

    本图显示超过一半的员工只有2个项目就离开了公司,但同样有从4-7个项目统计的员工离开。我们可以猜测一下,也许这意味着,项目数量在2或更少的员工工作不够,或者没有被高度重视,从而离开了公司?6个项目以上员工会过度劳累,从而离开公司?3个项目的员工离职率最低。

    • time_spend_company vs left

    离职员工在离职前大部分在公司已经工作了3-6年,在公司工作7-10年的员工没有人离开。

    • average_montly_hours vs left

    是一个很明显的双峰分布,说明员工平均每月工作时间少的(低于150小时)和工作时间多的(高于250小时)的员工离职率最高。

    所以一般离开公司的员工要么工作时间少的,要么过度工作的。

    • last_evaluation vs left

    又是一个双峰分布,表现糟糕的和表现出色的出现了离职的两个峰值,根据前边的分析,效绩评估出色的员工,公司没有相应的转化到升职和薪资上。0.6-0.8之间有比较好的员工留存。

    • satisfaction_level vs left

    出现了三个峰值,满意度低于0.1的员工基本离职,满意度在0.3-0.5之间离开的员工又到达一个峰值,满意度在0.8左右时,又出现了一个峰值,这些是满意度较高的员工,这些员工可能找到了更好的工作机会,离职不是对公司不满,这些员工对公司是有比较高的满意度的。

    • last_evaluation vs satisfaction_level

    在绩效评估与满意度的散射矩阵中,可以看到有三个不同的集群。

    集群1:满意度低于0.2,绩效评估大于0.75,这可以很好地表明离开公司的员工都是好员工,但对自己的工作感到不满意,当你受到高度评价的时候,这个集群应该代表着“过度劳累”的员工。

    集群2:满意度在0.35~0.45之间,绩效评估在0.58以下,这可以被看作是雇员受到了不太好的评价,这可能意味着这些员工表现不好,所以员工自己的满意度也不好,这个集群代表着表现不佳的员工。

    集群3:满意度在0.7~1之间,评价大于0.8,这可能意味着这个集群的员工是最理想的,他们热爱他们的工作,公司对他们的表现评价很高,这个类别的员工离开可能是因为他们找到了另一个工作机会。

    4、总结

    员工离职概述:

    离职员工工作时间大部分是~6hours /天(工作)和~10小时/天(劳累);

    大部分离职员工薪资都在low~medium这一档,薪资水平低;

    离职员工,几乎都没有得到升职;

    大多数离职员工的评价分数在0.6以下和0.8以上;

    离职员工大多数有2个项目,但同样有4-7个项目的员工离开,3个项目的员工离职率最低;

    完成项目数,每月平均工作时间,绩效评估有正相关关系。意味着你工作越多,得到的评价就越高;

    离职率、满意度与薪酬呈负相关关系。这意味着较低的满意度和工资产生了较高的离职率;

    公司需要考虑的问题:

    1、失去优秀员工会让公司产生多大损失?招新人和优秀老员工之间的成本与变现孰轻孰重?

    2、什么原因产生了较低的满意度?

    3、为什么离开的员工平均比没有离开的员工得到更高的评价,甚至是项目数量的增加?低评价的员工不应该更倾向于离开公司吗?

    优秀员工看中的是良好的待遇,和更好的职业发展,这些因素都直接影响员工的主观感受,公司给予了员工高的评价,但没有相应转化到薪资和升职的变量中,即使一部分离职的优秀员工给予了公司不错的满意度,但依然不能阻挡他们会追寻更好的工作机会。

    展开全文
  • kaggle 电商数据分析

    千次阅读 2020-06-17 17:01:46
    kaggle电商零售数据分析案例项目背景数据清洗退货情况分析客户RFM分析 项目背景 这个数据集是kaggle上面的一个电商数据集,其中包含2010年12月12日至2011年12月9日之间在英国注册的非商店在线零售的所有交易。该公司...

    kaggle电商零售数据分析案例

    项目背景

    这个数据集是kaggle上面的一个电商数据集,其中包含2010年12月12日至2011年12月9日之间在英国注册的非商店在线零售的所有交易。该公司主要销售各种独特的礼品之类,其中许多客户该公司是批发商。因此本次数据分析将对客户进行分析,并对客户进行相关分层处理。

    数据清洗

    import pandas as pd
    import numpy as np
    import matplotlib.pyplot as plt
    import os
    import warnings
    import missingno as msno
    warnings.filterwarnings('ignore')
    
    data = pd.read_csv('data.csv',encoding = 'utf-8', dtype = {'CustomerID' : str})
    data.head()
    

    看到这个是数据的前五行
    这就是data数据集的前五行。
    接下来观看数据概况

    data.describe()
    

    在这里插入图片描述
    我们看到这里面Description和CustomerID字段都有相应的缺失,因此我们选择将这里面的数据进行处理。

    首先我们选择将Description字段里面缺失的行删除,另外我们将CustinerID中确实字段填补为U。

    data.dropna(axis = 0, subset = ['Description'], inplace = True)
    data.CustomerID = data.CustomerID.fillna('U')
    

    做好以上处理后我们看每个客户的每个订单的购买商品数量

    temp = data.groupby(by=['CustomerID', 'InvoiceNo'], as_index=False)['StockCode'].count()
    temp.rename(columns = {'StockCode': 'product numbers'})
    

    在这里插入图片描述

    退货情况分析

    数据集标签InvioceNo里有一些编号包含’C’,这代表这些订单时退货订单,因此我们需要对这些退货订单进行处理。我们写一个函数,找出退货订单的原订单。这里面会有如下两种情况:

    1. 退货订单没有对应的原订单
    2. 退货订单存在一个或一个以上的原订单。
      因此我们创建一个quantity_canceled的标签,用来记录每一个订单退货量,方便我们之后计算相应的单笔订单真实消费额。
    data_cleaned = data.copy(deep = True)
    data_cleaned['quantity_canceled'] = 0
    unpaired_invoice = []
    paired_invoice = []
    for index, col in data.iterrows():
       if col['Quantity'] > 0 or col['Description'] == 'Discount':
           continue
       #提取出和取消订单的商品配对的原订单    
       df_test = data[(data['CustomerID'] == col['CustomerID'])
                     &(data['StockCode'] == col['StockCode'])
                     &(data['InvoiceDate'] < col['InvoiceDate'])
                     &(data['Quantity']) > 0]
       
       if (len(df_test) == 0):
           unpaired_invoice.append(index)
       
       elif (len(df_test) == 1):
           index_order = df_test.index[0]
           data_cleaned.loc[index_order, 'quantity_canceled'] = -col['Quantity']
           paired_invoice.append(index)
           
       elif (len(df_test) > 1):
           df_test.sort_index(axis=0 ,ascending=False, inplace = True)
           for ind, val in df_test.iterrows():
               if val['Quantity'] < -col['Quantity']:
                   continue
               data_cleaned.loc[ind, 'quantity_canceled'] = -col['Quantity']
               paired_invoice.append(index) 
               break 
    

    经过上述代码处理后,我们就得到了两个列表,一个列表叫unpaired_invoice,这个里面记录了没有配对的订单。还有一个列表叫做paired_invoice,表示配对的订单。
    我们查看列表长度,发现分别有7106和1994条。

    我们接下来先探索相应的退货情况。

    #分别提取数量为正的订单和数量为负的订单
    data_canceled = data[data['Quantity'] <= 0]
    data_all = data[(data['Quantity'] > 0)]
    
    data_canceled['month'] = pd.to_datetime(data_canceled['InvoiceDate']).dt.month
    data_all['month'] = pd.to_datetime(data_all['InvoiceDate']).dt.month
    
    data_canceled['year'] = pd.to_datetime(data_canceled['InvoiceDate']).dt.year
    data_all['year'] = pd.to_datetime(data_all['InvoiceDate']).dt.year
    
    data_canceled['price'] = - data_canceled['Quantity'] * data_canceled['UnitPrice']
    data_all['price'] = data_all['Quantity'] * data_all['UnitPrice']
    

    按照年月分别统计退货金额和退货率,tt这个表就是展示分月的退货总金额,pp代表所有订单数量为正的购物总金额,tt / pp 就代表退货率情况。

    tt = data_canceled.groupby(['year','month'])['price'].sum().unstack()
    pp = data_all.groupby(['year','month'])['price'].sum().unstack()
    
    plt.figure(figsize = (8,6))
    plt.title('各月份退货金额',fontsize = 16)
    plt.bar(tt.loc[2011].index, tt.loc[2011].values)
    
    plt.figure(figsize = (8,6))
    plt.title('各月份退货比例',fontsize = 16)
    plt.plot((tt / pp).loc[2011].index, (tt / pp).loc[2011].values,)
    

    在这里插入图片描述
    在这里插入图片描述
    我们看到在1月份和12月份相应的退货金额和退货比例都是上升的,这里的原因应该是在那个时候西方人可能会过圣诞节之类的节日,出现大批购物的次数变多,同时相应的退货次数也增多了,这和我们的双十一有没有点像啊,购买之后都是会有大批退货。

    客户RFM分析

    RFM分析主要包括三个维度,R代表客户最近一次购物消费的时间,F代表客户消费的频率, M代表客户消费总金额。为了进行这一分析,我们首先对数据进行以下处理,删除掉最近之前未配对的quantity为负的订单,由于配对订单已经包含在data_cleaned这一特征里面的quantity_canceled标签中,因此我们对配对的订单也予以删除

    data_cleaned.drop(unpaired_invoice, axis = 0, inplace = True)
    data_cleaned.drop(paired_invoice, axis = 0, inplace =True)
    

    为了计算RFM,我们计算total_price,这包含了客户的退货订单,同时取每个客户每笔订单的时间

    data_cleaned['TotalPrice'] = data_cleaned['UnitPrice'] * (data_cleaned['Quantity'] - data_cleaned['quantity_canceled'])
    data_cleaned.sort_values('CustomerID')[:5]
    
    invoice_price = data_cleaned.groupby(by = ['CustomerID','InvoiceNo'], as_index = False).agg({'TotalPrice': 'sum','InvoiceDate': 'first'})
    invoice_price.rename(columns = {'TotalPrice': 'basket_price'}, inplace =True)
    invoice_price = invoice_price.loc[invoice_price['basket_price'] > 0]
    invoice_price.head()
    

    在这里插入图片描述
    接下来我们就可以计算相应的R_value, F_value, M_value了。

    invoice_price['year'] = np.array([i.year for i in invoice_price['date']])
    invoice_price['month'] = np.array([i.month for i in invoice_price['date']])
    invoice_price['day'] = np.array([i.day for i in invoice_price['date']])
    
    R_value = invoice_price['date'].max() - invoice_price.groupby('CustomerID')['date'].max()
    F_value = invoice_price.groupby('CustomerID')['InvoiceNo'].count()
    M_value = invoice_price.groupby('CustomerID')['basket_price'].sum()
    
    #观察R_value的频数分布
    plt.title('R_value分布直方图')
    plt.hist(R_value.dt.days, bins = 20)
    
    #观察F_value的频数分布
    plt.title('F_value分布直方图')
    plt.hist(F_value[F_value < 20], bins = 20)
    
    #观察M_value的频数分布
    plt.title('M_value分布直方图')
    plt.hist(M_value[M_value < 4000], bins = 30)
    

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    接下来我们对R_value,F_value, M_value进行离散化分箱,便于我们对客户分类。

    #分箱
    R_bins = [0, 30, 60, 90, 360, 720]
    F_bins = [1, 2,  5, 10, 20, 2000]
    M_bins = [0, 500, 2000, 5000, 10000, 2000000]
    
    R_score = pd.cut(R_value.dt.days, R_bins, labels = [5, 4, 3, 2, 1], right = False)
    F_score = pd.cut(F_value, F_bins, labels = [1, 2, 3, 4, 5], right = False)
    M_score = pd.cut(M_value, M_bins, labels = [1, 2, 3, 4, 5], right = False)
    
    rfm = pd.concat([R_score, F_score, M_score], axis = 1)
    rfm.rename(columns = {'date': 'R_score', 'InvoiceNo': 'F_score', "basket_price": 'M_score'}, inplace = True)
    

    我们这里选取R_value、F_value、M_value的均值作为相应的分割点,分为高低等级。

    rfm['R'] = np.where(rfm['R_score'] > 3.57, '高', '低')
    rfm['F'] = np.where(rfm['F_score'] > 2.03, '高', '低')
    rfm['M'] = np.where(rfm['M_score'] > 1.89, '高', '低')
    
    rfm['value'] = rfm['R'] + rfm['F'] + rfm['M']
    rfm
    

    在这里插入图片描述
    最后我们根据value标签对客户进行分类

    def trans_value(x):
        if x == '高高高':
            return '重要价值客户'
        
        elif x == '高低高':
            return '重要发展客户'
        elif x == '低高高':
            return '重要保持客户'
        elif x == '高高低':
            return '一般价值客户'
        elif x == '低低高':
            return '重要挽留客户'
        elif x == '高低低':
            return '一般发展客户'
        elif x == '低高低':
            return '一般客户'
        
        else:
            return '一般挽留客户'
            
    rfm['用户等级'] = rfm['value'].apply(trans_value)
    rfm['用户等级'].value_counts()      
            
    

    在这里插入图片描述
    最后我们用plotly这个包来进行展示分类

    import plotly as py
    import plotly.graph_objs as go
    pyplot = py.offline.iplot
    py.offline.init_notebook_mode(connected=True)
    
    trace_basic = [go.Bar(x = rfm['用户等级'].value_counts().index,
                         y = rfm['用户等级'].value_counts().values,
                         marker = dict(color = 'orange'), opacity = 0.5)]
    
    layout = go.Layout(title = '用户等级情况', xaxis = dict(title = '用户重要度'))
    
    figure_basic = go.Figure(data = trace_basic, layout = layout)
    pyplot(figure_basic)
    
    trace = [go.Pie(labels = rfm['用户等级'].value_counts().index,
                    values = rfm['用户等级'].value_counts().values,
                    textfont = dict(size = 12, color = 'white'), opacity = 0.5)]
    layout = go.Layout(title = '用户等级比例')
    
    figure = go.Figure(data = trace, layout = layout)
    pyplot(figure)
    
    

    在这里插入图片描述
    在这里插入图片描述

    展开全文
  • kaggle电影数据分析报告

    千次阅读 2020-07-28 19:12:51
    数据来源:kaggle数据科学社区上TMDB 5000 Movie Dataset数据集:TMDB 5000 Movie Dataset 数据介绍:TMDB是一个电影资料库,本项目数据主要为欧美地区2017年之前的5000部电影 分析目的:对电影数据进行分析研究,...

    在这里插入图片描述在这里插入图片描述在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述在这里插入图片描述在这里插入图片描述
    在这里插入图片描述在这里插入图片描述
    在这里插入图片描述在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述
    在这里插入图片描述

    一、项目背景与介绍

    数据来源:kaggle数据科学社区上TMDB 5000 Movie Dataset数据集:TMDB 5000 Movie Dataset

    数据介绍:TMDB是一个电影资料库,本项目数据主要为欧美地区2017年之前的5000部电影

    分析目的:对电影数据进行分析研究,了解电影市场趋势,为电影制作提供决策。

    二、定义问题

    1、各类型电影数量会随时间如何变化吗?是如何变化的?

    2、不同类型的电影的盈利情况有何差异?

    3、原创电影与改编电影的有何差异?

    4、电影主要产自哪些地区?

    5、观众喜好与哪些因素有关?

    6、Universal Pictures与Paramount Pictures两家巨头公司的业绩如何?

    三、理解数据

    导入相关安装包,读取数据
    在这里插入图片描述查看数据信息在这里插入图片描述在这里插入图片描述观察数据,发现 genres、keywords、production_companies、production_countries、spoken_languages、cast、crew列为JSON编码的字符串。

    删去credits的列title,因为该列和movies的列重复了,再把两个数据集合并,删去不在研究范围内的值。

    credits中movie_id列 和 movies中id列是对应关系,故以此为连接合并两个数据集。
    在这里插入图片描述

    各字段含义:

    ● id:标识号

    ● movie_id:标识号

    ● popularity:在 Movie Database 上的相对页面查看次数

    ● budget:预算(美元)

    ● revenue:收入(美元)

    ● original_language:原始语言

    ● spoken_languages:输出语言

    ● original_title:原始电影名称

    ● cast:演员列表,按 | 分隔,最多 5 名演员

    ● crew:剧组

    ● title:电影名称

    ● status:电影状态

    ● homepage:电影首页的 URL

    ● tagline:电影的标语

    ● keywords:与电影相关的关键字,按 | 分隔,最多 5 个关键字

    ● overview:剧情摘要

    ● runtime:电影时长

    ● genres:风格列表,按 | 分隔,最多 5 种风格

    ● 制作公司列表:production_companies按 | 分隔,最多 5 家公司

    ● production_countries:制作国家

    ● release_date:首次上映日期

    ● vote_count:评分次数

    ● vote_average:平均评分

    三、数据清洗

    1、选择子集

    在这里插入图片描述通过 info()和describe ()方法查看要研究的数据是否存在异常情况
    在这里插入图片描述可以看到release_date、runtime存在缺失值。
    在这里插入图片描述
    可以看到budget,revenue,vote_average,vote_count最小值为0,这些列中可能存在异常值。

    2、缺失值处理

    在这里插入图片描述查找资料,填充缺失值
    在这里插入图片描述查看runtime的缺失值
    在这里插入图片描述查找资料,填充缺失值
    在这里插入图片描述

    3、异常值处理

    budget,revenue,vote_average,vote_count最小值为0,可能存在异常,用平均值填充。
    在这里插入图片描述

    4、数据格式转换

    查看release_date列的数据
    在这里插入图片描述需要转换为日期类型并提取年份
    在这里插入图片描述

    5、特殊格式数据处理

    本项目要进行研究的数据中的genres,keywords,production_companies,production_countries列均为json格式,不便对其进行分析。需通过json.loads先将json字符串转换为字典列表形式,再使用循环,选取需要的数据,本次分析取出name键所对应的值,并用“|”分隔。
    在这里插入图片描述

    四、数据分析及可视化

    1、各类型电影数量变化和分布

    (1)各类型电影逐年的数量变化

    genre_set集合存放所有电影类型
    在这里插入图片描述genre_year_df存放各个年份的不同电影类型的电影总数
    在这里插入图片描述绘图:
    在这里插入图片描述在这里插入图片描述由图可见,约从1992开始,电影市场开始爆发式增长,其中戏剧类(Drama)和喜剧类(Comedy)这两个类型是随时间增长幅度最大,其次是惊悚片(Thriller)和动作片(Action)。

    (2)各类型电影数量分布

    各类型电影总数
    在这里插入图片描述 各类型电影数量分布图:
    在这里插入图片描述在这里插入图片描述从图中可看出,排名前5的类型为Drama、Comedy、Thriller、Action和Romance。其中即使是排名前5的类型中,每一类型之间的差距也不算小。Drama类稳稳的在榜首。

    2、各类型电影的盈利情况

    profit_df存放电影类型和利润数据

    profit_by_genre存放计算后的各类型电影的利润平均值,利润=收入-支出成本
    在这里插入图片描述 各电影类型平均收益分布图:
    在这里插入图片描述在这里插入图片描述

    3、原创电影与改编电影的差异

    keywords中的包含"based on novel"的是改编电影,其余是原创电影。

    创建一个DataFrame存放原创电影与改编电影original_recompose,包含type、 profit 、 budget 等字段,计算出利润率 profit_rate=profit/budget 。

    在这里插入图片描述

    (1)电影数量对比

    在这里插入图片描述
    在这里插入图片描述原创电影占据了几乎所有的市场,只有少部分为改编电影

    (2)利润及利润率对比

    在这里插入图片描述在这里插入图片描述改编电影的支出要高于原创电影,且对应的收入和利润也要高于原创电影

    4、电影产地分布

    统计出各个地区的电影数量,country_df存储电影产地的数据

    在这里插入图片描述 绘制饼图:

    在这里插入图片描述
    在这里插入图片描述由图可见,美国电影数量一骑绝尘,占比达到了61.4%,名副其实的产出大国(应该也与该网站收录的数据有关)。其次是英国,德国,法国,加拿大。

    5、观众喜好与哪些因素有关

    (1)电影类型与受欢迎度的关系

    popularity字段是在 Movie Database 上的相对页面查看次数,根据这个数据来判断电影的受欢迎度。

    在这里插入图片描述绘图
    在这里插入图片描述在这里插入图片描述可见,最受观众欢迎的电影类型的前五名是

    Adventure、Animation、Science Fiction、Fantasy 、Action

    (2)电影时长与受欢迎度的关系

    在这里插入图片描述在这里插入图片描述受欢迎度较高的电影的时长基本在90-150分钟之间。 电影时长太长或太短的电影受欢迎程度较低。

    (3)电影评分与受欢迎度的关系

    在这里插入图片描述
    在这里插入图片描述评分高的电影不一定受欢迎度高,可能存在冷门好片。 但受众欢迎度高的,电影评分基本也不低,群众的眼睛是雪亮的,制作公司产出好片,获得受众喜爱的概率也更高。经过更多受众的检验,该电影为好片的可靠性也更高。

    6、Universal Pictures与Paramount Pictures两家巨头公司的业绩如何?

    company_df存放两家公司、电影类型、盈利的数据
    在这里插入图片描述

    (1)利润对比

    在这里插入图片描述绘图
    在这里插入图片描述在这里插入图片描述 由图可见,两家公司利润总额并无明显差异,Universal Pictures公司略高于Paramount Pictures公司。

    (2)出品的电影类型对比

    company_df存放两家公司、各类型电影,数值为1的代表属于该类型。

    在这里插入图片描述在这里插入图片描述绘图Universal Pictures:
    在这里插入图片描述在这里插入图片描述

    绘图Paramount Pictures:
    在这里插入图片描述在这里插入图片描述

    7、电影关键词

    keywords_list存放从keywords中提取出的关键词,去掉一些没有实际含义的字符串和短语
    在这里插入图片描述 通过WordCloud包生成词云图

    在这里插入图片描述

    展开全文
  • 项目背景:提供两年的每...本次共享单车数据分析项目数据源于Kaggle [https://www.kaggle.com/c/bike-sharing-demand/data]。 1.2 载入数据 // 载入工具包 import numpy as np import pandas as pd import matplotlib.
  • Kaggle TMDB电影数据分析项目实战

    千次阅读 2020-06-26 17:48:24
    分析步骤Kaggle TMDB电影数据分析项目实战数据集分析结果 Kaggle TMDB电影数据分析项目实战 注:该项目为博主第一次数据分析项目,代码部分参考了:这篇文章。 希望大家支持一下原作者。从下一篇开始将全部为原创...
  • 翻译: 大邓作者: Bex T标题: Weekly Awesome Tricks And Best Practices From Kaggle链接: https://towardsdev....
  • kaggle_diabetes-源码

    2021-04-21 10:34:49
    kaggle_diabetes
  • kaggle数据分析项目--heart disease(2)

    千次阅读 2019-04-20 11:41:37
    还是一样针对kaggle项目的heart disease数据而言。 1、带密度图的直方图 上一篇我有提到distplot函数可以画带密度的直方图,但是我昨天画的时候发现效果图很矮,今天发现问题所在:问题是因为我在distplot函数里添加...
  • kaggle--零售数据分析

    千次阅读 2020-04-29 00:11:06
    标题:kaggle零售数据分析 一、数据描述及字段描述 二、明确问题 三、理解和分析数据 四、结论和建议 一、数据描述及字段描述 1)此次数据集来自在线零售业务的交易数据,该公司主要销售礼品,大部分出售对象是面向...
  • kaggle的关于在线零售业务的交易数据,用于数据分析和处理。 数据包含541910行,8个字段,字段内容为: InvoiceNo: 订单编号,每笔交易有6个整数,退货订单编号开头有字母’C’。 StockCode: 产品编号,由5个整数...
  • pandas电影数据分析,从1000部电影中获取评分的平均分、导演的人数、呈现rating,runtime的分布情况、统计电影分类(genre)的情况,如需免费获取,请留下邮箱地址。
  • Kaggle数据分析 Titanic

    2021-09-24 19:36:12
    1、该NoteBook的数据分析流程: 探索性数据分析–>清洗数据–>建模 2、数据特征分析·相关性分析 https://zhuanlan.zhihu.com/p/136771737 (1)该NoteBook中用的是DataFrame.corr()函数计算列与列之间的...
  • 本次数据分析的数据集来自Kaggle的World University Rankings的cwurData.csv。数据集共包含2201行14列(含标题行),官方给出的每列的描述如下: world_rank: world rank for university institution: name of ...
  • python数据分析-数据下的奥运百年-Kaggle数据研究.PDF
  • 原创kaggle内核,分数在1.79484左右 。有需要打比赛的朋友可以看看这个代码
  • Kaggle项目数据分析--heart disease

    千次阅读 2019-04-19 17:53:54
    Kaggle项目数据分析--heart disease 最近在学习了用python的matplotlib库、seaborn库、numpy、pandas库等做数据分析,matplotlib库、seaborn库主要是用来画图的,看了相关教程后不得不感慨python的画图功能真的很...
  • 数据分析kaggle大赛泰坦尼克号生还概率数据
  • 项目背景: 客户是一个电影制作的新公司,他们将制作一部新电影。客户想确保电影能够成功,从而使新公司立足市场。 提出问题: 电影类型是如何随着时间的推移发生变化的?...数据来源于Kaggle项目数据 ...
  • TMDB电影数据分析,包括Kaggle上的原始数据集,以及代码,实现电影类型和票房,利润等的关系,对比两个公司的电影类型收入,拍摄集中年份,画出饼图,条形图,折线图,并进行关键词的提取,做出词云图。
  • 数据下载地址:https://www.kaggle.com/carrie1/ecommerce-data 要是直接点进去显示404,则可以直接去kaggle搜索‘E-Commerce Data’即可 字段含义: 数据形状:542k 行x 8列,8个字段分别为发票号,发票日期,商品...
  • 芝加哥汽车超速数据【Kaggle数据】 希望对大家的建模以及数据分析能够提供极大的帮助。
  • kaggle 电影评论情感分析(Bag of Words Meets Bags of Popcorn)的全部数据集,自己上网找了半天都找不到,最后是让同学上外网下的,在这里分享给想学习nlp的同学
  • 本文对原始数据集中的特征进行分析与展示 import numpy as np # linear algebra import pandas as pd # data processing, CSV file I/O import matplotlib.pyplot as plt from sklearn.preprocessing import ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 19,962
精华内容 7,984
关键字:

kaggle数据分析