Python Convertir lista ordenada de cambios relativos a cambios absolutos

Supongamos que tengo un conjunto de objetos que contienen: (cambio relativo, tiempo de cambio):

(+1,0) (-1, 1) (+1,3) (+1, 3) (-1, 5) (+1, 9)  

Ahora quiero reemplazar los cambios relativos por su valor absoluto, comenzando en 0:

(1,0) (0, 1) (1,3) (2, 3) (1, 5) (2, 9)
 0+1   0+1-1  0+1-1+1 ...

¿Cuál es la mejor manera de hacer esto? ¿Existe una función de Python que me permita

  • Iterar sobre una lista (ordenada) de objetos
  • Almacenar internamente un valor absoluto
  • leer el cambio de cada objeto, actualizar el valor absoluto interno
  • reemplazar el cambio relativo con el valor absoluto

preguntado el 22 de mayo de 12 a las 20:05

Si los valores son (a, b) y (c, d), ¿cuál sería su salida? -

@robert: si entiendo la pregunta correctamente, el resultado sería [(a, b), (a+c, d)]. -

2 Respuestas

Así es como puede hacer esto con una lista de comprensión:

>>> data = [(+1,0), (-1, 1), (+1,3), (+1, 3), (-1, 5), (+1, 9)]
>>> [(sum(x[0] for x in data[:i+1]), data[i][1]) for i in range(len(data))]
[(1, 0), (0, 1), (1, 3), (2, 3), (1, 5), (2, 9)]

O un poco más eficiente (no llama sum() para cada valor):

result = [data[0]]
for change, t in data[1:]:
    result.append((result[-1][0]+change, t))

Como dijiste que estos son objetos, probablemente necesitarás reemplazar la indexación con un atributo get, por ejemplo, el x[0] in sum(x[0] for x in data[:i+1]) podría convertirse x.change.

contestado el 22 de mayo de 12 a las 20:05

Usted puede haga esto con una lista de comprensión, pero me parece más un trabajo para un generador, dados los términos específicos de su solicitud. Esta es una solución muy generalizada que debería funcionar tanto con secuencias como con iteradores. Realiza el equivalente de Haskell scanl en el iterable que se le pasó, con un valor inicial opcional como último argumento.

El primer argumento debe ser una función que tome dos argumentos, el estado acumulado actual y el siguiente elemento de la secuencia, y devuelva el siguiente estado acumulado. Podría ser tan simple como operator.add o algo más complejo.

>>> def scan(f, seq, init=None):
...     seq = iter(seq)
...     state = seq.next() if init is None else init
...     yield state
...     for i in seq:
...         state = f(state, i)
...         yield state

Suma acumulada (es decir, números triangulares):

>>> import operator
>>> list(scan(operator.add, range(10)))
[0, 1, 3, 6, 10, 15, 21, 28, 36, 45]

Comenzando con un valor inicial diferente:

>>> list(scan(operator.add, range(1, 10), 10))
[10, 11, 13, 16, 20, 25, 31, 38, 46, 55]

Aplicado a su problema:

>>> diffs = [(1, 0), (-1, 1), (1, 3), (1, 3), (-1, 5), (1, 9)]
>>> list(scan(lambda x, y: (x[0] + y[0], y[1]), diffs))
[(1, 0), (0, 1), (1, 3), (2, 3), (1, 5), (2, 9)]

Con un valor inicial diferente, solo por diversión.

>>> list(scan(lambda x, y: (x[0] + y[0], y[1]), diffs, (5, -1)))
[(5, -1), (6, 0), (5, 1), (6, 3), (7, 3), (6, 5), (7, 9)]

contestado el 23 de mayo de 12 a las 16:05

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.