Filtrado de datos

El filtrado de datos es el pan de cada día de la analítica de comercio. Envision proporciona un amplio respaldo para lograr esto. El filtrado está respaldado por las condiciones where (dónde) y when (cuándo), que pueden utilizarse alternativamente para filtrar grandes bloques de script a la vez, o bien para filtrar instrucciones individuales. Esta sección proporciona una introducción práctica a estos conceptos.


Un script ilustrativo

Una vez más, comencemos con el conjunto de datos de ejemplo, al que debería poder acceder desde la ruta /sample en su cuenta de Lokad. El script a continuación es moderadamente complejo e ilustra algunos de los patrones de filtrado de datos disponibles en Envision. Si aún no lo ha hecho, le recomendamos que, antes de de comenzar a trabajar con la información incluida en esta sección, lea Haciendo cálculos con Envision.
read "/sample/Lokad_Items.tsv"
read "/sample/Lokad_Orders.tsv" as O

show label "Filtering data" a1f1 tomato

oend := max(O.Date)
when date > oend - 365
LastYearQty = sum(O.Quantity)
where StockOnHand + StockOnOrder > LastYearQty
show table "Overstocked items, +1 year of stock" a2f3 tomato with \
Id 
Name 
StockOnHand + StockOnOrder as "Stock" 
LastYearQty

where O.NetAmount > 1000
show table "Large transactions over $1000" a4f5 tomato with \
Id 
Name 
O.Date 
O.Quantity 
O.NetAmount 
O.ClientCode

lastDay := monday(oend)
firstDay := lastDay - 52 * 7
when date >= firstDay & date < lastDay
Week.sold := sum(O.NetAmount)
show linechart "Sold by week" a6f7 tomato unit:"$" with
Week.sold
Le sugerimos que comience por copiar y pegar este script en su cuenta de Lokad y que lo ejecute una vez para observar el panel de información resultante. Si todo funciona correctamente, debería ver el siguiente panel de información.

Image

Bloques de filtrado

Cuando se comienza a trabajar con, por ejemplo, todos los datos históricos que no tengan más de un año de antigüedad, lo más probable es que se tengan que hacer muchos cálculos. En una situación de este tipo, debería aplicarse el filtro solo año pasado a todos esos cálculos. Por este motivo, con Envision, el filtrado de datos sucede en su mayor parte a través de bloques de código. El fragmento de script a continuación ilustra dos bloques de filtro anidados. Un primer bloque que comienza en la línea 1 con una condición when, seguido por un segundo bloque que comienza en la línea 3 con una condición where.
when date > oend - 365
LastYearQty = sum(O.Quantity)
where StockOnHand + StockOnOrder > LastYearQty
show table "Overstocked items, +1 year of stock" a2f3 tomato with
Id 
Name 
StockOnHand + StockOnOrder as "Stock" 
LastYearQty
Envision es un lenguaje que distingue los espacios en blanco que se encuentran al principio de cada línea. Nos referimos a una secuencia de líneas que comienza con la misma cantidad de espacios en blanco que un bloque de código; y los bloques de código pueden ser anidados; es decir, un bloque puede contener otro bloque.

Si es la primera vez que encuentra un patrón de programación de este tipo, puede que resulte un poco desconcertante. No obstante, en la práctica, el editor de script de Envision le proporciona bastante respaldo integrado: cuando presiona Enter al final de una línea de script que contiene una condición de filtrado, se incluye automáticamente una sangría en la línea siguiente con dos espacios en blanco adicionales.

Nota de desarrollador: Envision adopta un patrón de inclusión de espacios en blanco bastante similar a la utilizada en Python. Este método se adapta bien a Envision, que también pretende proporcionar una sintaxis concisa para gestionar datos. Debido a que Envision no tiene bucles ni ramas, el grado de sangría raramente excede 3 niveles, lo que hace que estos espacios en blanco sean muy manejables en la práctica.

Un bloque de filtrado comienza con una condición —más detalles sobre eso a continuación—, que puede ser verdadera o falsa para cada línea de cada tabla. Dentro del bloque de filtrado, solo las líneas en las que la condición es verdadera siguen estando presentes, mientras que todas las demás líneas se filtran. Como el mismo nombre lo sugiere, la condición when (cuándo) implica un filtrado temporal y se aplica a todas las tablas indexadas con una columna Date. La condición where (dónde) generalmente se utiliza para filtrar los artículos y, cuando se utiliza, el filtro se aplica a todas las tablas indexadas con una columna Id.

Una vez que un bloque comienza dentro de un script de Envision, es como si se mantuvieran todas las líneas que satisfacen el filtro. De otro modo, todo lo demás queda igual, y toda parte del script que pudiera escribirse fuera del bloque puede moverse dentro del bloque. En particular, es posible definir otro bloque de filtrado dentro de un bloque. Este patrón se denomina anidado. En el ejemplo anterior, el bloque where que comienza en la línea 3 está anidado dentro del bloque when que comienza en la línea 1.

Composición de una condición

Una condición es una expresión que puede evaluarse como verdadera o falsa. La noción de filtrado se basa en la composición de condiciones que se evalúan con los datos de entrada y, posiblemente, con los resultados obtenidos de cálculos intermedios. Envision ofrece la posibilidad de componer condiciones ricas, como lo ilustra el siguiente script:
lastDay := monday(oend)
firstDay := lastDay - 52 * 7
when date >= firstDay & date < lastDay
Week.sold := sum(O.NetAmount)
show linechart "Sold by week" a6f7 tomato unit:"$" with
Week.sold
En este fragmento de código, el operador and indica que tanto las expresiones a la derecha como a la izquierda del operador tienen que ser verdaderas. Los operadores lógicos respaldados por Envision son: and, or y not. Además, se pueden comparar números con los operadores numéricos == (igualdad), != (desigualdad), <= (menor o igual que), >= (mayor que), < (menor que), > (mayor que). El siguiente fragmento de código ilustra el modo en que estos operadores se combinan y evalúan.
a := 1 > 10 // falso
b := not a // verdadero
c := a | b // verdadero
d := a & b // falso
e := 10 >= 3 | 5 > 7 // verdadero
show table "Conditions" with a, b, c, d, e
Cuando un bloque de filtrado se anida dentro de otro bloque, es como si se utilizara un operador and con las dos condiciones ubicadas a la izquierda y a la derecha del operador and.

En el ejemplo anterior, tal vez haya notado la presencia de la sintaxis monday(oend). Esta es una llamada de función a la función denominada monday. Para cualquier fecha, esta función devuelve el último lunes que haya habido hasta la fecha pasada como argumento (incluida). Así, si se pasa un lunes como argumento, la función devuelve la misma fecha.

Esta función monday() se utiliza para definir una ventana de tiempo compuesta por dos semanas completas (en lugar de semanas parciales). De hecho, al realizar una agregación semanal, solo deberían considerarse las semanas enteras; de lo contrario, el primer y el último punto de datos podrían mostrar valores extrañamente bajos, lo que simplemente reflejaría que se ha utilizado solo una semana parcial.

Filtrado de tablas por fecha

Envision ofrece amplio respaldo para filtrar tablas con respecto a condiciones temporales. El script que acabamos de ver filtra todas las líneas que tengan más de 1 año de antigüedad. Observemos con más detenimiento la parte relevante del script.
oend := max(O.Date)
when date > oend - 365
  LastYearQty = sum(O.Quantity)
Envision trata las fechas internamente como un número entero de días desde el 1 de enero de 2001. Esto ofrece varias ventajas, incluida la posibilidad de realizar operaciones aritméticas sobre las fechas. Por ejemplo, restar 7 a una fecha da la semana anterior, mientras que restar 365 da una fecha (aproximada) del año anterior.

La palabra clave date también viene con un comportamiento especial. Implícitamente, la variable date se refiere a todas las tablas que contienen una columna Date. Debido a que solo hay una tabla implicada aquí, la tabla O, es como si hubiéramos escrito O.Date > end - 365. Sin embargo, la sintaxis date no solo es más concisa, sino que también se aplica a todas las tablas relevantes al mismo tiempo. Si en lugar de buscar las cantidades vendidas estuviéramos interesados en las cantidades compradas, podría escribirse así:
// carga de PurchaseOrders (pedidos de compra)
read "/sample/Lokad_PurchaseOrders.tsv" as PO 

// fragmentado

when date > end - 365
  LastYearQty = sum(PO.Quantity)

Filtrado de artículos

Además del filtrado de líneas por fecha, Envision también ofrece la posibilidad del filtrado de artículos, que es otra necesidad frecuente cuando se quiere excluir ciertos productos, ubicaciones, categorías, etc. El script que vimos al principio de esta sección ilustra el modo en que el alcance puede restringirse a artículos que podrían más bien calificarse como inventario muerto. Las líneas de script relevantes se especifican a continuación.
where StockOnHand + StockOnOrder > LastYearQty
show table "Overstocked items, +1 year of stock" a2f3 tomato with
Id 
Name 
StockOnHand + StockOnOrder as "Stock" 
LastYearQty
La palabra clave where va seguida de una condición. La condición se aplica a tres vectores que implícitamente pertenecen a la tabla Items. De hecho, esta es la única tabla a la que se puede hacer referencia sin tener el nombre de la tabla como prefijo en el nombre de la variable. Por ejemplo, es necesario utilizar O.NetAmount y no solo NetAmount al referirse a las líneas en la tabla de pedidos O. Aquí, la condición puede leerse como: incluir solo los artículos en los que la suma de las existencias disponibles más las existencias pedidas sea mayor que la cantidad de unidades vendidas el año pasado.

Una vez que se define una condición para la tabla Items, toda tabla que incluya una columna Id —es decir, un identificador que haga referencia a los artículos en línea con las convenciones de Envision— se filtra de modo similar. Este comportamiento es idéntico al filtrado temporal introducido anteriormente. De hecho, cuando se comienza a filtrar artículos, no tiene sentido mantener las líneas de órdenes de ventas o de compra que ya no están unidas a ningún artículo, ya que estos artículos se han filtrado y han quedado fuera del alcance.

El filtrado de artículos afecta la definición del alcance de los nuevos vectores calculados. Ilustremos esto con un pequeño fragmento de script.
where StockOnHand > 5
GreaterThanFive = "yes"
show table "Hello" with Name 
GreaterThanFive // ¡CORRECTO!

// a partir de esta línea, estamos fuera del bloque de filtrado
show table "Hello" with Name 
GreaterThanFive // ¡INCORRECTO!
La línea 10 es incorrecta, porque el vector GreaterThanFive solo se ha definido para las líneas en las que la condición StockOnHand > 5 era verdadera. Por lo tanto, si bien el vector está correctamente definido dentro del bloque, y, por lo tanto, puede utilizarse como se ilustra en la línea 5, este vector no puede utilizarse fuera del bloque de filtrado, porque algunos de sus valores quedarían indefinidos. Esta situación puede arreglarse asegurándose de que el vector se defina adecuadamente para todos sus valores de artículo, como se ilustra a continuación.
GreaterThanFive = "no"
where StockOnHand > 5
GreaterThanFive = "yes"
show table "Hello" with Name 
GreaterThanFive // ¡CORRECTO!

// a partir de esta línea, estamos fuera del bloque de filtrado
show table "Hello" with Name 
GreaterThanFive // ¡CORRECTO!
Este fragmento de código comienza en la línea 1 con una definición apropiada del vector GreaterThanFive en todos los artículos. Esta definición se revisa en la línea 4 para un subconjunto de artículos. Sin embargo, esta revisión no cambia el hecho de que el vector GreaterThanFive esté explícitamente definido para todos los artículos y, como resultado, lo que muestra la línea 12 ahora es correcto.

Filtrado de tablas arbitrarias

Si bien el filtrado de fechas o artículos es muy útil, a veces es necesario filtrar precisamente una tabla específica. Esta tarea puede realizarse con la palabra clave where. Veamos las líneas que ilustran esta capacidad de Envision.
where O.NetAmount > 1000
show table "Large transactions over $1000" a4f5 tomato with
Id 
Name 
O.Date 
O.Quantity 
O.NetAmount 
O.ClientCode
Aquí estamos filtrando la tabla O para excluir todas las líneas de tabla que sean menores que $1000. Las líneas que no se filtran se muestran a través de show table en las líneas 2 y 3. Este ejemplo ilustra el modo en que es posible filtrar una sola tabla. Este filtro solo afecta la tabla O, y todas las demás tablas quedan intactas.

Si dentro de un bloque de filtrado se calcula un vector asociado a la tabla O, el acceso a este vector se restringe al bloque mismo. Ya hemos observado este comportamiento para los artículos, ahora echemos un vistazo al modo en que se aplica a las tablas arbitrarias.
where O.NetAmount > 1000
O.LargeTxn = "yes"
show table "Large transactions" with 
Name 
O.LargeTxn // ¡CORRECTO!
// última línea del bloque
// sangría reducida, estamos fuera del bloque
show table "Large transactions" with 
Name 
O.LargeTxn // ¡INCORRECTO!
Debido a que el vector O.LargeTxn no está definido para todas las líneas de la tabla O, solo la línea 5 es correcta, mientras que la 10 es incorrecta. Del mismo modo que hicimos en el ejemplo anterior, este script puede arreglarse definiendo adecuadamente un valor LargeTxn para toda la tabla O. Esto puede realizarse con el script a continuación.
O.LargeTxn = "no"
where O.NetAmount > 1000
O.LargeTxn = "yes"
show table "Large transactions" with 
Name 
O.LargeTxn // ¡CORRECTO!
// última línea del bloque
// sangría reducida, estamos fuera del bloque
show table "Large transactions" with 
Name 
O.LargeTxn // ¡CORRECTO!
Como regla general, Envision intenta permitir "fugas" de bloques tanto como sea posible: un vector calculado dentro de un bloque puede utilizarse fuera del bloque, siempre que este uso no viole la regla de que todos los valores vectoriales estén explícitamente definidos cuando aparece un vector en la parte derecha de una asignación.

Azúcares sintácticos para el filtrado

El patrón sintáctico utilizado por Envision filtrado-y-sangría (filter-and-indent) para el filtrado es conciso y legible, pero cuando los filtros son varios, la sangría puede volverse difícil de descifrar. Por esta razón, Envision proporciona algunos azúcares sintácticos, es decir, sintaxis alternativas que requieren menos sangría. Veremos esta sintaxis en esta sección.

Omisión de sangrías cuando se utilizan varios filtros

Envision solo requiere un nivel adicional de sangría por filtro si esos filtros se utilizarán en forma separada. Si solo interesa el ámbito más interno, solo se necesita un nivel de sangría, como se ilustra a continuación:
// cada filtro 'where' lleva 
// su propio nivel de sangría
where O.Quantity > 10
where StockOnHand < 100
show table "Filtered orders" with 
O.Quantity

// pero cuando se utilizan varios filtros, 
// se requiere una sola sangría
where O.Quantity > 10
where StockOnHand < 100 // ¡sin sangría aquí!
show table "Filtered orders" with O.Quantity
El segundo bloque tiene la misma semántica que el primero, pero necesita solo una sangría. De forma más general, la sintaxis es la siguiente:
where A
when B
where C
show table "Filtered by A, B and C" with X
// igual que
where A
when B
where C
show table "Filtered by A, B and C" with X

Combinación de filtros con la palabra clave and

Hemos vistos que, en Envision, el operador booleano AND se representa con el símbolo &. No obstante, Envision también ofrece una palabra clave and diferente que proporciona una semántica levemente diferente:
// los dos filtros 'where' anidados
where O.NetAmount > 1000 
where StockOnHand > 10
show table "Filtered transactions" with 
Name 
O.Quantity

// pueden escribirse como un solo filtro con 'and'
where O.NetAmount > 1000 and StockOnHand > 10
show table "Filtered transactions" with Name 
O.Quantity
El uso de la palabra clave and es estrictamente equivalente al anidado de filtros where. A través de la palabra clave and, es posible introducir varios filtros en secuencia, con un solo nivel de sangría. De forma más general, tenemos:
where A
where B
where C
// fragmentado

// puede reescribirse
where A and B and C
// fragmentado
En la práctica, la palabra clave and ofrece la posibilidad de combinar varios filtros que no están pensados para ser utilizados por separado.

Sin sangría con la palabra clave keep

Un patrón de código frecuente consiste en la introducción de filtros al principio del script para limitar el análisis de los datos a un ámbito específico. Si bien la sintaxis de filtrado de Envision funciona relativamente bien en este escenario, todo el script de Envision acaba escribiéndose con uno o dos niveles de sangría. La palabra clave keep ofrece un modo de eliminar estas sangrías:
// el filtro 'where' introduce un bloque con sangría
where O.Quantity > 10
// comienzo del bloque
show table "Inside the filter" with 
sum(O.Quantity)
// fin del bloque
show table "Outside the filter" with 
sum(O.Quantity)

// pero cuando se utiliza 'keep', 
// el filtro se aplica sin sangría
keep where O.Quantity > 10
show table "Inside the filter" with 
sum(O.Quantity)
La palabra clave keep debería colocarse antes de where o when e indica que el filtro se ejecuta sin sangría. El filtro se mantiene activo hasta el final del alcance.
where A
keep where B
show table "Filtered by A and B" with X
// fin de filtros A y B
show table "Not filtered" with X
Así, si en una línea de script sin sangría se coloca la palabra clavekeep, el filtro se aplica hasta el final del script de Envision.

Filtros insertados con sufijo

Hasta ahora, todos los filtros que hemos observado estaban escritos como bloques de filtrado. Sin embargo, Envision también proporciona una sintaxis alternativa, más compacta, que se conoce como sufijos de condición. Regresemos al cálculo de las cantidades vendidas el año pasado.
when date > oend - 365
  LastYearQty = sum(O.Quantity)
Este script puede reescribirse del siguiente modo:
LastYearQty = sum(O.Quantity) when date > oend - 365
Para los lectores familiarizados con las bases de datos relacionales, esta sintaxis podrá parecer similar al modo en que se escriben las condiciones where en SQL. En Envision, esta sintaxis es principalmente un azúcar sintáctico para evitar introducir bloques de una línea cuando es solo una instrucción la que se debe escribir dentro del bloque. Tanto where como when pueden “sufijarse” en el lado derecho de una asignación

Los agregadores pueden filtrarse insertados con las condiciones when o where. Envision también ofrece la posibilidad de agregar un modificador else en la condición. Por ejemplo, es incorrecto escribir lo siguiente:
oend := max(O.Date)
lastDay := monday(oend)
Week.sold := sum(O.NetAmount) when date < lastDay
show linechart "Sold by week" with 
Week.sold // ERRÓNEO
PorqueWeek.sold no se define en todo el alcance, ya que ha sido filtrado en la línea anterior. Sin embargo, al agregar una opción else, estamos definiendo Week.sold correctamente en todas partes, lo que luego puede visualizarse.
oend := max(O.Date)
lastDay := monday(oend)
Week.sold := sum(O.NetAmount) when date < lastDay else 0
show linechart "Sold by week" with Week.sold // CORRECTO