Scripting con series temporales

Nota técnica nº 57
Marzo de 2021

Por Tessa Hayman

Tessa Hayman te guía a través de la complejidad del manejo de las series temporales mediante scripts de Python y te explica cómo crear tus series temporales personalizadas de forma que se puedan visualizar en la interfaz de usuario.

¿Qué es una serie temporal?

Una serie temporal es un conjunto de datos relacionados con un objeto, y estos datos cambian con el tiempo. Por ejemplo, el recuento de una sección se almacenaría en una serie temporal con valores para cada intervalo de detección, digamos 15 minutos.

En Aimsun Next, puedes ver una serie temporal en la pestaña de series temporales de un objeto. Por ejemplo, para una réplica:

Una serie temporal es un objeto de la claseGKTimeSerie y se almacena en un atributo, unaGKColumndel objeto al que se refiere. Puedes leer la desviación estándar, el Máx, el Mín, la Media y el RMS del GKTimeSerie objeto.

Cada GKTimeSerietiene un objeto de descripción asociado (GKTSDescription). Contiene información sobre la duración, la hora de inicio y de finalización y el intervalo de la serie temporal a la que se aplica.

ElGKTimeSerieIndex se utiliza para acceder a un intervalo concreto de una serie temporal de la que quieres leer o escribir un valor. Para especificar el 5º intervalo, utilizarías GKTimeSerieIndex(4) ya que la numeración comienza en 0.

Cómo leer los datos de una serie temporal

Para obtener una serie temporal de un objeto puedes llamar al método getDataValueTS(), pasando como argumento la GKColumn que la almacena.

Si quieres obtener directamente el valor de un intervalo de una serie temporal de un objeto, puedes llamar en su lugar al método getDataValueInTS() pasando como argumento el GKTimeSerieIndex además de laGKColumn que almacena la serie temporal.

Ten en cuenta que getDataValueInTS() devuelve una tupla, en la que el primer elemento es el valor de la serie temporal y el segundo es la desviación.

Para encontrar el nombre de una columna, puedes mirar en la ventana de tipos después de haber recuperado una base de datos o haber ejecutado el modelo. Por ejemplo, aquí están los resultados de GKSection.

El nombre se compone de:

  • STATIC or DYNAMIC
  • Object type
  • Output name
  • Replication ID
  • Vehicle type ID (o 0 para todos los vehículos)
  • Lane number (o 0 para todos los carriles)

Por ejemplo, para leer la densidad en un carril de una sección durante un intervalo de una réplica, se podría utilizar la siguiente función. Observa que el primer intervalo está etiquetado como 0, el segundo como 1 y así sucesivamente.

				
					def readdensity( section, interval, replicationid, lanenumber, vehicleid=0 ):
column = model.getColumn( "DYNAMIC::GKSection_density_"+str(replicationid)+"_"+str(vehicleid)+"_"+str(lanenumber) )
	(value, deviation) = section.getDataValueInTS( column, GKTimeSerieIndex(interval) )
	return value

				
			

Cómo escribir datos en una serie temporal

Para crear nuevas series temporales, tenemos que

  • Crea la columna que almacena la serie temporal asociada a cada objeto de un tipo determinado
  • Crear una descripción de la serie temporal (GKTSDescription), que define, al menos, la hora de inicio, la hora de finalización, el intervalo de la serie temporal y, opcionalmente, también el valor nulo y cómo calcular un valor agregado para todo el periodo
  • Mientras recorres los objetos, recorre los intervalos y llama al método setDataValueInTS() para rellenar el valor de ese intervalo

Por ejemplo, el siguiente script crea una serie temporal para los nodos en la que almacena el tiempo de ciclo del plan de control activo en ese momento del día. Recorre el periodo cubierto por el plan maestro de control en intervalos de 15 minutos. Debes asignarlo al menú contextual de los Planes de Control Maestro y ejecutarlo haciendo clic con el botón derecho del ratón en un plan de control maestro.

				
					fromDate = QDateTime(QDate().currentDate(), target.initialTime())
toDate = fromDate.addSecs(target.duration().toSeconds()[0])
interval = GKTimeDuration(0, 15, 0)
fromDate = fromDate.addSecs(interval.toSeconds()[0])

type = model.getType("GKNode")
column = type.getColumn( "GKNode::cycle_%s" % target.getId(), GKType.eSearchOnlyThisType )
if column == None:
	column = type.addColumn( "GKNode::cycle_%s" % target.getId(), GK.BuildContents("Cycle Time", target), GKColumn._GKTimeSerie, GKColumn.eExternal )
column.setConversion(GK.eConversionUndefined, "", "")

tsDescription = GKTSDescription.getCreateDescription( column, "cycleTime" )
tsDescription.setAggregationType(GK.eAggregationUndefined)
tsDescription.setNullValue(-1)
tsDescription.setTime(fromDate, toDate, interval)

now = fromDate
while now <= toDate:
	fromTime = now.time().second()+now.time().minute()*60+now.time().hour()*3600
	for node in model.getCatalog().getObjectsByType(type).itervalues():
		controlJunction = target.getControlJunction(node.getId(), fromTime, fromTime+1)[0]
		if controlJunction != None:
			cycle = controlJunction.getCycle()
			node.setDataValueInTS(column, GKTimeSerieIndex(tsDescription.getInterval(now)), cycle, 0.0, tsDescription)
	now = now.addSecs(interval.toSeconds()[0])

				
			

Ten en cuenta que la columna se crea con GK.BuildContents, para que el originador, en este caso el plan maestro de control seleccionado, esté correctamente configurado.

Si la columna y la descripción de la serie temporal están correctamente definidas, la serie temporal aparecerá en el diálogo de nodos señalizados.

Y si ejecutas el script para varios planes maestros de control, puedes utilizar el gráfico de series temporales para detectar rápidamente la diferencia, por ejemplo, entre el ciclo durante el día de la semana y el ciclo durante el fin de semana.

  • ¿Tienes alguna pregunta? Ponte en contacto.
  • ¿Tienes alguna pregunta? Ponte en contacto.

SHARE

Share on linkedin
Share on twitter
Share on email