Scikit-mobility:一个用于分析、生成和风险评估移动数据的Python库
仅用于站内搜索,没有排版格式,具体信息请跳转上方微信公众号内链接
过去十年见证了大规模移动数据集的出现,例如由GPS设备生成的轨迹、通话详单记录和来自社交媒体平台的地理标记帖子。这些数据集促进了各种移动性分析应用的广泛科学产出,从计算流行病学到城市规划和交通工程。
文献主要涉及:与原始时空轨迹相关的数9据清洗问题;侧重于发现支配人类移动的统计“规律”;设计能够逼真地重现人类移动规律的合成轨迹生成算法;致力于解决关键的隐私问题,提出了在数据库中执行个人再识别的技术。但是,目前还没有一款统计软件能够支持科学家和从业者处理移动数据分析中提到的所有方面。
本文,我们将介绍一个实用的Python库——scikit-mobility,它旨在提供一系列API,方便快捷分析移动数据和模拟人类移动习惯。
关键词
数据科学;人类移动性;移动性分析;时空分析;大数据;网络科学;数据挖掘;Python;数学建模;迁移模型;隐私
过去十年见证了数字轨迹大规模数据集的出现,以前所未有的规模和细节描绘了人类的移动轨迹。相关研究逐渐展开,涉及到不同的领域。这些领域的研究都表明,人类移动不是随机的,而是具有可预测的模式。因此,研究人类移动,对各行各业都有广泛现实意义。
尽管移动性分析对许多科学和工业领域越来越重要,但目前还没有一款统计软件能够支持科学家和从业者处理所有移动性分析内容。为了填补这一空白,scikit-mobility被开发了出来。
scikit-mobility是一个Python库,旨在为科学家和从业者提供一个环境,以重现现有研究并对移动数据进行分析。具体而言,该库允许用户:
加载和表示个人和集体级别的移动性数据。
使用最先进的技术对移动性数据进行清洗和预处理,例如轨迹聚类、压缩、分段和过滤。
通过使用个人和集体级别上刻画移动模式的主要指标分析移动性数据,例如旅行和特征距离的计算、对象和位置熵、位置频率、等待时间、起始-目的地矩阵等等。
运行最流行的机制生成模型来模拟个体移动性,例如探索和偏好返回模型(EPR)及其变体,以及通勤和迁移流动,例如引力模型和辐射模型。
通过模拟与隐私攻击的大量回识别风险相关联,估计与给定移动性数据集分析相关的隐私风险。
scikit-mobility可在GitHub上公开获取,链接为:https ://scikit-mobility. github.io/scikit-mobility/。
有关如何使用该库进行移动性分析的教程可在以下链接中找到:https ://github. com/scikit-mobility/tutorials。
描述scikit-mobility的所有类和函数的文档可在以下链接中找到:https ://scikit-mobility. github.io/scikit-mobility/。
scikit-mobility提供了两种数据结构,用于处理原始轨迹和地点之间的流动。这两种数据结构都是在DataFrame(Pandas核心对象之一)的基本上扩展。因此,TrajDataFrame和FlowDataFrame都继承了DataFrame提供的所有功能,以及读写表格数据(例如移动性数据集)的高效优化。这个选择使得scikit-mobility具有广泛的兼容性。
需要注意的是,当前版本的库设计用于处理纬度和经度系统(epsg:4326),这是移动性分析实际场景中最常用的系统。因此,当库的函数计算距离时,默认使用Haversine公式。
移动性数据描述了一组对象在观测期间的移动情况。这些对象可以代表个体、动物、私人车辆、船只,甚至是运动场上的运动员。移动性数据通常以自动方式收集,作为人类活动在电子设备上的副产品(如手机、GPS设备、社交网络平台、视频摄像机),并以轨迹的形式存储,轨迹是时间顺序的一系列时空点,表示对象在某一位置停留或穿过。
在移动性分析的文献中,轨迹通常被形式化定义如下:对象u的轨迹是一个时间顺序的元组序列,其中是一个位置,和是该位置的坐标,是相应的时间戳,如果<,则有<。
在scikit-mobility中,一组轨迹由TrajDataFrame(图1)描述。
TrajDataFrame是pandasDataFrame的扩展,具有特定的列名和数据类型。TrajDataFrame中的一行表示轨迹的一个点,由三个必填字段(也称为列)描述:纬度(类型:浮点数)、经度(类型:浮点数)和日期时间(类型:日期时间)。
此外,还可以指定两个可选列。第一个是uid:它标识与轨迹点相关联的对象,可以是任何类型(字符串、整数或浮点数)。如果uid不存在,scikit-mobility假设TrajDataFrame包含与单个移动对象相关联的轨迹。第二个是tid(任意类型),指定轨迹点所属轨迹的标识符。如果tid不存在,scikit-mobility假设TrajDataFrame中所有与uid相关联的行都属于同一个轨迹。需要注意的是,除了必填列之外,用户可以根据需要向TrajDataFrame添加任意数量的列,因为scikit-mobility中的数据结构继承了所有pandasDataFrame的功能。
每个TrajDataFrame还具有两个必填属性:
crs(类型:字典):表示与轨迹相关联的坐标参考系统。默认为epsg:4326(纬度/经度参考系统)。
parameters(类型:字典):表示已应用于TrajDataFrame的操作。该属性是一个字典,其中键是已应用的函数的标识(详见第3节)。
然后,我们使用TrajDataFrame类的from_file方法从文件路径加载移动性数据。
需要注意的是,与lat、lng和datetime列对应的值必须分别为float、float和datetime类型,否则库会引发异常。
TrajDataFrame构造函数强制将三个必填列的值转换为预设类型。只有在转换失败时,它才会引发异常。例如,构造函数可以成功将字符串\“39. 1432\“转换为浮点数39. 1432,但无法将字符串\“39. 2ui2\“转换为浮点数(因此引发异常)。
起始地-目的地矩阵,也称为流动,是移动性数据的另一种常见表示形式。而轨迹是指单个对象的移动,流动则是指一组地点之间对象的聚合移动。流动的一个例子是城市社区之间的日常通勤流动。形式上,我们定义起始地-目的地矩阵如下:
起始地-目的地矩阵T是一个n×m矩阵,其中n是不同的“起始地”位置数,m是不同的“目的地”位置数,Tij是从位置i到位置j的对象数量。
在scikit-mobility中,起始地-目的地矩阵由FlowDataFrame结构描述。FlowDataFrame是pandasDataFrame的扩展,具有特定的列名和数据类型。
FlowDataFrame中的一行表示两个地点之间的对象流动,由三个必填列描述:起始地(任意类型)、目的地(任意类型)和流动量(类型:整数)。同样,用户可以根据需要向FlowDataFrame添加任意数量的列。
在移动性任务中,通常通过将坐标映射到空间分割来将领域离散化,即使用可数数量的几何形状(例如正方形、六边形)覆盖二维空间的空间镶嵌,称为瓦片,不重叠且没有间隙。例如,在对移动流动进行分析或预测时,空间镶嵌用于聚合人们在位置之间移动的流动(镶嵌的瓦片)。因此,每个FlowDataFrame都与一个空间镶嵌关联,该空间镶嵌是一个geopandasGeoDataFrame,包含两个必填列:tile_ID(任意类型)表示位置的标识符;geometry表示描述位置在领域上的几何形状(例如正方形、六边形、邻域的形状)。
需要注意的是,FlowDataFrame中起始地和目的地列中的每个位置标识符必须存在于关联的空间镶嵌中。否则,库会引发异常。类似地,如果FlowDataFrame中的起始地和目的地列的类型与关联的空间镶嵌中的tile_ID列的类型不同,scikit-mobility也会引发异常。
以下代码从对应的文件中加载空间镶嵌(spatialtessellation,或空间网格)和FlowDataFrame。
空间镶嵌和FlowDataFrame的结构如下所示。
由于空间镶嵌是一个geopandasGeoDataFrame,它支持任何类型的几何形状(例如多边形、点)。然而,应避免使用点几何形状,因为它无法正确表示空间镶嵌的瓦片。一般来说,应优先选择多边形和多多边形形状来描述瓦片。
与任何分析过程一样,移动性数据分析需要进行数据清理和预处理步骤。预处理模块允许用户执行三个主要的预处理步骤:噪声过滤、停留检测和轨迹压缩。需要注意的是,如果TrajDataFrame包含多个用户的多个轨迹,预处理方法会自动应用于单个轨迹,并在必要时应用于单个对象。
轨迹数据通常存在噪声,通常是由于记录错误,例如信号接收差。当点的坐标与之前的点相比存在较大误差时,最好的解决方案是过滤掉这些点。在scikit-mobility中,filter方法会过滤掉速度大于max_speed参数(默认设置为500km/h)的点。为了使用filter函数,首先导入预处理模块。
然后,我们在包含GPS轨迹的TrajDataFrame上应用过滤,将最大速度设置为10km/h:
如上所示,217,653个点中有108,874个点被过滤掉。过滤的强度由max_speed参数控制,值越小,过滤越强。
轨迹中的一些点可以表示兴趣点(POIs),例如学校、餐厅和酒吧,或者它们可以表示用户特定的位置,如家和工作地点。这些点通常被称为停留点(StayPoints)或停靠点(Stops),可以用不同的方式检测到。常见的方法是应用空间聚类算法,通过观察轨迹点的空间邻近性将它们聚类在一起。在scikit-mobility中,检测模块中的stops函数可以找到对象访问的停留点。例如,要识别停留点,其中对象在给定的点周围至少停留minutes_for_a_stop分钟,在距离spatial_radius_km×stop_radius_factor的范围内,我们可以使用以下代码:
如代码片段所示,为了指示用户离开停留位置的时间,在TrajDataFrame中添加了一个新列leaving_datetime。
轨迹压缩的目标是减少轨迹点的数量,同时保留轨迹的结构。这一步通常在停留点检测步骤之后应用,并且会显著减少轨迹点的数量。在scikit-mobility中,我们可以使用预处理模块下的compression模块中的方法之一。例如,要合并所有彼此距离小于0. 2km的点,可以使用以下代码:
一旦压缩完成,轨迹将呈现较少的点数,可以通过使用scikit-mobility中描述的数据可视化功能轻松绘制这些点。表1列出了scikit-mobility中可用的轨迹预处理方法。
scikit-mobility的一个使用场景是对移动数据集进行探索性数据分析,其中包括轨迹和流的可视化。
为此,TrajDataFrame和FlowDataFrame都具有方法,允许用户使用folium库生成交互式可视化。选择folium的原因是,鉴于移动数据的复杂性,用户可能需要缩放和与轨迹、流和分割进行交互。这种交互在静态绘图库(如matplotlib)中是不可能的。用户可以将交互式绘图保存在. html文件中,或者可以截屏并保存为. png文件。
TrajDataFrame的plot_trajectory方法在地图上绘制按时间排序的轨迹点,通过直线将它们连接起来。如果列uid存在且包含多个对象,则轨迹点首先按uid进行分组,然后按datetime进行排序。具有许多点的大型TrajDataFrame在可视化时可能会计算量很大。可以使用两个参数来减少要绘制的数据量:max_users(类型:int,默认值:10)限制要绘制轨迹的对象数量,而max_points(类型:int,默认值:1000)限制每个对象要绘制的轨迹点数量,即如果需要,将对对象的轨迹进行下采样,并绘制最多max_points个点。可以通过参数自定义绘图样式,以指定轨迹线的颜色、线宽、透明度,以及要使用的地图瓦片类型。用户还可以绘制标记,表示轨迹的起点和终点。
plot_trajectory方法以及所有其他绘图方法都返回一个folium. Map对象,可以通过其他folium和scikit-mobility函数将额外的数据可视化在同一地图上。folium. Map对象可以通过map_f参数传递给绘图方法(默认值为None,表示将移动数据绘制在新地图上)。
下面是plot_trajectory方法生成的绘图示例:
注意,如果轨迹代表抽象的移动性,例如从社交媒体帖子或手机通话中提取的移动性,可能会出现不考虑道路网络上的墙壁、建筑物和类似结构的直线。
默认情况下,TrajDataFrame表示一组个体的完整移动性,即覆盖整个观测期(例如一个月)。用户可以使用预处理函数(如detection. stops函数)将个体的轨迹分割,并将整个轨迹分割成子轨迹,添加适当的列来标识它们(例如tid列)。此时,用户可以通过选择创建的列的值来可视化TrajDataFrame的部分内容。
TrajDataFrame的plot_stops方法在地图上将停留点的位置绘制为标记。此方法需要具有constants. LEAVING_DATETIME列的TrajDataFrame,该列由scikit-mobility函数用于检测停留点创建。参数max_users(类型:int,默认值:10)限制要绘制停留点的对象数量。可以通过参数自定义绘图样式,以指定标记的颜色、半径和透明度,以及要使用的地图瓦片类型。popup参数(默认值:False)允许增强绘图的交互性,当用户点击标记时,弹出窗口会显示在停留点的信息,如坐标、对象的uid、到达时间和离开时间。
下面是由plot_stops方法生成的绘图示例。请注意,如果TrajDataFrame中存在cluster列(例如在应用cluster方法时),则停留点会根据该列的值自动上色(以区分不同的停留点簇)。
绘图在x轴上显示时间,并显示一系列不同颜色的矩形,表示对象对各个停留点的访问。矩形的长度表示访问的持续时间:左边缘表示到达时间,右边缘表示离开时间。矩形的颜色表示停留点的聚类:属于同一聚类的停留点具有相同的颜色(颜色代码与plot_stops方法使用的代码一致)。白色矩形表示对象正在移动。
下面是由plot_diary方法生成的绘图示例:
用户可以将多个移动对象的日记绘制在一起以进行比较:
FlowDataFrame有两种主要的绘图方法:plot_tessellation在地理地图上绘制空间镶嵌的瓦片,plot_flows在地理地图上绘制连接空间镶嵌瓦片之间存在流动性的质心的线条。
绘图样式可以通过参数进行自定义,以指定瓦片的颜色、透明度和地图瓦片类型。popup_features参数(类型:列表,默认值:[constants. TILE_ID])可以增强绘图的交互性,当用户点击瓦片时会显示弹出窗口,其中包含在参数列表中指定的空间镶嵌的GeoDataFrame中的列中的信息。
下面是由plot_tessellation方法生成的绘图示例:
FlowDataFrame的plot_flows方法将流动性绘制在地理地图上,以连接FlowDataFrame的空间镶嵌瓦片的质心。对于具有许多起点-终点对的大型FlowDataFrame,可视化可能会计算密集。min_flow参数(类型:整数,默认值:0)可用于指定仅显示大于min_flow的流动性。每条线的粗细取决于流量,并可以通过flow_weight、flow_exp和style_function参数进行指定。绘图样式可以通过其他参数进一步自定义,以指定流动线的颜色、透明度和地图瓦片类型。flow_popup和tile_popup参数可增强绘图的交互性,当用户点击流动线或起点位置的圆圈时,弹出窗口显示流动性或来自某个位置的流动性的信息。该方法返回一个folium. Map对象,可以通过其他folium和scikit-mobility函数使用该对象在同一地图上可视化其他数据。folium. Map对象可以通过map_f参数传递给plot_flows方法(默认值为None,表示在新地图上绘制流动性)。
下面是由plot_flows方法生成的绘图示例:
用户还可以在同一绘图中可视化空间镶嵌和流动性
在过去的十年中,已经提出了几种度量方法来捕捉个体和集体级别的人类移动模式。个体度量方法总结了单个移动对象的移动模式,而集体度量方法总结了整个人群的移动模式。例如,所谓的回旋半径及其变体量化了个体行进的特征距离,而多种受香农熵启发的度量方法被提出来量化个体移动的可预测性。
下面的代码计算了两个度量:对象的行进距离和它们的回旋半径。首先,我们从库中导入这两个函数。
然后,我们分别调用TrajDataFrame上的两个函数。
这些函数的输出是一个带有两列的pandasDataFrame:uid列包含对象的标识符;第二列的名称与调用的函数名称相对应,其中包含了该对象的计算度量值。例如,在DataFramejl_df中,DataFrame的jump_lengths列包含该对象的所有行进距离列表。
类似地,在DataFramerg_df中,radius_of_gyration列包含该对象的回旋半径。
集体度量的使用方式类似。下面的代码计算了一个集体度量-每个位置的访问次数(由任何对象)。首先,我们导入函数。
然后,我们在TrajDataFrame上调用该函数。
表格3和表格4分别列出了可用的个体和集体度量。
人类移动性生成算法的目标是创建一个人口模型,其移动模式在统计上与真实个体的移动模式无法区分。
生成算法通常生成与单个移动对象相对应的合成轨迹,假设一个对象独立于其他对象。scikit-mobility实现了最常见的个体生成算法,如探索和偏好回归模型及其变种和DITRAS。
每个生成算法都是一个Python类。首先,我们实例化算法。然后,我们调用generate方法开始生成合成轨迹。
下面的代码展示了生成描述1000个代理在一个Tessellation的位置之间移动并在输入中指定的一段时间内的合成轨迹的TrajDataFrame的代码。首先,我们从库中导入生成算法的类(DensityEPR)。
然后,我们从文件加载空间网格化,这是代理必须移动的空间网格化对象,并且我们指定模拟的开始时间和结束时间为pandas的datetime对象。
最后,我们实例化DensityEPR模型,并通过generate方法开始模拟,该方法接受开始时间、结束时间、网格化对象、代理数量和其他模型特定参数作为输入。模拟的输出是一个包含1000个代理商轨迹的TrajDataFrame。
集体生成算法估计一组离散位置之间的空间流动。使用集体生成算法估计的空间流动的示例包括邻域之间的通勤出行、市之间的迁移流动、州之间的货运以及地区之间的电话通话。在scikit-mobility中,集体生成算法接受一个镶嵌对象作为输入。
要成为集体算法的有效输入,网格化应包含两列:几何和相关性,这两列用于计算集体算法使用的两个变量:网格之间的距离和每个网格的重要性(也称为“吸引力”)。集体算法生成一个包含生成流动和指定为算法输入的镶嵌的FlowDataFrame。
scikit-mobility实现了最常见的集体生成算法:重力模型和辐射模型。我们用基于重力模型的示例说明如何在scikit-mobility中使用生成算法。
实现重力模型的Gravity类有两个主要方法:fit方法使用训练的FlowDataFrame校准模型的参数;generate方法在给定的镶嵌上生成流动。以下代码展示了如何使用这两个方法估计纽约州各县之间的通勤流动。
首先,我们从文件中加载镶嵌:
镶嵌包含人口列,用作每个网格(县)的相关性变量。接下来,我们从文件中加载县之间的观察到的通勤流动:
让我们使用观察到的流动来拟合带有幂律避让函数的单约束重力模型的参数(有关重力模型的更多细节,请参见[Barbosaetal.,2018])。
首先,我们实例化模型:
然后,我们调用fit方法从之前加载的FlowDataFrame拟合参数:
最后,我们使用拟合的模型在相同的网格化上生成流动。通过设置out_format=\“probabilities\“参数,我们指定在返回的FlowDataFrame的flow列中希望观察到单位流动(出行)的概率。
移动数据是敏感的,因为个体的移动可以揭示机密个人信息或允许在数据库中重新识别个体,从而产生严重的隐私风险。
实际上,《通用数据保护条例》(GDPR)明确要求数据控制者对最高风险的数据分析进行数据保护影响评估。因此,scikit-mobility为移动分析领域的科学家提供了用于评估与给定数据集分析相关的隐私风险的工具。
在文献中,隐私风险评估依赖于通过恶意对手的攻击来通过数据库中的移动对象的重新识别。隐私风险评估的常见框架。
假设在攻击期间,恶意对手以某种方式获得了对匿名化移动数据集的访问权限,即一个移动数据集,在该数据集中,与轨迹相关联的移动对象是未知的。此外,假设恶意对手以某种方式获取了关于在已获取的数据集中表示的个体的轨迹(或其中一部分轨迹)的信息。基于这些信息,通过估计该个体的移动数据在已获取的数据集中相对于其他个体的移动数据的唯一性来计算重新识别该个体的风险。
scikit-mobility提供了几种攻击模型,每个模型都是作为一个Python类实现的。例如,在位置攻击模型中,实现了LocationAttack类,恶意对手知道个体访问的一定数量的位置,但是他们不知道访问的时间顺序。要实例化一个LocationAttack对象,我们可以运行以下代码:
参数knowledge_length指定恶意对手知道每个对象移动的位置的数量。根据knowledge_length位置的所有可能组合中的最坏情况组合,计算重新识别风险。
为了评估与作为TrajDataFrame表示的移动数据集相关联的重新识别风险,我们将其指定为assess_risk方法的输入,该方法返回一个包含TrajDataFrame中每个对象的uid和相关的重新识别风险的pandasDataFrame,其中风险以risk列(类型:float,范围:[0,1],其中0表示最低风险,1表示最高风险)的形式表示。
由于对更大规模的数据集进行风险评估可能耗时较长,scikit-mobility提供了仅针对一部分对象进行评估的选项,通过参数targets。例如,在以下代码中,我们仅计算uid为1和2的对象的重新识别风险:
在计算过程中,不一定对所有位置组合进行评估以评估移动对象的重新识别风险:当找到具有最大重新识别风险(例如,风险为1)的组合时,所有其他组合都不会被计算,以使计算更快。然而,如果用户希望无论如何计算所有组合,可以将参数force_instances(类型:布尔值,默认值:False)设置为True:
结果是一个包含每个组合的参考编号的pandasDataFrame,并且对于每个组合,风险和组成该组合的每个位置由属性instance_elem指示。在表6中,我们列出了库中提供的隐私攻击方法。
在本文中,我们介绍了scikit-mobility,这是一个用于分析、生成和隐私风险评估的移动数据的Python库。scikit-mobility允许用户管理两种基本类型的移动数据——轨迹和流量,并提供了几个模块,每个模块专门用于移动数据分析的特定方面。
scikit-mobility的优势在于它提供了一个单一的环境,可以处理移动分析的不同方面,例如数据预处理和清理,计算移动度指标,生成合成轨迹和流量,以及评估隐私风险。该库的当前版本也有一些局限性。例如,由于pandasDataFrame必须完全加载到内存中,可以分析的移动数据集的大小受限于用户计算机内存的容量。此外,该库目前仅设计用于使用纬度和经度参考系统,但可以轻松适应任何参考系统。
我们对scikit-mobility的未来发展设想有两个方向。一方面,我们计划添加更多的模块,以涵盖更广泛的移动数据分析方面。例如,我们计划包括用于预测个体下一个访问位置的算法。我们还将考虑包括一个执行地图匹配的模块,即将轨迹的点分配给街道网络,并计算轨迹之间的相似性的模块。
另一方面,我们计划从计算角度改进该库。尽管在当前版本中,scikit-mobility易于使用,并且在几GB级别的移动数据集上效率相当高,但在TB级别的大规模移动数据集上不具备可扩展性。由于每年都有类似于pandas但计算效率更高的新Python库(例如dask)正在开发,我们计划重新实现scikit-mobility中的关键函数,以便利用这些库的计算效率。这个方面在目前并不是关键的,但当该库被科学界广泛采用时,它将变得至关重要。
代码请在https ://github. com/scikit-mobility/scikit-mobility/tree/master/examples下载。
翻译整理:我得学城
长按👇关注-数据STUDIO-设为星标,干货速递