Nota Técnica nº 57
março de 2021

Por Tessa Hayman

Tessa Hayman o orienta pela complexidade de lidar com séries temporais por meio de scripts Python e explica como criar sua série temporal personalizada de uma forma que possa ser visualizada na IU.

O que é uma série temporal?

Uma série temporal é um conjunto de dados relacionados a um objeto, e esses dados mudam com o tempo. Por exemplo, a contagem em uma seção seria armazenada em uma série temporal com valores para cada intervalo de detecção, digamos 15 minutos. No Aimsun Next, você pode visualizar uma série temporal na guia de série temporal de um objeto. Por exemplo, para uma replicação:

 

Uma série temporal é um objeto da classe GKTimeSerie e é armazenada em um atributo, um GKColumn , do objeto a que se refere. Você pode ler o desvio padrão, Max, Min, Médio e RMS do objeto GKTimeSerie .

Cada GKTimeSerie possui um objeto de descrição associado ( GKTSDescription ). Contém informações sobre a duração, hora de início e término, intervalo da série temporal a que se aplica.

O objeto GKTimeSerieIndex é usado para acessar um intervalo específico para uma série temporal para a qual você deseja ler ou gravar um valor. Para especificar o 5º intervalo, você usaria GKTimeSerieIndex (4) já que a numeração começa em 0.

Como ler dados de uma série temporal

Para obter uma série temporal de um objeto pode-se chamar o método getDataValueTS () , passando como argumento o GKColumn que o armazena.

Se você deseja obter diretamente o valor de um intervalo de uma série temporal de um objeto, pode-se chamar o método getDataValueInTS () , passando como argumento o GKTimeSerieIndex além do GKColumn que armazena a série temporal.

Observe que getDataValueInTS () retorna uma tupla, na qual o primeiro item é o valor da série temporal e o segundo é o desvio.

Para localizar um nome de coluna, você pode olhar na janela de tipos depois de recuperar um banco de dados ou executar o modelo. Por exemplo, aqui estão as saídas para GKSection.

O nome é composto por:

  • ESTÁTICO ou DINÂMICO
  • Tipo de objeto
  • Nome de saída
  • ID de replicação
  • ID do tipo de veículo (ou 0 para todos os veículos)
  • Número da faixa (ou 0 para todas as faixas)

Por exemplo, para ler a densidade em uma faixa de uma seção durante um intervalo de uma replicação, a seguinte função pode ser usada. Observe que o primeiro intervalo é rotulado como 0, o segundo é rotulado como 1 e assim por diante.

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

Como gravar dados em uma série temporal

Para criar uma nova série temporal, temos que:

  • Criar a coluna que armazena a série temporal associada a cada objeto de um determinado tipo
  • Criar uma descrição da série temporal ( GKTSDescription ), que define, pelo menos, a hora de início, hora de término, intervalo da série temporal e, opcionalmente, também o valor nulo e como calcular um valor agregado para todo o período
  • Enquanto percorre os objetos, percorra os intervalos e chame o método setDataValueInTS () para preencher o valor desse intervalo

Por exemplo, o script a seguir cria uma série temporal para nós, na qual armazena o tempo de ciclo do plano de controle ativo naquela hora do dia. Percorre o período coberto pelo plano mestre de controle em intervalos de 15 minutos. Você deve atribuí-lo ao menu de contexto dos Planos de controle mestre e executá-lo clicando com o botão direito em um plano de controle mestre.

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])

Observe que a coluna é criada usando GK.BuildContents , de forma que o originador, neste caso o plano de controle mestre selecionado, seja definido corretamente.

Se a coluna e a descrição da série temporal forem definidas corretamente, a série temporal aparecerá no diálogo de nós sinalizados.

E se você executar o script para vários planos de controle mestre, poderá usar o gráfico de série temporal para detectar rapidamente a diferença, por exemplo, entre o ciclo durante a semana e o ciclo durante o fim de semana.