Salut tout le monde,
Cela fait un moment que je n'ai pas publié de message, et depuis, je pense avoir gagné en maîtrise de JMP. Utiliser les LLM en IA, en plein essor, pour apprendre le JSL en autodidacte fonctionne à merveille. Cependant, je rencontre un problème qui me taraude depuis très longtemps avec JMP.
Comme le titre l'indique, cela résulte de la concaténation de grandes tables sans duplication de valeurs. J'utilise JMP 18. J'ai joint deux tables de données de test (avec un ensemble de données évidemment très limité et épuré ; les vraies tables contiennent plus de 100 000 lignes et plus de 30 colonnes). Celles-ci devraient probablement être jointes à un projet, oups.
Laissez-moi vous expliquer le contexte (beaucoup de contexte pour une question finalement très directe, toutes mes excuses) :
J'ai un workflow relativement développé pour l'analyse régulière des données. J'ai une table de suivi des échantillons, avec leurs identifiants et leurs informations associées. J'ai une table de données générales, que j'alimente avec les informations de mes tests. Les deux sont liées par une jointure virtuelle, avec l'identifiant de l'échantillon comme clé. Dans la table d'informations sur les échantillons, chaque identifiant d'échantillon est unique. Dans la table de données de cycle, chaque identifiant d'échantillon comporte plusieurs lignes de données, chacune avec (idéalement) un identifiant de cycle unique. Chaque combinaison unique d'identifiant d'échantillon et d'identifiant de cycle est associée à des données (diverses mesures de performance).
Un test comporte plusieurs cycles. En fait, un test est en direct et le numéro de cycle ainsi que les données associées sont constamment mis à jour. J'exporte régulièrement les données de cycle de chaque échantillon, les importe dans JMP, puis je concatène les deux tables : la dernière capture instantanée et la table de données globales. Ce processus crée de nombreux doublons. Prenons l'exemple de l'échantillon A. Dans la table de données globales, l'échantillon A a déjà l'ID de cycle 1 -> 1000. Dans la dernière capture instantanée, l'échantillon A contiendra les données de l'ID de cycle 1 -> 1200. Je concatène mes tables et la table de données globales aura, pour l'échantillon A, l'ID de cycle 1 -> 1000 *2 et 1001 -> 1200 *1.
Pour mon flux de travail personnel, ce n'est pas idéal, mais ça va. Je sélectionne toutes les colonnes, puis toutes les lignes en double et je les supprime. Ce n'est pas optimal, mais tant pis.
Le problème vient en fait de mon travail récent. Ma façon de construire mes données massives et la combinaison avec des jointures virtuelles constituent une stratégie très efficace pour filtrer les données. Mon équipe l'a remarqué et, à force de travail, j'ai réussi à convaincre les autres d'adopter des stratégies similaires. Cependant, je suis l'utilisateur JMP le plus expérimenté de notre groupe. Les autres n'ont pas le même temps ni la même motivation pour apprendre à gérer les subtilités de JMP/JSL. C'est normal, c'est une responsabilité que j'accepte avec plaisir.
Mais je veux qu’ils aient accès à ma capacité à traiter les données.
J'ai trouvé une solution. L'idée est de gérer la table de données en masse comme une table externe. Je la mettrai à jour via un projet administrateur. J'ai écrit des scripts qui s'initialisent automatiquement sur leurs projets, définissant des fonctions pour actualiser automatiquement une copie locale intégrée du fichier maître dans leurs projets. Cela fonctionne parfaitement : je peux appeler un instantané de la table de données maître dans les projets d'autres utilisateurs ! Super, j'en suis très satisfait. Le problème est que l'approche la plus logique est de ne pas leur permettre de travailler sur cet instantané. Le script que j'ai écrit supprimera cette table à chaque actualisation et générera une nouvelle table identique portant le même nom. Je souhaite conserver cette configuration : cela empêchera les utilisateurs d'avoir 30 copies de la table maître à différents moments de l'instantané, sous des noms différents. L'étape suivante consiste à mettre à jour la table de données de travail de l'utilisateur avec les nouvelles données de la copie maître.
J'essaie d'écrire un script pour y parvenir. Il me faut mettre à jour la table de travail tout en préservant les ajouts des utilisateurs.
J'ai essayé d'utiliser la fonction Update(), mais d'après ce que j'ai compris, elle ne fait que remplacer les données existantes par de nouvelles. Je n'arrive pas à trouver comment ajouter de nouvelles lignes. Joindre ajoute de nouvelles lignes, mais crée également une nouvelle table. Aucune de ces solutions ne fonctionne pour moi. La seule solution que je trouve est la concaténation. Je ne veux pas que les utilisateurs aient à gérer des doublons. Ces doublons peuvent également être très graves : les tables de données contiennent des centaines de milliers de lignes, et il peut y en avoir jusqu'à 50 000 à la fois.
Mon approche actuelle (JSL inclus) consiste à créer des colonnes temporaires dans la table principale et la table de travail. Celles-ci concaténent l'ID d'échantillon || l'ID de cycle, créant ainsi une clé de colonne unique, appelée keyKey. Je souhaite ensuite créer une seconde colonne dans la table principale. Cette colonne comparera les keyKey des tables 1 et 2 ; appelons-la la colonne Selector. Si keyKey existe dans les deux tables, la valeur de la colonne Selector doit être 0 ; si elle n'existe que dans la table principale, elle doit être 1. Je souhaite ensuite que le script sélectionne toutes les lignes ayant une valeur de 1, crée une table de sous-ensemble temporaire basée sur cette sélection, puis concatène ce sous-ensemble à la table de travail. Boum ! Concaténation sans doublons. MAIS… Je n'arrive absolument pas à comparer les keyKey de deux tables et à utiliser cette comparaison pour générer une valeur de colonne.
J'essaie désespérément d'éviter d'utiliser des boucles For Each Row(). Je ne veux pas surcharger la mémoire des tables de données de plus de 100 000 lignes.
<
Les noms par défaut sont ici (1) ;
// --- Configuration ---
workingTableName = "Test_Données_De_Travail";
readOnlyMasterName = "Master_Data_Test";
keyColA = "ID de cellule";
keyColB = "ID du cycle";
// --- Obtenir des handles et effectuer des vérifications d'erreurs ---
dtWorking = Table de données(workingTableName);
dtMaster = Table de données( readOnlyMasterName );
Si( Est vide( dtWorking ) | Est vide( dtMaster ), Throw("Assurez-vous que les deux tables sont ouvertes.") );
// --- ÉTAPE 1 : Mettre à jour les lignes existantes---
//Print( "Étape 1 : Mise à jour des lignes existantes..." );
//dtWorking << Mise à jour(
// Avec( dtMaster ),
// Correspondance des colonnes( :Name(keyCols[1]) = :Name(keyCols[1]), :Name(keyCols[2]) = :Name(keyCols[2]) )
//);
// --- ÉTAPE 2 : Rechercher de nouvelles lignes à l'aide d'une clé concaténée temporaire ---
Print( "Étape 2 : Recherche de nouvelles lignes à l'aide d'une boucle explicite For Each Row..." );
// Ajoutez d’abord les colonnes de clés temporaires vides aux deux tables.
dtWorking << Nouvelle colonne( "~temp_key~", Caractère );
dtMaster << Nouvelle colonne( "~temp_key~", Caractère );
// Remplir la clé de la table de travail
Pour chaque ligne( dtWorking,
:Name("~temp_key~") = :Name("ID de cellule") || "|" || Char(:Name("ID de cycle"));
);
// Remplir la clé de la table principale
Pour chaque ligne( dtMaster,
:Name("~temp_key~") = :Name("ID de cellule") || "|" || Char(:Name("ID de cycle"));
);
// Créez une liste de recherche rapide de toutes les clés temporaires dans la table de travail.
workingKeys = Tableau associatif( dtWorking:Name("~temp_key~") );
//Ajoutez une nouvelle colonne « drapeau » à la table principale.
dtMaster << Nouvelle colonne( "~is_match~", numérique
//Définissez sa valeur à l'aide d'une formule. La formule renvoie 0 si la clé est nouvelle et 1 si elle correspond.
Colonne( dtMaster, "~is_match~" ) << Définir la formule(
Si( est manquant( workingKeys[ :Name("~temp_key~") ] ),
0, // Non trouvé dans workingKeys -> C'est une nouvelle ligne
1 // Trouvé dans workingKeys -> C'est une ligne existante
)
);
// --- ÉTAPE 3 : Créer une nouvelle table temporaire à partir des lignes sélectionnées ---
Si( N lignes( dtMaster << Obtenir les lignes sélectionnées ) > 0,
Print( "Trouvé " || Char( N Rows( dtMaster << Obtenir les lignes sélectionnées ) ) || " nouvelles lignes. Création d'une table temporaire..." );
dtTempNewRows = dtMaster << Sous-ensemble(
Lignes sélectionnées (1),
Nom de la table de sortie (« Nouvelles lignes temporaires »)
);
dtTempNewRows << Afficher la fenêtre(1);
,
Print("Aucune nouvelle ligne trouvée à ajouter.");
);
// --- ÉTAPE 4 : Nettoyer les colonnes temporaires ---
//Essayez( dtWorking << Supprimer les colonnes( "~temp_key~" ) );
//Essayez( dtMaster << Supprimer les colonnes( "~temp_key~" ) );
//dtMaster << Effacer la sélection ;
Print( "Test terminé." );>
De plus, si quelqu'un pouvait m'aider à comprendre comment utiliser des variables de chaîne pour l'identification du nom de colonne (étape 1, commentée car je ne savais pas non plus comment résoudre ce problème), ce serait formidable.
Quoi qu'il en soit, merci d'avoir lu ce post colossal,
En retard