El generador formula
Úselo cuando una columna no se sortea sino que se calcula, a partir de las otras columnas de la misma fila. Un peso que sigue a una estatura. El IMC de ambos. Un precio por una cantidad. Un resto que quiere imprimir en vez de comprobar.
<gen type="formula" expr="0.75 * Height - 58 + 6 * Z" decimals="1"/>
expr= es el mismo lenguaje pequeño que if=: los mismos
operadores, las mismas funciones, los mismos nombres para las mismas columnas. La única
diferencia es adónde va la respuesta: if= la consume como un sí/no, y una fórmula la
conserva como el valor de su columna.
| Atributo | Qué define |
|---|---|
expr | Obligatorio. La aritmética que ES esta columna |
decimals | Dígitos tras el punto, 0 a 10. Sin él el valor se imprime completo |
Una columna que sigue a otra
Los datos reales no son un conjunto de columnas independientes: el peso sigue a la estatura, los ingresos a la educación, la superficie al precio. Una fórmula es cómo se dice eso:
<sequence name="Height"><gen type="number" distribution="normal" mean="170" sd="10" decimals="1"/></sequence>
<sequence name="Z"> <gen type="number" distribution="normal" mean="0" sd="1" decimals="4"/></sequence>
<sequence name="Weight"><gen type="formula" expr="0.75 * Height - 58 + 6 * Z" decimals="1"/></sequence>
<sequence name="BMI"> <gen type="formula" expr="Weight / pow(Height / 100, 2)" decimals="1"/></sequence>
<sequence name="Label"> <gen type="formula" expr="BMI > 25 ? over : normal"/></sequence>
152.3,62.1,26.8,over 187.3,73.4,20.9,normal 172.9,69.6,23.3,normal 164.9,59.8,22.0,normal 159.3,60.9,24.0,normal 157.4,63.9,25.8,over
Height se sortea, Z es el ruido que impide que la relación sea una línea recta, y las
otras tres se calculan. Z no tiene que imprimirse: deje una columna fuera de
<block> y se queda en el cálculo sin llegar al archivo. Un config de ciencia de datos
suele tener varias así.
La última línea merece atención aparte: un ternario hace que una fórmula produzca una etiqueta, no solo un número, que es como un conjunto de entrenamiento consigue su columna objetivo.
La división, y el resto que sí puede imprimir
Aquí la división es real, y siempre lo fue — es el único operador que no puede quedarse entero, porque los enteros no son cerrados bajo él:
<sequence name="N"> <gen type="number" value="1..20"/></sequence>
<sequence name="Half"><gen type="formula" expr="N / 2"/></sequence>
<sequence name="Rem"> <gen type="formula" expr="N % 3"/></sequence>
<sequence name="Row"> <gen type="formula" expr="_count"/></sequence>
n=15 half=7.5 rem=0 row=1 n=9 half=4.5 rem=0 row=2 n=9 half=4.5 rem=0 row=3 n=5 half=2.5 rem=2 row=4
_count y cualquier otro valor incorporado se puede leer, así
que una tendencia a mano es expr="100 + 0.05 * _count".
true / false, no 1 / 0expr="BMI > 25" da la palabra true, igual que _last, _first y cualquier columna
anomaly_flag: una sola escritura para una bandera en todo el motor.
Un conjunto de entrenamiento suele querer la otra forma, y el ternario está ahí mismo:
<gen type="formula" expr="BMI > 25 ? 1 : 0"/>
Los enteros siguen siendo enteros
Un operando que ES un número entero se lleva como tal y solo se vuelve doble cuando algo se
lo pide. Así que una fórmula es exacta donde sus entradas lo son —1000000 * 1000000 da la
respuesta correcta, no una redondeada— y solo se vuelve aproximada cuando el config pidió
algo inexacto. Las reglas son las mismas que en todo el lenguaje de expresiones y están
escritas en Expresiones.
0.1 + 0.2 == 0.3 es falso, aquí y en cualquier otro lenguaje, porque 0.1 no tiene
forma binaria exacta. Es aritmética IEEE honesta, no una rareza de este motor —pero
significa que una rama escrita como if="A + B == 0.3" puede no dispararse nunca. Compare
con < y >, o redondee ambos lados primero.
Una fuente vacía da una respuesta vacía
Una celda que parent= o
missing= dejó vacía no es un cero, y una fórmula
que la lee produce nada en vez de inventar un número:
<sequence name="H" parent="G.M"><gen type="number" value="170..190"/></sequence>
<sequence name="W"><gen type="formula" expr="H * 2"/></sequence>
F,, F,, M,170,340 M,172,344
Las filas donde H no tiene valor dejan W vacía también. Es la misma regla que
siguen acumulado y estadística al saltarse una celda
vaciada, vista desde el otro lado — y es lo que hace segura una fórmula sobre una
columna con missing=: los vacíos siguen vacíos en vez de volverse aritmética.
En una rama
Una fórmula lee solo su propia fila, así que también puede ser una de las ramas de una
elección por fila: un <case> de
un <switch> o de un <mix>, a cualquier profundidad, o una de las ramas <gen if="…"> de una
secuencia. Cada fila que la rama recibe se calcula a partir de esa fila.
<tdc>
<env count="6" seed="shop" local="es">
<sequence name="Qty"><gen type="number" value="0..4"/></sequence>
<sequence name="Total"><gen type="number" value="10..90"/></sequence>
<switch name="Each" on="Qty">
<case is="0"><data>sin compra</data></case>
<default><gen type="formula" expr="Total / Qty" decimals="2"/></default>
</switch>
</env>
<block><line><data>${{Qty}},${{Total}},${{Each}}</data></line></block>
</tdc>
0,77,sin compra 4,76,19.00 4,46,11.50 4,81,20.25 1,70,70.00 0,59,sin compra
Total / Qty es una división por cero en las filas donde Qty vale 0, y esas filas pertenecen
a la otra rama, así que a la fórmula nunca se le pregunta por ellas. La misma columna escrita
con if= funciona igual:
<sequence name="Each">
<gen if="Qty > 0" type="formula" expr="Total / Qty" decimals="2"/>
<gen type="text" value="sin compra"/>
</sequence>
_count dentro de una rama es el número de la propia fila, como en cualquier otro sitio. Dos
cosas quedan fuera de una rama, y check lo dice con TDC295: una
fórmula que lee prev(), porque necesita las filas anteriores y se construye en orden sobre
toda la corrida, y cualquier fórmula como parte o campo de una <sequence> compuesta. Calcúlela
como una secuencia propia e insértela en el registro donde el registro se imprime.
Los envoltorios que una fórmula no toma
mask=, case=, missing=, missing_as=, repeat=, anomaly= y
anomaly_factor= se rechazan con TDC015 en lugar de
aceptarse y ser ignorados. Una fórmula se resuelve antes de que corra la capa de
formato — la misma posición que ocupan running y stat.
La respuesta existe un paso más adelante y es mejor, porque funciona donde el valor se IMPRIME:
<data>${{Weight|mask:x}}</data>
Qué capa: formula o <compute>
Ambas calculan un valor, y la separación no es cuestión de gusto:
formula | matemáticas — fracciones, división, funciones, cualquier valor derivado |
<compute> | dígitos de control y forma del texto — módulo 11, Luhn, relleno, recorte |
<compute> trabaja solo con enteros a propósito, porque eso es lo que un dígito de control
necesita. Escribir una fórmula en su árbol de etiquetas se puede, y es un suplicio:
(x - lo) / (hi - lo) allí es media pantalla de anidamiento y aquí trece caracteres.
Qué rechaza check
Todo lo que una fórmula necesita se sabe desde el config, así que nada espera a la corrida:
- sin
expr=—TDC294. Una fórmula ES su expresión. - un
expr=que no se analiza —TDC294, señalando el punto exacto. - un nombre que no es una columna declarada arriba —
TDC240, el mismo código que usanrunningystatpara la misma regla, con undid you meancuando el nombre se parece a uno real. Este es el que más importa: una errata en unif=es una palabra suelta y la rama deja de dispararse en silencio, pero una errata en una fórmula llega a la aritmética. - una fórmula donde no puede significar nada —
TDC295: una que leeprev()dentro de un<case>o de una ramaif=, y cualquier fórmula como parte o campo de una<sequence>compuesta. Vea En una rama.
La corrida rechaza dos más, porque dependen de los valores y no del config: aritmética
sobre una columna de texto (de donde saldría el NaN, y un archivo lleno de NaN sin
aviso es peor que una corrida detenida) y una división por cero.
Detalles
-
Lee su propia fila. La fila i se calcula de la fila i y de nada más, así que una fórmula no consume aleatoriedad: añadir una deja todas las demás columnas donde estaban.
-
Orden de declaración. Una fórmula se construye con columnas que ya existen, así que cada nombre en
expr=debe pertenecer a una secuencia declarada arriba. -
Hace streaming. Leer solo su propia fila es exactamente aquello sobre lo que está construido el motor de streaming, así que una fórmula corre allí como cualquier columna sorteada y la memoria no crece con el número de filas. Medido sobre un config: 1 M de filas 2,1 s, 5 M 3,9 s, 20 M 9,5 s y un archivo de 291 MB, con la memoria pico subiendo 1,3× mientras las filas subían 20×. El motor en memoria, con los mismos 5 M, tardó 12,5 s y usó más memoria, y a 20 M no llega.
Esto es lo que separa una fórmula de acumulado y estadística: esos dos necesitan filas distintas de esta —todas las anteriores, y todas sin más—, así que se quedan en memoria por definición, no por omisión.
Vea también
- Expresiones — los operadores, las funciones y las reglas de los enteros.
- Acumulado — cuando la respuesta necesita las filas anteriores.
- Estadística — cuando necesita toda la corrida.
- Number — las columnas sorteadas que una fórmula lee.