Promedio timedelta en la lista

Quiero calcular el tiempo promedio entre fechas en una lista. Aunque lo siguiente funciona bien, me pregunto si hay una forma más inteligente.

delta = lambda last, next: (next - last).seconds + (next - last).days * 86400 total = sum(delta(items[i-1], items[i]) for i in range(1, len(items))) average = total / (len(items) - 1) 

Por cierto, si tienes una lista de timedeltas o fechas, ¿por qué incluso haces matemáticas?

 datetimes = [ ... ] # subtracting datetimes gives timedeltas timedeltas = [datetimes[i-1]-datetimes[i] for i in range(1, len(datetimes))] # giving datetime.timedelta(0) as the start value makes sum work on tds average_timedelta = sum(timedeltas, datetime.timedelta(0)) / len(timedeltas) 

Prueba esto:

 from itertools import izip def average(items): total = sum((next - last).seconds + (next - last).days * 86400 for next, last in izip(items[1:], items)) return total / (len(items) - 1) 

En mi opinión hacerlo de esta manera es más legible. Un comentario para los lectores menos inclinados matemáticamente de su código podría ayudar a explicar cómo está calculando cada delta. Para lo que vale, una expresión de generador tiene las instrucciones de código de operación menos (y creo que menos lentas) de todo lo que miré.

  # The way in your question compiles to.... 3 0 LOAD_CONST 1 ( at 0xb7760ec0, file "scratch.py", line 3>) 3 MAKE_FUNCTION 0 6 STORE_DEREF 1 (delta) 4 9 LOAD_GLOBAL 0 (sum) 12 LOAD_CLOSURE 0 (items) 15 LOAD_CLOSURE 1 (delta) 18 BUILD_TUPLE 2 21 LOAD_CONST 2 ( at 0xb77c0a40, file "scratch.py", line 4>) 24 MAKE_CLOSURE 0 27 LOAD_GLOBAL 1 (range) 30 LOAD_CONST 3 (1) 33 LOAD_GLOBAL 2 (len) 36 LOAD_DEREF 0 (items) 39 CALL_FUNCTION 1 42 CALL_FUNCTION 2 45 GET_ITER 46 CALL_FUNCTION 1 49 CALL_FUNCTION 1 52 STORE_FAST 1 (total) 5 55 LOAD_FAST 1 (total) 58 LOAD_GLOBAL 2 (len) 61 LOAD_DEREF 0 (items) 64 CALL_FUNCTION 1 67 LOAD_CONST 3 (1) 70 BINARY_SUBTRACT 71 BINARY_DIVIDE 72 STORE_FAST 2 (average) 75 LOAD_CONST 0 (None) 78 RETURN_VALUE None # #doing it with just one generator expression and itertools... 4 0 LOAD_GLOBAL 0 (sum) 3 LOAD_CONST 1 ( at 0xb777eec0, file "scratch.py", line 4>) 6 MAKE_FUNCTION 0 5 9 LOAD_GLOBAL 1 (izip) 12 LOAD_FAST 0 (items) 15 LOAD_CONST 2 (1) 18 SLICE+1 19 LOAD_FAST 0 (items) 22 CALL_FUNCTION 2 25 GET_ITER 26 CALL_FUNCTION 1 29 CALL_FUNCTION 1 32 STORE_FAST 1 (total) 6 35 LOAD_FAST 1 (total) 38 LOAD_GLOBAL 2 (len) 41 LOAD_FAST 0 (items) 44 CALL_FUNCTION 1 47 LOAD_CONST 2 (1) 50 BINARY_SUBTRACT 51 BINARY_DIVIDE 52 RETURN_VALUE None 

En particular, dejar caer la lambda nos permite evitar hacer un cierre, construir una tupla y cargar dos cierres. Cinco funciones son llamadas de cualquier manera. Por supuesto, este tipo de preocupación por el rendimiento es algo ridículo, pero es bueno saber qué está pasando bajo el capó. Lo más importante es la legibilidad y creo que hacerlo de esta manera también tiene una alta calificación.