2017-10-29 3 views
0

Ich empfange Punkte in großer Anzahl von einem Sensor in Echtzeit. Ich benötige jedoch nur vier Kategorien von Punkten, nämlich top_left, top_right, bottom_left und bottom_right. Ich habe eine if-Anweisung in elif Python 2 wie folgt:Schneller als if-elif-Anweisung

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 

Jeder Punkt hat eine ID und x, y, z-Parameter. Dies ist eine eingebaute Klasse. Ich zeige hier nur eine Beispielklasse.

Gibt es einen effizienten Weg, um die gleiche Laufzeit zu erreichen.

Antwort: Ich füge die Ergebnisse hinzu, die ich von den Antworten erhalten habe. Es scheint, dass the answer by Elis Byberi am schnellsten ist. Unten ist mein Testcode:

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) 

Siehe unten die zurückgegebenen Ausgabe:

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

In welcher Weise effizient? Laufzeit, Lesbarkeit oder Zeitaufwand für die Eingabe? –

+0

@NickPredey: Runtime –

+1

Das ist so effizient, wie eine Kontrollstruktur bekommen kann, denke ich – arielnmz

Antwort

2

Sie können ein Diktat zum Speichern von Objekten verwenden. Dict ist in der Schlüsselsuche sehr effizient.

Die Verwendung von dict ist doppelt so schnell wie die Verwendung von if else block.

Dies ist der effizienteste Weg, in 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

Warum das Wörterbuch auf diese Schlüssel beschränken und Ausnahmen für alle anderen Point.id behandeln? Fügen Sie dem Diktat einfach jede Point.id hinzu und verwenden Sie nur die Schlüssel, an denen Sie interessiert sind. – wwii

+0

Denkst du, dass wenn der Block sonst schneller ist als die Ausnahmebehandlung? Ich habe einen Benchmark gemacht und es ist doppelt so schnell, als wenn ich sonst blocken würde. –

+0

Nein, machen Sie einfach ein Wörterbuch mit * jeder * Point.id - beschränken Sie es nicht und verwalten Sie die Einschränkung mit der Ausnahmebehandlung. Nachfolgender Code kann nur die relevanten Schlüssel verwenden und den anderen ignorieren. – wwii

1

Statt Liste Verständnis der Verwendung:

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

eine Schleife verwenden und die Punkte bei der Erstellung zugewiesen werden:

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 

Dies So erhalten Sie alles in einer Iteration statt in zwei.

+0

Leider habe ich keine Kontrolle über den Index dieses Arrays. Es kann eine beliebige ganze Zahl sein. Bitte überprüfen Sie die Frage noch einmal. Danke vielmals. –

+0

@RaviJoshi Antwort aktualisiert – alfasin

1

Hier ist eine Art und Weise, die schneller sein sollte, weil es eine einziges if verwendet, ob ein das Extrem Point ist einer von denen, um zu bestimmen, vertreten auf der Grundlage ihrer id Attribut, plus die if verwendet das sehr schnelle Wörterbuch in Betrieb für Mitgliedschaftstests. Im Wesentlichen, was es getan wird, ist das bbox Wörterbuch mit Schlüsseln vorgeladen, die den vier gesuchten Ids entsprechen, die das Überprüfen auf irgendwelcher von ihnen eine einzelne verhältnismäßig effiziente Operation machen.

Beachten Sie, dass, wenn Punkte mit Duplikat id s in der Point Liste vorhanden sind, der zuletzt gesehene der ausgewählte sein wird. Beachten Sie außerdem, dass einige der endgültigen Variablen den Wert None anstelle einer Point Instanz haben, wenn kein Punkt mit einem übereinstimmenden id gefunden wird.

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

Vielen Dank. Ich mag den Codierungsstil besonders 'Weisen Sie die Werte der bbox Variablen mit sinnvollen Namen zu –