Tengo una lista que incluye conjuntos de vectores, todos con 3 coordenadas. Necesito obtener una nueva lista que incluya el promedio de las coordenadas respectivas de estos vectores, según sus equipos. En particular, tengo una lista de listas (de listas) de números:

list=[[[1,1,1],[0,1,0]],[[0,0,2]],[[1,1,1],[2,2,2],[2,2,1]]]

Lo que quiero es obtener la siguiente lista:

new_list=[[1/2,1,1/2],[0,0,2],[5/3,5/3,4/3]]

Que proviene de sumar las coordenadas respectivas de los dos primeros vectores de list y luego dividirlos por su número, es decir:

[(1+0)/2, (1+1)/2, (0+1)/2]

Luego concatenando el tercer elemento de list

[0,0,2]

Y finalmente agregando las "coordenadas" respectivas de los últimos 3 vectores y dividiéndolos por su número, es decir:

[(1+2+2)/3,(1+2+2)/3,(1+2+1)/3]

He intentado hacer lo anterior con una serie de for loops anidados, pero sigo perdiendo la pista y obtengo resultados incorrectos. Lamento si mi problema está mal expresado, pero realmente no pude encontrar una mejor manera de describir mi situación.

0
user7831701 27 oct. 2017 a las 16:31

3 respuestas

La mejor respuesta

En primer lugar, usaría tuplas para las listas de tres elementos, ya que no las mutarás:

l = [
     [
      (1, 1, 1),
      (0, 1, 0)
     ],
     [
      (0, 0, 2)
     ],
     [
      (1, 1, 1),
      (2, 2, 2),
      (2, 2, 1)
     ]
    ]

Si desea una solución pitónica de una sola línea, elija:

print(list(map(lambda *args: sum(args)/len(args), *(map(lambda *args: sum(args)/len(args), *item) for item in l))))

También se podría hacer con una definición de función en lugar de lambda, ya que se usa dos veces:

def mean(*args):
    return sum(args)/len(args)

print(list(map(mean, *(map(mean, *item) for item in l))))

Como puede ver, la solución está usando una función / lambda que calcula la media de los argumentos pasados. También está utilizando generadores y map.

Vamos a verlo paso a paso:

for items in (map(mean, *item) for item in l):
    print(list(items))
# [0.5, 1.0, 0.5]
# [0.0, 0.0, 2.0]
# [1.66666667, 1.666666667, 1.33333333]
step_1 = (map(mean, *item) for item in l)
step_2 = map(mean, *step_1)
print(list(step_2))

En el primer paso estamos creando un generador (piense en él como si fuera una matriz eficiente en memoria que solo puede usar una vez) para cada elemento en la lista, el * está extrayendo las listas de segundo nivel y pasándolas como múltiples argumentos para el mapa de funciones. Entonces, básicamente, map está recibiendo map(mean, (1,1,1), (0,1,0)) en la primera iteración. La función de mapa llama a la función media una vez por elemento en las tuplas que pasa un elemento de cada tupla: mean(1,0), mean(1,1) y mean(1,0) y luego las agrupa en un objeto de mapa que puede pensar que es el igual que el generador Y recuerde que todo esto se hizo para cada elemento en el primer nivel de la lista. Entonces tenemos un generador que producirá 3 objetos de mapa: uno con mean(1,0), mean(1,1) y mean(1,0); una segunda con mean(0), mean(0) y mean(2) y una última con mean(1,2,2), mean(1,2,2) y mean(1,2,1).

El segundo paso es aplicar la misma función con la misma expresión de mapa una vez más para que obtengamos un solo objeto de mapa con: mean(mean(1,0),mean(0),mean(1,2,2)), mean(mean(1,1),mean(0),mean(1,2,2)) y mean(mean(1,0),mean(2),mean(1,2,1)). Al forzarlo a transformarse en una lista, podemos imprimirlo en la consola.

La versión lambda es exactamente la misma pero con funciones anónimas.

Si está utilizando Python 2.7, deberá cambiar len(args) por float(len(args)) (2 veces sin la versión lambda, solo una vez en la versión def.

0
Adirio 27 oct. 2017 a las 14:18

Esto es más un problema de contabilidad que otra cosa. Sugiero desglosar las cosas y no tratar de ser elegante (¡solo haz que funcione!)

mlist=[[[1,1,1],[0,1,0]],[[0,0,2]],[[1,1,1],[2,2,2],[2,2,1]]]
A = mlist[0]
A1,A2 = A[0],A[1]
B = mlist[1]
C = mlist[2]
C1,C2,C3 = C[0],C[1],C[2]
A = [(x + y)/2 for x,y, in zip(A1,A2)]
C = [np.sum(x)/len(x) for x in zip(C1,C2,C3)]
new_list = [A,B,C]
0
Mohammad Athar 27 oct. 2017 a las 13:51

¡Necesitas zip! Itere a través de su lista de entrada, comprima las sublistas y luego, para cada elemento en el resultado comprimido, ¡encuentre el promedio!

>>> lis = [[[1,1,1],[0,1,0]],[[0,0,2]],[[1,1,1],[2,2,2],[2,2,1]]]
>>> centroid = lambda inp: [[sum(m)/float(len(m)) for m in zip(*l)] for l in inp]
>>> centroid(lis)
[[0.5, 1.0, 0.5], [0.0, 0.0, 2.0], [1.6666666666666667, 1.6666666666666667, 1.3333333333333333]]

Para explicar la función centroide,

>>> [zip(*l) for l in lis] #iterate through the input list and zip them to get what you call `adding the respective coordinates of the first two vectors of list`
[[(1, 0), (1, 1), (1, 0)], [(0,), (0,), (2,)], [(1, 2, 2), (1, 2, 2), (1, 2, 1)]]
>>> [[m for m in zip(*l)] for l in lis]
[[(1, 0), (1, 1), (1, 0)], [(0,), (0,), (2,)], [(1, 2, 2), (1, 2, 2), (1, 2, 1)]]
>>> [[sum(m) for m in zip(*l)] for l in lis]
[[1, 2, 1], [0, 0, 2], [5, 5, 4]]
>>> [[sum(m)/len(m) for m in zip(*l)] for l in lis]
[[0, 1, 0], [0, 0, 2], [1, 1, 1]]
>>> [[sum(m)/float(len(m)) for m in zip(*l)] for l in lis]
[[0.5, 1.0, 0.5], [0.0, 0.0, 2.0], [1.6666666666666667, 1.6666666666666667, 1.3333333333333333]]

Nota: Si está utilizando python3 y superior, no necesita convertir su entero en float () explícitamente mientras divide.

2
Keerthana Prabhakaran 27 oct. 2017 a las 13:51