项目背景
随着智能手机的普及和手机用户的激增,共享单车作为城市交通系统的一个重要组成部分,以绿色环保、便捷高效、经济环保为特征蓬勃发展。共享单车企业通过在校园、公交站点、居民区、公共服务区等提供服务,完成交通行业最后一块“拼图”,与其他公共交通方式产生协同效应。共享单车有助于缓解城市短距离交通出行和“最后一公里”难题,但共享单车由于其运营特点,对企业在城市投放和调度单车的规划管理方面,存在较大挑战。
基于上述背景,本文基于上海摩拜单车的2016年8月份随机抽样大约10万条的开放订单数据进行分析,挖掘出数据背后的规律,用数据勾勒出摩拜共享单车的使用与用户出行现状,从而有助于摩拜共享单车企业更好地推出营销策略,定位新单车的投放区域,调控车辆布置,更好地服务用户。
注:本项目的数据、代码和图表可戳:摩拜共享单车数据分析项目数据、代码、图表 下载
数据探索
这一步中我们统观数据的全貌,对数据有个大体的了解,对数据进行质量探索和特征分析。
读取数据并查看数据集数据
import pandas as pd
from math import radians, cos, sin, asin, sqrt,ceil
import numpy as np
import geohash
#数据读取
data = pd.read_csv("./mobike_shanghai_sample_updated.csv")
print(data.head(10))
该数据集为摩拜共享单车企业提供的上海城区2016年8月随机抽样的10万多条用户骑行用车数据(订单数据),包含交易编号、用户ID、车辆ID、骑行起点经纬度、骑行终点经纬度、租赁时间、还车时间、骑行轨迹经纬度等数据。
查看数据集属性类型,在这里我们可以看出租赁时间和还车时间的数据类型为object类型,我们紧接着可以把它转化为datetime类型
print(data.info())
data['start_time'] = pd.to_datetime(data['start_time'])
data['end_time'] = pd.to_datetime(data['end_time'])
print(data.info())
查看数据集的空值分布情况,数据不存在空值
print(data.isnull().sum())
数据挖掘
该数据集虽然多达10万多条用车交易数据,但每一条用车交易数据里只有10个特征,并且该数据集和之前的电影数据集不同,之前的电影数据集每一个特征都是独立的信息字段,不和其他特征产生明显关联,而该数据集的特征之间拥有明显的相关性,我们可以通过关联组合特征之间的关系得到新的特征,或者从一个特征反映出来的多个方面组合出多个新的特征扩充数据集,掌握事物的多个方面,挖掘出数据更多潜在的规律,从而使得后面的数据分析可以进行更多维度的分析,得到对数据的更多认识。
如何发现新特征:
- 租赁时间 + 还车时间 => 骑行时长
- 骑行起点经纬度 + 骑行终点经纬度 => 骑行的位移
- 摩拜单车骑行轨迹经纬度 => 骑行的路径
- 租赁时间 => 星期几(即每笔骑行订单发生在星期几) + 时间段(即每笔订单发生在一天24小时的哪个时间段)
- 骑行时长 => 订单金额(粗略估计)
- 每笔订单金额 + 每笔订单租赁时间 + 每笔订单用户ID=> 用户分级(RFM模型)
- 骑行起点终点经纬度 => 骑行起点终点所处的地区
租赁时间 + 还车时间 => 骑行时长
新增“lag”列,通过开始时间 - 结束时间计算得到骑行时长,并把时长单位统一为分钟。
data["lag"] = (data.end_time - data.start_time).dt.seconds/60
骑行起点经纬度 + 骑行终点经纬度 => 骑行的位移
新增“distance”列,通过计算骑行起点和终点的经纬度得到骑行的位移,单位为千米。
geodistance()封装的是通过两点经纬度求两点直线距离的数学公式。对背后公式的推导过程和数学原理感兴趣的可以戳 https://blog.csdn.net/sunjianqiang12345/article/details/60393437 了解。
def geodistance(item):
lng1_r, lat1_r, lng2_r, lat2_r = map(radians, [item["start_location_x"], item["start_location_y"], item["end_location_x"], item["end_location_y,"]]) # 经纬度转换成弧度
dlon = lng1_r - lng2_r
dlat = lat1_r - lat2_r
dis = sin(dlat/2)**2 + cos(lat1_r) * cos(lat2_r) * sin(dlon/2)**2
distance = 2 * asin(sqrt(dis)) * 6371 * 1000 # 地球平均半径为6371km
distance = round(distance/1000,3)
return distance
#data按行应用geodistance()得到distance列的数值
data["distance"] = data.apply(geodistance,axis=1)
摩拜单车骑行轨迹经纬度 => 骑行的路径
新增“adderLength”列,通过计算“track”列数据得到骑行路径。
把骑行轨迹字符串按“#”分隔符分隔成列表,得到骑行轨迹的经纬度信息列表
通过不断两两轮询该骑行轨迹列表,每一次按顺序取出两个列表元素(前轨迹采样点的经纬度和后轨迹采样点的经纬度)按“,”分隔符分隔,得到四个值(前轨迹采样点的经度、前轨迹采样点的纬度、后轨迹采样点的经度、后轨迹采样点的纬度),组合成一个item字典传入给geodistance()计算出两点的位移,再把每一小段每一小段的位移累加起来得到骑行的路径。
#通过摩拜单车的踪迹获取每次交易骑行的路径
def geoaadderLength(item):
track_list = item["track"].split("#")
adderLength_item = {
}
adderLength = 0
for i in range(len(track_list)-1):
start_loc = track_list[i].split(",")
end_loc = track_list[i+1].split(",")
adderLength_item["start_location_x"],adderLength_item["start_location_y"] = float(start_loc[0]),float(start_loc[1])
adderLength_item["end_location_x"],adderLength_item["end_location_y"] = float(end_loc[0]),float(end_loc[1])
adderLength_each = geodistance(adderLength_item)
adderLength = adderLength_each + adderLength
return adderLength
data["adderLength"] = data.apply(geoaadderLength,axis=1)
租赁时间 => 星期几 + 时间段(24小时制)
新增“weekday”(即每笔骑行订单发生在星期几)和“hour”(即每笔订单发生在一天24小时的哪个时间段)
data['weekday'] = data.start_time.apply(lambda x: x.isoweekday())
data['hour'] = data.start_time.apply(lambda x: x.utctimetuple().tm_hour)
骑行时长 => 订单金额
新增“cost”列,根据每笔订单的骑行时长,粗略估计订单金额,参照2016年摩拜收费标准,按每30分钟收取1元。
data['cost'] = data.lag.apply(lambda x: ceil(x/30))
订单金额 => 用户分级(RFM模型)
由于我们拥有了每笔交易的用户id、消费金额、消费时间,我们可以考虑运用RFM模型,对用户进行分级,这里采取的模型是RFM模型。
RFM模型是进行用户价值细分的一种方法,是用以研究用户的数学模型。
- R(Recency)最近一次消费时间:表示用户最近一次消费距离现在的时间;
- F(Frequency)消费频率:消费频率是指用户在统计周期内购买商品的次数;
- M(Monetary)消费金额:消费金额是指用户在统计周期内消费的总金额,体现了消费者为企业创利的多少;
这3个维度,帮助我们把用户划分为标准的8类
其模型构建过程可分为如下步骤:
- 计算RFM值
R值:即每个用户最后一次租赁共享单车时间距9月1日多少天;(因为数据集只包含2016年8月份的数据,所以我们站在9月1的时间节点上统计每个用户最近一次消费距离现在的时间)
F值:即每个用户累计租赁单车频次;
M值:即每个 用户累积消费金额;
#因数据集仅包含八月份发起的订单数据,故以9月1日为R值计算基准
data['r_value_single'] = data.start_time.apply(lambda x: 32 - x.timetuple().tm_mday)
# 按每个用户id所有订单日期距9/1相差天数的最小值作为r值
r_value = data.groupby(['userid']).r_value_single.min()
f_value = data.groupby(['userid']).size() # 按每个用户id八月累积订单数量作为f值
m_value = data.groupby(['userid']).cost.sum() # 按每个用户id八月累积消费金额作为m值
#把r值、f值、m值组合成DataFrame
rfm_df = pd.DataFrame({
'r_value':r_value,'f_value':f_value,"m_value":m_value})