基于手机信令数据的数据清洗和出行方式识别

数据来自2020年第十一届服务外包创新大赛A06赛题。

数据清洗

与定位误差不超过10米、时间采样均匀的GPS数据相比,手机信令数据的精确度较低且时间间隔不均匀,因此更需要对其进行数据清洗,下面对手机信令数据的特征做具体分析:


(1)存在大量脏数据

在数据清洗领域中,脏数据被定义为那些不在给定的范围内、对于实际业务毫无意义、或是数据格式非法、以及错误操作发生时产生的数据。移动通信网络在设计之初的主要目的是为了便利全球用户的移动通信,并未考虑对手机用户进行精确定位的需求,加上通信环境复杂所导致的多径效益、漂移效应等,使得手机信令数据中既包含大量的常规脏数据,即空值、重复值、错误值等,也包含漂移数据这类由于基站架设缺陷或是高层建筑干扰所产生的数据。因此,在对手机信令数据进行进一步的挖掘之前,首先对数据进行清洗是十分必要的。

(2)数据量巨大

据工业和信息化部统计显示,2018年我国净增移动电话用户达到1.49亿户,总数达到15.7亿户,移动电话用户普及率达到112.2部/百人,比2017年末提高了10.2部/百人。数据同时显示,2018年,全国净增移动通信基站29万个,总数达648万个。其中4G基站净增43.9万个,总数达到372万个。4G用户总数达到11.7亿户,全年净增1.69亿户。同时,手机产生的信令数据量也在不断上升。通常中国一线城市,每分钟产生的信令数据量高达180兆,即每天15亿条数据。潍坊电信于2015年9月1日产出的数据总量为4.14G,共包含19万用户一共7733万个轨迹点。由此可见常规的单节点计算机是无法满足如此巨大的计算需求的,因此本文使用Hadoop分布式系统作为数据处理的平台。

(3)时间间隔不均匀

手机用户的主被动上网行为均具有不确定性,这导致了每名手机用户轨迹点的时间采样间隔并不均匀。某些连续点的采样间隔不足1秒,但有些连续点的采样间隔却有数分钟,如何在信令数据清洗与挖掘过程中规避其时间间隔不均匀的缺陷,是后续需要考虑的问题。

针对手机信令数据中包含大量脏数据的问题,本章设计了一种针对手机信令数据的层次化清洗方案:首先将手机信令数据看作常规数据,清洗掉其中的无效数据;随后针对手机信令数据所特有的漂移及乒乓切换现象,设计相应的算法对其进行处理,最后将清洗后的数据以人为单位按时间正序排序。

数据清洗过程:

1、清洗无效数据

无效数据通常包括异常记录和重复记录。

(1)缺失数据属于一种异常记录,由于信令系统可能会出现不稳定等情况,则会造成少量的数据缺失,这些缺失数据必然会对后续的分析带来严重的影响,因此需要将其进行剔除。通过遍历数据表中的所有字段,筛选出多个字段为空值的记录,并将该数据行删除。若一条记录仅存在少数几个空值字段,则可采用采用均值、众数、最大值、最小值、中位数等方法补全数据。

(2)错误数据也是属于一种异常记录,这里的错误数据是指那些不在监测范围内的数据以及解码错误的数据。如,要统计数据字典中某一区域过夜信息(21:00-次日07:00)时,需要遍历数据表中相关字段的标签,将不在该区域和对应时间段内的数据过滤掉。

(3)重复数据是指各字段完全相同的连续数据,这种无效数据往往也是由于信令采集系统不稳定所造成的。大量的重复数据会增加运算成本,同时对于区域人口驻留信息、区域人口停留信息等停留点信息的识别会带来一定程度的误差。因此,采用基本字段匹配算法,对数据库数据的重复记录检测以及重复元素检测,识别出多条重复记录,将第一条记录的结束时间改为最后一条记录的结束时间,保留第一条记录即可。

2、处理异常数据

在实际情况下,由于移动通信网络自身存在的缺陷和外界环境的干扰,手机通信信号往往会受到多种因素的影响,于是所生成的手机信令数据会产生以下几种类型异常数据,很好的识别并剔除这些数据,能够有助于对人口密度及交通流量的监测。

(1)冗余数据

为了确保手机和基站之间不中断的联系,以实现阶段性的位置更新,因此需要手机隔一段时间向移动通信网络发送一条当前所谓的基站信息。但是当用户长时间停留在某一固定地点时,就会产生大量的位置更新信息,也就是是所谓的冗余位置数据。这些由于静止状态而产生的冗余信息是无法反映人口的动态特征,同时会增加无效数据量,因此不利于人口的动态监测,需要遍历所有用户记录,识别出由于不同原因所产生的冗余数据并将其剔除。例如,针对数据字典中的区域出现量统计,遍历所有用户记录,识别出小区编号和位置区编号字段长时间未发生变化的用户,也就意味着该用户没有出行,则在统计区域出现信息时不应考虑该用户。

(2)乒乓数据

乒乓数据是指手机在多个相邻近的基站间出现交替切换现象而产生的数据,但用户并未发生一定距离的移动行为。理论上,当用户移动到某一基站时,会自动切换到该基站的信号。实际上,在移动通信网络中由于无线电波传播过程中存在反射、绕射、散射等现象导致很短的时间段内信号出现异常的波动,也就是所谓的“乒乓效应”,可见这类数据会高估出行人口的数量,因此需要处理这些数据。遍历所有记录,识别出在两个基站间连续多次切换,且切换间隔仅为几秒钟的数据行,并将其剔除。

(3)漂移数据

在某些情况下,手机信号会从邻近的基站突然切换到相对较远的基站,并在一定时间之后切换回邻近的基站,这种现象为信号漂移现象,由此产生的数据为漂移数据。这种漂移数据的出现会对人口的动态监测产生严重的影响,例如在样本人流向统计中,很可能多统计较远基站的人口,而少统计邻近基站的人口,不仅如此,所统计的去向区域也会存在错误。因此,需要对漂移的数据进行判定。设置城市漂移速度阈值,通过计算第i条数据和第i+l条数据的距离和时间间隔,进而得到其速度v,若该速度v超过阈值,那么删除第i+l条数据并重新计算第i条数据和第i+2条数据的速度重复上述比较过程(前提是两条数据为同一用户),以此过程遍历所有的数据。

综上所述,本系统数据清洗过程如下:

  1. 抽取timestamp,imsi,lac_id,cell_id 四个字段
  2. 去除空间信息残缺的记录条目(imsilac_idcell_id中为空)
  3. 去除imsi中,包含特殊字符的数据条目(‘#’,’*’,’^’
  4. timestamp时间戳转换格式 20190603000000--年月日时分秒
  5. 去除干扰数据条目(不是2018.10.03当天的数据)
  6. 去除两数据源关联后经纬度为空的数据条目
  7. 去除异常数据(冗余、乒乓、漂移数据)
  8. 去除重复数据
  9. 以人为单位,按时间正序排序

源代码:


import pandas as pd

import numpy as np



#读取数据

data = pd.read_csv(r'./dataset/original.csv')

#提取所需字段

data.drop(labels=['phone', 'tmp0','tmp1','nid','npid'], axis=1, inplace=True)

#去除空间信息残缺的记录

for i in data['imsi'][data['imsi'].isnull() == True].index:

    data.drop(labels=i, inplace=True)

for i in data['cell_id'][data['cell_id'].isnull() == True].index:

    data.drop(labels=i, inplace=True)

for i in data['lac_id'][data['lac_id'].isnull() == True].index:

    data.drop(labels=i, inplace=True)

#去除imsi中含特殊字符的数据

data = data[ ~ data['imsi'].str.contains('#', regex=False)]

data = data[ ~ data['imsi'].str.contains('*', regex=False)]

data = data[ ~ data['imsi'].str.contains('^', regex=False)]

#时间戳转换

data['timestamp']=pd.to_datetime(data['timestamp']//1000,unit='s')

data['timestamp'] = data['timestamp'].apply(lambda x:x.strftime('%Y%m%d%H%M%S'))

#去除干扰条目

data = data[data['timestamp'].str.contains('20181003', regex=False)]

#重置序列index

data = data.reset_index(drop=True)

#去除两数据源关联后经纬度为空的数据

data['lac_id'] = data['lac_id'].astype(int)

data['cell_id'] = data['cell_id'].astype(int)

data['laci'] = data['lac_id'].map(str)+"-"+data['cell_id'].map(str)

for i in data['cell_id'][data['longitude'].isnull() == True].index:

    data.drop(labels=i, inplace=True)

for i in data['lac_id'][data['latitude'].isnull() == True].index:

    data.drop(labels=i, inplace=True)

#重置序列index

data = data.reset_index(drop=True)

#清洗乒乓数据

def cleaning_PP_data(new_data):

  groups = new_data.groupby('imsi')

  def is_between(time):

      timestart = 20181003020000

      timeend = 20181003060000

      if time > timestart and time < timeend:

          return True

  count = 0

  for name,group in groups:#

  #group = groups.get_group(460000095005565778)

      group = group.sort_values(by=['timestamp', 'timestamp1'], axis= 0, ascending=True).reset_index(drop=True)

      #print(name)

      #print(group)

      #print(len(group))

      #循环标记乒乓记录 ['PP'] = True

      group['PP'] = False

      for i in range(0 , len(group) -2):

          laci1 = group.at[i,'laci']

          laci2 = group.at[i+1,'laci']

          laci3 = group.at[i+2,'laci']

          if laci1 == laci3 and laci1 != laci2:

              group.at[i+1,'PP'] = True

      #print(group)

      list_PP_duplicates = list(group.duplicated(['PP']))

      #print(list_PP)

      i = 0

      #循环查找单乒乓记录(例:CABAD),计算穿越速度并与步行平均速度(6km/h)比较 大于则 B数据laci及经纬度替换为A的数据

      while i < len(group) -2:

          PP1 = group.at[i,'PP']

          PP2 = group.at[i+1,'PP']

          PP3 = group.at[i+2,'PP']

          if PP2 == True and PP1 == False and PP3 == False:

              distance = geodistance(group.at[i+1,'longitude'],group.at[i+1,'latitude'],group.at[i+2,'longitude'],group.at[i+2,'latitude'])

              if distance > 1 :

                  timespan = get_hours(str(get_datetime(group.at[i+2,'timestamp']) - get_datetime(group.at[i+1,'timestamp'])))

                  speed = distance / timespan

                  if speed > 6:

                      group.at[i+1,'laci'] = group.at[i,'laci']

                      group.at[i+1,'longitude'] = group.at[i,'longitude']

                      group.at[i+1,'latitude'] = group.at[i,'latitude']

              #将凌晨02:0O:00—06:0O:00来回切换的数据(指其中的B记录的timel或time2落在该时段内),直接替换为相邻坐标。

              elif is_between(group.at[i+1,'timestamp']) or is_between(group.at[i+1,'timestamp1']):

                  group.at[i+1,'laci'] = group.at[i,'laci']

                  group.at[i+1,'longitude'] = group.at[i,'longitude']

                  group.at[i+1,'latitude'] = group.at[i,'latitude']

          elif PP2 == True and PP1 == False and PP3==True:#多乒乓记录(例:CABABD以及CABABAD),随list_PP_duplicates寻找乒乓结束点,从前往后逐个替换为前一条记录坐标

              group.at[i+1,'laci'] = group.at[i,'laci']

              group.at[i+1,'longitude'] = group.at[i,'longitude']

              group.at[i+1,'latitude'] = group.at[i,'latitude']

              t = 2

              while list_PP_duplicates[i+t]==True:

                  group.at[i+t,'laci'] = group.at[i+t-1,'laci']

                  group.at[i+t,'longitude'] = group.at[i+t-1,'longitude']

                  group.at[i+t,'latitude'] = group.at[i+t-1,'latitude']

                  t += 1

              i = i + t + 1

              continue

          i += 1   

      list_duplicates = list(group.duplicated(['laci']))

      tmp_index = None

      tmp = None

      #print(len(list_duplicates))

      for i in range(0,len(list_duplicates)):

          if list_duplicates[i] == False :

              #print(i)

              #print(tmp)

              #print(tmp_index)

              if tmp_index != None:

                  if tmp != None:

                      group.at[tmp_index,'timestamp1'] = tmp

                      tmp = None

                      tmp_index = i

              else:#首条记录

                  tmp_index = i

          else:

              tmp = group.at[i,'timestamp1']

      #print(group)

      group = group.drop_duplicates(['laci'])

      if count == 0:

          new_data1 = group

          count += 1

      else:

          new_data1 = new_data1.append(group)

  new_data1 = new_data1.reset_index(drop=True)

  new_data1.drop(labels=['PP'], axis=1, inplace=True)

  #new_data1.to_csv('./result/tmp.csv')

  return new_data1



#清洗漂移数据

def Cleaning_drift_data(new_data):

  groups = new_data.groupby('imsi')

  count = 0

  for name,group in groups:#

      #print(name)

      #print(group)

  #group = groups.get_group(460000095005565778)

      group = group.sort_values(by=['timestamp', 'timestamp1'], axis= 0, ascending=True).reset_index(drop=True)

      i = 0

      #循环查找用户连续记录计算穿越速度>120则删除第i+1 条记录,继续查找i 与i+2(此时变成i+1)

      while i < len(group)-1 :

          distance = geodistance(group.at[i,'longitude'], group.at[i,'latitude'], group.at[i+1,'longitude'], group.at[i+1,'latitude'])

          #print(distance)

          timespan = get_hours(str(get_datetime(group.at[i+1,'timestamp']) - get_datetime(group.at[i,'timestamp1'])))

          if timespan != 0:

              speed = distance / timespan

          else :

              pass

              #print(group[group.index == i],group[group.index == i+1])

          if speed > 120 :

              group.drop(group.index[i + 1],inplace=True)

              group = group.reset_index(drop = True)

              if i != len(group) -1:

                  continue

          i += 1

      list_duplicates = list(group.duplicated(['laci']))

      tmp_index = None

      tmp = None

      #print(len(list_duplicates))

      for i in range(0,len(list_duplicates)):

          if list_duplicates[i] == False :

              #print(i)

              #print(tmp)

              #print(tmp_index)

              if tmp_index != None:

                  if tmp != None:

                      group.at[tmp_index,'timestamp1'] = tmp

                      tmp = None

                      tmp_index = i

              else:#首条记录

                  tmp_index = i

          else:

              tmp = group.at[i,'timestamp1']

      #print(group)

      group = group.drop_duplicates(['laci'])

      if count == 0:

          new_data2 = group

          count += 1

      else:

          new_data2 = new_data2.append(group)

  new_data2 = new_data2.reset_index(drop=True)

  return new_data2

#去除重复数据

data = data.drop_duplicates().reset_index(drop=True)

#以人为单位,按时间正序排序

data = data.sort_values(by=['imsi', 'timestamp'], axis= 0, ascending=True).reset_index(drop=True)

出行方式识别-基于先验知识计算各出行方式的隶属度的模糊辨识模型

参考文献:[1]刘华斌. 手机信令数据背景下城市交通出行方式选择辨识方法研究[D].北京交通大学,2019.DOI:10.26944/d.cnki.gbfju.2019.001045.

对城市整体尺度而言,一天中各区域、各断面的交通状态差异巨大,居民出行行为也非一成不变,采用固定的特征标准规则判断出行特征取值阈值显然会出现严重。城市交通的这种复杂特性,十分适合使用模糊理论进行数学描述。故本系统以模糊理论为基础,采用出行距离、出行时长和出行速度三种出行特征描述出行方式。利用总结的出行特征先验知识,初步确定5种出行方式各自出行方式特征的基本隶属度函数形式,根据数据集城市(广州市)的居民出行实际情况标定函数参数,构建出行方式模糊辨识模型,对大样本城市交通出行方式进行基本辨识。

  1. 特征先验知识提取

       进入21世纪以来,不同规模城市相继开展了城市居民出行调查,如:北京、上海、广州、深圳、石家庄等,积攒了大量珍贵的出行调查数据,统计了各种城市交通出行方式的出行特征。大量学术文献基于此类调研数据,对各种出行方式的出行距离、出行时长、出行平均速度等特征进行了分析、归纳和总结。近十年来,一些国内大型城市如北京、广州等还会以交通发展年度报告、交通运输运行年报的形式定期发布各市交通运行统计数据,为针对出行的各种研究提供了重要的参考资料。

       常见的城市交通出行特征及取值范围如下表所示,分别整理自北京市2017年和广州市2017年各城市交通出行方式特征统计结果。

2017年北京市交通出行方式特征表

2017年广州市交通出行方式特征表

       基于上述统计知识,给出适用于本系统的各出行方式特征先验知识如下

(1)各出行方式平均出行距离范围:

选择步行出行的平均出行距离范围为:0-1km,通常情况下不超过2km;

选择自行车出行的平均出行距离范围为:0—3km,通常情况下不超过5km;

选择公交车出行的平均出行距离范围为:0-15km,通常不低于1km,不超过20km;

选择小汽车出行的平均出行距离范围为:0一30km,通常不低于2km;

选择地铁出行的平均出行距离范围为:0-30km,通常不低于3km。

(2)各出行方式平均出行时长范围:

选择步行出行的平均出行时长范围为:0-25min,通常情况下不超过30min;

选择自行车出行的平均出行时长范围为:5-25min,通常情况下不超过30min;

选择公交车出行的平均出行时长范围为:10-40min,通常情况下不超过60 min;

选择小汽车出行的平均出行时长范围为:10一90min,通常情况下不低于10 min;

选择地铁出行的平均出行时长范围为:10——80min,通常情况下不低于10min。

(3)各出行方式平均出行速度范围:

选择步行出行的平均出行速度范围为:0一5km/h,通常情况下不超过8km/h;

选择自行车出行的平均出行速度范围为:5——15km/h,通常情况下不低于2km/h,通常不超过18km/h;

选择公交车出行的平均出行速度范围为:10-20km/h,通常情况下不低于5km/h,通常不超过30km/h;

选择小汽车出行的平均出行速度范围为:15—40km/h,通常情况下不低于10km/h,不超过60km/h;

选择地铁出行的平均出行速度范围为:10——30km/h,通常情况下不低于10km/h,不超过40km/h。

  1. 模糊理论基本概念

       美国学者扎德(L.A.Zadeh)于1965年创建了一种用于描述“含糊”现象的数学理论——模糊集合论。这种方法把待考察对象及对其描述的模糊概念作为模糊集合,构建适当的隶属函数,通过模糊集合有关的运算及变换,对模糊对象进行分析。模糊集合论以模糊数学为基础,研宄有关非精确的现象。以思维中年轻、很好、暖和等概念为例,其所描述的对象属性并不能简单地用“是”或“否”来回答。模糊集合就是指类似这样的具有某个模糊概念所描述的对象的全体。

       模糊集常用表示方法有:

  1. 解析法,即给出隶属函数的具体表达式;
  2. Zadeh记法。如,分母是论域中的元素,分子是该元素对应的隶属度;
  3. 序偶法。如,序偶对的前者是论域中的元素,后者是该元素对应的隶属度;
  4. 向量法,在有限论域的场合,给论域中元素规定一个表达的顺序,则可以将序偶法简写为隶属度的向量式,如

       本系统构建模糊集如下:

                                   出行方式模糊集合

                                                 出行特征模糊集合

  1. 隶属度函数

       本系统采用隶属度函数给出模糊集(出行方式特征)的解析式。隶属度是模糊评价函数里的概念,是对受多种因素影响的事物做出全面评价的一种十分有效的多因素决策方法,其特点是评价结果不是绝对地肯定或否定,而是以一个模糊集合来表示。若对研究的范围 U 中的任一元素x,都有一个与之对应,则称A为U上的模糊集,A(x)为x对A的隶属度。当x在U中变动时,A(x)为A的隶属函数。

       用隶属度函数表征x属于A的程度的高低。隶属度A(x)越接近于1,意味着x属于A的程度越高;反之,A(x)越接近于0则x属于A的程度越低。

       正确定义隶属度函数,是运用模糊集合理论解决实际问题的基础。隶属度函数的一般确定原则如下:

  1. 用来表示隶属度函数的模糊集合应是凸模糊集合;
  2. 变量所取的隶属度函数通常是对称且平衡的,模糊变量的标值选择一般为3—9个为宜;
  3. 隶属度函数应该避免不合适的重复;
  4. 应对在相同的研宄范围上使用的具有语意顺序的模糊集合进行合理排序;
  5. 研究范围中,每个点应隶属于至少一个隶属度函数区域;
  6. 同一个输入值不应出现多于一个隶属度函数上同时有最大隶属度的现象;
  7. 对两个隶属度函数有重叠部分的情况存在时,重叠部分对两个隶属度函数的最大隶属度不应该有交叉。

       典型的隶属度函数一般有三角形隶属度函数、梯形隶属度函数、高斯隶属度函数、S型隶属度函数等。本系统采用高斯、S型两种隶属度函数,具体如下。

出行距离隶属度函数           出行时长隶属度函数      出行平均速度隶属度函数

  1. 辨识模型函数设计及辨识规则

       (1)模型函数设计

              根据先验知识,对于在“正负”方向均有限制的某种出行方式某种出行特征,采取高斯隶属函数;对于单方向有限制的出行特征,采用S型隶属度函数。

       (2)辨识规则

       根据出行距离、出行时长和平均出行速度3个影响特征综合辨识居民单次出行所选择的出行方式。辨识规则如下:

  1. 通过步行的出行距离隶属度、步行的出行时长隶属度和步行的平均出行速度隶属度,辨识步行出行方式隶属度值;
  2. 通过自行车的出行距离隶属度、步行的出行时长隶属度和步行的平均出行速度隶属度,辨识步行出行方式隶属度值;
  3. 通过公交车的出行距离隶属度、步行的出行时长隶属度和步行的平均出行速度隶属度,辨识步行出行方式隶属度值;
  4. 通过小汽车的出行距离隶属度、步行的出行时长隶属度和步行的平均出行速度隶属度,辨识步行出行方式隶属度值;
  5. 通过地铁的出行距离隶属度、步行的出行时长隶属度和步行的平均出行速度隶属度,辨识步行出行方式隶属度值;

       (3)基础建模

       辨识结果规定为:被分别判定为5种交通方式的隶属度最大值所对应的交通方式。(做PPT只要下面两个公式)

  • 8
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值