Datos coherentes y relacionales
Los generadores de datos falsos comunes llenan los campos de forma independiente, y así
salen pares imposibles: un Fiat con el modelo Altima (que es un Nissan), una ciudad
de un estado con un código postal de otro. TDC lo hace distinto.
El truco es simple: la dirección de una plantilla puede interpolar el valor de otro campo. El padre nombra el archivo del que se saca el hijo:
value="common.vehicle.model.${{Brand}}"
Si la marca sale Fiat, la dirección se vuelve common.vehicle.model.Fiat, y el modelo
se toma del archivo de Fiat — nunca un «Fiat Altima».
Los valores de abajo vienen de un seed fijo, así que son reproducibles, pero las
cadenas y proporciones exactas pueden diferir entre versiones del core. Tómelos como
ejemplos de la forma, no como garantías.
Cómo se ve
Dos secuencias: una marca y un modelo. El modelo
declara parent="Brand" (para que vea la marca
elegida) y la interpola en la dirección de la
template con ${{Brand}}:
<tdc>
<env count="5" seed="showroom" local="en">
<sequence name="Brand"><gen type="template" value="common.vehicle.brand"/></sequence>
<sequence name="Model" parent="Brand"><gen type="template" value="common.vehicle.model.${{Brand}}"/></sequence>
</env>
<block><line><data>${{Brand}} ${{Model}}</data></line></block>
</tdc>
Honda CR-V Toyota Corolla Ford Maverick Chevrolet Bolt EV Nissan Kicks
Cada modelo pertenece a su marca. Y common.vehicle.brand es un pack ponderado
(Toyota es común, Maybach es raro), así que las marcas mismas aparecen también en
proporciones realistas — se obtienen pares coherentes y una mezcla de mercado creíble
en una sola configuración.
Un hijo por padre — una cocina y su platillo
La misma forma sirve para cualquier par padre/hijo. Una cocina saca su propio platillo
(food.cuisine → food.dishByCuisine.<cocina>). Conviene usarlo cuando los dos
campos se verían absurdos si se sacaran de forma independiente: un «falafel coreano» no
convence a nadie:
<sequence name="Cuisine"><gen type="template" value="food.cuisine"/></sequence>
<sequence name="Dish" parent="Cuisine"><gen type="template" value="food.dishByCuisine.${{Cuisine}}"/></sequence>
Lebanese: Falafel Korean: Bulgogi Indian: Rogan Josh Chinese: Peking Duck Greek: Souvlaki
Un padre, varios hijos enlazados
Un mismo padre puede alimentar a más de un hijo. Cada hijo interpola el mismo valor del padre en su propia dirección, así que todos los campos de la fila quedan consistentes entre sí. Aquí un país (ponderado por población) saca tanto una capital como una moneda:
<sequence name="Country"><gen type="template" value="geo.country"/></sequence>
<sequence name="Capital" parent="Country"><gen type="template" value="geo.capitalByCountry.${{Country}}"/></sequence>
<sequence name="Currency" parent="Country"><gen type="template" value="geo.currencyByCountry.${{Country}}"/></sequence>
China — Beijing — Renminbi United States — Washington — US Dollar India — New Delhi — Indian Rupee Indonesia — Jakarta — Rupiah China — Beijing — Renminbi
Conviene usarlo cuando varios campos dependen de la misma clave: partes de una
dirección que cuelgan de un estado, detalles de producto que cuelgan de una categoría,
datos organizacionales que cuelgan de un departamento. Declare cada hijo con el mismo
parent y todos leerán el único valor elegido.
Cómo están acomodados los datos
El padre es una lista común y corriente; cada uno de sus valores tiene su propio archivo hijo, nombrado exactamente con ese valor:
data/packs/common/vehicle/
brand.txt # las marcas (el padre)
model/
Toyota.txt # modelos Toyota
Fiat.txt # modelos Fiat
Mercedes-Benz.txt # los nombres con guion o espacio también funcionan
La dirección del archivo es la ruta separada por puntos: model/Fiat.txt →
common.vehicle.model.Fiat. En la plantilla, ${{Brand}} completa el nombre del
archivo y TDC encuentra la lista correcta. Para agregar una marca, coloque
model/NewBrand.txt y añada una línea a brand.txt. Ya vienen listos conjuntos
coherentes para marcas de autos, food.cuisine, medical.specialtyCoherent,
work.industryCoherent, common.dev.languageCoherent, sport.sportCoherent y
geo.country.
Cosas que conviene recordar
- El padre se declara antes que el hijo — TDC materializa las
secuencias de arriba hacia abajo, así que
${{Brand}}lee un valor ya calculado. Un hijo que interpola un campo definido más abajo no tiene nada que leer. parent="Brand"enlaza el hijo con el padre y fija el orden. Para una búsqueda simple eso basta; el filtrado más estricto sobre un valor específico (parent="Brand.Fiat") se cubre en Dependencias jerárquicas.- Cada valor del padre necesita un archivo hijo que le corresponda, o la dirección
no se resolverá y saldrá un error. Por eso la lista del padre suele contener
exactamente los valores que tienen archivo (como
common.vehicle.brand). - Motor. Una configuración así siempre corre en el motor en memoria (el único que
resuelve una dirección por fila), así que la memoria crece con
count. Se trata de coherencia realista, no de generar gigabytes en streaming; Qué motor corre su configuración enumera esta forma y las otras cinco que se encaminan igual.
El pariente CSV — row + weight
Cuando los campos relacionados viven en un solo CSV en vez de en archivos por valor,
enlácelos con row: varios generadores
file con el mismo row leen la misma línea por
registro, así que los campos se mantienen en una sola fila de datos reales. Agregue
weight a uno de ellos para sacar esa línea según su frecuencia real:
<sequence name="Place">
<gen name="City" type="file" src="cities.csv" column="city" row="loc" weight="population"/>
<gen name="State" type="file" src="cities.csv" column="state" row="loc"/>
</sequence>
Seattle, WA Austin, TX Chicago, IL Seattle, WA Denver, CO
Como ambos generadores comparten row="loc", la ciudad y su estado nunca se separan en
registros distintos; weight="population" en la ciudad hace que los lugares más grandes
aparezcan con más frecuencia. Todos los detalles están en el
generador File.
El primo numérico — una columna calculada a partir de otra
Los dos mecanismos anteriores mantienen juntos los valores sorteados: un archivo decide de qué archivo sale otro, o una línea de CSV alimenta varios campos. Un número a menudo se mantiene de otra manera — no se sortea en absoluto, sino que se calcula a partir de la columna de al lado. El peso sigue a la estatura; el área sigue al precio; el total sigue a la cantidad y a la tarifa.
Eso es formula:
<sequence name="Height"><gen type="number" distribution="normal" mean="170" sd="10" decimals="1"/></sequence>
<sequence name="Noise"> <gen type="number" distribution="normal" mean="0" sd="1" decimals="4"/></sequence>
<sequence name="Weight"><gen type="formula" expr="0.75 * Height - 58 + 6 * Noise" decimals="1"/></sequence>
171.2, 77.5 177.6, 83.4 164.6, 76.9 164.4, 68.4 175.8, 74.8
Noise es lo que impide que el par sea una línea recta, y no hace falta imprimirlo: una
secuencia que se queda fuera de <block> sigue participando en el cálculo. Dos columnas
que se mueven juntas así son de lo que un modelo puede aprender de verdad; dos sorteos
independientes de los mismos rangos, no.
Una tercera vía es un parámetro de distribución: lambda="Traffic * 0.1" da forma al
sorteo mismo según otra columna, en lugar de calcular un valor a posteriori. Véase
Un parámetro puede seguir a otra columna.
Véase también
- Dependencias jerárquicas — filtrar un hijo por un valor específico del padre.
- Secuencias — declarar campos y el enlace
parent. - Los generadores Template y File.