一.目的
基于基站定位数据的商圈分析
移动通信网络会记录用户手机的相关信息,比如手机所处的基站区域编号,所处基站的时间等。根据这些数据可以进行商圈划分,目的是
为了研究潜在的顾客的分布以制定适宜的商业对策。如:可划分商业区、住宅区以及工作区
二.数据
数据来源【Python数据分析与挖掘实战(第14章基于基站定位数据的商圈分析)】,可自行搜索下载。
原始数据集没有找到,找到的数据是被统计,转换,过滤过的数据:
这四个特征的统计方法是:
对于某个基站(观测窗口为L天,基站N个,用户M个;
某个用户i在j天在某个基站的 =》 工作日上班时间停留时间为weekday,凌晨停留时间为night,周末停留时间weekend,是否停留为stay【1:停留;0:无停留】):
工作日上班时间人均停留时间(9:00-18:00)
凌晨人均停留时间(00:00-07:00)
周末人均停留时间
日均人流量
基站编号,工作日上班时间人均停留时间,凌晨人均停留时间,周末人均停留时间,日均人流量
36902, 78, 521, 602, 2863
36903, 144, 600, 521, 2245
36904, 95, 457, 468, 1283
36905, 69, 596, 695, 1054
三.代码
通过提取和处理特征,利用聚类方法聚成几个区域,并对区域进行分析。详细代码:手机基站定位数据的商圈分析(https://github.com/jiangnanboy/spark_tutorial)
public static void businessCircleStatistics(SparkSession session) {
String path = PropertiesReader.get("intermediate_business_circle_csv");
/**
* +--------+--------------------------+----------------+----------------+----------+
* |基站编号 |工作日上班时间人均停留时间 |凌晨人均停留时间 |周末人均停留时间 |日均人流量|
* +--------+--------------------------+----------------+----------------+----------+
* | 36902| 78| 521| 602| 2863|
* | 36903| 144| 600| 521| 2245|
* | 36904| 95| 457| 468| 1283|
* | 36905| 69| 596| 695| 1054|
* | 36906| 190| 527| 691| 2051|
* +--------+--------------------------+----------------+----------------+----------+
* |-- 基站编号: integer (nullable = true)
* |-- 工作日上班时间人均停留时间: integer (nullable = true)
* |-- 凌晨人均停留时间: integer (nullable = true)
* |-- 周末人均停留时间: integer (nullable = true)
* |-- 日均人流量: integer (nullable = true)
*/
Dataset<Row> dataset = session.read()
.option("sep", ",")
.option("header", "true")
.option("inferSchema", "true")
.csv(path);
/**
* 转为特征向量
* +---------+--------------------+
* |stationID| features|
* +---------+--------------------+
* | 36902|[78.0,521.0,602.0...|
* | 36903|[144.0,600.0,521....|
* | 36904|[95.0,457.0,468.0...|
* | 36905|[69.0,596.0,695.0...|
* | 36906|[190.0,527.0,691....|
* +---------+--------------------+
*/
dataset = dataset.map((MapFunction<Row,Row>) row -> {
int stationID = row.getInt(0);
double weekdayAvg = (double) row.getInt(1);
double nightAvg = (double) row.getInt(2);
double weekendAvg = (double) row.getInt(3);
double stayAvg = (double) row.getInt(4);
return RowFactory.create(stationID, Vectors.dense(new double[]{weekdayAvg, nightAvg, weekendAvg, stayAvg}));
}, RowEncoder.apply(new StructType(new StructField[]{
new StructField("stationID", DataTypes.IntegerType,false, Metadata.empty()),
new StructField("features", SQLDataTypes.VectorType(), false, Metadata.empty())
})));
/**
* 数据标准化
*/
MinMaxScalerModel featureScaler = new MinMaxScaler()
.setInputCol("features")
.setOutputCol("scaledFeatures")
.fit(dataset);
/**
*+---------+--------------------+--------------------+
* |stationID| features| scaledFeatures|
* +---------+--------------------+--------------------+
* | 36902|[78.0,521.0,602.0...|[0.10386473429951...|
* | 36903|[144.0,600.0,521....|[0.26328502415458...|
* | 36904|[95.0,457.0,468.0...|[0.14492753623188...|
* | 36905|[69.0,596.0,695.0...|[0.08212560386473...|
* | 36906|[190.0,527.0,691....|[0.37439613526570...|
* +---------+--------------------+--------------------+
*/
Dataset<Row> scaledData = featureScaler.transform(dataset);
/**
* 轮廓系统确定簇数,可以看出分为3类最佳
* k: 2 silhouette: 0.5063659448997802
* k: 3 silhouette: 0.629019144457301
* k: 4 silhouette: 0.32319167016337247
* k: 5 silhouette: 0.30681655682008674
* k: 6 silhouette: 0.39947777279975305
* k: 7 silhouette: 0.31054738863541337
* k: 8 silhouette: 0.3417574406084828
* k: 9 silhouette: 0.30133745097199804
* k: 10 silhouette: 0.12586962519806658
*/
int k = selectOptimalK(scaledData, 10);
//model,利用层次聚类
BisectingKMeans bkm = new BisectingKMeans().setFeaturesCol("scaledFeatures")
.setK(k) //簇数
.setSeed(1);
BisectingKMeansModel model = bkm.fit(scaledData);
/**
* 预测结果,后面根据聚类结果划分出不同的商圈,对3类数据中的4个特征进行分析,定义3类商圈的不定定位进行商业活动,具体可看《python数据分析与挖掘实战》一书中的第14章。
* 根据3类数据的活动并异,可划分商业区、住宅区以及工作区
* +---------+--------------------+--------------------+----------+
* |stationID| features| scaledFeatures|prediction|
* +---------+--------------------+--------------------+----------+
* | 36902|[78.0,521.0,602.0...|[0.10386473429951...| 1|
* | 36903|[144.0,600.0,521....|[0.26328502415458...| 1|
* | 36904|[95.0,457.0,468.0...|[0.14492753623188...| 1|
* | 36905|[69.0,596.0,695.0...|[0.08212560386473...| 1|
* | 36906|[190.0,527.0,691....|[0.37439613526570...| 1|
* +---------+--------------------+--------------------+----------+
*/
Dataset<Row> predictions = model.transform(scaledData);
predictions.show(5);
/**
* 聚类中心
* [0.13227119317053798,0.04483188044831879,0.19956941131772793,0.7100471677339725]
* [0.1886016451233843,0.8021375921375923,0.7629929621455044,0.09096028267984407]
* [0.8643640466871185,0.048015925680159235,0.12134333562021299,0.3287583779747489]
*/
Vector[] centers = model.clusterCenters();
for(Vector center : centers) {
System.out.println(center);
}
}