2017-02-28 11 views
1

내 머리를 감싸는 데 어려움을 겪고 있습니다.루비 : 고유 값에 대한 배열 검사 및 최소 거리 반환 (호버린 수식)

광섬유 네트워크를 구성하는 두 개의 CSV가 있습니다. 하나는 위도 용이고 하나는 경도 용입니다. 이것들은 KMZ 파일에서 가져 왔고 두 CSV는 열악한 KMZ로 인해 각각 170k 행으로 구성됩니다.

나는 광섬유 네트워크와 비교하기를 원하는 잠재 고객의 CSV가 있습니다. 최소 거리 (Haversine 수식을 사용하여 계산)가 5280 피트 미만이면 아웃풋 CSV 파일로 출력됩니다.

나는 과거에는 20k가 너무 많았지 만 지금은 170k가 넘는다. 상상할 수 있듯이 출력 csv 파일은 3 백만 행과 계산으로 엄청납니다.

그럼 내가해야 할 일은 일반적으로 MySQL MIN() 함수를 사용하여 확인해야하지만 주소와 그룹별로 최소 거리를 반환하는 것이 더 낫습니다. 주소 당 최소 거리를주의하십시오. 각 주소에 대해 여러 행을 원하지 않습니다. 나는 어쩌면 .uniqarr#min 출력 5,280 아래 CSV. 그런 다음, 실제로 존재하는 경우의 거리에 그것을 밀어없이 고객 당 최소 주소를 사용하여 고객 (반환하는 방법을 생각하기 위해 노력하고있어

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 

그리고 관련 고객은 출력 CSV 배열에만 넣으십시오.

psuedocode와 관련하여 거리가 고객 당 최소 인 경우 고객 가치가 고유한지 확인한 다음 출력 CSV로 밀어 넣으십시오. 내 루프에서 루프를 구현하는 방법에 대해 100 %.

모든 통찰력이 인정됩니다.

답변

0

먼저 성능 문제는 어디에 있습니까? 나는 fib_coords을 계산하지 않고 customers을 통해 루핑한다고 가정 할 것입니다. 몇 가지 변경 사항이 있습니다 :

1) 전체 고객 CSV 파일을 한 번에 메모리로 읽어 들이지 만 CSV::for_each 방법을 사용하여 customers CSV 파일을 반복합니다. 전체 CSV 파일을로드하는 것은 아마도 fib_coords 배열에 더 잘 사용될 수있는 공정한 메모리 비트를 사용하고있을 것입니다. 이는 customersfib_coords 루프의 순서를 반대로 바꾸는 것을 의미합니다.

2) 두 번째로 전체 fib_coords 배열 검색을 피할 수 있습니다. 첫 번째 열을 위도 순서로 정렬하는 경우 가능한 최소 위도 (customer.latitude - 5280ft)를 계산하고 fib_coords에서 첫 번째 일치 항목을 찾아 bsearch을 사용하면 선형 검색보다 훨씬 빠르며 위도가 fib_coords이 될 때까지 위도까지 fib_coords이 범위를 벗어났습니다 (> customer.latitude + 5280ft).

+0

주요 성능 문제는 단지 괴물 출력 CSV 파일이있는 것입니다. 3-4 백만 레코드 및 엑셀에서 열려고하는 것은 악몽입니다 (파일 크기가 크고 그 이상이 아니라면 0.5GB에 가깝습니다). 그런 다음 MySQL 테이블에 모든 행을 삽입하고 feet_to_fiber에서 MIN()을 사용하고 주소별로 그룹화합니다. 여기로 자고 있지만 아침에 접근 방법을 먼저 시도 할 것입니다. – DnfD

+0

3 백만에서 4 백만 고객이 있다고 하시겠습니까? 출력 파일의 길이는 중요하지 않지만 많은 고객이 처리 할 수는 있습니다.추가 처리를하려는 경우 지리 좌표를 처리 할 수있는 확장 기능이있는'Postgres '와 같은 것을 고려할 것이지만 데이터베이스가 좋은 아이디어 인 것처럼 들립니다. 나는 또한 이것을 위해 ElasticSearch를 사용하여 성공했다. –

+0

처음'swfl_1a_geocoded.csv'는 10,000 명의 고객이 있습니다. 무슨 일이 벌어지고 있는지 위에서 언급 한 csv의 각 고객은 광섬유에서 모든 위도/경 쌍을 통과하여 출력 CSV에서 여러 결과를 반환합니다. 따라서 XYZ 주소의 Jane Doe는 5,280 피트 아래에서 150 개의 출력 결과를 얻을 수 있습니다. 하지만 저는 XYZ에서 Jane Doe에 대해서만 관심이 있습니다. XYZ에서 5,280 피트 아래의 모든 결과 중 최소값입니다. 그래서 중복 된 고객을 출력 파일에 출력하는 것을 피하고 Ruby를 통해 고유 한 스타일 검증을 수행하려고합니다. – DnfD