Si vous parcourez une table de données ou utilisez Recode, ou utilisez Get Rows Où modifier les valeurs dans une table de donnéesUne récente discussion communautaire script pour rechercher et remplacer une valeur numérique par une valeur manquante dans tout le tableau a eu une très belle entrée fournie par @SDF1 qui a été choisie 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 ma norme de secours "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 Get Rows Where() "très rapide" ait trouvé les lignes.
Une autre solution, que j'ai vue poussé recommandé par la communauté est la fonction Recode de JMP.
Donc:
Hypothèse : les fonctions JMP intégrées de Get Rows Where et Recode seront plus rapides que le bouclage 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 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 sur 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;
)
));
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)
Il s'agit de la valeur d'origine à modifier dans la discussion mentionnée ci-dessus.Le 999 doit être remplacé par manquant (.) par les scripts.
Méthode:
- Démarrez JMP 16.
- Lire le tableau de données sur la capacité des semi-conducteurs
- 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 du 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 obtenir les lignes où
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 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......
Mélange et nettoyage des donnéesIngénierie Qualité et ProcédésRe : Si vous parcourez une table de données ou utilisez Recode, ou utilisez Get Rows Où modifier les valeurs dans une table de données
@jthi a écrit:
Je suis peut-être allé un peu trop loin à ce sujet (mais j'ai au moins eu 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.
....
je viens d'utiliser @jthi Le scénario de :
https://community.jmp.com/t5/Discussions/Should-you-Loop-through-a-data-table-or-use-Recode-or-use-Get/mp/381766/highlight/true#M63232
reproduire les performances des différentes approches sur un système avec Jmp (16.2 & ) 17.1.
J'ai mis en évidence certaines approches (incl. lieu ) qui doivent aujourd'hui être à l'intérieur début/fin de la mise à jour des données *)
pour se débarrasser d'une augmentation importante du temps avec une fraction de pourcentage croissante de 999 s.
Les différences du facteur 2 pourraient être causées par les différents ordinateurs.
Qui a un système avec Jmp 15 et 17 - pour une comparaison directe ?
*) bien que le sous-ensemble ait été ouvert "invisible".

Re : Si vous parcourez une table de données ou utilisez Recode, ou utilisez Get Rows Où modifier les valeurs dans une table de données@hogi a écrit:
...
Si un utilisateur sait si le problème, un très bon marché la solution est d'ajouter un Éval()
bon marché concernant la complexité du code JSL par rapport à la pénalité de temps.
Mais pour les tables plus longues Évaluer(i) perd encore beaucoup contre Substitut(... ,NameExpr(As Column()))) .
Donc : toujours assez rapide (par rapport à la version originale
, mais pas aussi rapide que ce à quoi je m'attendais.
Pourquoi? Quelle est la différence fondamentale ?
Re : Si vous parcourez une table de données ou utilisez Recode, ou utilisez Get Rows Où modifier les valeurs dans une table de donnéesHm, il est peut-être possible de passer JSL en mode convivial (qui pré-évalue les variables si cela conduit à de meilleures performances) ?
souhait: obtenir des lignes où : problème de vitesse
Si un utilisateur est conscient du problème, un très bon marché la solution est d'ajouter un Éval() :
start = HP Time();<BR />for(i=5,i<= ncols(dt),i++,<BR />dt << get rows where( As Column( dt, Eval(i) ) == 999 );<BR />);<BR />Show( (HP Time() - start) / 1000000 );start = HP Time();
i = 5;
dt << get rows where( As Column( dt, Eval(i) ) == 999 );
Show( (HP Time() - start) / 1000000 );
Re : Si vous parcourez une table de données ou utilisez Recode, ou utilisez Get Rows Où modifier les valeurs dans une table de donnéesSalut @ron_horne , le problème vient du obtenir des lignes où , donc la vitesse devrait être/est indépendante de ces paramètres.
D'un autre côté, cela fait une énorme différence si l'index de colonne est explicite ou s'il s'agit d'une variable (même si la variable a une valeur assignée).
Ce n'était pas clair pour moi - comme j'ai trouvé cette conversation bien !
Je ne sais pas combien de codes JSL pourraient être plus rapides d'un facteur de ~ N (lignes du tableau de données)...
Le problème peut-il être résolu par ce qui se passe en arrière-plan de JSL ?
Ou y a-t-il quelque chose de fondamental dans l'architecture JSL qui spécifie qu'un tel comportement est prévu et ne peut pas être modifié ?
Si c'est le cas : un message d'avertissement pourrait aider les codeurs à ne pas se laisser piéger par ce problème...
Names Default To Here( 1 );
dt = Open( "$SAMPLE_DATA/Semiconductor Capability.jmp" );
For( k = 1, k <= 4, k++,
dt << Concatenate( dt, dt, dt, Append to first table )
);
nuke = Function( {},
For( k = 1, k <= 10, k++,
Column( dt, 5 )[Random Integer( 1, 1455 )] = 999
)
);
//dt << show window(0);
//dt<< Begin Data Update();
nuke();
start = HP Time();
i = 5;
dt << get rows where( As Column( dt, i ) == 999 );
Show( (HP Time() - start) / 1000000 );
nuke();
start = HP Time();
dt << get rows where( As Column( dt, 5 ) == 999 );
Show( (HP Time() - start) / 1000000 );
//dt << End Data Update();
Re : Si vous parcourez une table de données ou utilisez Recode, ou utilisez Get Rows Où modifier les valeurs dans une table de donnéesmerci @txnelson
pourriez-vous s'il vous plaît estimer l'effet de l'utilisation de tables invisibles et privées sur les différentes méthodes ? ils doivent constituer le moyen le plus rapide de modifier n'importe quel script pour améliorer les performances.
Re : Si vous parcourez une table de données ou utilisez Recode, ou utilisez Get Rows Où modifier les valeurs dans une table de donnéesWow, quelle merveilleuse astuce 
Je craignais que des problèmes avec Code de valeur manquant démarrera dès que vous essayerez :
Col Maximum(:NPN3)
[Comme nous le savons exclu les lignes doivent être exclu dans l'agrégation Col à la main (ou mieux : par Par groupe argument).]
Mais la surprise : Col. les agrégations respectent Codes de valeur manquants 
Pour être pointilleux : le Question PO du message original était assez implicite :
Et d'après les résultats, le Obtenir les lignes où doit être utilisé avec parcimonie.
Les commentaires et corrections sont les bienvenus......
traduit - avec une sensation de malaise dans mon ventre - par : "pourquoi diable?!?"
Grâce à @ErraticAttack pour résoudre l'énigme 
Re : Si vous parcourez une table de données ou utilisez Recode, ou utilisez Get Rows Où modifier les valeurs dans une table de donnéesCela a été une discussion tellement amusante et informative à suivre ! Des réponses brillantes. Je ne suis vraiment pas qualifié pour ajouter quoi que ce soit de valeur ici, je vais donc recourir à la « triche ».
La question OP concernait le remplacement des valeurs de 999 dans les données par des valeurs manquantes, car les 999 sont des codes de valeur manquants et nous ne voudrions pas qu'ils soient inclus dans les calculs. Ce n'est pas du tout dans l'esprit de cette ligne de questions très intéressante, mais ma contribution est qu'exploiter la propriété de colonne de code de valeur manquante est plus rapide que d'effectuer un remplacement sur les données et permet d'obtenir la même chose dans *la plupart* des objectifs 
start = HP Time();
For( i = 5, i <= N Cols( dt ), i++,
Column(dt,i) << Set Property( "Missing Value Codes", {999} )
);
Show( HP Time() - start);
~ 0,007 seconde sur mon mac m1.
Re : Si vous parcourez une table de données ou utilisez Recode, ou utilisez Get Rows Où modifier les valeurs dans une table de donnéesAh, je ne savais pas :
:nom() Agit comme Comme colonne() ?
Pas si clair d'après l'index des scripts 
Donc avec Remplaçant et Comme colonne() au lieu de Eval(Parse(Eval Insert(JSL Quote(name("^names[i]^") c'est:
start=hptime();
for(i=5,i<= ncols(dt),i++,
Eval(Substitute(Expr(column(dt,i)[dt<<GET rows="" where=""></GET>
Et après cette découverte surprenante Je suis presque surpris ici que le obtenir des lignes où est également rapide si la comparaison de nombres est remplacée par une comparaison de caractères 
start=hptime();
for(i=5,i<= ncols(dt),i++,
Eval(Substitute(Expr(column(dt,i)[dt<<GET rows="" where=""></GET>
Re : Si vous parcourez une table de données ou utilisez Recode, ou utilisez Get Rows Où modifier les valeurs dans une table de donnéesOui je comprends @brady_brady . On pourrait cependant faire ceci :
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éments.
Re : Si vous parcourez une table de données ou utilisez Recode, ou utilisez Get Rows Où modifier les valeurs dans une table de donnéesVous vous trompez un peu dans la comparaison : la fonctionnalité "Obtenir les lignes où" é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 il faudra faire un peu de marche pour le trouver sur chaque ligne.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 de procéder 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 3 000 fois plus rapidement, ce qui devrait le rendre comparable, voire meilleur, aux autres méthodes dont vous disposez.
Re : Si vous parcourez une table de données ou utilisez Recode, ou utilisez Get Rows Où modifier les valeurs dans une table de donnéesOui... l'obstacle est que même si les matrices sont accessibles avec un seul index (ce qui suppose une disposition en lignes principales), les tables de données ne le sont pas. Il n’existe donc 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 encore rapide, il le serait VRAIMENT si un accès à un seul index était disponible pour les tables.
Re : Si vous parcourez une table de données ou utilisez Recode, ou utilisez Get Rows Où modifier les valeurs dans une table de donnéesPour le ' surcharge dans la création d'une matrice' je soupçonne que abonnement à une table de données pourrait aider.@Craige_Hales je le saurais.
Re : Si vous parcourez une table de données ou utilisez Recode, ou utilisez Get Rows Où modifier les valeurs dans une table de donnéesVoici un moyen d'éviter les boucles, même si la création d'une matrice et sa réaffectation à la table entraînent une surcharge. 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 : Si vous parcourez une table de données ou utilisez Recode, ou utilisez Get Rows Où modifier les valeurs dans une table de donnéesJe suis peut-être allé un peu trop loin à ce sujet (mais j'ai au moins eu 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 ensembles de données (Semiconductor Capability.jmp et Probe.jmp) et modifié le nombre de valeurs manquantes dans les colonnes.
Loc() est le plus rapide à remplacer les valeurs (pas de surprise). 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 a plusieurs valeurs à remplacer.

Le plus rapide a fini par être loc_2d (je n'ai pas pris la peine de changer les noms de variables à partir de ceci : Utiliser Loc avec une matrice 2D ). Il faut encore faire une boucle pour définir les valeurs (du moins à ma connaissance), donc la raison pour laquelle cela est plus rapide que simplement Loc() est probablement qu'il sautera certaines colonnes. Vous trouverez ci-dessous 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 a dit (je n'ai même pas pris la peine de tester << obtenir des lignes sans cela, car ce serait très lent), mais j'ai testé le recodage avec et sans Column(). Je viens également de remarquer que j'ai raté une Column() lors du recodage, alors exécutez quelques tests rapides (non joints 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 quelques données de résultats ci-jointes. Le script comportera probablement des 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 testée du tout est d'utiliser << Début/Fin de la mise à jour des données.
Re : Si vous parcourez une table de données ou utilisez Recode, ou utilisez Get Rows Où modifier les valeurs dans une table de donnéesDiscussion intéressante! La notation matricielle du tableau 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, c'est presque 500 fois plus rapide que la boucle for (0,003 contre 1,5 seconde, ordinateur portable Mac JMP 15).
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".
Le bouclage sur les colonnes est toujours nécessaire car l'affectation directe pour l'ensemble de la matrice, que j'ai pensé un instant 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, après 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 : Si vous parcourez une table de données ou utilisez Recode, ou utilisez Get Rows Où modifier les valeurs dans une table de donnéesJ'aurais peut-être hésité à utiliser les 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). Utiliser des boucles est également bien 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 commencerai à chercher des moyens de la rendre plus rapide (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 une ligne, définir l'état de la ligne).
En plus de Loc(), l'utilisation de Summarize() semble être "beaucoup" plus rapide que mon option préférée de longue date pour obtenir des valeurs uniques dans la colonne (tableau associatif() << obtenir les clés). Lorsque le nombre de lignes augmente, le tableau associatif() << obtenir les clés 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 davantage Summarize() (je ne sais jamais quelle est la hauteur des tables de données).
Re : Si vous parcourez une table de données ou utilisez Recode, ou utilisez Get Rows Où modifier les valeurs dans une table de donnéesNous sautons donc AsColumn() avec (en allant plus avec une intuition et je n'ai pas testé que cela remplacerait 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 : Si vous parcourez une table de données ou utilisez Recode, ou utilisez Get Rows Où 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 d'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 originale, la boucle For est toujours la plus rapide mais les autres sont plus comparables.
Re : Si vous parcourez une table de données ou utilisez Recode, ou utilisez Get Rows Où modifier les valeurs dans une table de donnéesSalut @txnelson ,
Comparaison très intéressante, et pas non plus les résultats auxquels je m'attendais. Je me souviens quand j'ai commencé à apprendre la programmation à l'université et qu'on m'a également appris quelque chose de similaire : méfiez-vous des boucles car 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 très bon programmeur, j'ai tendance à me fier à 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 Get Rows Where s'est amélioré en réduisant le temps presque de moitié, c'est un changement substantiel.
Merci!,
DS
Re : Si vous parcourez une table de données ou utilisez Recode, ou utilisez Get Rows Où modifier les valeurs dans une table de donnéesPeut être @Craige_Hales pourrait nous apporter une solution encore plus rapide. J'ai eu l'idée d'utiliser les calculs matriciels (en particulier Loc()) à partir de ses publications/blog communautaires.
J'ai créé 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 les lignes << Sélectionner où et Supprimer ». Je l'ai remplacé par de la magie Loc() et c'est également devenu beaucoup plus rapide. J'aime beaucoup Loc() ces derniers temps.
Re : Si vous parcourez une table de données ou utilisez Recode, ou utilisez Get Rows Où modifier les valeurs dans une table de données@jthi ,
Votre code matriciel s’est révélé le plus rapide.
0,299795 secondes........
Re : Si vous parcourez une table de données ou utilisez Recode, ou utilisez Get Rows Où modifier les valeurs dans une table de donnéesJ'ai fait quelques optimisations au travail sur certains anciens scripts dont nous disposons. 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'un code JSL rapide.
Par exemple, vous pouvez modifier les lignes get par où suivre (je n'ai pas testé si elles remplacent 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 existe également la possibilité d'utiliser les propriétés de colonne, les étiquettes de valeur et les codes de valeur manquante, qui peuvent fonctionner dans certains cas. J'aime les utiliser lorsque cela est possible, car vous ne modifierez alors pas les données.