Tengo una rutina de pitón muy simple que implica recorrer una lista de aproximadamente 20,000 coordenadas de latitud y longitud y calcular la distancia de cada punto a un punto de referencia.deepcopy and python - consejos para evitar usarlo?
def compute_nearest_points(lat, lon, nPoints=5):
"""Find the nearest N points, given the input coordinates."""
points = session.query(PointIndex).all()
oldNearest = []
newNearest = []
for n in xrange(nPoints):
oldNearest.append(PointDistance(None,None,None,99999.0,99999.0))
newNearest.append(obj2)
#This is almost certainly an inappropriate use of deepcopy
# but how SHOULD I be doing this?!?!
for point in points:
distance = compute_spherical_law_of_cosines(lat, lon, point.avg_lat, point.avg_lon)
k = 0
for p in oldNearest:
if distance < p.distance:
newNearest[k] = PointDistance(
point.point, point.kana, point.english, point.avg_lat, point.avg_lon, distance=distance
)
break
else:
newNearest[k] = deepcopy(oldNearest[k])
k += 1
for j in range(k,nPoints-1):
newNearest[j+1] = deepcopy(oldNearest[j])
oldNearest = deepcopy(newNearest)
#We're done, now print the result
for point in oldNearest:
print point.station, point.english, point.distance
return
al principio me escribió esto en C, utilizando el mismo enfoque exacto, y trabaja muy bien allí, y es básicamente instantánea para NPOINTS < = 100. Así que decidí portarlo a Python porque quería usar SqlAlchemy para hacer otras cosas.
Lo porté por primera vez sin las declaraciones de copia profunda que ahora salpican el método, y esto causó que los resultados fueran "impares" o parcialmente incorrectos, porque algunos de los puntos solo se copiaban como referencias (¿supongo? ?) - pero todavía era casi tan rápido como la versión C.
Ahora, con las llamadas deepcopy añadidas, la rutina hace su trabajo correctamente, pero ha incurrido en una penalización de rendimiento extremo, y ahora lleva varios segundos hacer el mismo trabajo.
Esto parece ser un trabajo bastante común, pero evidentemente no lo estoy haciendo de manera pitonica. ¿Cómo debería estar haciendo esto para poder obtener los resultados correctos, pero no tengo que incluir una copia profunda en todas partes?
EDIT:
He golpeado en una solución mucho más simple y más rápido,
def compute_nearest_points2(lat, lon, nPoints=5):
"""Find the nearest N points, given the input coordinates."""
points = session.query(PointIndex).all()
nearest = []
for point in points:
distance = compute_spherical_law_of_cosines(lat, lon, point.avg_lat, point.avg_lon)
nearest.append(
PointDistance(
point.point, point.kana, point.english, point.avg_lat, point.avg_lon, distance=distance
)
)
nearest_points = sorted(nearest, key=lambda point: point.distance)[:nPoints]
for item in nearest_points:
print item.point, item.english, item.distance
return
Así que, básicamente, sólo estoy haciendo una copia completa de la entrada y la introducción de un nuevo valor - la distancia desde la punto de referencia. Luego, simplemente aplico 'ordenado' a la lista resultante, especificando que la clave de clasificación debe ser la propiedad de distancia del objeto PointDistance.
Esto es mucho más rápido que usar Deepcopy aunque confieso que realmente no entiendo por qué. ¿Supongo que se debe a las eficientes implementaciones de C python "ordenadas"?
¿Cómo es la clase 'PointDistance'? Si convierte la clase 'PointDistance' en una simple que solo se refiere al punto original y su distancia (es decir, que es prácticamente una tupla con dos elementos), no debería necesitar usar' deepcopy' ya que los puntos no cambiarán durante el algoritmo y la distancia es un número simple. –
@ Tamás sí, básicamente es solo un diccionario. sin embargo, esto definitivamente no funciona bien sin una copia profunda en el primer ejemplo. quizás funcionaría si eliminara completamente la clase y usara un diccionario en su lugar? para ser honesto, simplemente no tengo una comprensión suficientemente clara del modelo de referencia para saber qué sucedería en esos casos. ¿quizás podrías elaborarme o señalarme otros recursos o publicaciones sobre el tema? – si28719e