笔记:t2vec 数据预处理"/>
论文辅助笔记:t2vec 数据预处理
1 波尔图数据
curl .csv.zip -o data/porto.csv.zip
这条命令使用 curl
从给定的URL下载一个名为 train.csv.zip
的压缩文件,并将其保存为 data/porto.csv.zip
。
unzip data/porto.csv.zip
使用 unzip
命令解压 data/porto.csv.zip
这个文件。
mv train.csv data/porto.csv
这条命令将解压后的 train.csv
文件移动(或重命名)到 data
目录下,并将其命名为 porto.csv
。
cd preprocessing
进入名为 preprocessing
的目录。
julia porto2h5.jl
使用 Julia 语言执行 porto2h5.jl
这个脚本。将 porto.csv
转换为 .h5
格式的命令。
julia preprocess.jl
使用 Julia 语言执行 preprocess.jl
脚本。对数据进行一些预处理的操作。
1.1 porto2h5.jl
using ArgParse
#导入 ArgParse 模块( Julia 中用于解析命令行参数的一个库)include("utils.jl")
#导入了 utils.jl 文件args = let s = ArgParseSettings()@add_arg_table s begin"--datapath"arg_type=Stringdefault="/home/xiucheng/Github/t2vec/data"endparse_args(s; as_symbols=true)
end
#定义 args:
#`let s = ArgParseSettings() 创建一个新的 ArgParseSettings 对象,这是用于配置命令行参数解析的。
#`@add_arg_table s begin ... end 是一个宏,用于添加参数到 s 中。
# 这里定义了一个名为 --datapath 的参数,它的类型是 String,默认值是 "/home/xiucheng/Github/t2vec/data"。
#parse_args(s; as_symbols=true) 解析命令行参数,并将结果存储在 args 中。datapath = args[:datapath]
#从 args 中取出 --datapath 参数的值,并将其赋值给 datapath 变量。porto2h5("$datapath/porto.csv")
#调用 porto2h5 函数,参数是 porto.csv 文件的完整路径。
#这个函数是在 utils.jl 中定义的,它的功能是将 CSV 格式的数据转换为 .h5 格式。
1.2 utils.jl
波尔图数据集:数据集笔记: Porto_UQI-LIUWJ的博客-CSDN博客
using CSV, DataFrames, HDF5
#导入三个模块:CSV、DataFrames、HDF5。
#这些模块分别用于处理CSV文件、数据框操作和HDF5文件操作。"""
读取原始的波尔图出租车csv数据,将所有trip保存至hdf5文件中
每一个trip都是一个2*n的矩阵,n是trip的长度,每一行是经纬度
"""
function porto2h5(csvfile::String)df = CSV.File(csvfile) |> DataFrame#使用CSV模块读取CSV文件,然后将其转化为DataFrame对象。df = df[df.MISSING_DATA .== false, :]#对DataFrame df 进行筛选,去除其中 MISSING_DATA 列值为 true 的行。sort!(df, [:TIMESTAMP])#对DataFrame df 进行排序,按照 TIMESTAMP 列进行排序。println("Processing $(size(df, 1)) trips...")## 打印一个消息,告诉用户正在处理多少条出行记录。h5open("../data/porto.h5", "w") do f#使用HDF5模块打开(或创建)一个HDF5文件,并以写入模式进行操作。#这个文件将保存处理后的数据。num, num_incompleted = 0, 0'''初始化两个变量:num 记录成功保存的出行记录数量num_incompleted 记录不完整的出行记录数量'''for trip in df.POLYLINE对DataFrame中的 POLYLINE 列进行遍历,每一个元素都是一条出行轨迹。trytrip = Meta.parse(trip) |> evalcatch enum_incompleted += 1continueend'''使用 Meta.parse 尝试解析 trip,然后使用 eval 进行求值。如果解析或求值失败,增加 num_incompleted 的计数并跳过这次循环。'''tripLength = length(trip)tripLength == 0 && continue#获取出行轨迹的长度。如果长度为0,则跳过这次循环。trip = hcat(trip...)#使用 hcat 函数将 trip 里的元素水平串联起来。num += 1#增加成功处理的出行记录的数量。f["/trips/$num"] = trip#在HDF5文件中保存处理后的出行轨迹。f["/timestamps/$num"] = collect(0:tripLength-1) * 15.0#在HDF5文件中保存处理后的出行轨迹。num % 100_000 == 0 && println("$num")endattributes(f)["num"] = num#在HDF5文件的属性中保存成功处理的出行记录的总数量。println("Incompleted trip: $num_incompleted.\nSaved $num trips.")end
end
1.2.1 补充说明(Mata.parse)
Meta.parse
是Julia中的一个函数,它可以将一个字符串解析为Julia的表达式。- 在上述代码中,假设
trip
原始的值是一个表示列表或数组的字符串,例如:"[(2.0, 3.0), (4.0, 5.0)]"
,那么使用Meta.parse
可以将其转换为Julia中的一个真正的列表或数组。 - 一旦你有了一个Julia表达式,你想要执行它。这就是
eval
函数的作用。eval
取一个表达式作为输入,并执行它,返回执行的结果。- 不使用
eval
,你得到的是一个描述表达式结构的Expr
对象,而不是表达式的实际执行结果
- 还是以上面的
"[(2.0, 3.0), (4.0, 5.0)]"为例:
不使用eval的话:输出类型就是Expr 表达式
-
str = "[(2.0, 3.0), (4.0, 5.0)]" expr = Meta.parse(str) println(expr) println(typeof(expr)) ''' [(2.0, 3.0), (4.0, 5.0)] Expr '''
-
- 使用eval的话:输出类型就是float数组
-
str = "[(2.0, 3.0), (4.0, 5.0)]" expr = Meta.parse(str) |> eval println(expr) println(typeof(expr)) ''' [(2.0, 3.0), (4.0, 5.0)] Vector{Tuple{Float64, Float64}} '''
-
- 在上述代码中,假设
1.2.2 hcat(trip...)补充说明
- 在Julia中,
...
(也称为"splatting"操作符)用于将数组或集合的元素展开为单独的参数trip
是一个包含多个元组的数组,如[(a1, b1), (a2, b2), ...]
,使用trip...
会展开这些元组,效果等同于列出它们:(a1, b1), (a2, b2), ...
。
hcat
是Julia中的一个函数,用于水平地串联数组。它将多个数组(或元组)作为输入,并将它们并排放置,形成一个新的二维数组。
trip = [(2.0, 3.0), (4.0, 5.0), (6.0, 7.0)]
result = hcat(trip...)
result
''
1×3 Matrix{Tuple{Float64, Float64}}:(2.0, 3.0) (4.0, 5.0) (6.0, 7.0)
'''
1.3 preporcess.jl
1.3.1 一些定义和命令行参数
using JSON
using DataStructures
using NearestNeighbors
using Serialization, ArgParse
'''
导入了五个模块/库:
`JSON(处理JSON数据)
`DataStructures(提供数据结构)
`NearestNeighbors(最近邻搜索)
`Serialization(序列化和反序列化对象)
`ArgParse(命令行参数解析)
'''include("SpatialRegionTools.jl")args = let s = ArgParseSettings()@add_arg_table s begin"--datapath"arg_type=Stringdefault="/home/xiucheng/Github/t2vec/data"endparse_args(s; as_symbols=true)
end
'''
使用 ArgParse 库定义并解析命令行参数。
定义了一个命令行参数 --datapath,其类型为字符串,并为其提供了默认值。
'''datapath = args[:datapath]
#从解析的命令行参数中提取 datapath 参数的值,并赋值给变量 datapath
1.3.2 hyper-parameters.json相关的内容
param = JSON.parsefile("../hyper-parameters.json")
'''
使用 JSON.parsefile 函数读取一个名为 "hyper-parameters.json" 的JSON文件,并将内容解析为Julia的数据结构。
'''regionps = param["region"]
cityname = regionps["cityname"]
cellsize = regionps["cellsize"]
#从解析的JSON数据中提取关于空间区域的参数,包括城市名称和单元格大小。if !isfile("$datapath/$cityname.h5")println("Please provide the correct hdf5 file $datapath/$cityname.h5")exit(1)
end
#检查是否存在一个名为 cityname.h5 的HDF5文件。如果文件不存在,打印错误信息并退出程序。
1.3.2 SpatialRegion 对象
region = SpatialRegion(cityname,regionps["minlon"], regionps["minlat"],regionps["maxlon"], regionps["maxlat"],cellsize, cellsize,regionps["minfreq"], # minfreq40_000, # maxvocab_size10, # k4) # vocab_start
#使用提取的参数创建一个 SpatialRegion 对象(一个自定义的数据结构,在"SpatialRegionTools.jl" 文件中定义。println("Building spatial region with:cityname=$(region.name),minlon=$(region.minlon),minlat=$(region.minlat),maxlon=$(region.maxlon),maxlat=$(region.maxlat),xstep=$(region.xstep),ystep=$(region.ystep),minfreq=$(region.minfreq)")
#打印关于创建的 SpatialRegion 对象的详细信息。paramfile = "$datapath/$(region.name)-param-cell$(Int(cellsize))"
if isfile(paramfile)println("Reading parameter file from $paramfile")region = deserialize(paramfile)
elseprintln("Creating paramter file $paramfile")num_out_region = makeVocab!(region, "$datapath/$cityname.h5")serialize(paramfile, region)
end
'''
检查参数文件是否已经存在:
如果存在,则从该文件中读取 SpatialRegion 对象的数据。
如果不存在,则创建新的参数文件,并使用 makeVocab! 函数(在 "SpatialRegionTools.jl" 中定义)来填充它,并将 SpatialRegion 对象序列化到文件中。
'''println("Vocabulary size $(region.vocab_size) with cell size $cellsize (meters)")
#打印空间区域的词汇大小和单元格大小信息。println("Creating training and validation datasets...")
createTrainVal(region, "$datapath/$cityname.h5", datapath, downsamplingDistort, 1_000_000, 10_000)
#打印消息并使用 createTrainVal 函数(在 "SpatialRegionTools.jl" 中定义)创建训练和验证数据集。saveKNearestVocabs(region, datapath)
#使用 saveKNearestVocabs 函数(在 "SpatialRegionTools.jl" 中定义)保存最近的词汇信息。
1.4 SpatialRegionTools.jl
1.4.1 导入库和SpatialRegion结构体
#module SpatialRegionToolsusing StatsBase:rle
using HDF5
using DataStructures
using NearestNeighbors
using Statistics, Printf
include("utils.jl")
'''
导入库和模块:从StatsBase库导入rle函数。
导入HDF5库,用于处理HDF5格式的数据。
导入DataStructures库,提供各种数据结构。
导入NearestNeighbors库,用于最近邻搜索。
导入Statistics和Printf模块。
包含外部文件"utils.jl"。
'''#export SpatialRegion, makeVocab!, gps2vocab, saveKNearestVocabs,
# trip2seq, seq2trip, createTrainVal
#列出了此模块中定义的功能和数据结构,这些功能和数据结构可以在外部使用const UNK = 3"""
example:
SpatialRegion的使用示例。region = SpatialRegion("porto",-8.735152, 40.953673,-8.156309, 41.307945,cellsize, cellsize,50, # minfreq50_000, # maxvocab_size5, # k4) # vocab_start
"""mutable struct SpatialRegion
#声明一个可变的结构体(即其字段的值可以在创建后被更改)名为SpatialRegion。name::String #区域的名称,例如城市名。minlon::Float64minlat::Float64maxlon::Float64maxlat::Float64'''定义了一个边界框(bounding box),通过其最小和最大的经度和纬度来描述。这个边界框表示空间区域的地理范围。'''minx::Float64miny::Float64maxx::Float64maxy::Float64#边界框在某种度量空间(如米)上的表示。它表示经纬度转换为米后的坐标。xstep::Float64ystep::Float64#在x和y方向上的单元格大小或步长numx::Intnumy::Int#在x和y方向上的单元格数量minfreq::Int#最小频率,某个单元格中的轨迹数量达到多少时才被认为是"热门"的maxvocab_size::Int #词汇表的最大大小k::Intcellcount #描述每个单元格被击中(即有多少轨迹通过)的次数hotcell::Vector{Int} #表示被标记为"热门"的单元格的列表或索引hotcell_kdtree #与"热门"单元格相关的k-d树结构,用于高效的空间搜索hotcell2vocab::Dict{Int, Int} #一个字典,将"热门"单元格映射到词汇表IDvocab2hotcell::Dict{Int, Int} #一个字典,将词汇表ID映射回其对应的"热门"单元格vocab_start::Int #词汇表开始的索引或IDvocab_size::Int #词汇表的大小built::Bool#构造函数function SpatialRegion(name::String,minlon::Float64,minlat::Float64,maxlon::Float64,maxlat::Float64,xstep::Float64,ystep::Float64,minfreq::Int,maxvocab_size::Int,k::Int,vocab_start::Int)minx, miny = lonlat2meters(minlon, minlat)maxx, maxy = lonlat2meters(maxlon, maxlat)numx = round(maxx - minx, digits=6) / xstepnumx = convert(Int, ceil(numx))numy = round(maxy - miny, digits=6) / ystepnumy = convert(Int, ceil(numy))new(name,minlon, minlat, maxlon, maxlat,minx, miny, maxx, maxy,xstep, ystep,numx, numy, minfreq, maxvocab_size, k,Accumulator{Int, Int}(),Int[],Any,Dict(),Dict(),vocab_start,vocab_start,false)end
end
1.4.2 cell id和Web Mercator坐标之间的转换
"""
将给定的Web Mercator坐标(x, y)转换为一个单元格ID
"""
function coord2cell(region::SpatialRegion, x::Float64, y::Float64)xoffset = round(x - region.minx, digits=6) / region.xstepyoffset = round(y - region.miny, digits=6) / region.ystep#计算x/y坐标相对于区域的最小x/y坐标的偏移量(以单元格数计)xoffset = convert(Int, floor(xoffset))yoffset = convert(Int, floor(yoffset))#将x/y的偏移量向下取整,以得到x/y在哪个单元格内yoffset * region.numx + xoffset#由于每行有region.numx个单元格,所以yoffset乘以region.numx得到前面所有行的单元格总数。#然后再加上xoffset就得到了该坐标所在单元格的ID
end
"""
将一个单元格ID转换为对应的Web Mercator坐标(x, y)
"""
function cell2coord(region::SpatialRegion, cell::Int)yoffset = div(cell, region.numx)#将单元格ID除以每行的单元格数量。结果yoffset表示这个ID对应的单元格在第几行xoffset = mod(cell, region.numx)#计算单元格ID除以每行的单元格数量的余数。结果xoffset表示这个ID对应的单元格在这一行的第几列y = region.miny + (yoffset + 0.5) * region.ystepx = region.minx + (xoffset + 0.5) * region.xstep'''计算单元格中心的y坐标。首先,从区域的最小y坐标开始,并加上yoffset乘以单元格的高度(即region.ystep),这样我们就得到了该单元格的上边缘的y坐标。然后,再加上半个单元格的高度,即(0.5 * region.ystep)这样就得到了该单元格中心的y坐标'''x, y
end
1.4.3 cell id 和经纬度之间的转换
'''
将给定的GPS坐标(经度lon和纬度lat)转换为一个单元格ID
'''
function gps2cell(region::SpatialRegion, lon::Float64, lat::Float64)x, y = lonlat2meters(lon, lat)#将经度和纬度转换为Web Mercator坐标系中的x和y坐标coord2cell(region, x, y)#调用之前定义的coord2cell函数,将Web Mercator坐标转换为对应的单元格ID。
end
'''
将给定的单元格ID转换回其对应的GPS坐标(经度和纬度)
'''
function cell2gps(region::SpatialRegion, cell::Int)x, y = cell2coord(region, cell)#将单元格ID转换为Web Mercator坐标系中的x和y坐标meters2lonlat(x, y)#将Web Mercator坐标转换回经度和纬度
end
1.4.4 SpatialRegion 内的相对偏移量
'''
根据提供的GPS坐标(经度lon和纬度lat)计算其在SpatialRegion区域内的相对偏移量
'''
function gps2offset(region::SpatialRegion, lon::Float64, lat::Float64)x, y = lonlat2meters(lon, lat)#将GPS坐标转换为Web Mercator坐标系下的x和y坐标xoffset = round(x - region.minx, digits=6) / region.xstepyoffset = round(y - region.miny, digits=6) / region.ystep#计算x/y坐标的相对偏移量xoffset, yoffset
end
1.4.5 inregion 点/轨迹 是否在指定region中
'''
检查给定的经度lon和纬度lat是否在SpatialRegion区域内
'''
function inregion(region::SpatialRegion, lon::Float64, lat::Float64)lon >= region.minlon && lon < region.maxlon &&lat >= region.minlat && lat < region.maxlat'''检查经度和纬度是否位于region定义的边界之内。如果都在范围内,函数返回true否则返回false'''
end
'''
接受一个类型为Matrix{Float64}的trip参数,其中每列都是一个经度/纬度对
'''
function inregion(region::SpatialRegion, trip::Matrix{Float64})for i = 1:size(trip, 2)inregion(region, trip[:, i]...) || return falseendtrue'''循环检查trip矩阵中的每一个经纬度对是否都在region区域内。如果所有的点都在范围内,函数返回true否则一旦发现某个点不在范围内就立即返回false'''
end
1.4.6 MakeVocab! 为热点单元格创建词汇表
"""
该函数从hdf5文件中读取轨迹,统计每个单元格中的点数,并为最常出现的单元格建立一个词汇表
"""
function makeVocab!(region::SpatialRegion, trjfile::String)region.cellcount = Accumulator{Int, Int}()#为region初始化一个累加器,用于统计每个单元格中的点数num_out_region = 0#初始化一个变量来计数不在区域内的点h5open(trjfile, "r") do f#打开hdf5文件进行读取。num = read(attributes(f)["num"])#读取hdf5文件中的属性“num”,表示有多少轨迹。for i = 1:numtrip = read(f["/trips/$i"])#遍历每个轨迹,并读取其数据for p = 1:size(trip, 2)lon, lat = trip[:, p]#对于每个轨迹,遍历其所有的点。if !inregion(region, lon, lat)num_out_region += 1#如果点不在region内,则增加计数。elsecell = gps2cell(region, lon, lat)push!(region.cellcount, cell)#否则,将该点转换为一个单元格,并在cellcount累加器中增加该单元格的计数endendi % 100_000 == 0 && println("Processed $i trips")#每处理100,000个轨迹,打印一个消息endendmax_num_hotcells = min(region.maxvocab_size, length(region.cellcount))#确定热点单元格的最大数量。topcellcount = sort(collect(region.cellcount), by=last, rev=true)[1:max_num_hotcells]#对单元格按其计数排序,并选择前max_num_hotcells个。println("Cell count at max_num_hotcells:$(max_num_hotcells) is $(last(topcellcount[end]))")region.hotcell = filter(p -> last(p) >= region.minfreq, topcellcount) .|> first#筛选那些计数大于或等于minfreq的热点单元格。region.hotcell2vocab = Dict([(cell, i-1+region.vocab_start)for (i, cell) in enumerate(region.hotcell)])#为每个热点单元格构建一个到词汇ID的映射。region.vocab2hotcell = Dict(last(p) => first(p) for p in region.hotcell2vocab)#构建从词汇ID到热点单元格的反向映射。region.vocab_size = region.vocab_start + length(region.hotcell)#更新词汇的大小。coord = hcat(map(x->collect(cell2coord(region, x)), region.hotcell)...)#为热点单元格获取其坐标。region.hotcell_kdtree = KDTree(coord)#使用这些坐标构建一个KDTree,便于后续的搜索。region.built = true#标记region已构建。num_out_region
end
push!(region.cellcount, cell)
这行代码中的push!
是Julia中的一个函数,它用于将一个元素添加到集合的末尾。
- 在这里,它正在将
cell
值添加到region.cellcount
中。- 通常情况下,
push!
函数是用于数组的。
- 然而,在这个上下文中,
region.cellcount
是一个Accumulator
对象Accumulator
是一个特殊的数据结构,通常用于计数,其中键是要计数的项,值是相应的计数。所以,
push!(region.cellcount, cell)
这行代码在做以下事情:
- 检查
cell
是否已经作为键存在于region.cellcount
中。
- 如果
cell
已存在,那么其对应的计数值会增加1。- 如果
cell
不存在,那么它将被添加到Accumulator
中,并且其计数被设置为1。
1.4.7 找到最近的热点单元格
'''
从给定的单元格cell中找到k个最近的热点单元格
'''
function knearestHotcells(region::SpatialRegion, cell::Int, k::Int)@assert region.built == true "Build index for region first"#首先确保region的索引已经被构建。如果没有,函数会抛出一个错误。coord = cell2coord(region, cell) |> collect#将给定的单元格转换为其对应的坐标idxs, dists = knn(region.hotcell_kdtree, coord, k)#使用KDTree进行k最近邻搜索来找到k个最近的热点单元格的索引和到给定单元格的距离。region.hotcell[idxs], dists#返回找到的热点单元格和它们到给定单元格的距离。
end
'''
找到给定单元格cell的最近的热点单元格
'''
function nearestHotcell(region::SpatialRegion, cell::Int)@assert region.built == true "Build index for region first"#首先确保region的索引已经被构建hotcell, _ = knearestHotcells(region, cell, 1)#使用knearestHotcells函数找到单一最近的热点单元格。hotcell[1]#返回找到的热点单元格
end
1.4.8 为每个词汇找到k个最近的词汇和相应的距离
"""
为每个词汇找到k个最近的词汇和相应的距离,并将它们保存到一个hdf5文件中
"""
function saveKNearestVocabs(region::SpatialRegion, datapath::String)V = zeros(Int, region.k, region.vocab_size)D = zeros(Float64, region.k, region.vocab_size)'''定义两个矩阵V和D。V矩阵存储每个词汇的k个最近词汇的索引,而D矩阵存储与这些词汇的相应距离。'''for vocab in 0:region.vocab_start-1V[:, vocab+1] .= vocabD[:, vocab+1] .= 0.0end#使用一个for循环初始化矩阵V和D的前几列(从0到region.vocab_start-1)。#在这些列中,每个词汇的最近词汇被认为是它自己,距离是0for vocab in region.vocab_start:region.vocab_size-1cell = region.vocab2hotcell[vocab]kcells, dists = knearestHotcells(region, cell, region.k)kvocabs = map(x->region.hotcell2vocab[x], kcells)V[:, vocab+1] .= kvocabsD[:, vocab+1] .= distsend'''对于region.vocab_start到region.vocab_size-1的每一个词汇,使用knearestHotcells函数找到其最近的单元格和距离。然后将这些单元格转换为相应的词汇,并将结果存储在V和D中。'''cellsize = Int(region.xstep)file = joinpath(datapath, region.name * "-vocab-dist-cell$(cellsize).h5")#定义文件名。文件名是根据region.name、单元格的大小(region.xstep)和"-vocab-dist-cell"构建的。h5open(file, "w") do ff["V"], f["D"] = V, D#使用h5open函数将V和D矩阵保存到hdf5文件中。endprintln("Saved cell distance into $file")nothing'''打印一条消息,说明距离已经被保存到文件中。函数返回nothing,表示这个函数不返回任何值。'''
end
1.4.9 cell2vocab 单元格转换到词汇ID
function cell2vocab(region::SpatialRegion, cell::Int)@assert region.built == true "Build index for region first"if haskey(region.hotcell2vocab, cell)return region.hotcell2vocab[cell]elsehotcell = nearestHotcell(region, cell)return region.hotcell2vocab[hotcell]end'''如果单元格是热门单元格,则直接从hotcell2vocab字典中返回其词汇ID。如果不是热门单元格,则使用nearestHotcell函数找到其最近的热门单元格,并返回该热门单元格的词汇ID。'''
end
1.4.10 gps2vocab gps坐标转换到词汇ID
function gps2vocab(region::SpatialRegion, lon::Float64, lat::Float64)inregion(region, lon, lat) || return UNK#首先,检查GPS坐标是否在区域内。如果不在,就返回UNKcell2vocab(region, gps2cell(region, lon, lat))#如果GPS坐标在区域内,就使用cell2vocab函数和gps2cell函数将GPS坐标转换为单元格ID,然后将单元格ID转换为词汇ID
end
1.4.11 trip2seq seq2trip trip和词汇序列互转
'''
将一个trip(一系列GPS坐标)转换为一个词汇序列
'''
function trip2seq(region::SpatialRegion, trip::Matrix{Float64})seq = Int[]for i in 1:size(trip, 2)lon, lat = trip[:, i]push!(seq, gps2vocab(region, lon, lat))end#对于trip中的每个GPS点,使用gps2vocab函数将其转换为一个词汇ID,并将这些ID添加到seq数组中seq |> rle |> first#使用rle函数(对序列进行运行长度编码)处理seq,并返回结果中的第一个元素
end
'''
此函数接受一个整数向量seq,其中每个整数是一个词汇ID,并将其转换回相应的GPS坐标轨迹。
'''
function seq2trip(region::SpatialRegion, seq::Vector{Int})trip = zeros(Float64, 2, length(seq))for i = 1:length(seq)cell = get(region.vocab2hotcell, seq[i], -1)cell == -1 && error("i=$i is out of vocabulary")lon, lat = cell2gps(region, cell)#对于每个词汇ID,它首先查找与之关联的单元格ID(从vocab2hotcell),然后使用cell2gps转换为GPS坐标trip[:, i] = [lon, lat]endtrip#结果是一个二维矩阵,其中每列是一个GPS点的坐标
end
1.4.12 tripmeta, seqmeta 计算元数据
'''
从一个给定的轨迹中计算元数据,特别是轨迹的中心点坐标
'''
function tripmeta(region::SpatialRegion, trip::Matrix{Float64})mins, maxs = minimum(trip, dims=2), maximum(trip, dims=2)#计算轨迹中所有点的最小和最大坐标centroids = mins + (maxs - mins) / 2#计算并返回这些坐标范围的中点gps2offset(region, centroids...)#使用gps2offset将中心点转换为与区域关联的偏移量
end
'''
接受一个词汇序列并返回该序列对应的轨迹的中心点坐标
'''
function seqmeta(region::SpatialRegion, seq::Vector{Int})trip = seq2trip(region, seq)#使用seq2trip将词汇序列转换为一个GPS坐标轨迹tripmeta(region, trip)#使用tripmeta计算并返回轨迹的中心点坐标
end
1 .4.13
function createTrainVal(region::SpatialRegion,trjfile::String,datapath::String,injectnoise::Function,ntrain::Int,nval::Int;nsplit=5,min_length=20,max_length=100)seq2str(seq) = join(map(string, seq), " ") * "\n"#定义一个seq2str的局部函数,将序列转化为一个由空格分隔的字符串h5open(trjfile, "r") do f#使用h5open打开h5格式的轨迹文件trainsrc = open("$datapath/train.src", "w")traintrg = open("$datapath/train.trg", "w")trainmta = open("$datapath/train.mta", "w")validsrc = open("$datapath/val.src", "w")validtrg = open("$datapath/val.trg", "w")validmta = open("$datapath/val.mta", "w")#为训练和验证数据集创建文件,包括源数据、目标数据和元数据文件for i = 1:ntrain+nval# 遍历h5文件中的轨迹trip = f["/trips/$i"] |> read# 从h5文件中读取单个轨迹min_length <= size(trip, 2) <= max_length || continue#检查轨迹长度是否在指定的范围内trg = trip2seq(region, trip) |> seq2str#将轨迹转化为目标序列并格式化meta = tripmeta(region, trip)mta = @sprintf "%.2f %.2f\n" meta[1] meta[2]# 计算轨迹的元数据并格式化noisetrips = injectnoise(trip, nsplit)# 使用injectnoise函数在原始轨迹上生成噪声轨迹srcio, trgio, mtaio = i <= ntrain ? (trainsrc, traintrg, trainmta) : (validsrc, validtrg, validmta)## 根据当前轨迹的索引,确定是训练数据还是验证数据for noisetrip in noisetrips# 对于每个噪声轨迹src = trip2seq(region, noisetrip) |> seq2str## 转化噪声轨迹为源序列并格式化write(srcio, src)write(trgio, trg)write(mtaio, mta)## 将源序列、目标序列和元数据写入相应的文件endi % 100_000 == 0 && println("Scaned $i trips...")#i >= 8_000 && breakendclose(trainsrc), close(traintrg), close(trainmta), close(validsrc), close(validtrg), close(validmta)endnothing
end
1.4.13 保存/加载空间区域
'''
将一个空间区域的参数保存到文件
'''
function saveregion(region::SpatialRegion, paramfile::String)save(paramfile,# JLD cannot handle Accumulator correctly#"cellcount", region.cellcount.map,"hotcell", region.hotcell,"hotcell2vocab", region.hotcell2vocab,"vocab2hotcell", region.vocab2hotcell,"hotcell_kdtree", region.hotcell_kdtree,"vocab_size", region.vocab_size)nothing
end'''
从文件中加载一个空间区域的参数,并更新给定的空间区域对象
'''
function loadregion!(region::SpatialRegion, paramfile::String)jldopen(paramfile, "r") do f#region.cellcount = read(f, "cellcount")region.hotcell = read(f, "hotcell")region.hotcell2vocab = read(f, "hotcell2vocab")region.vocab2hotcell = read(f, "vocab2hotcell")region.hotcell_kdtree = read(f, "hotcell_kdtree")region.vocab_size = read(f, "vocab_size")region.built = trueendnothing
end
2 其他城市
要处理一个新城市的数据,你需要按照特定的格式提供一个 hdf5 输入文件,并设置适当的超参数
- 提供 hdf5 输入
- 为目标城市提供一个名为
t2vec/data/cityname.h5
的 hdf5 文件,其中cityname
是你要处理的城市的名称 - hdf5 输入的格式
attributes(f)["num"]
:存储的是轨迹的总数。f["/trips/i"]
:存储的是第 i 条轨迹的 GPS 数据,它是一个 2xn 的矩阵。第一行是经度序列,第二行是纬度序列。f["/timestamps/i"]
:存储的是第 i 条轨迹的时间戳序列,它是一个 n 维的向量。
- 为目标城市提供一个名为
- 设置超参数
- 在
t2vec/hyper-parameters.json
文件中,需要为目标城市设置适当的超参数。
- 在
更多推荐
论文辅助笔记:t2vec 数据预处理
发布评论