Recibo puntos en gran número de un sensor en tiempo real. Sin embargo, solo necesito 4 categorías de puntos, es decir, top_left, top_right, bottom_left y bottom_right. Tengo una declaración if-elif en Python 2 de la siguiente manera:

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

Cada punto tiene una id y x, y, z parámetros. Esta es una clase incorporada. Solo estoy mostrando una clase de muestra aquí.

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

¿Hay alguna manera eficiente de considerar el tiempo de ejecución para lograr lo mismo?

Respuesta: Estoy agregando los resultados que obtuve de las respuestas. Parece que la respuesta de Elis Byberi es la más rápida de todas. A continuación se muestra mi código de prueba:

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)

Vea a continuación la salida devuelta:

ravi@home:~/Desktop$ python test.py 
method 1: 0.174991846085
method 2: 0.0743980407715
method 3: 0.582262039185
1
Ravi Joshi 29 oct. 2017 a las 19:33

3 respuestas

La mejor respuesta

Puede usar un dict para guardar objetos. Dict es muy eficiente en la búsqueda de claves.

Usar dict es dos veces más rápido que usar if else block.

Esta es la forma más eficiente en 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
3
Elis Byberi 30 oct. 2017 a las 07:23

En lugar de usar la lista de comprensión:

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

Use un bucle y asigne los puntos durante la creación:

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

De esta manera, todo se hace en una iteración en lugar de dos.

1
alfasin 29 oct. 2017 a las 16:52

Aquí hay una forma que debería ser más rápida porque utiliza un solo if para determinar si un Point es uno de los que representan los extremos en función de su atributo id, más el {{X3} } utiliza la operación muy rápida del diccionario in para las pruebas de membresía. Esencialmente, lo que se hace es que el diccionario bbox está precargado con claves que corresponden a los cuatro identificadores buscados, lo que hace que la comprobación de cualquiera sea una operación relativamente eficiente.

Tenga en cuenta que si hay puntos con duplicados id s en la lista Point, el último visto será el seleccionado. También tenga en cuenta que si no se encuentra ningún punto con una id coincidente, algunas de las variables finales tendrán un valor de None en lugar de una instancia Point.

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))
1
martineau 30 oct. 2017 a las 17:20