注:数据集描述
变量 | 中文翻译 | 变量解释 |
---|---|---|
dt | 订单创建时间 | 顾客创建订单时间(以天为单位) |
order_id | 订单id | 顾客创建订单id |
waybill_id | 运货单id | 骑手接受订单生成的运货单id |
courier_id | 骑手id | 骑手id |
da_id | 地区id | 商业区id |
is_courier_grabbed | 骑手是否接受了订单 | 骑手是否接受了订单 |
is_weekend | 下单时间是否为周末 | 下单时间是否为周末 |
estimate_arrived_time | 预计到达时间 | 餐品预计到达顾客所在位置的时间 |
is_prebook | 该订单是否为预定订单 | 该订单是否为预定订单 |
poi_id | 商家id | 商家id |
sender_lng | 商家所在位置经度 | 商家所在位置经度 |
sender_lat | 商家所在位置维度 | 商家所在位置维度 |
recipient_lng | 顾客所在位置经度 | 顾客所在位置经度 |
recipient_lat | 顾客所在位置维度 | 顾客所在位置维度 |
grab_lng | 骑手所在位置经度 | 骑手未取餐前所在位置经度 |
grab_lat | 骑手所在位置维度 | 骑手未取餐所在位置维度 |
dispatch_time | 系统分配订单时间 | 订单进入分配系统的时间 |
grab_time | 骑手接受订单时间 | 骑手接受订单时间 |
fetch_time | 骑手取餐时间 | 骑手取餐时间 |
arrive_time | 餐品到达时间 | 餐品实际达到顾客所在位置时间 |
estimate_meal_prepare_time | 预计备餐时间 | 商家预计备餐时间 |
order_push_time | 订单推送时间 | 将运单分配给快递员的时间(快递员可以拒绝派遣) |
platform_order_time | 平台订单创建时间 | 平台订单创建时间(以小时为单位) |
该数据集可以分为离散型变量、时间型变量和经纬度型变量,在描述性统计中,我们对离散型数据进行频数分析,对时间性数据进行集中趋势、离散趋势和分布形态分析,对经纬度型数据通过聚类方法,对数据进行分析。
import pandas as pd
import matplotlib.pylab as plt
from pyl7vp import L7VP
import seaborn as sns
import matplotlib.pyplot as plt
import numpy as np
from scipy import stats
from matplotlib.cm import ScalarMappable
from IPython.display import Image
from sklearn.cluster import KMeans
一、时间型变量
(一)集中趋势
关于集中趋势,我们可以通过中位数指标,众数指标进行分析。在本数据集的时间型变量中,结合下面的研究内容,我们只对['platform_order_time'],即平台创建订单时间绘制直方图,进行集中趋势分析。
注:该数据集中的数据既有已接单订单的数据,又有未接单订单的数据。我们在下述的分析中,将数据集中未结单数据排除,支队已接单数据进行分析。
#导入数据
df = pd.read_csv('Meituan-INFORMS-TSL-Research-Challenge-main/Meituan-INFORMS-TSL-Research-Challenge-main/all_waybill_info_meituan_0322.csv/all_waybill_info_meituan_0322.csv',encoding='utf-8')
#df['platform_order_time'] = pd.to_datetime(df['platform_order_time'], unit='s')
after_df=df[df['is_courier_grabbed'] != 0].copy()
next_df = df[df['is_courier_grabbed'] != 0].copy()
after_df['platform_order_time'] = pd.to_datetime(after_df['platform_order_time'],unit='s')
# 提取订单时间的小时部分
order_hour = after_df['platform_order_time'].dt.hour
# 统计每个小时的订单数量
hourly_order_count = order_hour.value_counts().sort_index()
# 绘制直方图
plt.bar(hourly_order_count.index, hourly_order_count.values)
plt.xlabel('Hour of the day')
plt.ylabel('Order count')
plt.title('Order count by hour')
# 设置 x 轴刻度标签为时:00:00
plt.xticks(hourly_order_count.index, [f"{hour}:00:00" for hour in hourly_order_count.index], rotation=90)
plt.show()
运行结果:
#计算时间的平均数
mean_value = next_df['platform_order_time'].mean()
mean_value = round(mean_value, 2)
print("平均数为:",mean_value)
# 计算时间戳的中位数
median_time = after_df['platform_order_time'].quantile(0.5)
print("中位数:",median_time)
#计算时间的众数
mode_time = after_df['platform_order_time'].mode()
print("众数:",mode_time)
运行结果:
中位数: 2022-10-21 02:59:01 众数: 0 2022-10-19 03:29:41 1 2022-10-23 03:29:47 Name: platform_order_time, dtype: datetime64[ns]
根据结果可知,平台订单创建时间的中位数为2022-10-21 02:59:01,平台订单创建时间的众数为2022-10-19 03:29:41、2022-10-23 03:29:47,与直方图相符。且中位数和众数相差较大,因此该数据集可能存在一定程度的偏态。
现实意义: 根据上述数据可知,该地区送餐巅峰在凌晨三点,具体时间为03:29:41和03:29:47,我们由此引出以下猜测:
- 根据网上数据和统计结果,我们猜测该地区可能为上海、广州、或者为新疆。
- 上海、广州等地经济发达,夜间除餐饮外卖外,还有零售外卖,且24小时营业的店铺较多,因此在夜间外卖较多。
- 新疆喀什的外卖经济增长明显,并且与内陆存在时差,因此,在凌晨三点睡前有夜宵外卖,在上十点存在早餐外卖。
(二)离散程度
#计算时间的方差
variance = next_df['platform_order_time'].var()
variance = round(variance, 2)
print("方差:",variance)
# 计算标准差
std_deviation = next_df['platform_order_time'].std()
std_deviation = round(std_deviation, 2)
print("标准差:",std_deviation)
方差: 38639871761.7 标准差: 196570.27
根据结果可得,时间的方差与标准差为38704961797.577866、196735.7664421441,平均数为1666297808.5107381。由此可见,本数据集离散程度较大,数据的波动性较大,可能存在着较大的不确定因素影响。
因素: 可能受顾客意愿、现实环境等外部因素影响;
# 计算四分位数
quartiles = next_df['platform_order_time'].quantile([0.25, 0.5, 0.75])
print(quartiles)
0.25 1.666145e+09 0.50 1.666321e+09 0.75 1.666482e+09 Name: platform_order_time, dtype: float64
(三)分布形态¶
# 计算偏度
#next_df['platform_order_time'] = pd.to_datetime(next_df['platform_order_time'],unit='s')
skewness = next_df['platform_order_time'].skew()
skewness = round(skewness,2)
# 计算峰度
kurtosis =next_df['platform_order_time'].kurt()
kurtosis = round(kurtosis,2)
print("偏度为:", skewness)
print("峰度为:", kurtosis)
# 生成样本数据
data = next_df['platform_order_time']
# 绘制QQ图
stats.probplot(data, dist="norm", plot=plt)
plt.title('QQ Plot')
plt.xlabel('Theoretical Quantiles')
plt.ylabel('Ordered Values')
plt.show()
偏度为: -0.06 峰度为: -1.21
根据偏值与峰值结果,可以看出偏度小于0,数据分布呈现左偏或负偏,即数据左侧尾部比右侧尾部要长,大多数值位于均值的右侧;峰度小于-1.21,表示数据分布平坦,即数据分布更加分散。下述箱线图可验证。
上述的QQ图可以佐证,本数据集与正态分布并不相同,两端为“S”形曲线,中间为线性部分,表明数据尾部比正态分布有更多的极端值(或离群值)。
(四)异常值分析
after_df['order_hour'] = after_df['platform_order_time'].dt.hour
# 绘制箱线图
plt.figure(figsize=(10, 6))
sns.boxplot(y='order_hour', data=after_df,boxprops=dict(facecolor='lightblue'))
plt.ylabel('Hour of the Day')
plt.xlabel('Time')
plt.title('Boxplot of Order Hours Distribution')
plt.show()
二、离散型变量
该数据集中的离散型变量有is_courier_grabbed、is_weekend、is_prebook。我们对该变量进行频数统计,以了解该变量的分布情况和出现频率。
grabbed_counts = df['is_courier_grabbed'].value_counts()
weekend_counts = df['is_weekend'].value_counts()
prebook_counts = df['is_prebook'].value_counts()
# 创建一个包含两行两列的子图网格
fig, axs = plt.subplots(1, 3, figsize=(15, 5))
# 绘制第一个条形图
axs[0].bar(grabbed_counts.index, grabbed_counts.values,color='skyblue')
axs[0].set_title('Frequency of is_courier_grabbed')
axs[0].set_xlabel('is_courier_grabbed')
axs[0].set_ylabel('Frequency')
axs[0].set_xticks([0, 1])
axs[0].set_xticklabels(['Not Grabbed', 'Grabbed'])
# 绘制第er个条形图
axs[1].bar(weekend_counts.index, weekend_counts.values,color='salmon')
axs[1].set_title('Frequency of is_weekend')
axs[1].set_xlabel('is_weekend')
axs[1].set_ylabel('Frequency')
axs[1].set_xticks([0, 1])
axs[1].set_xticklabels(['Not weekend', 'weekend'])
# 绘制第三个条形图
axs[2].bar(prebook_counts.index, prebook_counts.values,color='lightgreen')
axs[2].set_title('Frequency of is_prebook')
axs[2].set_xlabel('is_prebook')
axs[2].set_ylabel('Frequency')
axs[2].set_xticks([0, 1])
axs[2].set_xticklabels(['Not prebook', 'prebook'])
# 调整子图之间的间距
plt.tight_layout()
# 显示图形
plt.show()
根据结果可知,‘骑手是否接单’这一变量中接单远远大于不接单;‘是否为周末’这一变量中,不是周末远远大于是周末;‘是否为预定’这一变量中,不是预定远远大于是预定。 下面我们具体了解一下这三组数据。
(一)是否接单变量
根据上述结果,已知‘是否接单’变量中接单远远大于不接单,我们通过公式:拒绝率 = 不接单数据个数/总数据个数;对拒绝率进行分析。
grabbed_counts = df[df['is_courier_grabbed']== 0]
reject_rate = len(grabbed_counts) / len(df)
reject_rate = round(reject_rate,2)
print(f'拒绝率:{reject_rate}')
拒绝率:0.13
根据上述结果,我们可以看出拒绝率为0.13,数值较低。这表明:
1、美团平台的派单系统设计良好,能够合理分配订单并满足骑手的需求。
2、骑手对工作环境和福利待遇比较满意,他们愿意接受派发的订单而不是选择拒绝。
3、平台能够有效地匹配订单需求与骑手供给之间的平衡,即需求供给平衡。
(二)是否为周末变量
# 统计周末和非周末的数量
weekend_counts = after_df['is_weekend'].value_counts()
# 自定义标签
labels = ['weekends', 'weeldays']
# 绘制饼状图
plt.figure(figsize=(6, 6))
plt.pie(weekend_counts, labels=labels, autopct='%1.1f%%', startangle=140, colors=['skyblue', 'lightcoral'])
plt.title('Weekend vs. Weekday Orders')
plt.show()
根据上图所示,我们可以看出在本数据集中,工作日占比要比周末占比多,这是由于在本数据集中一共收集了8天数据,其中工作日占比要比周末多。因此,这是很正常的现象。接下来,我们对周末与工作日在每小时的订餐数量进行比较分析。
# 将数据按照周末和工作日分组,并有存入新的数据集
weekend_data = after_df[after_df['is_weekend'] == 1]
weekday_data = after_df[after_df['is_weekend'] == 0]
# 提取订单时间的小时部分
weekend_hour = weekend_data['platform_order_time'].dt.hour
weekday_hour = weekday_data['platform_order_time'].dt.hour
# 统计每个小时的订单数量
hourly_weekend_hour = weekend_hour.value_counts().sort_index()/2
hourly_weekday_hour = weekday_hour.value_counts().sort_index()/6
# 设置柱状图的位置和宽度
bar_width = 0.35
index = range(24)
# 绘制双柱直方图,[i + bar_width for i in index]错开位置,避免重叠
plt.bar(index,hourly_weekend_hour, bar_width, label='Weekend', color='skyblue')
plt.bar([i + bar_width for i in index], hourly_weekday_hour, bar_width, label='Weekday', color='lightcoral')
# 设置图例和标签
plt.xlabel('Time')
plt.ylabel('Quantity')
plt.title('Quantity Comparison between Weekend and Weekday')
#plt.xticks([i + bar_width/2 for i in index], range(1,len(hourly_weekday_hour) + 1))
plt.xticks([i + bar_width/2 for i in index],[f"{hour}:00:00" for hour in index], rotation=90)
plt.legend()
# 显示图形
plt.show()
我们在设计该代码时,对周末数据与工作日数据进行了平均,从而可以更加准确、有针对性的对比周末与工作日的外卖数量。根据上述的双柱直方图,我们可以看出在每小时,工作日与周末的外卖数量趋势是一致的;整体来看,工作日的外卖数量多数多于周末的外卖数量。
(三)是否为预定订单变量
接下来,我们对是否为预定订单变量进行分析。首先我们将预定订餐与全部订餐进行比较;其次,我们将预定订单与自己进行比较,即在不同时期,预定订餐的数量。
# 将数据按照预定与非预定分组,并有存入新的数据集
prebook = after_df[after_df['is_prebook'] == 1]
not_prebook = after_df[after_df['is_prebook'] == 0]
# 提取订单日期
prebook_dt = prebook['dt']
not_prebook_dt = not_prebook['dt']
# 统计每个日期的订单数量
prebook_count = prebook_dt.value_counts().sort_index()
not_prebook_count = not_prebook_dt.value_counts().sort_index()
# 设置柱状图的位置和宽度
bar_width = 0.35
#index = range(8)
index = range(len(prebook_count.index))
# 绘制双柱直方图,[i + bar_width for i in index]错开位置,避免重叠
plt.bar(index,prebook_count, bar_width, label='prebook', color='skyblue')
plt.bar([i + bar_width for i in index], not_prebook_count, bar_width, label='not_prebook', color='lightcoral')
# 设置图例和标签
plt.xlabel('Time')
plt.ylabel('Quantity')
plt.title('Quantity Comparison between Weekend and Weekday')
#plt.xticks([i + bar_width/2 for i in index], range(1,len(prebook_dt) + 1))
plt.xticks([i + bar_width/2 for i in index], prebook_count.index, rotation=45)
plt.legend()
# 显示图形
plt.show()
根据上图可知,我们可以看出非预定订单远远多于预定订单。这表明:
1、即时需求增加:非预定订餐多于预定订单可能表示有更多的即时需求。这可能是因为消费者的生活节奏加快,更倾向于临时决定用餐方式,或者是因为特定时间段的即时需求较为集中。
2、灵活性需求增加:消费者对于用餐时间和地点的灵活性需求增加,更愿意根据自己的实际情况在用餐时决定订餐,而不是提前预定。
3、外卖文化的影响:外卖文化的普及可能导致了即时订餐的增加。消费者习惯了通过外卖平台随时随地点餐,并且更愿意选择即时配送。
4、商家服务能力提升:商家可能通过提高服务速度和配送效率来满足即时订餐的需求,从而吸引更多的非预定订餐。
# 绘制直方图
plt.bar(index, prebook_count)
plt.xlabel('Hour of the day')
plt.ylabel('Order count')
plt.title('Order count by hour')
#plt.xticks(数组,颜色/字体/倾斜程度)
plt.xticks(index,prebook_count.index, rotation=45)
# 显示图形
plt.show()
由上图可以看出,预定订单数量变化不明显,较为稳定。这表明了:
1、稳定的消费者群体:某些商家可能有一批忠实的消费者群体,他们习惯于提前预订并保持一定的消费频率。这些消费者对商家的产品或服务有较高的认可度和信赖度,因此他们在提前预订方面保持稳定。
2、稳定的需求趋势:某些商家所在的市场可能处于相对稳定的状态,消费者的用餐需求没有明显的波动或季节性变化。在这种情况下,预定订单数量可能会保持相对稳定。
3、有效的运营管理:某些商家可能有一套有效的运营管理系统,能够及时满足消费者的预定需求,并保持稳定的服务水平。这种情况下,消费者更愿意提前预订,因为他们信任商家能够按时提供服务。
4、良好的市场定位:某些商家可能通过准确的市场定位和产品定位,吸引到了稳定的目标消费群体。这些消费群体对商家的产品或服务有持续的需求,并愿意提前预订。
三、经纬度数据
在数据集中,存在三个变量的经纬度数据,分别是商家的经纬度数据('sender_lng', 'sender_lat'),顾客的经纬度数据('recipient_lng', 'recipient_lat')和骑手的经纬度数据('grab_lng', 'grab_lat')。我们对着三个变量进行聚类分析,首先我们通过肘部法则,明确簇的数量。然后,对数据进行聚类分析。
# 选择经度和纬度两列作为特征
longitude_columns =['sender_lng', 'recipient_lng', 'grab_lng']
latitude_columns = ['sender_lat', 'recipient_lat', 'grab_lat']
X = after_df[longitude_columns + latitude_columns].values
# 设置簇数量的范围
cluster_range = range(1, 10)
inertia_values = []
# 计算每个簇数量对应的簇内平方和
for num_clusters in cluster_range:
kmeans = KMeans(n_clusters=num_clusters)
kmeans.fit(X)
inertia_values.append(kmeans.inertia_)
# 绘制肘部法则图
plt.figure(figsize=(10, 6))
plt.plot(cluster_range, inertia_values, marker='o', linestyle='-')
plt.title('Elbow Method for Optimal Clusters')
plt.xlabel('Number of Clusters')
plt.ylabel('Inertia')
plt.xticks(cluster_range)
plt.grid(True)
plt.show()
根据上图,我们可以看出折线图的拐点在2,即k=2是该数据进行聚类分析的最佳选择。
# 选择经度和纬度两列作为特征
#X = after_df[['sender_lng', 'sender_lat', 'recipient_lng', 'recipient_lat', 'grab_lng', 'grab_lat']].values
longitude_columns =['sender_lng', 'recipient_lng', 'grab_lng']
latitude_columns = ['sender_lat', 'recipient_lat', 'grab_lat']
X = after_df[longitude_columns + latitude_columns].values
# 指定要分成的簇的数量
num_clusters = 2
# 使用 KMeans 算法进行聚类
kmeans = KMeans(n_clusters=num_clusters)
kmeans.fit(X)
# 获取聚类中心的坐标
cluster_centers = kmeans.cluster_centers_
#print(cluster_centers )
# 获取每个样本所属的簇
labels = kmeans.labels_
# 将聚类结果添加到原始数据中
after_df['cluster'] = labels
# 定义固定的调色板,黄色表示sender,绿色表示recipient,蓝色表示grab
palette = {'sender': 'pink', 'recipient': 'lightgreen', 'grab': 'lightblue'}
# 绘制聚类结果的散点图
plt.figure(figsize=(10, 6))
# 绘制recipient的点
sns.scatterplot(data=after_df, x='recipient_lng', y='recipient_lat', color=palette['recipient'], label='recipient')
# 绘制grab的点
sns.scatterplot(data=after_df, x='grab_lng', y='grab_lat', color=palette['grab'], label='grab')
# 绘制sender的点
sns.scatterplot(data=after_df, x='sender_lng', y='sender_lat', color=palette['sender'], label='sender')
plt.title('Clustering of Coordinates')
plt.show()
根据上图可知:
1、热点区域:聚类后,图中密集的点集表示了热点区域,即人口密集、商业中心、旅游景点等地区。这些区域可能是城市的中心地带,也可能是特定类型的商业区域或者社区。
2、空白区域:图中稀疏的点集表示了空白区域,即人口稀少或者相对较少活动的地区。这些区域可能是郊区、农村地区或者自然保护区等,也可能是交通条件不便利,地理条件艰苦,人口数量相对较少。
3、社会经济特征:不同聚类簇的分布特征可以反映出该地区的社会经济特征,如高收入社区、低收入社区、商业发达地区等。
4、区域发展趋势:通过观察聚类图的变化趋势,可以发现不同区域的发展趋势和变化规律,为城市规划、商业布局等提供参考依据。
四、准时到达率
Ontime_arrival = after_df[after_df['estimate_arrived_time'] > after_df['arrive_time']]
Ontime_arrival_rate = len(Ontime_arrival) / len(df)
Ontime_arrival_rate = round(Ontime_arrival_rate,2)
print(f'准时到达率:{Ontime_arrival_rate}')
上述结果可以看出准时达到率较高,这说明了:
1、高效的物流管理:高准时达到率通常意味着物流系统的高效管理,包括订单处理、路线规划、配送调度等环节都在有效控制之下。
2、良好的配送网络:高准时达到率可能反映了配送网络的覆盖范围广、配送速度快,使得骑手能够在短时间内将商品送达目的地。
3、骑手素质和服务水平高:高准时达到率可能表明骑手团队的素质和专业水平较高,能够准确、及时地完成配送任务,并提供良好的客户服务体验。
4、客户信任和满意度高:高准时达到率可能意味着客户对配送服务的信任度高,对品牌的满意度也相对较高,从而促进了再次购买和口碑传播。
5、效率与成本控制:高准时达到率可能也反映了企业在配送过程中的效率和成本控制能力,即在保证准时送达的前提下,有效控制了配送成本。