2017-02-28 8 views
1

これを回避するのに苦労します。Ruby:ユニークな値の配列をチェックし、最小距離を返す(haversineの式)

私はファイバーオプティックネットワークを構成する2つのCSVを持っています:1つは緯度用、もう1つは経度用です。これらはKMZファイルから取り出され、両方のCSVは、それぞれがKMZの構築が不十分なために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 

5280以下の距離が実際に存在している場合、私は、その後。多分CSV出力にそれを無理に勧めなくて.uniqarr#min、顧客ごとに最低限のアドレスを使用して(顧客を返すための方法を考えるしようとしています関連する顧客は出力CSV配列に入力するだけです。

擬似コードに関して:顧客あたりの距離が最小であれば、顧客価値が一意であることを確認してから、出力CSVに押し込みます。私のループの中でこれを実装する方法については100%。

すべての洞察が高く評価されます。

答えて

0

まず、パフォーマンスの問題はどこですか?私はfib_coordsを計算するのではなく、customersをループすると仮定します。

1)お客様のCSVファイル全体を一度にメモリに読み込むのではなく、CSV::for_eachメソッドを使用してcustomers CSVファイルをループします。 CSVファイル全体を読み込むのはおそらく、かなりのメモリを使用していて、fib_coords配列の方が良いでしょう。これは、customersfib_coordsループの順序を逆にすることを意味します。

2)第2に、fib_coordsアレイ全体を検索することを避けることができます。最初の列を並べ替えて緯度順に並べている場合は、最小許容緯度(customer.latitude - 5280ft)を計算し、fib_coordsで最初の一致候補を見つけ、bsearchを使用します。これは直線検索よりもはるかに高速であり、そこから緯度までfib_coordsfib_coordsが範囲外です(> customer.latitude + 5280ft)。

+0

主なパフォーマンスの問題は、モンスター出力のCSVファイルを持つことだけです。 3-4百万のレコードとそれをExcelで開こうとするのは悪夢です(ファイルサイズが大きく、それ以上ならば0.5GBに近い)。それから私がやることは、MySQLテーブルにすべての行を挿入し、foot_to_fiberにMIN()を使用し、アドレスでグループ化することです。ここに寝ているが、午前中にあなたのアプローチを最初に試してみる。 – DnfD

+0

あなたは300万〜400万人の顧客がいると言っていますか?出力ファイルの長さは重要ではありませんが、多くの顧客が処理する可能性はあります。さらなる処理をしようとしているのであれば、地理的座標を扱うための拡張がいくつかある 'Postgres'のようなものを考えてもデータベースはいい考えです。私はElasticSearchを使ってこれを成功させました。 –

+0

最初の 'swfl_1a_geocoded.csv'は10,000人の顧客です。何が起こっているのは、前述のCSVの各顧客がファイバ内のすべての緯/経のペアを実行しているため、出力CSVに複数の結果が返されていることです。そのため、XYZアドレスのJane Doeは5,280フィートの下で150の出力結果を持つかもしれません。しかし私はXYZのJane Doeにしか気をつけていませんが、これは5,280フィートの下にあるすべての結果の中で最小です。だから私は、出力ファイルに重複する顧客を出力することを避けようとしており、Rubyを介していくつかのユニークなスタイルの検証を行います。 – DnfD

関連する問題