En este artículo, aprenderá cómo aprovechar al máximo el Motor de Variables Sintéticas utilizando funciones especiales o su combinación para renderizar expresiones más complejas. Si aún no está familiarizado con las Variables Sintéticas, consulte aquí para aprovechar los beneficios de esta funcionalidad.
Requisitos
Una licencia Profesional o superior.
1. Creando una Variable Sintética usando datos de contexto
Contexto de Variable es una de las mejores maneras de almacenar metadatos que no son necesariamente un valor numérico. El uso más común del contexto de variable es para las coordenadas GPS de latitud y longitud. Aunque esos valores son numéricos, resulta bastante útil enviarlos en un solo punto en lugar de 2 variables separadas.
Los datos de contexto solo se pueden usar dentro de su expresión sintética si el contexto es un valor numérico. Acceder a él desde el editor Sintético es sencillo, solo necesita usar el operador de punto '.' sobre la variable, llamar a su contexto y luego al identificador de clave del contexto:
{YOUR_VARIABLE}.context.context-key
Nota: Para encontrar más información sobre el contexto y otras características para almacenar valores, consulte la documentación de la API de Ubidots.
Ejemplo:
Suponga que necesita usar la fórmula Haversine para calcular la distancia más corta entre dos ubicaciones sobre la superficie de la tierra basada en datos de Latitud y Longitud. Esto sería de la siguiente manera:
Δlat = lat2 -lat1Δlng = lng2 -lng1 a = sin²(Δlat/2) + cos lat1 ⋅ cos lat2 ⋅ sin²(Δlng/2) c = 2 ⋅ arctan2( √a, √(1−a) ) d = R ⋅ c
Donde lat
es latitud, lng
es longitud y R
es el radio de la tierra (radio medio = 6,373km
); tenga en cuenta que los ángulos deben estar en radianes para pasarlos a las funciones trigonométricas.
Tenga en cuenta que latitud y longitud son datos numéricos almacenados dentro del contexto de una variable como se muestra a continuación:
La expresión sintética para calcular la distancia se verá similar a esta en el editor de variable sintética:
Donde lat1, lat2, lng1
y lng2
utilizan el valor dentro del contexto para hacer los cálculos adecuados.
NOTA IMPORTANTE: El ejemplo anterior asume que las variables tienen la misma marca de tiempo. Si no, la fill_missing()
expresión necesita ser incluida.
2. Creando una Variable Sintética usando las propiedades del Dispositivo
Las propiedades de los dispositivos son una forma rápida de agregar metadatos a sus dispositivos Ubidots.
Es posible usar las propiedades del dispositivo para calcular una variable sintética siempre que la propiedad tenga un formato de número flotante. Para acceder a ella desde el editor sintético, necesita usar la clave ##{{device:<id>}} (recuerde cambiar <id> por el id del dispositivo al que desea acceder a la propiedad). Luego, necesita usar el operador de punto ‘.’ sobre el dispositivo, llamar a sus propiedades y luego al identificador de clave:
##{{device:<id>}}.properties.<key>
Nota: Para encontrar más información sobre el contexto y otras características para almacenar valores, consulte la documentación de la API de Ubidots.
Ejemplo:
Suponga que ha calibrado su dispositivo y guardado esta información en la propiedad del dispositivo, y desea realizar operaciones con esta información.
Por ejemplo, calcule la diferencia entre la temperatura actual y el valor de calibración.
Los resultados obtenidos con la expresión sintética se muestran a continuación:
3. Creando una Variable Sintética usando la marca de tiempo
De manera similar a cómo se accede a los datos de contexto, sucede con la marca de tiempo. Se puede acceder en Variables Sintéticas usando el operador de punto '.'.
{YOUR_VARIABLE}.timestamp
Ejemplo
Ejemplo:
Imagina que tienes una variable, como en la imagen anterior, que almacena un valor cada vez que una máquina SE ENCIENDE (1) o SE APAGA (0), y deseas calcular cuánto tiempo permanece la máquina en estado ON (1). Este cálculo se puede realizar utilizando la marca de tiempo de la variable como se muestra a continuación:
Donde:
previousTime
: Marcas de tiempo en milisegundos cuando la máquina estaba ENCENDIDA.actualTime
: Marcas de tiempo en milisegundos cuando la máquina estaba APAGADA.dV
: Diferencia entre el valor actual y el anterior para monitorear si hubo un cambio en el estado ON/OFF de la máquina.dT
: Tiempo que la máquina permanece ENCENDIDA.
Por último, se necesita evaluar el signo de dV
para verificar si hay un valor negativo, lo que a su vez indica que la máquina pasó de ON a OFF. Para ello, existe la función where()
, que en función de una condición de entrada, toma una de 2 acciones para el resultado de la condición True
o False
. Para el caso aquí, sirve bien para evaluar el signo y, si es verdadero, guardar la diferencia de tiempo entre los últimos 2 estados, es decir, dT
.
Recuerde que este tiempo siempre está en milisegundos [ms], si es necesario, puede convertirlo a segundos, minutos u horas multiplicando por el factor correcto.
4. Ejemplo de funciones de rango de datos
Las funciones de rango de datos son una opción perfecta para realizar análisis estadísticos para identificar tendencias en un período de tiempo. Con ellas, no solo podrá calcular un valor cada período de tiempo fijo, sino también tener cierta flexibilidad para seleccionar la marca de tiempo a la que se almacenarán los datos, y un desplazamiento inicial también, si es necesario.
Todas las funciones de rango de datos siguen la siguiente sintaxis:
function_name(variable, data_range, position="start", offset=0)
Donde function_name
y data_range
pueden ser cualquiera de las opciones descritas aquí, position
(opcional) puede ser cualquiera de 2 opciones, "start"
o "end"
, que indica si el valor se va a marcar en la parte inicial o final del data_range
, y finalmente, el parámetro offset
(opcional), como sugiere, desplaza el punto de inicio desde donde la función calculará.
NOTA IMPORTANTE: Los rangos de datos "W" y "M" se establecen por defecto en position="end"
mientras que el resto en position="start"
.
Ejemplos:
1.
mean(x, "1H")
La expresión sintética anterior calcula la mean
de la variable x
cada 1 hora ("1H"
), y dado que ninguno de los parámetros opcionales está definido, los valores se marcarán al comienzo de cada hora, es decir, todos los valores recibidos desde 00:00:00 hasta 01:00:00 se agregarán en promedio en un solo valor marcado a las 00:00:00.
2.
mean(x, "1H", position="end")
De manera similar al ejemplo 1., la expresión sintética 2. calculará el valor medio de la variable x
cada hora, pero en este caso, el motor, debido al parámetro position="end"
, marcará cada valor al final del rango de datos, por lo que para los valores recibidos desde 00:00:00 hasta 01:00:00, el valor agregado en promedio se marca a las 00:59:59.
3.
mean(x, "8H", offset=6)
Supongamos que está trabajando en una fábrica con el siguiente horario de turnos: 6:00 AM – 2:00 PM, 2:00 PM – 10:00 PM y 10:00 PM – 6:00 AM. Le gustaría promediar la variable x
cada 8 horas para coincidir con la duración de cada turno. Sin embargo, el motor sintético, por defecto, comienza a calcular a las 00:00:00 y no al comienzo de cada turno. Aquí es donde el último parámetro, offset
, resulta útil. Hacer offset=6
indica al motor que comience a calcular a las 06:00:00 en períodos de 8 horas, generando 3 valores por día marcados a las 06:00:00, 14:00:00 y 22:00:00.
4.
mean(x, "8H", position="end", offset=6)
Basado en el mismo ejemplo que en 3., ahora hemos utilizado todos los parámetros. En este caso, el sintético calculará comenzando a las 06:00:00 en una base de 8 horas, pero debido al parámetro position="end"
, los valores se marcarán a las 13:59:59, 21:59:59 y 05:59:59 para los turnos de 6:00 AM, 2:00 PM y 10:00 PM, respectivamente.
NOTA IMPORTANTE: El parámetro offset
solo funciona con valores que subdividen uniformemente 1 día.
5. Ejemplos de Funciones Sintéticas Especiales
Existen muchas instancias donde una Expresión Sintética involucra 2 o más series temporales de variables, y para esos casos, debido a la forma en que está construido el motor de Variables Sintéticas, cada uno de los valores a calcular debe tener la misma marca de tiempo, de lo contrario, el motor no calculará o producirá resultados inesperados. Sin embargo, no siempre, si no en la mayoría de los casos, las series temporales de las variables no cumplirán con esta regla. Para tales instancias, existe la función fill_missing()
, que básicamente llena los vacíos donde hay un valor faltante en cualquiera de las series temporales de variables utilizadas dentro de la expresión. Esta función sigue la siguiente sintaxis por defecto:
fill_missing(expression, first_fill="ffill", last_fill="None", fill_value="None")
Donde el único argumento obligatorio es expression
. Los otros, first_fill
, last_fill
y fill_value
son opcionales. Además, los argumentos opcionales first_fill
y last_fill
pueden tomar cualquiera de 3 valores: ffill
(relleno hacia adelante), bfill
(relleno hacia atrás) o None
, y fill_value
puede ser None
o cualquier otro valor numérico
.
Para entender mejor esta función, supongamos que tiene un conjunto de variables que desea sumar. Como puede ver en la tabla a continuación, las variables A, B, C y D no tienen datos en todas las marcas de tiempo, por lo que se necesita la función fill_missing()
para llenar los vacíos y, en última instancia, realizar el cálculo. Los ejemplos del 1 al 4 se basan en dicha tabla:
Ejemplos
1.
fill_missing(A + B + C + D)
Por defecto, la expresión fill_missing()
establece el parámetro first_fill
como ffill
, lo que significa que la función llenará los vacíos hacia adelante, comenzando en un punto donde tiene suficientes datos para llenar los vacíos para todas las series involucradas. El resultado sería el siguiente:
2.
fill_missing(A + B + C + D, first_fill="bfill")
Por otro lado, el first_fill
también se puede establecer como bfill.
Los vacíos se llenarían como se muestra a continuación:
3.
fill_missing(A + B + C + D, first_fill="ffill", last_fill="bfill")
Supongamos que necesita llenar todos los vacíos de las variables, en ese caso necesitaría establecer tanto first_fill
como last_fill
en la expresión, generando lo siguiente:
4.
fill_missing(A + B + C + D, fill_value=0)
Además, puede llenar los vacíos con un valor. Para este caso, la función establece un "0" en todos los vacíos que tienen las variables.