Ciao a tutti,
È passato un po' di tempo dal mio ultimo post, e in questo lasso di tempo credo di essere diventato un po' più esperto nell'uso di JMP. Senza dubbio, usare i corsi di laurea specialistica in intelligenza artificiale (AI-LLM) in rapida espansione per autoapprendere JSL ha funzionato a meraviglia. Tuttavia, mi sto imbattendo in un problema che mi tormenta da molto tempo con JMP.
Come suggerisce il titolo, deriva dalla concatenazione di tabelle di grandi dimensioni senza duplicazione di valori. Utilizzo JMP 18. Ho allegato due tabelle di dati di prova (ovviamente con un set di dati molto limitato e ripulito; quelle reali includono più di 100.000 righe e più di 30 colonne). Probabilmente dovrebbero essere allegate a un progetto, ops.
Vorrei spiegare il contesto (molto contesto per una domanda in definitiva molto diretta, mi scuso):
Ho un flusso di lavoro ragionevolmente sviluppato per l'analisi regolare dei dati. Ho una tabella di monitoraggio dei campioni, con gli ID dei campioni e le relative informazioni. Ho una tabella dati generale, che inserisco con le informazioni dei miei test. Le due sono collegate tramite un join virtuale, con l'ID del campione come chiave. Nella tabella delle informazioni sui campioni, ogni ID campione è univoco. Nella tabella dei dati del ciclo, ogni ID campione ha più righe di dati, ciascuna con (idealmente) un ID ciclo univoco. Ogni combinazione univoca di ID campione e ID ciclo ha dati associati (varie metriche di performance).
Un test ha più cicli: in effetti, un test è attivo e il numero del ciclo e i dati associati vengono costantemente aggiornati. Periodicamente, esporto i dati del ciclo per ciascun campione, importandoli in JMP e quindi concatenando le due tabelle: l'ultimo snapshot e la tabella dei dati in blocco. Questo processo crea moltissimi duplicati. Ad esempio, prendiamo il campione A. A nella tabella dei dati in blocco ha già ID ciclo 1 -> 1000. Nell'ultimo snapshot, A avrà dati per ID ciclo 1 -> 1200. Concatenando le mie tabelle, la tabella dei dati in blocco avrà, per il campione A, ID ciclo 1 -> 1000 *2 e 1001 -> 1200 *1.
Per il mio flusso di lavoro personale, non è l'ideale, ma va bene. Seleziono tutte le colonne, seleziono tutte le righe duplicate e le elimino. Non è l'ideale, ma va bene così.
Il problema in realtà nasce da ciò su cui ho lavorato di recente. Il modo in cui costruisco i miei dati in blocco, e la combinazione con i virtual join, rappresenta una strategia molto efficace per la filtrazione dei dati. Il mio team se n'è accorto e, con un certo impegno, sono riuscito a convincere le persone ad adottare strategie simili. Tuttavia, sono l'utente JMP più avanzato del nostro gruppo. Gli altri non hanno lo stesso tempo o impegno per imparare a gestire le complessità di JMP/JSL. Questo va bene, è una responsabilità che accolgo con piacere.
Ma voglio che abbiano accesso alla mia capacità di elaborare dati.
Ho trovato una soluzione. L'idea è di gestire la tabella dei dati in blocco come tabella esterna. La aggiornerò utilizzando un progetto di tipo amministrativo. Ho scritto script che si inizializzano automaticamente sui loro progetti, definendo funzioni per aggiornare automaticamente una copia locale incorporata del file master nei loro progetti. Funzionano perfettamente: posso richiamare correttamente uno snapshot della tabella dei dati master nei progetti di altre persone! Ottimo, ne sono molto soddisfatto. Il problema è che l'approccio più logico è impedire loro di lavorare su quello snapshot. Lo script che ho scritto eliminerà quella tabella ogni volta che si aggiorna e ne genererà una nuova identica con lo stesso nome al suo posto. Voglio che questo rimanga invariato: impedirà agli utenti di avere 30 copie della tabella master con nomi diversi in vari momenti dello snapshot. Il passo successivo è quindi aggiornare la tabella dei dati di lavoro dell'utente con i nuovi dati provenienti dalla copia master.
Sto cercando di scrivere uno script per raggiungere questo obiettivo. Ho bisogno che aggiorni la tabella di lavoro mantenendo ciò che gli utenti hanno aggiunto.
Ho provato a usare la funzione Update(), ma per quanto ne so sostituisce solo i dati esistenti con nuovi dati. Non riesco proprio a trovare un modo per far sì che aggiunga nuove righe. Join aggiunge nuove righe ma crea anche una nuova tabella. Nessuna delle due soluzioni funziona per me. L'unico modo che riesco a trovare è concatenare. Ma non voglio che gli utenti abbiano a che fare con righe duplicate. I duplicati possono anche essere piuttosto seri: le tabelle dati sono composte da centinaia di migliaia di righe e potrebbero esserci fino a 50.000 duplicati alla volta.
Il mio approccio attuale (JSL incluso) consiste nel creare colonne temporanee nella tabella master e nella tabella di lavoro. Queste concatenano l'ID campione || ID ciclo, creando una chiave univoca per una singola colonna, chiameremo keyKey. Voglio quindi creare una seconda colonna nella tabella master. Voglio che questa colonna confronti la chiave keyKey nella tabella 1 e nella tabella 2, chiameremo la colonna Selector. Se keyKey esiste in entrambe le tabelle, voglio che il valore nella colonna Selector sia 0; se esiste solo nella tabella master, voglio che il valore sia 1. Voglio quindi che lo script selezioni tutte le righe con valore 1, crei una tabella di sottoinsieme temporanea basata su tale selezione, quindi concateni tale sottoinsieme alla tabella di lavoro. Boom. Concatenazione senza duplicati. MA. Non riesco proprio a capire come confrontare keyKey tra due tabelle e usare quel confronto per generare un valore di colonna.
Sto cercando disperatamente di evitare di usare i cicli For Each Row(). Non voglio un carico di memoria così elevato per tabelle dati con dimensioni superiori a 100.000 righe.
<
I nomi predefiniti sono qui (1);
// --- Configurazione ---
workingTableName = "Test_dati_di_lavoro";
readOnlyMasterName = "Test_dati_master";
keyColA = "ID cella";
keyColB = "ID ciclo";
// --- Ottieni handle ed esegui controlli degli errori ---
dtWorking = Tabella dati (workingTableName);
dtMaster = Tabella dati ( readOnlyMasterName );
If( Is Empty( dtWorking ) | Is Empty( dtMaster ), Throw("Assicurati che entrambe le tabelle siano aperte.") );
// --- PASSAGGIO 1: Aggiorna le righe esistenti---
//Print( "Passaggio 1: aggiornamento delle righe esistenti..." );
//dtWorking << Aggiorna(
// Con (dtMaster),
// Colonne di corrispondenza ( :Name(keyCols[1]) = :Name(keyCols[1]), :Name(keyCols[2]) = :Name(keyCols[2]) )
//);
// --- PASSAGGIO 2: Trova nuove righe utilizzando una chiave concatenata temporanea ---
Print( "Passaggio 2: ricerca di nuove righe utilizzando il ciclo esplicito For Each Row..." );
// Aggiungere prima le colonne chiave temporanee vuote a entrambe le tabelle.
dtWorking << Nuova colonna( "~temp_key~", Carattere );
dtMaster << Nuova colonna( "~temp_key~", Carattere );
// Popola la chiave per la tabella di lavoro
Per ogni riga (dtWorking,
:Name("~temp_key~") = :Name("ID cella") || "|" || Char(:Name("ID ciclo"));
);
// Popola la chiave per la tabella master
Per ogni riga (dtMaster,
:Name("~temp_key~") = :Name("ID cella") || "|" || Char(:Name("ID ciclo"));
);
// Crea un elenco di ricerca rapida di tutte le chiavi temporanee nella tabella di lavoro.
workingKeys = Array associativo( dtWorking:Name("~temp_key~") );
//Aggiungi una nuova colonna "flag" alla tabella master.
dtMaster << Nuova colonna( "~is_match~", Numerico
//Imposta il suo valore utilizzando una formula. La formula restituisce 0 se la chiave è nuova, 1 se corrisponde.
Colonna( dtMaster, "~is_match~" ) << Imposta Formula(
Se(Manca(workingKeys[ :Name("~temp_key~") ] ),
0, // Non trovato in workingKeys -> È una nuova riga
1 // Trovato in workingKeys -> È una riga esistente
)
);
// --- PASSAGGIO 3: Crea una nuova tabella temporanea dalle righe selezionate ---
Se (N Righe (dtMaster << Ottieni Righe Selezionate ) > 0,
Print( "Trovato" || Char( N Rows( dtMaster << Ottieni righe selezionate ) ) || " nuove righe. Creazione tabella temporanea..." );
dtTempNewRows = dtMaster << Sottoinsieme(
Righe selezionate (1),
Nome tabella di output ("Nuove righe temporanee")
);
dtTempNewRows << Mostra finestra(1);
,
Print("Nessuna nuova riga trovata da aggiungere.");
);
// --- PASSAGGIO 4: Pulisci le colonne temporanee ---
//Prova( dtWorking << Elimina colonne( "~temp_key~" ) );
//Prova( dtMaster << Elimina colonne( "~temp_key~" ) );
//dtMaster << Cancella selezione;
Print( "Test completato." );>
Inoltre, se qualcuno potesse aiutarmi a capire come utilizzare le variabili stringa per l'identificazione dei nomi delle colonne (Passaggio 1, commentato perché non sono riuscito a capire come risolvere anche quello), sarebbe fantastico.
Comunque, grazie per aver letto questo gigantesco post,
Tardi