Ruby:检查数组的唯一值并返回最小距离(haversine 公式)
Posted
技术标签:
【中文标题】Ruby:检查数组的唯一值并返回最小距离(haversine 公式)【英文标题】:Ruby: checking array for unique values and also returning minimum distance (haversine formula) 【发布时间】:2017-02-28 22:52:24 【问题描述】:很难理解这一点。
我有两个组成光纤网络的 CSV:一个用于纬度,一个用于经度。这些是从 KMZ 文件中提取的,由于 KMZ 构建不佳,两个 CSV 都包含 170k 行。
我有一个潜在客户的 CSV,我想将其与光纤网络进行比较。如果最小距离(使用 Haversine 公式计算)小于 5280 英尺,则会将其打印到输出 csv 文件中。
过去我在这方面取得了成功,当时没有那么多纬度/经度对:过去是 20k,但现在我们有 170k。输出的 csv 文件变得非常庞大,您可以想象:300 万行并且还在增加。
然后我要做的是检查(通常使用 mysql MIN() 函数,但我确信有更好的方法)返回每个地址和按地址分组的最小距离:因为你真的只关心关于每个地址的最小距离。您不希望每个地址有多行。
require 'csv'
require 'haversine'
#this could be put into one file, works as is
fib_lat = CSV.read("swfl_fiber_lat.csv")
fib_long = CSV.read("swfl_fiber_long.csv")
#use zip to read both arrays at the same time
fib_coords = fib_lat.map(&:last).zip(fib_long.map(&:last))
#multiple column CSV with customer data, headers turned on
customers = CSV.read("swfl_1a_geocoded.csv", headers:true)
CSV.open('swfl-output-data-within-1mile.csv','w', :write_headers=> true, :headers => ['First Name','Last Name','Latitude','Longitude','Feet to Fiber','Address','City','State','Zip','County','Company','Title Code Description','PrimarySIC6 Description','Business Status Code Description','Phone Number','Tollfree Phonenumber','EmployeeSize Location Description','Sales Volume Location Decode','Telecommunications Expense','Email Address']) do |csv_object|
fib_coords.each do |fib_lat, fib_long|
customers.each do |cust|
if (Haversine.distance(cust[2].to_f, cust[3].to_f, fib_lat.to_f, fib_long.to_f).to_feet < 5280)
data_out = ["#cust[0],#cust[1],#cust[2].to_f,#cust[3].to_f, #Haversine.distance(cust[2].to_f, cust[3].to_f, fib_lat.to_f, fib_long.to_f).to_feet.round(2),#cust[5],#cust[6],#cust[7],#cust[8],#cust[9],#cust[10],#cust[11],#cust[12],#cust[13],#cust[14],#cust[15],#cust[16],#cust[17],#cust[18]"]
csv_object << data_out
end
end
end
end
我正在想办法返回客户(可能使用 .uniq
arr#min
并且仅使用每个客户的最小地址,而不将其推入输出 CSV。然后,如果确实存在低于 5,280 的距离和关联的客户,仅将其放入输出 CSV 数组中。
关于伪代码:如果距离是每个客户的最小值,请确保客户价值是唯一的,然后将其推入输出 CSV。只是没有 100% 了解如何在我的一系列循环中实现这一点。
任何和所有的见解都是值得赞赏的。
【问题讨论】:
【参考方案1】:首先,您的性能问题在哪里?我将假设它不是计算fib_coords
,而是循环遍历customers
。我会做出一些改变:
1) 我不会一次性将整个客户 CSV 文件读入内存,而是使用 CSV::for_each
方法循环遍历 customers
CSV 文件。加载整个 CSV 文件可能会使用相当多的内存,可以更好地用于fib_coords
数组。这意味着颠倒 customers
和 fib_coords
循环的顺序。
2) 其次,您可以避免搜索整个fib_coords
数组。如果按第一列排序,使其按纬度顺序,计算可能的最小纬度(customer.latitude - 5280ft
),使用bsearch
在fib_coords
中找到第一个潜在匹配项,这比线性搜索和循环要快得多fib_coords
从那里直到fib_coords
中的纬度超出范围(> customer.latitude + 5280ft
)。
【讨论】:
主要的性能问题只是有一个怪物输出 CSV 文件。 3-400 万条记录并试图在 excel 中打开它是一场噩梦(文件大小很大,如果不是更多的话,接近 0.5gb)。然后我要做的是将所有这些行插入到 MySQL 表中,并在 feet_to_fiber 上使用 MIN() 并按地址分组。在这里睡觉,但会在早上第一件事尝试你的方法。 您是说您有 3 到 4 百万客户吗?输出文件的长度应该无关紧要,尽管许多客户可能会进行处理。如果您正在尝试进行进一步处理,那么听起来数据库确实是一个好主意,尽管我会考虑像Postgres
这样的东西,它有一些用于处理地理坐标的扩展。我也成功地使用了 ElasticSearch。
最初的 swfl_1a_geocoded.csv
是大约 10,000 个客户。发生的情况是,上述 csv 中的每个客户都通过光纤中的每个纬度/经度对运行,因此在输出 CSV 中返回多个结果。因此,XYZ 地址的 Jane Doe 在 5,280 英尺下可能有 150 个输出结果。但我只关心 XYZ 的 Jane Doe,这是 5,280 英尺以下所有结果中的最小值。所以我试图避免将重复的客户输出到输出文件,并通过 Ruby 进行一些独特性样式验证。
正如我在第 (2) 点中所说的,我会将客户循环放在外面。如果对 lat/long 对进行了排序,则可以循环遍历它们的最小集合,并使用标准最小代码的变体找到具有最小距离的对。以上是关于Ruby:检查数组的唯一值并返回最小距离(haversine 公式)的主要内容,如果未能解决你的问题,请参考以下文章