2016-06-24 3 views
2

これは私の最初の質問ですので、間違いを許してください。Python:必要なデータセットを見つけるための多対多の比較

私は、次の例のようにいくつかの情報(〜10000000 +)線で大きなファイル(CSV)持っている:私の目標は、各行を読んで、箱の体積を計算し、1以内です

date;box_id;box_length;box_width;box_height;weight;type 
--snip-- 
1999-01-01 00:00:20;nx1124;10;4;5.5;2.1;oversea 
1999-01-01 00:00:20;np11r4;8;3.25;2;4.666;local 
--snip-- 

を(たとえば、00:00:00 - 00:00:59)2つ以上のボックスの音量が類似している場合(+ 10%の差異)、録音してからタイムスタンプとタイプを記録する必要があります。現時点で

、私はブルートフォースアプローチを使用しています。各回線を通じて

  • 計算量
  • は、次の行と計算量
  • に行く

    • 実行が
    • 繰り返しを比較します1時間の時間差が検出されるまで
    • リストから最初のボックスを削除する
    • 私の1時間ウィンドウが1,2,3,4を持っている場合
    • 例えば

    、私はこの

    1 
    2 == 1 
    3 == 1 then == 2 
    4 == 1 then == 2 then == 3 
    5 == 2 then == 3 then == 4 # removed 1 from list(1hr window moved down) 
    6 == 2 then == 3 then == 4 then == 5 
    7 == 2 then == 3 then == 4 then == 5 then == 6 
    .... so on .... 
    
    をやっている2番目のボックスで、リストに
  • 繰り返しプロセスを別のボックスを追加

    これは、私が考えている最高のものです。なぜなら、各ボックスを、指定された時間枠内の他のものとそれぞれ比較する必要があるからです。しかし、これは現時点では非常に遅いです。

    私はより良いアルゴリズムを探していますが、どの方向に向かうべきかはわかりません。私はいくつかの優れたツール(これまでのところ、Pandasは私のお気に入りです)を学ぼうとしていますが、必要な方法でこれらのツールがデータを処理できるようにするためのアルゴリズムを実装する必要があると仮定しています。

    もし私が私のpythonコード(ソース)を投稿するのに役立ちます。

    更新以下は私のコードです。私はいくつかの行(try/catchブロックなど、無効なファイルパス/形式、型変換エラー処理など)を省略しました。私は5秒のウィンドウのために少し作業するコードをカスタマイズしました。続き

    は、比較を行う実際のプログラムで続いてBoxクラス

    from datetime import datetime 
    from time import mktime 
    
    class Box(object): 
        """ Box model """ 
    
        def __init__(self,data_set): 
         self.date = data_set[0] 
         self.timestamp = self.__get_time() 
         self.id = data_set[1] 
         self.length = float(data_set[2]) 
         self.width = float(data_set[3]) 
         self.height = float(data_set[4]) 
         self.weight = int(data_set[5]) 
         self.volume = self.__get_volume() 
    
        def __get_time(self): 
         """ convert from date string to unix-timestamp """ 
         str_format = '%Y-%m-%d %H:%M:%S' 
         t_tuple = datetime.strptime(self.date, str_format).timetuple() 
         return mktime(t_tuple) 
    
        def __get_volume(self): 
         """ calculate volume of the box """ 
         return (self.length * self.width * self.height) 
    

    です。私は便宜のためユーティリティファイルとmain.pyファイルを一緒に組み合わせました。

    from csv import reader 
    from io import open as open_file 
    from os import path 
    from sys import argv, exit 
    from time import time 
    
    # custom lib 
    from Box import Box 
    
    def main(): 
    
        file_name = str.strip(argv[1]) 
        boxes_5s = [] 
        diff = 0 
    
        similar_boxes = [] 
    
        for row in get_file(file_name): 
         if row: 
          box = Box(row) 
    
          if len(boxes_5s) > 0: 
           diff = box.timestamp - boxes_5s[0].timestamp 
           if diff < 6: 
            boxes_5s.append(box) 
           else: 
            similar_boxes += get_similar(boxes_5s) 
            del boxes_5s[0] # remove the oldest box 
            boxes_5s.append(box) 
          else: 
           boxes_5s.append(box) 
    
         print(similar_boxes) 
    
    
    def get_file(file_name): 
        """ open and return csv file pointer line by line """ 
        with open_file(file_name,'rb') as f: 
         header = f.readline() 
         print(header) 
         rows = reader(f, delimiter=';') 
    
         for r in rows: 
          yield r 
         else: 
          yield '' 
    
    
    def get_similar(box_list): 
        """ compare boxes for similar volume """  
    
        num_boxes = len(box_list) 
    
        similar_boxes = [] 
        record_str = "Box#{} Volm:{} and #{} Volm:{}" 
        for i in xrange(num_boxes): 
         box_1 = box_list[i] 
    
         for j in xrange(i+1, num_boxes): 
          box_2 = box_list[j] 
    
          vol_diff = abs((box_1.volume - box_2.volume)/box_1.volume) <= 0.1 
    
    
          if vol_diff: similar_boxes.append(record_str.format(box_1.id,box_1.volume,box_2.id, box_2.volume)) 
    
        return similar_boxes 
    
    if __name__ == "__main__": 
        main() 
    

    ありがとうございます。

  • +0

    おそらくそれらを最初に注文してから似たようなボックスを見つけてください。 – armonge

    +2

    それが役に立ちます。 [最小限の、完全で検証可能なサンプルを作成する方法](http://stackoverflow.com/help/mcve/) – rll

    +0

    私の質問の漠然とした性質のために申し訳ありません、私は後で私はいくつかのコードを投稿します家 –

    答えて

    1

    最初のタイムスタンプを1時間のウィンドウの開始として(時のビンは常に時間:00:00から開始するのではなく)、データ量は数千万行にも及ぶ可能性があります(時間注文entriesinファイルを期待する)こと:

    date;box_id;box_length;box_width;box_height;weight;type 
    1999-01-01 00:00:20;nx1124;10;4;5.5;2.1;oversea 
    1999-01-01 00:00:20;np11r4;8;3.25;2;4.666;local 
    1999-01-01 00:10:20;np11r3;8;3.25;2.1;4.665;local 
    1999-01-01 00:20:20;np11r2;8;3.25;2.05;4.664;local 
    1999-01-01 00:30:20;np11r1;8;3.23;2;4.663;local 
    1999-01-01 00:40:20;np11r0;8;3.22;2;4.662;local 
    1999-01-01 00:50:20;dp11r4;8;3.24;2;4.661;local 
    1999-01-01 01:00:20;cp11r3;8;3.25;2;4.666;local 
    1999-01-01 01:01:20;bp11r2;8;3.26;2;4.665;local 
    1999-01-01 01:02:20;ap11r1;8;3.22;2;4.664;local 
    1999-01-01 01:03:20;zp11r0;12;3.23;2;4.663;local 
    1999-01-01 02:00:20;yp11r4;8;3.24;2;4.662;local 
    1999-01-01 04:00:20;xp11r4;8;3.25;2;4.661;local 
    1999-01-01 04:00:21;yy11r4;8;3.25;2;4.661;local 
    1999-01-01 04:00:22;xx11r4;8;3.25;2;4.661;oversea 
    1999-01-01 04:59:19;zz11r4;8;3.25;2;4.661;local 
    

    利回り:

    1999-01-01 00:00:20 1999-01-01 00:30:20 local 
    1999-01-01 00:00:20 1999-01-01 00:50:20 local 
    1999-01-01 00:00:20 1999-01-01 00:00:20 local 
    1999-01-01 00:00:20 1999-01-01 00:20:20 local 
    1999-01-01 00:00:20 1999-01-01 00:10:20 local 
    1999-01-01 00:00:20 1999-01-01 00:00:20 oversea 
    1999-01-01 00:00:20 1999-01-01 01:00:20 local 
    1999-01-01 01:00:20 1999-01-01 01:01:20 local 
    1999-01-01 01:00:20 1999-01-01 01:03:20 local 
    1999-01-01 04:00:20 1999-01-01 04:00:21 local 
    1999-01-01 04:00:20 1999-01-01 04:00:22 oversea 
    1999-01-01 04:00:20 1999-01-01 04:59:19 local 
    
    サンプル入力ファイルで

    #! /usr/bin/env python 
    from __future__ import print_function 
    
    import csv 
    import datetime as dt 
    import math 
    import collections 
    
    
    FILE_PATH_IN = './box_data_time_ordered_100k_sparse.csv' 
    TS_FORMAT = '%Y-%m-%d %H:%M:%S' 
    TS_TOKEN = 'date' 
    SIMILAR_ENOUGH = 0.1 
    BoxEntry = collections.namedtuple(
        'BoxEntry', ['start_ts', 'a_ts', 't_type', 'b_volume']) 
    
    
    def box_volume(box_length, box_width, box_height): 
        """Volume in cubic of length units given.""" 
        return box_length * box_width * box_height 
    
    
    def filter_similar_box_volumes(box_entries): 
        """Ordered binary similarity comparator using complex algorithm 
        on a medium large slice of data.""" 
    
        def _key(r): 
         """sort on volume.""" 
         return r.b_volume 
    
        entries_volume_ordered = sorted(box_entries, key=_key) 
        collector = [] 
        for n, box_entry in enumerate(entries_volume_ordered[1:], start=1): 
         one = box_entry.b_volume 
         prev_box_entry = entries_volume_ordered[n] 
         previous = prev_box_entry.b_volume 
         if one and math.fabs(one - previous)/one < SIMILAR_ENOUGH: 
          if box_entry not in collector: 
           collector.append(box_entry) 
          if prev_box_entry not in collector: 
           collector.append(prev_box_entry) 
        return collector 
    
    
    def hourly_boxes_gen(file_path): 
        """Simplistic generator, yielding hour slices of parsed 
        box data lines belonging to 1 hour window per yield.""" 
    
        csv.register_dialect('boxes', delimiter=';', quoting=csv.QUOTE_NONE) 
        start_ts = None 
        cx_map = None 
        hour_data = [] 
        an_hour = dt.timedelta(hours=1) 
        with open(file_path, 'rt') as f_i: 
         for row in csv.reader(f_i, 'boxes'): 
          if cx_map is None and row and row[0] == TS_TOKEN: 
           cx_map = dict(zip(row, range(len(row)))) 
           continue 
          if cx_map and row: 
           a_ts = dt.datetime.strptime(row[cx_map[TS_TOKEN]], TS_FORMAT) 
           t_type = row[cx_map['type']] 
           b_length = float(row[cx_map['box_length']]) 
           b_width = float(row[cx_map['box_width']]) 
           b_height = float(row[cx_map['box_height']]) 
           b_volume = box_volume(b_length, b_width, b_height) 
           if start_ts is None: 
            start_ts = a_ts 
            hour_data.append(
             BoxEntry(start_ts, a_ts, t_type, b_volume)) 
           elif a_ts - an_hour < start_ts: 
            hour_data.append(
             BoxEntry(start_ts, a_ts, t_type, b_volume)) 
           else: 
            yield filter_similar_box_volumes(hour_data) 
            hour_data = [BoxEntry(start_ts, a_ts, t_type, b_volume)] 
            start_ts = a_ts 
         if hour_data: 
          yield filter_similar_box_volumes(hour_data) 
    
    
    def main(): 
        """Do the thing.""" 
        for box_entries in hourly_boxes_gen(FILE_PATH_IN): 
         for box_entry in box_entries: 
          print(box_entry.start_ts, box_entry.a_ts, box_entry.t_type) 
    
    if __name__ == '__main__': 
        main() 
    

    いくつかの注意事項:オーバーライドなしのstrptimeメソッドの日時クラスにアクセスするための別名を持つ特定の方言と読み取りに使用

    1. csvモジュール、(セミコロンとしてデリミタデフォルトされていない)

    2. インポート日時、モジュール名 - YMMV

    3. は、発電機機能

    4. 容積と類似でチャンク時間ウィンドウリーダーをカプセル化します別々の取引で計算します。

    5. 何とかそうでなければならないボリュームが順序付けられた単純なフィルタアルゴリズムであり、mが候補マッチの数である場合にはO(m)である。

    6. 名前付きタプルをコンパクトな格納に使用しますが、意味のあるアドレッシングも使用します。

    7. クロック調整1時間窓(ブートストラップするために第1のタイムスタンプを使用していない)と、1つのニーズが調整コードビットを実装するには、しかし

    そうでない不思議からのサンプルコードを待っ自明であるべきですOP ;-)

    が更新されているので、イベントリッチ時間は、O(n^2)アルゴリズムがすべての時間を食べるようにはなりません(ネストされたループが削除された_naive )。

    これらの約100k行(86400+)の類似度チェックの候補が3600個あるサンプルに1秒ごとにエントリを1日追加すると、約10秒かかりました。

    +0

    ありがとう!はい、これは非常に役に立ちます。私はこの線に沿って何かを使用していますが、現時点では非常に原油です。明日このソリューションを試してみます。 :) –

    関連する問題