Dovresti scorrere una tabella di dati o utilizzare Ricodifica o utilizzare Ottieni righe dove modificare i valori in una tabella di datiUna recente discussione della comunità script per trovare e sostituire un valore numerico con un valore mancante nell'intera tabella ha avuto un ingresso molto carino fornito da @SDF1 quella è stata scelta come soluzione
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] = .
)
)
);
Quando ho visto la soluzione, mi è venuto in mente un vecchio insegnamento che diceva approssimativamente: "Non scorrere una tabella di dati.Il ciclo è lento. Utilizza le funzionalità integrate di JMP."
Quindi ho iniziato a modificare @SDF1 codice.
Ho stabilito che il mio standard di fallback "Get Rows Where()" sarebbe stato più veloce.Dopotutto, consente di impostare tutte le righe trovate con un'istruzione di assegnazione, dopo che il metodo "molto veloce" Get Rows Where() ha trovato le righe.
Un'altra soluzione, che ho visto spinto consigliata dalla comunità è la funzione Recode in JMP.
COSÌ:
Ipotesi: le funzioni JMP integrate di Ottieni righe dove e Ricodifica saranno più veloci del looping down e attraverso un'intera tabella di dati.
Ho selezionato la tabella dati Semiconductor Capability come tabella di buone dimensioni, 132 colonne e 1455 righe
Ho scritto un piccolo script che imposta 10 valori casuali in ciascuna colonna su 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)
Questo è il valore originale da modificare nella Discussione menzionata sopra.Il 999 deve essere cambiato in mancante (.) dagli script.
Metodo:
- Avvio JMP 16.
- Leggi la tabella dati sulla capacità dei semiconduttori
- Esegui lo script per impostare i 999 valori in ciascuna colonna
- Ripetere i 2 passaggi precedenti per ciascuno dei 3 metodi e ricodificare il tempo fino al millisecondo.
- Ripetere i passaggi precedenti per JMP15
Ecco lo script per la ricodifica
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 codice per Ottieni righe dove
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>
Ed ecco i risultati sorprendenti

Il Ciclo For vince a mani basse.E dai risultati, Ottieni righe dove dovrebbe essere usato con parsimonia.
Sono graditi commenti e correzioni....
Combinazione e pulizia dei datiIngegneria della qualità e dei processiRi: Dovresti scorrere una tabella di dati o utilizzare Ricodifica o utilizzare Ottieni righe dove modificare i valori in una tabella di dati
@jthi ha scritto:
Forse sono andato un po' troppo in profondità su questo argomento (ma almeno ho avuto un'idea di come eseguire questo tipo di test in seguito, se necessario).
Ho scritto una sorta di script di test con espressioni diverse per testare diversi modi di sostituire il 999 con quello mancante.
....
Ho appena usato @jthi la sceneggiatura:
https://community.jmp.com/t5/Discussions/Should-you-Loop-through-a-data-table-or-use-Recode-or-use-Get/mp/381766/highlight/true#M63232
riprodurre le prestazioni dei diversi approcci su un sistema con Jmp (16.2 & ) 17.1.
Ho evidenziato alcuni approcci (incl. loc ) che oggigiorno devono essere all'interno inizio/fine aggiornamento dati *)
per eliminare un grave aumento del tempo con una frazione percentuale crescente di 999 secondi.
Le differenze del fattore 2 potrebbero essere causate dai diversi computer.
Chi ha un sistema con Jmp 15 e 17 - per un confronto diretto?
*) nonostante il sottoinsieme sia stato aperto "invisibile".

Ri: Dovresti scorrere una tabella di dati o utilizzare Ricodifica o utilizzare Ottieni righe dove modificare i valori in una tabella di dati@hogi ha scritto:
...
Se un utente è a conoscenza del problema, un molto economico la soluzione è aggiungere un Valutazione()
economico per quanto riguarda la complessità del codice JSL rispetto alla penalità di tempo.
Ma per tavoli più lunghi Valutazione(i) perde di nuovo in modo significativo Sostituisci(... ,EsprNome(Come Colonna()))) .
Quindi: ancora abbastanza veloce (rispetto alla versione originale
, ma non così veloce come mi sarei aspettato.
Perché? Qual è la differenza fondamentale?
Ri: Dovresti scorrere una tabella di dati o utilizzare Ricodifica o utilizzare Ottieni righe dove modificare i valori in una tabella di datiHm, forse è possibile passare JSL a una modalità user-friendly (che pre-valuta le variabili se ciò porta a prestazioni migliori)?
desiderio: ottieni righe dove: problema di velocità
Se un utente è a conoscenza del problema, molto economico la soluzione è aggiungere un Valutazione():
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 );
Ri: Dovresti scorrere una tabella di dati o utilizzare Ricodifica o utilizzare Ottieni righe dove modificare i valori in una tabella di datiCIAO @ron_horne , il problema è con ottieni righe dove , quindi la velocità dovrebbe essere/è indipendente da tali impostazioni.
D'altra parte, fa un'enorme differenza se l'indice della colonna è esplicito o se è una variabile (anche se alla variabile è assegnato un valore).
Questo non mi era chiaro: quanto è stata bella questa conversazione!
Non so quanti codici jsl disponibili potrebbero essere più veloci di un fattore ~ N (righe della tabella dati) ...
Il problema può essere risolto da ciò che accade sullo sfondo di JSL?
Oppure c'è qualcosa di fondamentale nell'architettura JSL che specifica che tale comportamento è previsto e non può essere modificato?
Se è così: un messaggio di avviso potrebbe aiutare i programmatori a non farsi intrappolare da questo problema...
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();
Ri: Dovresti scorrere una tabella di dati o utilizzare Ricodifica o utilizzare Ottieni righe dove modificare i valori in una tabella di datiGrazie @txnelson
potresti stimare l'effetto dell'utilizzo di tabelle invisibili e private sui diversi metodi? devono essere il modo più veloce per modificare qualsiasi script per aumentare le prestazioni.
Ri: Dovresti scorrere una tabella di dati o utilizzare Ricodifica o utilizzare Ottieni righe dove modificare i valori in una tabella di datiWow, che trucco meraviglioso 
Temevo che i problemi con Codice valore mancante inizierà non appena proverai:
Col Maximum(:NPN3)
[Come lo sappiamo escluso le righe devono essere escluso in Col aggregazione a mano (o meglio: a cura di Raggruppa per discussione).]
Ma la sorpresa: Col rispetto delle aggregazioni Codici valore mancanti 
Per essere pignoli: il Domanda OP del post originale era abbastanza implicito:
E dai risultati, il Ottieni righe dove dovrebbe essere usato con parsimonia.
Sono graditi commenti e correzioni....
tradotto - con una sensazione di disagio allo stomaco - come: "perchè mai?!?"
Grazie a @ErraticAttack per aver risolto il puzzle 
Ri: Dovresti scorrere una tabella di dati o utilizzare Ricodifica o utilizzare Ottieni righe dove modificare i valori in una tabella di datiÈ stata una discussione così divertente e istruttiva da seguire! Risposte brillanti. Non sono davvero qualificato per aggiungere nulla di valore qui, quindi ricorrerò all'"imbroglio".
La domanda del PO riguardava la sostituzione dei valori di 999 nei dati con quelli mancanti perché 999 mancano codici di valore e non vorremmo che fossero inclusi nei calcoli. Non è affatto nello spirito di questa linea di domande molto interessante, ma il mio contributo è che sfruttare la proprietà della colonna del codice del valore mancante è più veloce che eseguire qualsiasi sostituzione sui dati e ottiene la stessa cosa per *la maggior parte* degli scopi 
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 secondi sul mio mac m1.
Ri: Dovresti scorrere una tabella di dati o utilizzare Ricodifica o utilizzare Ottieni righe dove modificare i valori in una tabella di datiAh, non sapevo:
:nome() agisce come Come colonna() ?
Non così chiaro dall'indice degli script 
Quindi, con Sostituire E Come colonna() invece di Valutazione(Analizza(Inserisci valutazione(Citazione JSL(nome("^nomi[i]^") è:
start=hptime();
for(i=5,i<= ncols(dt),i++,
Eval(Substitute(Expr(column(dt,i)[dt<<GET rows="" where=""></GET>
E dopo questa scoperta sorprendente Sono quasi sorpreso qui che il ottieni righe dove è anche veloce se il confronto dei numeri viene sostituito da un confronto dei caratteri 
start=hptime();
for(i=5,i<= ncols(dt),i++,
Eval(Substitute(Expr(column(dt,i)[dt<<GET rows="" where=""></GET>
Ri: Dovresti scorrere una tabella di dati o utilizzare Ricodifica o utilizzare Ottieni righe dove modificare i valori in una tabella di datiSi, capisco @brady_brady . Si potrebbe fare questo, tuttavia:
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];
Ma dubito fortemente che questo sarebbe più veloce di quello che già hai, anche a causa della moltiplicazione saggia degli elementi.
Ri: Dovresti scorrere una tabella di dati o utilizzare Ricodifica o utilizzare Ottieni righe dove per modificare i valori in una tabella di datiQui hai sbagliato un po' il confronto: la funzionalità "Ottieni righe dove" valuterà la sua espressione su ogni riga, quindi devi ridurre al minimo qualsiasi ricerca superflua prima di eseguire il ciclo - in particolare, il tuo iteratore è esterno e dovremo camminare un po' per trovarlo su ogni riga.Inoltre, valuterà la funzione AsColumn su ogni riga, cercando ogni volta sia "dt" che "i".Questo è _molto_ costoso.Il modo corretto per farlo sarebbe il seguente:
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>
Noterai che questo viene eseguito nell'ordine di 3000 volte più velocemente, il che dovrebbe renderlo paragonabile se non migliore agli altri metodi che hai
Ri: Dovresti scorrere una tabella di dati o utilizzare Ricodifica o utilizzare Ottieni righe dove modificare i valori in una tabella di datiSì... l'ostacolo è che mentre è possibile accedere alle matrici con un singolo indice (che presuppone il layout della riga principale), le tabelle di dati non possono. Pertanto, purtroppo, non esiste alcun analogo a
m[loc(m, 9999)] = .
dove m è una matrice pxq, nel mondo delle tabelle dati... da qui le righe che precedono e seguono questa nel codice sopra. Sebbene il codice sia ancora veloce, sarebbe VERAMENTE veloce se fosse disponibile l'accesso a indice singolo per le tabelle.
Ri: Dovresti scorrere una tabella di dati o utilizzare Ricodifica o utilizzare Ottieni righe dove modificare i valori in una tabella di datiPer il ' sovraccarico nella creazione di una matrice", lo sospetto abbonamento alla tabella dati potrebbe aiutare.@Craige_Hales lo saprebbe.
Ri: Dovresti scorrere una tabella di dati o utilizzare Ricodifica o utilizzare Ottieni righe dove modificare i valori in una tabella di datiEcco un modo per evitare il looping, anche se c'è un sovraccarico nella creazione di una matrice e nella successiva assegnazione alla tabella. Questo approccio funziona rapidamente su questi dati, più o meno come loc_2d, ma man mano che la tabella diventa enorme, mi aspetterei che le sue prestazioni relative ne risentano, ma non l'ho verificato.
expr_mat = expr(
m = dt[0, 5 :: N Col( dt )];
m[Loc( m, 999 )] = .;
dt[0, 5 :: N Col( dt )] = m;
);
Ri: Dovresti scorrere una tabella di dati o utilizzare Ricodifica o utilizzare Ottieni righe dove modificare i valori in una tabella di datiForse sono andato un po' troppo in profondità su questo argomento (ma almeno ho avuto un'idea di come eseguire questo tipo di test in seguito, se necessario).
Ho scritto una sorta di script di test con espressioni diverse per testare diversi modi di sostituire il 999 con quello mancante. Ho testato con due set di dati (Semiconductor Capability.jmp e Probe.jmp) e modificando la quantità di valori mancanti nelle colonne.
Loc() è il più veloce nel sostituire i valori (nessuna sorpresa in questo). Missing Value Codes() è "sempre" veloce ma ha casi d'uso un po' più limitati rispetto a Loc() ed è un po' più complicato da usare se ci sono più valori da sostituire.

Il più veloce è diventato loc_2d (non si è preoccupato di cambiare i nomi delle variabili da questo: Utilizzo di Loc con una matrice 2D ). Devo ancora eseguire il ciclo per impostare i valori (almeno per quanto ne so), quindi molto probabilmente il motivo per cui è più veloce del semplice Loc() è che salterà alcune colonne. Di seguito sono riportati expr_loc2d e 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]] = .;
);
);
Inoltre sembra essere importante utilizzare AsColumn(Column(dt, i)) come @Ryan_Gilmore ha detto (non mi sono nemmeno preso la briga di testare << get rows where senza di essa, perché sarà molto lento), ma ho testato la ricodifica con e senza Column(). Inoltre ho appena notato che ho perso una Column() dalla ricodifica, quindi esegui un paio di test rapidi (non allegati nei dati o nello script):
Differenza abbastanza grande tra questi tre:
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)))
)
);
);

Lo script del test e alcuni dati sui risultati allegati. Molto probabilmente lo script avrà degli errori qua e là. Dovrebbe essere abbastanza semplice aggiungere nuove espressioni se qualcuno lo desidera. Una cosa che non ho testato affatto è l'utilizzo di << Inizio/Fine aggiornamento dati.
Ri: Dovresti scorrere una tabella di dati o utilizzare Ricodifica o utilizzare Ottieni righe dove modificare i valori in una tabella di datiDiscussione interessante! La notazione della matrice della tabella dati, che penso sia apparsa per la prima volta in JMP 13, è abbastanza efficiente per l'assegnazione.
Per questo particolare problema è quasi 500 volte più veloce del ciclo for (0,003 contro 1,5 secondi, laptop 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);
Il try() è necessario per evitare un errore per le colonne senza "999".
Il ciclo sulle colonne è ancora necessario perché l'assegnazione diretta per l'intera matrice, che per un momento ho pensato sarebbe stata ancora più veloce, non funziona. Errore: "Più indici richiedono singoli indici a ogni livello interno nell'accesso o nella valutazione...", che a pensarci bene ha senso.
// 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 )] = .;
Ri: Dovresti scorrere una tabella di dati o utilizzare Ricodifica o utilizzare Ottieni righe dove modificare i valori in una tabella di datiAd un certo punto avrei potuto essere diffidente nell'usare i loop, ma dopo aver seguito un corso sugli algoritmi (che non ho mai finito...) un po' meno. Devi solo essere efficiente con loro, molto spesso fai troppi loop (complessità temporale e notazione Big O). Inoltre usare i loop è molto più semplice che cercare di risolvere problemi con la matematica pura.
Con JMP di solito scelgo semplicemente le opzioni sicure che ho imparato ad un certo punto, poiché la velocità raramente è un problema. Ma quando il tempo diventa un problema (l'apertura di un'applicazione ha richiesto più di 6 minuti al lavoro) inizierò a cercare modi per renderla più veloce (sono riuscito a farla avviare in meno di 2 minuti). In questo caso sono riuscito a renderlo più veloce utilizzando Loc() con diverse combinazioni di altre funzioni (elimina riga, imposta stato riga).
Oltre a Loc(), l'utilizzo di Summarize() sembra essere "molto" più veloce rispetto alla mia opzione preferita da sempre per ottenere valori univoci nella colonna (array associativo() << ottieni chiavi). Quando il conteggio delle righe aumenta, l'array associativo() << get keys diventa sempre più lento e Summarize() non sembra soffrire dello stesso problema, almeno non così velocemente. Quindi ho provato a iniziare a utilizzare di più Summarize() (non so mai quanto siano alti i datatable).
Ri: Dovresti scorrere una tabella di dati o utilizzare Ricodifica o utilizzare Ottieni righe dove modificare i valori in una tabella di datiQuindi saltiamo AsColumn() con (andando più con un'intuizione e non ho testato che questo sostituirà tutti i valori corretti):
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);
Ri: Dovresti scorrere una tabella di dati o utilizzare Ricodifica o utilizzare Ottieni righe dove modificare i valori in una tabella di datiSono d'accordo che Loc() è il metodo migliore.
Per me, il rallentamento sembra verificarsi a causa di As Column. Vedo una differenza significativa tra,
start = HP Time();
For( i = 5, i <= N Cols( dt ), i++,
As Column( column(dt, i) );
);
Show( HP Time() - start );
E
start = HP Time();
For( i = 5, i <= N Cols( dt ), i++,
As Column( dt, i );
);
Show( HP Time() - start );
Modifica della sintassi della colonna As nel codice da @txnelson domanda originale, il ciclo For è ancora il più veloce ma gli altri sono più paragonabili.
Ri: Dovresti scorrere una tabella di dati o utilizzare Ricodifica o utilizzare Ottieni righe dove modificare i valori in una tabella di datiCIAO @txnelson ,
Confronto molto interessante, e nemmeno i risultati che mi sarei aspettato. Ricordo quando ho iniziato a studiare programmazione al college e mi è stato anche insegnato qualcosa di simile: fare attenzione ai loop perché sono lenti.
Di sicuro, con alcuni calcoli e in alcuni programmi, ho riscontrato che i cicli For sono molto lenti, più lenti delle alternative. Non essendo un programmatore molto bravo, però, tendo a ripiegare su ciò che so fare, che non è sempre veloce o elegante. A volte per me, la logica di un ciclo mi permette di scrivere un codice funzionante per fare quello che voglio che faccia, il che mi permette di passare ai passaggi successivi.
Trovo interessante che le opzioni For Loop e Recode siano leggermente più lente per JMP 16 rispetto a 15, ma Get Rows Where è migliorata riducendo il tempo quasi della metà, si tratta di un cambiamento sostanziale.
Grazie!,
DS
Ri: Dovresti scorrere una tabella di dati o utilizzare Ricodifica o utilizzare Ottieni righe dove modificare i valori in una tabella di datiForse @Craige_Hales potrebbe darci una soluzione ancora più rapida. Ho avuto l'idea di utilizzare i calcoli Matrix (in particolare Loc()) dai post/blog della sua community.
Ho reso più veloce il lavoro di uno strumento che apre il set di dati e quindi elimina i vecchi dati (in base alla selezione dell'utente). Originariamente utilizzava << Seleziona dove ed Elimina righe. L'ho sostituito con un po' di magia Loc() ed è diventato anche molto più veloce. Mi è piaciuto molto Loc() ultimamente.
Ri: Dovresti scorrere una tabella di dati o utilizzare Ricodifica o utilizzare Ottieni righe dove modificare i valori in una tabella di dati@jthi ,
Il tuo codice basato su matrice è risultato il più veloce.
0,299795 secondi........
Ri: Dovresti scorrere una tabella di dati o utilizzare Ricodifica o utilizzare Ottieni righe dove modificare i valori in una tabella di datiHo ottimizzato il lavoro su alcuni vecchi script che abbiamo. Alcune delle cose che dovevo cambiare riguardavano l'ottenimento delle righe dove. Ho iniziato a utilizzare sempre più operazioni su matrici (principalmente Loc()) se ho bisogno di avere un codice JSL veloce.
Ad esempio, potresti modificare get rows where in seguito (non ho verificato se sostituisce i valori corretti o tutti):
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);
Esiste anche la possibilità di utilizzare le proprietà della colonna Etichette valore e Codici valore mancanti che potrebbero funzionare in alcuni casi. Mi piace usarli quando possibile, perché in questo modo non modificherai i dati.