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 数组。这意味着颠倒 customersfib_coords 循环的顺序。

2) 其次,您可以避免搜索整个fib_coords 数组。如果按第一列排序,使其按纬度顺序,计算可能的最小纬度(customer.latitude - 5280ft),使用bsearchfib_coords 中找到第一个潜在匹配项,这比线性搜索和循环要快得多fib_coords 从那里直到fib_coords 中的纬度超出范围(&gt; 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 公式)的主要内容,如果未能解决你的问题,请参考以下文章

用java求一组数中最大值与次大值并返回,T﹏T

比较数组中的值并删除 Ruby/Rails 中不同的项目

Java泛型,返回数组最大值最小值

返回唯一值并避免遍历未过滤的范围

最小唯一数组总和

如何检查一个值是不是存在于Ruby中的数组中