Devez-vous parcourir une table de données ou utiliser Recode, ou utiliser Get Rows Where pour modifier les valeurs dans une table de donnéesUne discussion communautaire récente Re : script pour trouver et remplacer une valeur numérique par une valeur manquante dans tout le tableau avait une très belle entrée fournie par @SDF1 qui a été choisi comme solution
names default to here(1);
dt=current data table();
For( i = 1, i <= N Rows( dt ), i++,
For( l = 1, l <= N Cols( dt ), l++,
If( Column( l )[i] == 999,
Column( l )[i] = .
)
)
);
Lorsque j'ai vu la solution, un vieil enseignement m'est venu à l'esprit qui disait en gros : "Ne parcourez pas une table de données.La boucle est lente. Utilisez les fonctionnalités intégrées de JMP"
J'ai donc commencé à modifier @SDF1 code.
J'ai déterminé que mon standard de secours de "Get Rows Where()" serait plus rapide.Après tout, cela permet à toutes les lignes trouvées d'être définies avec une seule instruction d'affectation, après que le "très rapide" Get Rows Where() a trouvé les lignes.
Une autre solution que j'ai vue poussé recommandée par la communauté est la fonction Recode de JMP.
Alors:
Hypothèse : L'intégration des fonctions JMP de Get Rows Where et Recode sera plus rapide que la boucle vers le bas et sur l'ensemble d'une table de données.
J'ai sélectionné le tableau de données Semiconductor Capability comme un tableau de bonne taille, 132 colonnes et 1455 lignes
J'ai écrit un petit script qui définit 10 valeurs aléatoires dans chaque colonne à 999.
names default to here(1);
dt=current data table();
for(i=5,i<=ncols(dt),i++,
if(col stddev(ascolumn(dt,i))!=0,
for(k=1,k<=10,k++,
column(dt,i)[randominteger(1,1455)]=999;
)
));
Il s'agit de la valeur d'origine à modifier dans la discussion mentionnée ci-dessus.Le 999 doit être changé en manquant (.) par les scripts.
Méthode:
- Démarrez JMP 16.
- Lire dans le tableau de données Semiconductor Capability
- Exécutez le script pour définir les 999 valeurs dans chaque colonne
- Répétez les 2 étapes ci-dessus pour chacune des 3 méthodes et recodez le temps à la milliseconde près.
- Répétez les étapes ci-dessus pour JMP15
Voici le script pour le Recode
Names Default To Here( 1 );
dt = Current Data Table();
start = HP Time();
For( i = 5, i <= N Cols( dt ), i++,
dt << Recode Column(
As Column( dt, i ),
{If( _rcNow == 999, ., _rcNow )},
Target Column( As Column( dt, i ) )
)
);
show(hptime()-start)
Le code pour Get Rows Where
names default to here(1);
dt=current data table();
start=hptime();
for(i=5,i<= ncols(dt),i++,
column(dt,i)[dt<<GET rows="" where=""></GET>
Et voici les des résultats surprenants

La boucle For gagne haut la main.Et d'après les résultats, Get Rows Where doit être utilisé avec parcimonie.
Les commentaires et corrections sont les bienvenus......
Tableau de donnéesScriptRe : Devez-vous parcourir une table de données ou utiliser Recode, ou utiliser Get Rows Where pour modifier les valeurs dans une table de donnéesOui je comprends @brady_brady . On pourrait faire ceci, cependant:
NamesDefaultToHere(1);
// Make an example table with some rogue '999' values
nr = 10; // Number of rows
nc = 10; // Number of columns
p = 0.1; // Fraction of rogue values
val = 999; // Rogue value
m = J(nr, nc, RandomInteger(1, 10));
m = Shape(m, nr*nc, 1);
m[RandomIndex(nr*nc, Floor(nr*nc*p))] = val;
m = Shape(m, nr, nc);
dt = AsTable(m);
dt << setName("Rogue Values");
Wait(3);
// Set rogue values to '.'
mask = J(nr*nc, 1, 1);
mask[Loc(dt[0,0] == val)] = .;
mask = Shape(mask, nr, nc);
dt[0,0] = mask :*</img> dt[0,0];
Mais je doute fort que ce soit plus rapide que ce que vous avez déjà, notamment à cause de la multiplication par élément.
Re : Devez-vous parcourir une table de données ou utiliser Recode, ou utiliser Get Rows Where pour modifier les valeurs dans une table de donnéesVous avez un peu tort la comparaison ici - la fonctionnalité "Get Rows Where" évaluera son expression sur chaque ligne, vous devez donc minimiser les recherches superflues avant d'exécuter la boucle - en particulier, votre itérateur est externe et il devra faire un peu de marche pour le trouver sur chaque rangée.En outre, il évaluera la fonction AsColumn sur chaque ligne, en recherchant à la fois 'dt' et 'i' à chaque fois.C'est _très_ cher.La bonne façon d'y parvenir serait la suivante:
names default to here(1);
dt=current data table();
start=hptime();
names = dt << Get Column Names( "string" );
for(i=5,i<= ncols(dt),i++,
Eval( Parse( Eval InserT( JSL Quote(
column(dt,i)[dt<<GET rows="" where=""></GET>
Vous remarquerez que cela s'exécute de l'ordre de 3000 fois plus rapidement, ce qui devrait le rendre comparable sinon meilleur que les autres méthodes que vous avez
Re : Devez-vous parcourir une table de données ou utiliser Recode, ou utiliser Get Rows Where pour modifier les valeurs dans une table de donnéesOui ... l'obstacle est que, bien que les matrices soient accessibles avec un seul index (ce qui suppose une disposition en ligne principale), les tables de données ne le peuvent pas. Ainsi, il n'y a malheureusement pas d'analogue à
m[loc(m, 9999)] = .
où m est une matrice pxq, dans le monde des tables de données... d'où les lignes précédant et suivant celle-ci dans le code ci-dessus. Bien que le code soit toujours rapide, il serait VRAIMENT rapide si l'accès à un seul index était disponible pour les tables.
Re : Devez-vous parcourir une table de données ou utiliser Recode, ou utiliser Get Rows Where pour modifier les valeurs dans une table de donnéesPour le ' frais généraux lors de la création d'une matrice ", je soupçonne que abonnement à une table de données pourrait aider.@Craige_Hales saurait.
Re : Devez-vous parcourir une table de données ou utiliser Recode, ou utiliser Get Rows Where pour modifier les valeurs dans une table de donnéesVoici un moyen d'éviter les boucles, bien qu'il y ait une surcharge lors de la création d'une matrice et de la réaffectation à la table après. Cette approche fonctionne rapidement sur ces données, à peu près de la même manière que loc_2d, mais à mesure que la table devient énorme, je m'attendrais à ce que ses performances relatives en souffrent, mais je ne l'ai pas vérifié.
expr_mat = expr(
m = dt[0, 5 :: N Col( dt )];
m[Loc( m, 999 )] = .;
dt[0, 5 :: N Col( dt )] = m;
);
Re : Devez-vous parcourir une table de données ou utiliser Recode, ou utiliser Get Rows Where pour modifier les valeurs dans une table de donnéesJe suis peut-être allé un peu trop loin à ce sujet (mais au moins j'ai une idée de la façon de faire ce type de test plus tard si je le dois).
J'ai écrit une sorte de script de test avec différentes expressions pour tester différentes façons de remplacer le 999 par manquant. J'ai testé avec deux jeux de données (Semiconductor Capability.jmp et Probe.jmp) et en modifiant la quantité de valeurs manquantes dans les colonnes.
Loc() est le plus rapide pour remplacer les valeurs (pas de surprise là-bas). Missing Value Codes() est "toujours" rapide mais il a des cas d'utilisation un peu plus limités par rapport à Loc() et c'est un peu plus compliqué à utiliser s'il y aurait plusieurs valeurs à remplacer.

Le plus rapide a fini par être loc_2d (n'a pas pris la peine de changer les noms de variables à partir de ceci : Utilisation de Loc avec une matrice 2D ). Il faut encore boucler pour obtenir des valeurs définies (du moins à ma connaissance), donc la raison la plus probable pour laquelle cela est plus rapide que juste Loc() est qu'il sautera certaines colonnes. Ci-dessous se trouvent expr_loc2d et expr_loc :
expr_loc = Expr(
for(i=5,i<= ncols(testData),i++,
column(testData,i)[Loc(testData[0,i], 999)]=.;
);
);
expr_loc_2d = Expr(
mat = testData[0,5::NCols(testData)];
nr = N Rows(mat);
nc = N Cols(mat);
blacklocs = Loc(mat, 999) - 1;
rowlocs = Floor((blacklocs) / nc ) + 1;
collocs = Mod((blacklocs), nc ) + 1 + 4;
uniqCols = Associative Array(collocs) << get keys;
For(i_2d = 1, i_2d <= N Items(uniqCols), i_2d++,
testData[rowlocs[Loc(collocs, uniqCols[i_2d])], uniqCols[i_2d]] = .;
);
);
Il semble également important d'utiliser AsColumn(Column(dt, i)) comme @Ryan_Gilmore dit (je n'ai même pas pris la peine de tester << obtenir des lignes où sans cela, car ce sera très lent), mais j'ai testé le recodage avec et sans Column(). Je viens également de remarquer que j'ai raté une colonne () de recode, alors exécutez quelques tests rapides (non attachés dans les données ou le script):
Assez grande différence entre ces trois:
expr_recode_ascolumn = Expr(
For(i = 5, i <= N Cols(testData), i++,
testData << Recode Column(
As Column(dt, i),
{If( _rcNow == 999, ., _rcNow)},
Target Column(As Column(testData, i))
)
);
);
expr_recode_ascolumn_column = Expr(
For(i = 5, i <= N Cols(testData), i++,
testData << Recode Column(
As Column(dt, i),
{If( _rcNow == 999, ., _rcNow)},
Target Column(As Column(Column(testData, i)))
)
);
);<BR />
expr_recode_ascolumn_column_column = Expr(
For(i = 5, i <= N Cols(testData), i++,
testData << Recode Column(
As Column(Column(dt, i)),
{If( _rcNow == 999, ., _rcNow)},
Target Column(As Column(Column(testData, i)))
)
);
);

Le script de test et certaines données de résultat jointes. Le script aura probablement quelques erreurs ici et là. Il devrait être assez facile d'ajouter de nouvelles expressions si quelqu'un le souhaite. Une chose que je n'ai pas du tout testée est d'utiliser << Begin/End data update.
Re : Devez-vous parcourir une table de données ou utiliser Recode, ou utiliser Get Rows Where pour modifier les valeurs dans une table de donnéesDébat intéressant ! La notation matricielle de la table de données, qui, je pense, est apparue pour la première fois dans JMP 13, est assez efficace pour l'affectation.
Pour ce problème particulier, il est presque 500 fois plus rapide que la boucle for (0,003 contre 1,5 seconde, ordinateur portable JMP 15 Mac).
start = HP Time();
For( i = 5, i <= N Cols( dt ), i++,
Try( dt[Loc( dt[0, i], 999 ), i] = . )
);
Show( HP Time() - start);
Le try() est nécessaire pour éviter une erreur pour les colonnes sans "999".
La boucle sur les colonnes est toujours nécessaire car l'affectation directe pour l'ensemble de la matrice, que j'ai un instant pensé serait encore plus rapide, ne fonctionne pas. Erreur : "Plusieurs indices nécessitent des indices uniques à chaque niveau interne d'accès ou d'évaluation...", ce qui, à la réflexion, est logique.
// The left Data Table matrix syntax is OK on its own, but not for assignment.
dt[0, 5 :: N Col( dt )][Loc( dt[0, 5 :: N Col( dt )], 999 )] = .;
Re : Devez-vous parcourir une table de données ou utiliser Recode, ou utiliser Get Rows Where pour modifier les valeurs dans une table de donnéesJ'ai peut-être été méfiant à l'idée d'utiliser des boucles à un moment donné, mais après avoir suivi un cours d'algorithme (que je n'ai jamais terminé ..), un peu moins. Il faut juste être efficace avec eux, bien souvent on boucle beaucoup trop (complexité temporelle et notation Big O). De plus, utiliser des boucles est beaucoup plus facile que d'essayer de résoudre des problèmes avec des mathématiques pures.
Avec JMP, j'utilise généralement les options sûres que j'ai apprises à un moment donné, car la vitesse est rarement un problème. Mais lorsque le temps est un problème (une application a mis plus de 6 minutes à s'ouvrir au travail), je vais commencer à chercher des moyens de l'accélérer (j'ai réussi à la faire démarrer en moins de 2 minutes). Dans ce cas, j'ai réussi à le rendre plus rapide en utilisant Loc() avec différentes combinaisons d'autres fonctions (supprimer la ligne, définir l'état de la ligne).
En plus de Loc(), l'utilisation de Summarize() semble être "bien" plus rapide par rapport à mon option préférée depuis longtemps pour obtenir des valeurs uniques dans la colonne (tableau associatif() << get keys). Lorsque le nombre de lignes augmente, Associative array() << get keys devient de plus en plus lent et Summarize() ne semble pas souffrir du même problème, du moins pas aussi vite. J'ai donc essayé de commencer à utiliser Summarize() plus (ne savez jamais quelle hauteur les tables de données obtiennent).
Re : Devez-vous parcourir une table de données ou utiliser Recode, ou utiliser Get Rows Where pour modifier les valeurs dans une table de donnéesDonc, nous sautons AsColumn() avec (aller plus avec une intuition et je n'ai pas testé que cela remplacera toutes les valeurs correctes):
start=hptime();
for(i=5,i<= ncols(dt),i++,
column(dt,i)[Loc(dt[0,i], 999)]=.;
if(mod(i,10)==0,show(i));
);
show(hptime()-start);
Re : Devez-vous parcourir une table de données ou utiliser Recode, ou utiliser Get Rows Where pour modifier les valeurs dans une table de donnéesJe suis d'accord que Loc() est la meilleure méthode.
Pour moi, le ralentissement semble se produire à cause de As Column. Je vois une différence significative entre,
start = HP Time();
For( i = 5, i <= N Cols( dt ), i++,
As Column( column(dt, i) );
);
Show( HP Time() - start );
et
start = HP Time();
For( i = 5, i <= N Cols( dt ), i++,
As Column( dt, i );
);
Show( HP Time() - start );
Modification de la syntaxe As Column dans le code de @txnelson question initiale, la boucle For est toujours la plus rapide mais les autres sont plus comparables.
Re : Devez-vous parcourir une table de données ou utiliser Recode, ou utiliser Get Rows Where pour modifier les valeurs dans une table de donnéesSalut @txnelson ,
Comparaison très intéressante, et pas les résultats auxquels je m'attendais non plus. Je me souviens quand j'ai commencé à apprendre la programmation à l'université et qu'on m'a aussi appris quelque chose de similaire : méfiez-vous des boucles parce qu'elles sont lentes.
Bien sûr, avec certains calculs et dans certains programmes, j'ai trouvé que les boucles For étaient très lentes - plus lentes que les alternatives. N'étant pas un grand programmeur, j'ai tendance à me rabattre sur ce que je sais faire, ce qui n'est pas toujours rapide ou élégant. Parfois pour moi, la logique d'une boucle me permet d'écrire un code fonctionnel pour faire ce que je veux qu'il fasse, ce qui me permet de passer aux étapes suivantes.
Je trouve intéressant que les options For Loop et Recode soient légèrement plus lentes pour JMP 16 par rapport à 15, mais le Get Rows Where s'est amélioré en réduisant le temps presque de moitié, c'est un changement substantiel.
Merci!,
DS
Re : Devez-vous parcourir une table de données ou utiliser Recode, ou utiliser Get Rows Where pour modifier les valeurs dans une table de donnéesPeut-être @Craige_Hales pourrait nous avoir une solution encore plus rapide. J'ai eu l'idée d'utiliser les calculs matriciels (en particulier Loc()) à partir de ses messages/blog communautaires.
J'ai rendu un outil au travail plus rapide qui ouvre l'ensemble de données, puis supprime les anciennes données (en fonction de la sélection de l'utilisateur). À l'origine, il utilisait << Select Where et Delete rows. Je l'ai remplacé par de la magie Loc() et c'est aussi devenu beaucoup plus rapide. J'ai beaucoup aimé Loc() ces derniers temps.
Re : Devez-vous parcourir une table de données ou utiliser Recode, ou utiliser Get Rows Where pour modifier les valeurs dans une table de données@jthi ,
Votre code basé sur la matrice est arrivé comme le plus rapide.
0,299795 secondes........
Re : Devez-vous parcourir une table de données ou utiliser Recode, ou utiliser Get Rows Where pour modifier les valeurs dans une table de donnéesJ'ai fait quelques optimisations au travail sur certains anciens scripts que nous avons. Certaines des choses que j'ai dû changer étaient liées à l'emplacement des lignes. J'ai commencé à utiliser de plus en plus d'opérations matricielles (principalement Loc()) si j'ai besoin d'avoir du code JSL rapide.
Par exemple, vous pouvez modifier les lignes get où suivre (n'a pas testé s'il remplace les valeurs correctes ou toutes):
start=hptime();
for(i=5,i<= ncols(dt),i++,
column(dt,i)[Loc(ascolumn(dt,i) << get as matrix, 999)]=.;
if(mod(i,10)==0,show(i));
);
show(hptime()-start);
Il est également possible d'utiliser les propriétés de colonne Étiquettes de valeur et Codes de valeur manquants qui peuvent fonctionner dans certains cas. J'aime les utiliser lorsque cela est possible, car vous ne modifierez alors pas les données.