目录
学习目标
- GPS经纬度介绍
Pandas
分组聚合- 出租车与网约车经纬度统计
地图数据统计(基础知识)
在任务三,我们希望通过分析和统计能够发现:
- 巡游车与网约车的空间分布;
- 上下客点分布密度:上下车位置分布;
GPS经纬度
- WGS-84原始坐标系,一般用国际GPS记录仪记录下来的经纬度,通过GPS定位拿到的原始经纬度,Google和高德地图定位的的经纬度(国外)都是基于WGS-84坐标系的;但是在国内是不允许直接用WGS84坐标系标注的,必须经过加密后才能使用;
- GCJ-02坐标系,又名“火星坐标系”,是我国国测局独创的坐标体系,由WGS-84加密而成,在国内,必须至少使用GCJ-02坐标系,或者使用在GCJ-02加密后再进行加密的坐标系,如百度坐标系。高德和Google在国内都是使用GCJ-02坐标系,可以说GCJ-02是国内最广泛使用的坐标系;
- 百度坐标系BD-09,百度坐标系是在GCJ-02坐标系的基础上再次加密偏移后形成的坐标系,只适用于百度地图;
地图坐标系转换方法:https://github.com/wandergis/coordTransform_py
分组聚合统计
对数据集进行分组并对各组应用一个函数(无论是聚合还是转换),通常是数据分析工作中的重要环节。在将数据集加载、融合、准备好之后,通常就是计算分组统计或生成透视表。pandas提供了一个灵活高效的gruopby功能,它使你能以一种自然的方式对数据集进行切片、切块、摘要等操作。
关系型数据库和SQL(Structured Query Language,结构化查询语言)能够如此流行的原因之一就是其能够方便地对数据进行连接、过滤、转换和聚合。但是,像SQL这样的查询语言所能执行的分组运算的种类很有限。
通过分组聚合统计,可以完成:
- 使用一个或多个键(形式可以是函数、数组或DataFrame列名)分割pandas对象。
- 计算分组的概述统计,比如数量、平均值或标准差,或是用户定义的函数。
- 应用组内转换或其他运算,如规格化、线性回归、排名或选取子集等。
- 计算透视表或交叉表。
- 执行分位数分析以及其它统计分组分析。
Hadley Wickham(许多热门R语言包的作者)创造了一个用于表示分组运算的术语"split-apply-combine"(拆分-应用-合并)。
- 第一个阶段,pandas对象(无论是Series、DataFrame还是其他的)中的数据会根据你所提供的一个或多个键被拆分(split)为多组。拆分操作是在对象的特定轴上执行的。例如,DataFrame可以在其行(axis=0)或列(axis=1)上进行分组。
- 将一个函数应用(apply)到各个分组并产生一个新值。
- 所有这些函数的执行结果会被合并(combine)到最终的结果对象中。结果对象的形式一般取决于数据上所执行的操作。下图大致说明了一个简单的分组聚合过程。
聚合指的是任何能够从数组产生标量值的数据转换过程,比如mean、count、min以及sum等。
地图数据统计(实践)
统计巡游车与网约车分布
下面代码以将以巡游车GPStaxiGps20190531.csv
为案例进行数据统计:
taxigps2019 = pd.read_csv(INPUT_PATH + 'taxiGps20190531.csv', nrows=MAX_ROWS,
dtype = {
'DRIVING_DIRECTION': np.uint16,
'OPERATING_STATUS': np.uint8,
'LONGITUDE': np.float32,
'LATITUDE': np.float32,
'GPS_SPEED': np.float16
})
taxigps2019 = taxigps2019[taxigps2019.columns[::-1]]
taxigps2019['GPS_TIME'] = pd.to_datetime(taxigps2019['GPS_TIME'])
taxigps2019.sort_values(by=['CARNO','GPS_TIME'], inplace=True)
taxigps2019.reset_index(inplace=True, drop=True)
taxigps2019.head()
- 统计每辆巡游车最早、最晚出现的记录:
df_first = taxigps2019.groupby(['CARNO']).first()
LATITUDE | LONGITUDE | GPS_TIME | DRIVING_DIRECTION | GPS_SPEED | OPERATING_STATUS | |
---|---|---|---|---|---|---|
CARNO | ||||||
0006d282be70d06881a7513b69fcaa60 | 24.479755 | 118.146935 | 2019-05-31 01:31:20 | 292 | 0.000000 | 1 |
000e8886a7b27ca761e34d59b1dee35c | 24.550379 | 118.103012 | 2019-05-31 01:31:38 | 148 | 72.187500 | 6 |
001df76bfa67259259f596c6dd353e6a | 24.499088 | 118.141182 | 2019-05-31 01:31:09 | 278 | 37.000000 | 6 |
001e3756542dc796b402dfd1b56fd4ec | 24.471125 | 118.105560 | 2019-05-31 01:31:14 | 346 | 35.093750 | 6 |
002b23a3762ea245f18cc896a55579d2 | 24.544592 | 118.102463 | 2019-05-31 01:31:21 | 199 | 0.300049 | 1 |
... | ... | ... | ... | ... | ... | ... |
ffe9d9afeabd49c83ef8f7f5f09556cd | 24.526094 | 118.143852 | 2019-05-31 01:31:50 | 0 | 0.000000 | 1 |
ffea5e974b86b835b97eea1cd34fe3b8 | 24.479177 | 118.150314 | 2019-05-31 01:31:36 | 14 | 0.000000 | 1 |
ffedc235a5a3d891139a2d3ec2396b3e | 24.482244 | 118.116447 | 2019-05-31 01:31:24 | 32 | 0.000000 | 1 |
fff0bc740b883d541d098f5063e1f43f | 24.533218 | 118.139542 | 2019-05-31 01:32:01 | 64 | 0.000000 | 1 |
fff20f025f560278d601b2fd47e1f6b7 | 24.427284 | 118.133896 | 2019-05-31 01:31:26 | 208 | 56.812500 | 1 |
6768 rows × 6 columns
df_last = taxigps2019.groupby(['CARNO']).last()
LATITUDE | LONGITUDE | GPS_TIME | DRIVING_DIRECTION | GPS_SPEED | OPERATING_STATUS | |
---|---|---|---|---|---|---|
CARNO | ||||||
0006d282be70d06881a7513b69fcaa60 | 24.498701 | 118.030182 | 2019-05-31 23:59:58 | 54 | 44.406250 | 1 |
000e8886a7b27ca761e34d59b1dee35c | 24.538876 | 118.129890 | 2019-05-31 23:59:49 | 232 | 0.000000 | 1 |
001df76bfa67259259f596c6dd353e6a | 24.488588 | 118.157196 | 2019-05-31 23:59:47 | 252 | 30.093750 | 6 |
001e3756542dc796b402dfd1b56fd4ec | 24.524464 | 118.147095 | 2019-05-31 23:59:49 | 80 | 12.898438 | 6 |
002b23a3762ea245f18cc896a55579d2 | 24.497768 | 118.180374 | 2019-05-31 23:59:45 | 0 | 0.000000 | 1 |
... | ... | ... | ... | ... | ... | ... |
ffe9d9afeabd49c83ef8f7f5f09556cd | 24.526243 | 118.145790 | 2019-05-31 23:59:22 | 275 | 0.000000 | 1 |
ffea5e974b86b835b97eea1cd34fe3b8 | 24.587500 | 118.018143 | 2019-05-31 23:59:19 | 321 | 0.000000 | 1 |
ffedc235a5a3d891139a2d3ec2396b3e | 24.497433 | 118.136009 | 2019-05-31 23:59:35 | 155 | 0.000000 | 1 |
fff0bc740b883d541d098f5063e1f43f | 24.533175 | 118.139519 | 2019-05-31 23:59:46 | 61 | 0.000000 | 1 |
fff20f025f560278d601b2fd47e1f6b7 | 24.457750 | 118.075989 | 2019-05-31 23:59:49 | 334 | 25.093750 | 6 |
6768 rows × 6 columns
- 统计每辆巡游车最早最晚的时间间隔:
df = df_last['GPS_TIME'] - df_first['GPS_TIME']
df = df.reset_index()
df['GPS_HOUR'] = df['GPS_TIME'].dt.seconds / 3600
df['GPS_HOUR'] = df['GPS_HOUR'].astype(int)
df.set_index('CARNO', inplace=True)
GPS_TIME | GPS_HOUR | |
---|---|---|
CARNO | ||
0006d282be70d06881a7513b69fcaa60 | 22:28:38 | 22 |
000e8886a7b27ca761e34d59b1dee35c | 22:28:11 | 22 |
001df76bfa67259259f596c6dd353e6a | 22:28:38 | 22 |
001e3756542dc796b402dfd1b56fd4ec | 22:28:35 | 22 |
002b23a3762ea245f18cc896a55579d2 | 22:28:24 | 22 |
... | ... | ... |
ffe9d9afeabd49c83ef8f7f5f09556cd | 22:27:32 | 22 |
ffea5e974b86b835b97eea1cd34fe3b8 | 22:27:43 | 22 |
ffedc235a5a3d891139a2d3ec2396b3e | 22:28:11 | 22 |
fff0bc740b883d541d098f5063e1f43f | 22:27:45 | 22 |
fff20f025f560278d601b2fd47e1f6b7 | 22:28:23 | 22 |
6768 rows × 2 columns
- 统计每辆巡游车的经纬度和速度极差:
taxigps2019 = taxigps2019[taxigps2019['LATITUDE'] != 0]
taxigps2019 = taxigps2019[taxigps2019['LONGITUDE'] != 0]
df['LATITUDE_PTP'] = taxigps2019.groupby(['CARNO'])['LATITUDE'].apply(np.ptp)
df['LONGITUDE_PTP'] = taxigps2019.groupby(['CARNO'])['LONGITUDE'].apply(np.ptp)
df['GPS_SPEED_PTP'] = taxigps2019.groupby(['CARNO'])['GPS_SPEED'].apply(np.ptp)
df['LATITUDE_PTP']
# CARNO
# 0006d282be70d06881a7513b69fcaa60 0.082424
# 000e8886a7b27ca761e34d59b1dee35c 0.218086
# 001df76bfa67259259f596c6dd353e6a 0.089241
# 001e3756542dc796b402dfd1b56fd4ec 0.208961
# 002b23a3762ea245f18cc896a55579d2 0.113722
# ...
# ffe9d9afeabd49c83ef8f7f5f09556cd 0.113094
# ffea5e974b86b835b97eea1cd34fe3b8 0.203405
# ffedc235a5a3d891139a2d3ec2396b3e 0.254045
# fff0bc740b883d541d098f5063e1f43f 0.102463
# fff20f025f560278d601b2fd47e1f6b7 0.188639
# Name: LATITUDE_PTP, Length: 6768, dtype: float64
df['LONGITUDE_PTP']
# CARNO
# 0006d282be70d06881a7513b69fcaa60 0.167244
# 000e8886a7b27ca761e34d59b1dee35c 0.228439
# 001df76bfa67259259f596c6dd353e6a 0.111763
# 001e3756542dc796b402dfd1b56fd4ec 0.176155
# 002b23a3762ea245f18cc896a55579d2 0.113892
# ...
# ffe9d9afeabd49c83ef8f7f5f09556cd 0.082634
# ffea5e974b86b835b97eea1cd34fe3b8 0.204063
# ffedc235a5a3d891139a2d3ec2396b3e 0.160561
# fff0bc740b883d541d098f5063e1f43f 0.118431
# fff20f025f560278d601b2fd47e1f6b7 0.170204
# Name: LONGITUDE_PTP, Length: 6768, dtype: float64
df['GPS_SPEED_PTP']
# CARNO
# 0006d282be70d06881a7513b69fcaa60 79.6250
# 000e8886a7b27ca761e34d59b1dee35c 90.6875
# 001df76bfa67259259f596c6dd353e6a 74.1250
# 001e3756542dc796b402dfd1b56fd4ec 98.1250
# 002b23a3762ea245f18cc896a55579d2 78.1250
# ...
# ffe9d9afeabd49c83ef8f7f5f09556cd 85.1250
# ffea5e974b86b835b97eea1cd34fe3b8 83.3125
# ffedc235a5a3d891139a2d3ec2396b3e 98.1250
# fff0bc740b883d541d098f5063e1f43f 87.0000
# fff20f025f560278d601b2fd47e1f6b7 100.6875
# Name: GPS_SPEED_PTP, Length: 6768, dtype: float64
- 通过统计经纬度是不是全天都为0,我们可以剔除58辆全天GPS都异常的车。
- 计算一下每辆巡游车的平均经纬度(每天运动的中心),并绘制热力度:
df['LONGITUDE_MEAN'] = taxigps2019.groupby(['CARNO'])['LONGITUDE'].mean()
df['LATITUDE_MEAN'] = taxigps2019.groupby(['CARNO'])['LATITUDE'].mean()
df = df.dropna()
df['LONGITUDE_MEAN']
# CARNO
# 0006d282be70d06881a7513b69fcaa60 118.136398
# 000e8886a7b27ca761e34d59b1dee35c 118.063110
# 001df76bfa67259259f596c6dd353e6a 118.132309
# 001e3756542dc796b402dfd1b56fd4ec 118.124062
# 002b23a3762ea245f18cc896a55579d2 118.137367
# ...
# ffe9d9afeabd49c83ef8f7f5f09556cd 118.147385
# ffea5e974b86b835b97eea1cd34fe3b8 118.064346
# ffedc235a5a3d891139a2d3ec2396b3e 118.120026
# fff0bc740b883d541d098f5063e1f43f 118.122116
# fff20f025f560278d601b2fd47e1f6b7 118.128235
# Name: LONGITUDE_MEAN, Length: 6764, dtype: float32
df['LATITUDE_MEAN']
# CARNO
# 0006d282be70d06881a7513b69fcaa60 24.498341
# 000e8886a7b27ca761e34d59b1dee35c 24.573448
# 001df76bfa67259259f596c6dd353e6a 24.480791
# 001e3756542dc796b402dfd1b56fd4ec 24.505524
# 002b23a3762ea245f18cc896a55579d2 24.490599
# ...
# ffe9d9afeabd49c83ef8f7f5f09556cd 24.519753
# ffea5e974b86b835b97eea1cd34fe3b8 24.537611
# ffedc235a5a3d891139a2d3ec2396b3e 24.519087
# fff0bc740b883d541d098f5063e1f43f 24.505892
# fff20f025f560278d601b2fd47e1f6b7 24.480259
# Name: LATITUDE_MEAN, Length: 6764, dtype: float32
学习资源
学习Pandas
最好的教程是《利用 Python 进行数据分析》,这本书是Pandas
作者撰写,非常适合大家进行学习。如何用Pandas
实现的思路,如何实现的又快又好是需要积累经验的。
任务
- 统计
20190531
出租车在LONGITUDE(118.155060±0.01)、LATITUDE(24.506035±0.01)方位内打车的平均等待时间。
taxiOrder = pd.read_csv(
'data/taxiOrder20190531.csv',
dtype = {
'GETON_LONGITUDE': np.float32,
'GETON_LATITUDE': np.float32,
'GETOFF_LONGITUDE': np.float32,
'GETOFF_LATITUDE': np.float32,
'PASS_MILE': np.float32,
'NOPASS_MILE': np.float32,
'WAITING_TIME': np.float32,
}
)
taxiOrder = taxiOrder.rename(columns={'CAR_NO': 'CARNO'})
taxiOrder['GETON_DATE'] = pd.to_datetime(taxiOrder['GETON_DATE'])
taxiOrder['GETOFF_DATE'] = pd.to_datetime(taxiOrder['GETOFF_DATE'])
taxiOrder.sort_values(by=['CARNO', 'GETON_DATE'], inplace=True)
taxiOrder.reset_index(inplace=True, drop=True)
df = taxiOrder.loc[
(taxiOrder['GETON_LONGITUDE'] > (118.155060 - 0.01)) &
(taxiOrder['GETON_LONGITUDE'] < (118.155060 + 0.01)) &
(taxiOrder['GETON_LATITUDE'] > (24.506035 - 0.01)) &
(taxiOrder['GETON_LATITUDE'] < (24.506035 + 0.01))
]
df['WAITING_TIME'].mean()
# 276.4931945800781
- 统计
20190531
-20190609
期间出租订单经纬度上平均等待时间长的位置(且位置出现评率大于5)。
taxiOrder = []
filenames = [
'data/taxiOrder20190531.csv', 'data/taxiOrder20190601.csv', 'data/taxiOrder20190602.csv',
'data/taxiOrder20190603.csv', 'data/taxiOrder20190604.csv', 'data/taxiOrder20190605.csv',
'data/taxiOrder20190606.csv', 'data/taxiOrder20190607.csv', 'data/taxiOrder20190608.csv',
'data/taxiOrder20190609.csv'
]
for filename in filenames:
taxiOrder.append(
pd.read_csv(
filename,
dtype = {
'GETON_LONGITUDE': np.float32,
'GETON_LATITUDE': np.float32,
'GETOFF_LONGITUDE': np.float32,
'GETOFF_LATITUDE': np.float32,
'PASS_MILE': np.float32,
'NOPASS_MILE': np.float32,
'WAITING_TIME': np.float32,
})
)
taxiOrder = pd.concat(taxiOrder, ignore_index=True)
se_time = taxiOrder.groupby(['GETON_LONGITUDE', 'GETON_LATITUDE'])['WAITING_TIME'].mean().sort_values(ascending=False)
se = taxiOrder.groupby(['GETON_LONGITUDE', 'GETON_LATITUDE']).count()['CAR_NO'].sort_values(ascending=False)
for i, j in enumerate(se_time.index):
if se[j] > 5:
print(j, se[j], se_time[j])
break
# (118.16363525390625, 24.511585235595703) 6 32906.0
- 对比2019年和2020年出租车端午节订单的平均等待时间,是如何变化的(上升、下降还是不变)?
taxiOrder_2019 = []
taxiOrder_2020 = []
filenames_2019 = [
'data/taxiOrder20190607.csv', 'data/taxiOrder20190608.csv', 'data/taxiOrder20190609.csv'
]
filenames_2020 = [
'data/taxiOrder20200625.csv', 'data/taxiOrder20200626.csv', 'data/taxiOrder20200627.csv',
]
for filename in filenames_2019:
taxiOrder_2019.append(
pd.read_csv(
filename,
dtype = {
'GETON_LONGITUDE': np.float32,
'GETON_LATITUDE': np.float32,
'GETOFF_LONGITUDE': np.float32,
'GETOFF_LATITUDE': np.float32,
'PASS_MILE': np.float32,
'NOPASS_MILE': np.float32,
'WAITING_TIME': np.float32,
})
)
taxiOrder_2019 = pd.concat(taxiOrder_2019, ignore_index=True)
for filename in filenames_2020:
taxiOrder_2020.append(
pd.read_csv(
filename,
dtype = {
'GETON_LONGITUDE': np.float32,
'GETON_LATITUDE': np.float32,
'GETOFF_LONGITUDE': np.float32,
'GETOFF_LATITUDE': np.float32,
'PASS_MILE': np.float32,
'NOPASS_MILE': np.float32,
'WAITING_TIME': np.float32,
})
)
taxiOrder_2020 = pd.concat(taxiOrder_2020, ignore_index=True)
taxiOrder_2019['WAITING_TIME'].mean()
# 243.95602416992188
taxiOrder_2020['WAITING_TIME'].mean()
# 215.83151245117188