Hola a todos,
Ha pasado un tiempo desde mi última publicación, y creo que me he vuelto un poco más competente con JMP. Sin vergüenza alguna, usar los crecientes LLM de IA para autoaprender JSL ha funcionado de maravilla. Sin embargo, me estoy encontrando con un problema que me ha estado molestando durante mucho tiempo con JMP.
Como indica el título, se deriva de la concatenación de tablas grandes sin duplicar valores. Estoy usando JMP 18. He adjuntado dos tablas de datos de prueba (con un conjunto de datos obviamente muy limitado y depurado; las reales incluyen más de 100 000 filas y más de 30 columnas). Probablemente deberían adjuntarse a un proyecto, ¡vaya!
Permítanme explicarles los antecedentes (mucho contexto para una pregunta muy directa, disculpas):
Cuento con un flujo de trabajo bastante desarrollado para el análisis regular de datos. Dispongo de una tabla de seguimiento de muestras con sus identificadores y su información asociada. Tengo una tabla de datos generales que relleno con la información de mis pruebas. Ambas están vinculadas mediante una unión virtual, con el identificador de muestra como clave. En la tabla de información de muestras, cada identificador de muestra es único. En la tabla de datos de ciclo, cada identificador de muestra tiene varias filas de datos, cada una con (idealmente) un identificador de ciclo único. Cada combinación única de identificador de muestra e identificador de ciclo tiene datos asociados (diversas métricas de rendimiento).
Una prueba tiene varios ciclos; de hecho, una prueba está activa y el número de ciclo y sus datos asociados se actualizan constantemente. Periódicamente, exporto los datos del ciclo de cada muestra, los importo a JMP y luego concateno las dos tablas: la última instantánea y la tabla de datos masivos. Este proceso crea muchos duplicados. Por ejemplo, tomemos la muestra A. En la tabla de datos masivos, A ya tiene el ID de ciclo 1 -> 1000. En la última instantánea, A tendrá datos para el ID de ciclo 1 -> 1200. Concateno las tablas y la tabla de datos masivos tendrá, para la muestra A, el ID de ciclo 1 -> 1000 *2 y 1001 -> 1200 *1.
Para mi flujo de trabajo personal, esto no es ideal, pero está bien. Selecciono todas las columnas, selecciono todas las filas duplicadas y las borro. No es óptimo, pero da igual.
El problema, en realidad, surge de lo que he estado trabajando últimamente. La forma en que construyo mis datos masivos y la combinación con uniones virtuales es una estrategia muy eficaz para el filtrado de datos. Mi equipo lo ha notado y, con esfuerzo, he convencido a otros para que adopten estrategias similares. Sin embargo, soy el usuario más avanzado de JMP de nuestro grupo. Los demás no tienen el mismo tiempo ni dedicación para aprender a manejar las complejidades de JMP/JSL. No hay problema, es una responsabilidad que acepto.
Pero quiero que tengan acceso a mi capacidad de procesar datos.
He encontrado una solución. La idea es mantener la tabla de datos masivos como una tabla externa. La actualizaré mediante un proyecto similar a un administrador. He creado scripts que se inicializan automáticamente en sus proyectos y definen funciones para actualizar automáticamente una copia local incrustada del archivo maestro en sus proyectos. Funcionan correctamente: puedo acceder a una instantánea de la tabla de datos maestros en los proyectos de otros usuarios. ¡Genial, estoy muy contento! El problema es que lo más lógico es no permitirles trabajar en esa instantánea. El script que he creado eliminará esa tabla cada vez que se actualice y generará una nueva tabla idéntica con el mismo nombre en su lugar. Quiero que esto se mantenga así: evitará que los usuarios tengan 30 copias de la tabla maestra en diferentes momentos de la instantánea con diferentes nombres. El siguiente paso es actualizar la tabla de datos de trabajo del usuario con los nuevos datos de la copia maestra.
Estoy intentando escribir un script para lograrlo. Necesito que actualice la tabla de trabajo y conserve lo que los usuarios hayan añadido.
He intentado usar la función Update(), pero, por lo que he podido averiguar, solo reemplaza los datos existentes con datos nuevos. No encuentro la manera de que agregue nuevas filas. La función "Unir" agrega nuevas filas, pero también crea una nueva tabla. Ninguna de las soluciones me funciona. La única forma que encuentro es concatenar. Pero no quiero que los usuarios tengan que lidiar con filas duplicadas. Los duplicados también pueden ser bastante graves: las tablas de datos tienen cientos de miles de filas y podría haber hasta 50 000 duplicados a la vez.
Mi enfoque actual (incluido JSL) consiste en crear columnas temporales en las tablas maestra y de trabajo. Estas concatenan el ID de muestra || ID de ciclo, creando una clave de columna única, llamada keyKey. Luego, quiero crear una segunda columna en la tabla maestra. Quiero que esta columna compare keyKey de las tablas 1 y 2, llamada la columna Selector. Si keyKey existe en ambas tablas, el valor de la columna Selector debe ser 0; si solo existe en la tabla maestra, debe ser 1. Después, quiero que el script seleccione todas las filas con el valor 1, cree una tabla de subconjunto temporal basada en esa selección y luego concatene ese subconjunto a la tabla de trabajo. ¡Genial! Concatenación sin duplicados. Pero, por más que me esfuerce, no logro entender cómo comparar keyKey entre dos tablas y usar esa comparación para generar un valor de columna.
Estoy intentando evitar desesperadamente el uso de bucles For Each Row(). No quiero esa carga de memoria para tablas de datos de más de 100 000 filas.
<
Los nombres predeterminados están aquí (1);
// --- Configuración ---
nombreDeTablaDeTrabajo = "Prueba_De_Datos_De_Trabajo";
readOnlyMasterName = "Prueba_de_Datos_Maestros";
keyColA = "ID de celda";
keyColB = "ID del ciclo";
// --- Obtener identificadores y realizar comprobaciones de errores ---
dtWorking = Tabla de datos(nombreDeTablaDeTrabajo);
dtMaster = Tabla de datos(readOnlyMasterName);
Si (Está vacío (dtWorking) | Está vacío (dtMaster), lanzar ("Asegúrese de que ambas tablas estén abiertas.") );
// --- PASO 1: Actualizar filas existentes---
//Imprimir( "Paso 1: Actualizar filas existentes..." );
//dtWorking << Actualizar(
// Con(dtMaster),
// Coincidir columnas( :Nombre(keyCols[1]) = :Nombre(keyCols[1]), :Nombre(keyCols[2]) = :Nombre(keyCols[2]) )
//);
// --- PASO 2: Buscar nuevas filas usando una clave concatenada temporal ---
Print("Paso 2: Encontrar nuevas filas usando un bucle explícito "Para cada fila...");
// Primero agregue las columnas de clave temporal en blanco a ambas tablas.
dtWorking << Nueva columna( "~temp_key~", Carácter );
dtMaster << Nueva columna( "~temp_key~", Carácter );
// Rellene la clave para la tabla de trabajo
Para cada fila (dtWorking,
:Nombre("~temp_key~") = :Nombre("ID de celda") || "|" || Char(:Nombre("ID de ciclo"));
);
// Rellene la clave para la tabla maestra
Para cada fila (dtMaster,
:Nombre("~temp_key~") = :Nombre("ID de celda") || "|" || Char(:Nombre("ID de ciclo"));
);
// Cree una lista de búsqueda rápida de todas las claves temporales en la tabla de trabajo.
workingKeys = Matriz asociativa( dtWorking:Name("~temp_key~") );
//Agrega una nueva columna "bandera" a la tabla maestra.
dtMaster << Nueva columna( "~is_match~", Numérico
//Establezca su valor mediante una fórmula. La fórmula devuelve 0 si la clave es nueva y 1 si coincide.
Columna(dtMaster, "~is_match~") << Establecer Fórmula(
Si (Falta (workingKeys[ :Name("~temp_key~") ] ),
0, // No se encontró en workingKeys -> Es una nueva fila
1 // Se encuentra en workingKeys -> Es una fila existente
)
);
// --- PASO 3: Crear una nueva tabla temporal a partir de las filas seleccionadas ---
Si (N filas (dtMaster << Obtener filas seleccionadas) > 0,
Print( "Encontrado " || Char( N Filas( dtMaster << Obtener Filas Seleccionadas ) ) || " nuevas filas. Creando tabla temporal..." );
dtTempNewRows = dtMaster << Subconjunto(
Filas seleccionadas (1),
Nombre de la tabla de salida ("Nuevas filas temporales")
);
dtTempNewRows << Mostrar ventana(1);
,
Print("No se encontraron nuevas filas para agregar.");
);
// --- PASO 4: Limpiar columnas temporales ---
//Intenta( dtWorking << Eliminar columnas( "~temp_key~" ) );
//Intenta( dtMaster << Eliminar columnas( "~temp_key~" ) );
//dtMaster << Borrar selección;
Print("Prueba completada." );>
Además, si alguien pudiera ayudarme a descubrir cómo usar variables de cadena para identificar nombres de columnas (Paso 1, comentado porque tampoco pude descubrir cómo solucionarlo), sería genial.
De todos modos, gracias por leer esta publicación gigantesca.
Tarde