2017-10-29 9 views
0

センサからポイントをリアルタイムに受信しています。しかし、ちょうど4つのカテゴリーのポイント、すなわち、top_left、top_right、bottom_left、およびbottom_rightが必要です。私は次のようにPython 2でif-elif文を持っています:if-elifよりも速い

from random import random, randint 

# points below are received from sensor. however, 
# here in this post I am creating it randomly. 
points = [Point(randint(0, i), random(), random(), random()) for i in range(100)] 

# 4 categories 
top_left, top_right, bottom_left, bottom_right = None, None, None, None 
for p in points: 
    if p.id == 5: 
     top_left = p 
    elif p.id == 7: 
     top_right = p 
    elif p.id == 13: 
     bottom_left = p 
    elif p.id == 15: 
     bottom_right = p 

print top_left.id, top_left.x, top_left.y, top_left.z # check variable 

各点にはidとx、y、zパラメータがあります。これは組み込みクラスです。ここではサンプルクラスを紹介しています。

class Point(): 
    def __init__(self, id, x, y, z): 
     self.id = id 
     self.x = x 
     self.y = y 
     self.z = z 

これを達成するための実行時間を考慮する効率的な方法はありますか。

回答: 私は答えから得た結果を追加しています。中でも最も速いのはthe answer by Elis Byberiです。以下は私のテストコードです:

class Point(): 
    def __init__(self, id, x, y, z): 
     self.id = id 
     self.x = x 
     self.y = y 
     self.z = z 

from random import random, randint 
n = 1000 
points = [Point(randint(0, i), random(), random(), random()) for i in range(n)] 

def method1(): 
    top_left, top_right, bottom_left, bottom_right = None, None, None, None 
    for p in points: 
     if p.id == 5: 
      top_left = p 
     elif p.id == 7: 
      top_right = p 
     elif p.id == 13: 
      bottom_left = p 
     elif p.id == 15: 
      bottom_right = p 
    #print top_left.id, top_left.x, top_left.y, top_left.z 

def method2(): 
    categories = { 
     5: None, # top_left 
     7: None, # top_right 
     13: None, # bottom_left 
     15: None # bottom_right 
    } 

    for p in points: 
     categories[p.id] = p 

    top_left = categories[5] 
    #print top_left.id, top_left.x, top_left.y, top_left.z 

def method3(): 
    name_to_id = {'top_left': 5, 'top_right': 7, 'bottom_left': 13, 'bottom_right': 15} 
    ids = [value for value in name_to_id.values()] 
    bbox = {id: None for id in ids} 

    for point in points: 
     try: 
      bbox[point.id] = Point(point.id, point.x, point.y, point.z) 
     except KeyError: # Not an id of interest. 
      pass 

    top_left = bbox[name_to_id['top_left']] 
    #print top_left.id, top_left.x, top_left.y, top_left.z 

from timeit import Timer 
print 'method 1:', Timer(lambda: method1()).timeit(number=n) 
print 'method 2:', Timer(lambda: method2()).timeit(number=n) 
print 'method 3:', Timer(lambda: method3()).timeit(number=n) 

が返される出力は、以下を参照してください:

[email protected]:~/Desktop$ python test.py 
method 1: 0.174991846085 
method 2: 0.0743980407715 
method 3: 0.582262039185 
+3

どのように効率的ですか?実行時間、可読性、または入力にかかる時間 –

+0

@NickPredey:Runtime –

+1

コントロール構造と同じくらい効率的ですが、私は – arielnmz

答えて

2

dictを使用してオブジェクトを保存できます。 Dictはキー検索で非常に効率的です。

dictの使用は、if elseブロックを使用する場合の2倍です。

これはPythonで最も効率的な方法である:ここでは

from random import random, randint 

class Point(): 
    def __init__(self, id, x, y, z): 
     self.id = id 
     self.x = x 
     self.y = y 
     self.z = z 

# points below are received from sensor. however, 
# here in this post I am creating it randomly. 
points = [Point(randint(0, i), random(), random(), random()) for i in 
      range(100)] 

# 4 categories 
categories = { 
    5: None, # top_left 
    7: None, # top_right 
    13: None, # bottom_left 
    15: None # bottom_right 
} 

for p in points: 
    categories[p.id] = p 

>>> print categories[5].id, categories[5].x, categories[5].y, categories[5].z # check variable 
5 0.516239541892 0.935096344266 0.0859987803457 
+0

辞書をこれらのキーに制限し、他のすべてのPoint.idの例外を処理する必要があるのはなぜですか?すべてのPoint.idをdictに追加して、関心のあるキーのみを使用しますか? – wwii

+0

elseブロックが例外処理より速いと思うか?私はベンチマークを行い、他のブロックと比較して2倍の速さです。 –

+0

いいえ、すべての* Point.idを持つ辞書を作成してください - それを制限せず、例外処理で制限を管理してください。後続のコードでは、関連するキーを使用して、他のキーを無視できます。 – wwii

1

代わりにリスト内包表記を使用する:

points = [Point(randint(0, i), random(), random(), random()) for i in range(100)] 

ループを使用して作成中のポイントを割り当てます。

points = [] 
for i in range(100): 
    p = Point(randint(0, i), random(), random(), random()) 
    points.append(p) 
    if p.id == 5: 
     top_left = p 
    elif p.id == 7: 
     top_right = p 
    elif p.id == 13: 
     bottom_left = p 
    elif p.id == 15: 
     bottom_right = p 

あなたは2回ではなく1回の反復ですべて完了します。

+0

残念ながら、私はこの配列のインデックスを制御できません。任意の整数にすることができます。再度質問を確認してください。どうもありがとう。 –

+0

@RaviJoshi Answer – alfasin

1

はそれがPointが彼らのidに基づい両極端を代表するものの一つであるかどうかを判断するために、単一のifを使用しているため、より高速であるべき方法ですまた、ifは、非常に高速な辞書in操作を使用してメンバーシップテストを行います。基本的にはbboxディクショナリには、検索対象の4つのIDに対応するキーがプリロードされており、のいずれかがの比較的効率的な操作をチェックします。

リストに重複するポイントがある場合は、最後に表示されたものが選択されていることに注意してください。一致する点が見つからない場合は、最終的な変数の一部はの代わりにNoneの値になります。

from random import randint, random 
from pprint import pprint 
from operator import attrgetter 


class Point(): 
    def __init__(self, id, x, y, z): 
     self.id = id 
     self.x = x 
     self.y = y 
     self.z = z 


points = [Point(randint(0, 20), random(), random(), random()) for i in range(100)] 
name_to_id = {'top_left': 5, 'top_right': 7, 'bottom_left': 13, 'bottom_right': 15} 
bbox = {id: None for id in name_to_id.values()} # Preload with ids of interest. 

for point in points: 
    if point.id in bbox: # id of interest? 
     bbox[point.id] = point 

# Assign bbox's values to variables with meaningful names. 
top_left = bbox[name_to_id['top_left']] 
top_right = bbox[name_to_id['top_right']] 
bottom_left = bbox[name_to_id['bottom_left']] 
bottom_right = bbox[name_to_id['bottom_right']] 

for point in [top_left, top_right, bottom_left, bottom_right]: 
    print('Point({}, {}, {}, {})'.format(point.id, point.x, point.y, point.z)) 
+0

ありがとうございました。私はコーディングスタイル、特に '意味のある名前を持つ変数にbboxの値を割り当てる 'が好きです。 –

関連する問題