Modificaciones de marca de tiempo

En los ejemplos de este tema se muestra cómo utilizar operadores para modificar la marca de tiempo de un evento. Si cambia la marca de tiempo del evento, puede cambiar el efecto de los eventos en las operaciones subsiguientes, por ejemplo uniones, agregaciones sobre ventanas, etc. Los siguientes métodos de extensión de LINQ son la base de esta funcionalidad.

Desplazar la hora de un evento

El operador ShiftEventTime() cambia la hora de inicio de cada evento del flujo según la expresión especificada.

En el siguiente ejemplo se desplaza la hora de cada evento de flujo en 15 minutos en el futuro.

// shift events by 15 minutes into the future.
var shifted = inputStream.ShiftEventTime(e => TimeSpan.FromMinutes(15)); 

En el siguiente ejemplo se atrasa 1 hora la hora de cada evento del flujo.

// shift events by 1 hour into the past.
var shifted = inputStream.ShiftEventTime(e => TimeSpan.FromHours(-1));

La expresión para especificar el cambio de hora puede hacer referencia a la hora de inicio del evento actual, pero no a su hora de finalización ni a su carga. El cambio no afecta a la duración ni a la carga del evento.

El valor DateTime.MinValue se considera que codifica un valor de hora igual a menos infinito. Si la hora de inicio del evento tiene este valor y se hace referencia a ella en la expresión especificada (en oposición a una constante), la expresión no se evalúa y la hora de inicio seguirá teniendo el valor DateTime.MinValue. En caso contrario, la expresión se evaluará en tiempo de ejecución, lo que podría seguir produciendo una excepción de desbordamiento.

Observe que el cambio horario especificado también se aplica a los eventos CTI que se pasan a este operador, porque ShiftEventTime afecta a las horas de inicio de todos los eventos del flujo.

Cambiar la duración de un evento

El operador AlterEventDuration() cambia la duración del evento. La duración del evento especifica el intervalo de tiempo en que es válido. La duración se define como una función en el evento, de forma que se puede calcular a partir de la hora de inicio, la hora de finalización o la carga del evento.

En el siguiente ejemplo se establece la duración del evento en 1 hora.

// set event duration to 1 hour.
var onehour = inputStream.AlterEventDuration(e => TimeSpan.FromHours(1));

En el siguiente ejemplo se establece la duración del evento en dos veces su duración actual.

// double event duration. 
var doubled = inputStream.AlterEventDuration(e => (e.EndTime - e.StartTime) * 2);

El valor DateTime.MaxValue se considera que codifica un valor de hora igual a más infinito. Si la hora de finalización del evento tiene este valor y se hace referencia a ella en la expresión especificada, la expresión no se evalúa y la hora de finalización seguirá teniendo el valor DateTime.MaxValue.

Cambiar el desplazamiento y la duración de un evento

El operador AlterEventLifetime() combina las funciones AlterEventDuration y ShiftEventTime para lograr la máxima expresividad.

El primer parámetro del método AlterEventLifeTime() especifica la nueva marca de tiempo de inicio y puede hacer referencia a la hora de inicio del evento actual. Este parámetro se debe especificar como una hora UTC. El segundo parámetro especifica la nueva duración y puede hacer referencia a los campos de hora de inicio, hora de finalización y carga del evento actual.

En el siguiente ejemplo se atrasa la hora del evento en 1 minuto, pero no se cambia la hora de finalización del evento (agregando un minuto adicional a la duración original) al especifica la nueva duración como segundo parámetro.

// shift event 1 minute into the past, but leave the end time (event duration) unchanged.
var newStream = inputStream.AlterEventLifetime(e => e.StartTime - TimeSpan.FromMinutes(1),
                                               e => e.EndTime - e.StartTime + TimeSpan.FromMinutes(1));]

Observe que el cambio de la hora de inicio especificado también se aplica a los eventos CTI que se pasan a este operador.

Vea también las observaciones relacionadas con DateTime.MinValue y DateTime.MaxValue anteriormente en este tema.

Convertir un flujo en un flujo de eventos de punto

El operador ToPointEventStream es una función adecuada para convertir los eventos perimetrales y de intervalo en eventos de punto (mediante la modificación de su duración a un único tic posterior a la hora de inicio del evento), tal como se muestra en el siguiente ejemplo.

var pointStream = inputStream.ToPointEventStream();

Solo se retiene la hora de inicio de los eventos cuando los eventos de intervalo se convierten para señalar a eventos.

Ajustar la duración de un evento

El operador ClipEventDuration toma dos flujos como parámetros y cambia la duración de cada evento del primer flujo en función de la hora de inicio del siguiente evento correspondiente del segundo flujo.

Hasta ahora, hemos visto operadores que permiten cambiar la duración de un evento en un intervalo de tiempo fijo. El operador ClipEventDuration proporciona un método muy flexible de ajustar la duración de eventos con respecto a otros eventos. En general, este operador se especifica en un flujo y toma otro flujo como parámetro, junto con una condición de coincidencia. El operador ajustará la duración de cada evento del primer flujo a la hora de inicio del evento "siguiente" (en cuanto al momento de aplicación) del otro flujo que cumpla la condición de coincidencia.

Como ejemplo, imagine dos flujos, stream1 y stream2, que tienen eventos con un "Id." de campo de carga útil. La siguiente instrucción ajusta todos los eventos de stream1 al siguiente evento de stream2 que tenga el mismo valor para "Id.":

var clipped = stream1.ClipEventDuration(stream2, (e1, e2) => e1.Id == e2.Id);

La condición de coincidencia se proporciona como una expresión sobre las dos cargas útiles de entrada. La semántica de esta instrucción se muestra en el siguiente diagrama:

Semántica de ClipEventDuration

El diagrama muestra cómo el primer evento en stream1 con Id. = UN se recorta al evento siguiente con Id. = UN en stream2. El otro evento de stream1, con Id. = B, no se ajusta, porque el siguiente evento coincidente de stream2 se produce después del final del evento de stream1.

Este comportamiento de ajusta ofrece muchas posibilidades de aplicación. Un requisito común que puede cumplir es la conversión de un flujo de puntos en un flujo de intervalos continuos, también denominado una "señal".

Conversión de punto en señal

En este caso, primero debe extender todos los eventos de punto, de forma que realmente lleguen al evento siguiente. Dicho de otro modo, debe aplicar un tiempo de espera que determine cuánto tiempo se supone que dura un evento hasta que se produzca el evento siguiente. Este tiempo de espera puede ser un intervalo de tiempo finito o infinito. Imaginemos un tiempo de espera de 60 segundos:

var extended = input.AlterEventDuration(e => TimeSpan.FromSeconds(60));

Con esta suposición, podemos utilizar el operador ClipEventDuration, proporcionando el propio flujo como su parámetro. Así, cada evento se ajustará al siguiente del mismo flujo, creando una serie continua de eventos de intervalo. Puesto que solo las horas de inicio del segundo flujo tienen importancia para la operación de ajuste, también podemos utilizar el flujo de puntos original:

var signal = extended.ClipEventDuration(input, (e1, e2) => true);

Aquí, la condición de coincidencia siempre es verdadera, suponiendo que estamos examinando un único flujo lógico, es decir, todos los eventos del flujo están asociados a un origen de datos único.

Los siguientes diagramas ilustran el efecto de la conversión de punto a señal mediante el operador ClipEventDuration:

Conversión de punto en señal con ClipEventDuration

Las dos instrucciones LINQ se pueden unir para formar solo una:

var signal = input.AlterEventDuration(e => TimeSpan.FromSeconds(60)).ClipEventDuration(input, (e1, e2) => true);

Si el flujo contiene varios flujos lógicos (por ejemplo, mediciones de varios dispositivos o los valores de varias cotizaciones), la clave respectiva (identificador de dispositivo o símbolo de valor bursátil) tendría que coincidir en la expresión booleana:

var signal = input.AlterEventDuration(e => TimeSpan.FromSeconds(60)).ClipEventDuration(input, (e1, e2) => e1.Symbol == e2.Symbol);

Crear sesiones

Otro caso de uso de ClipEventDuration es la creación de eventos de sesión para agregar anotaciones a eventos que se produjeron durante ese tipo de sesión. Imaginemos el siguiente esquema de evento, que describe los eventos de una interacción del usuario:

public class EventType
{
    public int UserId;
    public string Type;
    public DateTime Time;
    public byte[] data;
};

En este ejemplo, el campo de carga útil Type puede ser "inicio", "finalización" u "otro", para describir el inicio de una sesión de usuario, el final de una sesión, o eventos de usuario durante una sesión, respectivamente. El campo Time contiene la marca de tiempo de la interacción y data contiene información adicional. La tarea consiste en agregar anotaciones a cada evento con la hora de inicio de la sesión en la que se produjo el evento. Además, suponemos que cada sesión expira después de 10 minutos.

El siguiente diagrama muestra una serie de eventos de ejemplo en este escenario:

Crear eventos de sesión con ClipEventDuration

Primero, se aplica la expansión de la duración en función del tiempo de espera a todos los eventos de tipo "inicio":

var sessionStarts = from e in input
                    where e.Type == “start”
                    select e;
var sessionStartsExt = sessionStarts.AlterEventDuration(e => TimeSpan.FromMinutes(10));

Después, estos eventos de sesión se deben ajustar hasta su final respectivo, para cada identificador de usuario:

var sessionEnds = from e in input
                  where e.Type == “end”
                  select e;
var sessions = sessionStartsExt.ClipEventDuration(sessionEnds, (e1, e2) => e1.UserId == e2.UserId);

El diagrama muestra estas instrucciones:

Ajustar eventos de sesión con ClipEventDuration

Ahora, los eventos de la sesión se pueden unir a los eventos restantes:

var sessionActivity = from e in input
                      where e.Type == “other”
                      select e;
var annotated = from s1 in sessions
                join s2 in sessionActivity
                on s1.UserId equals s2.UserId
                select new {
                    s2.UserId,
                    s2.Type,
                    s2.Time,
                    s2.Data,
                    SessionStart = s1.Time
                }

En la unión, podemos hacer referencia a los eventos sessionActivity y a los campos del evento de sesión, de forma que podamos ensamblar el evento sessionActivity anotado, extrayendo la hora de inicio de la sesión de cada evento sessionActivity:

Unir eventos de sesión a otros eventos

Puesto que la condición de unión es la igualdad de UserId, el evento con UserId = Y en sessionActivity no se tiene en cuenta para esta sesión concreta, donde UserId = X.

Las instrucciones LINQ se pueden comprimir en un conjunto más conciso:

var sessions = input
                 .Where(e => e.Type == “start”)
                 .AlterEventDuration(e => TimeSpan.FromMinutes(10))
                 .ClipEventDuration(input.Where(e => e.Type == “end”), (e1, e2) => e1.UserId == e2.UserId);
var annotated = from s1 in sessions
                join s2 in input.Where(e => e.Type == “other”)
                on s1.UserId equals s2.UserId
                select new {
                    s2.UserId,
                    s2.Type,
                    s2.Time,
                    s2.Data,
                    SessionStart = s1.Time
                }

Vea también

Conceptos

Conceptos de servidor de StreamInsight