Sintassi accettabile incoerenteAllCols = dt << Get Column Names();
AllColNames = dt << Get Column Names(string);
for(i=3, i<=n items(AllCols), i++,
colcol = AllCols[i];
cname = AllColNames[i];
nnm1 = Col Number(colcol);
Fstr = "num(:\!""||cname||"\!"n)";
Fexpr = parse(Fstr);
newcol = dt << new column("Temp", numeric, formula(eval(evalexpr(Fexpr))));
nnm2 = Col Number(newcol);
if(nnm2 == nnm1,
newcol << delete formula();
dt << Delete Columns(colcol);
newcol << set name(cname), //else
dt << Delete Columns(newcol);
);
);Sto usando JMP18 su Windows. Quando scrivo codice che voglio usare spesso, mi piace creare un pulsante della barra degli strumenti per questo. Ma a volte trovo che il codice che è accettabile per essere eseguito da una finestra di script aperta non lo sia quando viene eseguito da un'icona della barra degli strumenti. Ecco un esempio, in cui sto eseguendo un ciclo tra le colonne e le sto cambiando da carattere a numerico se tutte le voci sono numeriche. Quando si esegue da una barra degli strumenti, l'obiezione è per la riga:
newcol = dt << new column("Temp", numeric, formula(eval(evalexpr(Fexpr))));
Questa sintassi è accettabile quando eseguo da una finestra di script!
Per quale motivo l'interprete JSL dovrebbe essere diverso in ogni situazione?
Quale sintassi sarebbe universalmente accettabile?
Per set di dati e colonne molto grandi, questo può essere lento. Esiste un modo migliore per farlo?
Automazione e scriptingFusione e pulizia dei datiFinestreRe: Sintassi accettabile incoerenteQuale messaggio di errore hai ricevuto? Penso che la finestra dello script abbia una tabella dati corrente, ma lo script della barra degli strumenti potrebbe non averla. Forse sto solo aggiungendo
currentDatatable(dt);
all'inizio della sceneggiatura sarebbe sufficiente.
Potresti
print(currentDatatable());
all'inizio dello script per scoprire cosa sa lo script della barra degli strumenti.
Re: Sintassi accettabile incoerenteLe barre degli strumenti potrebbero aggiungere nomi predefiniti qui (1) (che, secondo me, dovresti sempre avere) al tuo script

che può quindi potenzialmente mostrare alcuni errori che non sarebbero visibili se tutto fosse nell'ambito globale (ma di solito sono comunque errori). Costruisco le mie formule usando Eval(EvalExpr()) o Eval(Substitute())
Eval(EvalExpr(
newcol = dt << New Column("Temp", numeric, formula(Num(Expr(NameExpr(AsColumn(dt, i))))));
));
Per set di dati di grandi dimensioni potrebbe essere necessario prendere in considerazione il campionamento per qualcosa di simile. Esistono anche soluzioni non basate su formule che potrebbero essere più rapide, ma potrebbe dipendere anche dai dati.
Per qualche motivo non l'ho pubblicato nella comunità JMP (o almeno non sono riuscito a trovarlo), Colonne di caratteri in numeri (github.com) , puoi dare un'occhiata al codice per vedere come ho eseguito questa attività da https://github.com/jthi-jmp-tools/character-columns-to-numeric/blob/main/bin/bin/character_columns_to_numeric.jsl . Da allora ho rifatto questo script e se ricordo bene uso un metodo leggermente diverso al giorno d'oggi.
Re: Sintassi accettabile incoerentele icone della barra degli strumenti conoscono la tabella dati corrente():
Il problema è piuttosto quello inverso.
Si prega di definire "dt=current data table()" prima di
dt << Get Column Names();
Re: Sintassi accettabile incoerenteGrazie Craig.
Il messaggio di errore è:
Nome non risolto: 2PCS_X(um) alla riga 1 nell'accesso o nella valutazione di '2PCS_X(um)' , "2PCS_X(um)"n /*###*/
"2PCS_X(um)" è il nome della prima colonna da valutare.
Quindi l'espressione per la prima colonna dovrebbe essere:
newcol = dtTOSA << nuova colonna("Temp", numerico, formula(Num( :"2PCS_X(um)"n )));
Re: Sintassi accettabile incoerenteInoltre, nello script effettivo non uso dt come variabile. Uso una variabile molto più descrittiva, come dtTOSA. (vedi sopra) Ogni volta che voglio fare riferimento a una particolare tabella di dati, lo dichiaro sempre esplicitamente per evitare confusione, come ho fatto in questa affermazione. Non vedo come l'interprete possa pensare che mi riferisco a una tabella diversa.
Re: Sintassi accettabile incoerenteForse, ma il messaggio di errore suggerisce che JMP non sta cercando nel posto giusto "2PCS_X(um)"n. Non sono sicuro di quale contesto possa usare lo script della barra degli strumenti.
Re: Sintassi accettabile incoerenteCraig,
Ho seguito il tuo suggerimento di usare un'istruzione print e ho scoperto che non arrivavo mai alla riga che pensavo fosse il problema.
newcol = dt << new column("Temp", numeric, formula(eval(evalexpr(Fexpr))));Il problema era su questa linea.
nnm1 = Col Number(colcol);
Quindi l'ho cambiato in questo modo e ha funzionato.
nnm1 = dtTOSA << Col Number(colcol);
Ho scoperto che avevo anche bisogno di ricordare di nuovo a JMP qual era la tabella corrente più avanti nel codice e ho dovuto usare {} in Delete Columns prima che funzionasse. Per qualche motivo a JMP non importa se quelle parentesi graffe sono lì se eseguite da una finestra di script.
Il frammento di codice è ora questo. A proposito, questo è un frammento di un codice più lungo che crea anche questa e un'altra tabella.
AllCols = dtTOSA << Get Column Names();
AllColNames = dtTOSA << Get Column Names(string);
currentDatatable(dtTOSA);
for(i=3, i<=n items(AllCols), i++,
colcol = AllCols[i];
cname = AllColNames[i];
nnm1 = dtTOSA << Col Number(colcol);
Fstr = "num(:\!""||cname||"\!"n)";
Fexpr = parse(Fstr);
newcol = dtTOSA << new column("Temp", numeric, set formula(eval(evalexpr(Fexpr))));
nnm2 = dtTOSA << Col Number(newcol);
if(nnm2 == nnm1,
currentDatatable(dtTOSA);
newcol << delete formula();
dtTOSA << Delete Columns({colcol});
wait(0);
newcol << set name(cname), //else
dtTOSA << Delete Columns({newcol});
);
);Tuttavia, ora c'è un problema diverso. Anche se elimino la vecchia colonna prima di rinominare la nuova colonna, JMP mette un 2 indesiderato dopo il nome della colonna! Di nuovo, non lo fa eseguendo da una finestra di script. Ho provato a includere un'istruzione wait(0) ma non ha funzionato.
Come si può risolvere questo problema?
Re: Sintassi accettabile incoerenteIn realtà non esegue l'operazione di eliminazione della colonna.
dtTOSA << Delete Columns({colcol});Quindi vengono mantenute la colonna dei caratteri originale e la nuova colonna numerica.
Re: Sintassi accettabile incoerenteStranamente, se si passa alla condizione "else", l'istruzione delete funziona correttamente.
Re: Sintassi accettabile incoerenteSebbene la colonna venga indicata in 2 modi diversi (uno come nome della stringa di testo, l'altro come tipo di colonna), alla fine ha funzionato. Scrivo codice in jsl da 18 anni e trovo ancora incoerente e confusionario il riferimento alle colonne come variabili in jsl.
AllCols = dtTOSA << Get Column Names();
AllColNames = dtTOSA << Get Column Names(string);
currentDatatable(dtTOSA);
for(i=3, i<=n items(AllCols), i++,
colcol = AllCols[i];
cname = AllColNames[i];
nnm1 = dtTOSA << Col Number(colcol);
Fstr = "num(:\!""||cname||"\!"n)";
Fexpr = parse(Fstr);
newcol = dtTOSA << new column("Temp", numeric, set formula(eval(evalexpr(Fexpr))));
nnm2 = dtTOSA << Col Number(newcol);
if(nnm2 == nnm1,
currentDatatable(dtTOSA);
newcol << delete formula();
dtTOSA << Delete Columns(eval(cname));
currentDatatable(dtTOSA);
newcol << set name(cname), //else
dtTOSA << Delete Columns({newcol});
);
);Re: Sintassi accettabile incoerenteUtilizzo sempre le stringhe come base per le colonne e poi costruisco i miei riferimenti da esse come mi servono. Ho scoperto che questo è il modo più flessibile di gestire diversi tipi di riferimenti.
Ad esempio, se prendo il tuo script e faccio alcune ipotesi sul suo scopo (partendo dalla terza colonna, le esamino tutte, creo una nuova colonna formula in cui modifico i valori in numerici, confronto i valori mancanti ed eseguo azioni in base al risultato), lo scriverei così in JMP18:
Names Default To Here(1);
dtTOSA = New Table("Untitled",
Add Rows(4),
Compress File When Saved(1),
New Column("Column 1",
Numeric,
"Continuous",
Format("Best", 12),
Set Values([1, 2, 3, 4])
),
New Column("Column 2", Character, "Nominal", Set Values({"A", "1", "2", "3"})),
New Column("Column 3", Character, "Nominal", Set Values({"1", "2", "3", "4"})),
New Column("Column 4", Character, "Nominal", Set Values({"A", "B", "C", "D"}))
);
wait(1); // demo purposes
cols = dtTOSA << Get Column Names("String");
// Remove From(cols, 1, 2); // skipping this for demo purposes
For Each({colname}, cols,
// colname = cols[3]; // for testing
Eval(EvalExpr(
newcol = dtTOSA << New Column("Temp", numeric, Formula(
Num(Expr(Name Expr(As Column(dtTOSA, colname))))
));
));
newcol << Run Formulas;
newcol << delete formula();
orig_vals = Col Number(Column(dtTOSA, colname));
new_vals = Col Number(Column(dtTOSA, (newcol << get name)));
If(orig_vals == new_vals,
dtTOSA << Delete Columns(colname);
newcol << set name(colname);
,
dtTOSA << Delete Columns(newcol)
);
);
E se dovessi farlo dall'inizio ma usando comunque un'idea un po' simile alla tua, farei (almeno in questo momento) qualcosa del genere
Names Default To Here(1);
dt = New Table("Untitled",
Add Rows(4),
Compress File When Saved(1),
New Column("Column 1",
Numeric,
"Continuous",
Format("Best", 12),
Set Values([1, 2, 3, 4])
),
New Column("Column 2", Character, "Nominal", Set Values({"A", "1", "2", "3"})),
New Column("Column 3", Character, "Nominal", Set Values({"1", "2", "3", "4"})),
New Column("Column 4", Character, "Nominal", Set Values({"A", "B", "C", "D"}))
);
wait(1); // demo purposes
cols = dt << Get Column Names("Character", "String"); // We only need to check character columns
For Each({colname}, cols,
dt_summary = dt << Summary(
Group(Eval(colname)),
Freq("None"),
Weight("None"),
Link to original data table(0),
output table name("Summar of " || colname),
Private
);
orig_vals = Col Number(Column(dt_summary, 1));
Column(dt_summary, 1) << Set Data Type("Numeric");
new_vals = Col Number(Column(dt_summary, 1));
Close(dt_summary, no save);
If(orig_vals == new_vals, // we can change
Column(dt, colname) << Set Data Type("Numeric");
Column(dt, colname) << Set Modeling Type("Continuous");
);
);
Di solito li faccio utilizzando Transform Each e Num, ma a volte potrebbe essere sufficiente lasciare che JMP esegua le conversioni (ci sono casi in cui potresti non volerlo fare, vedi Num() dall'indice di scripting e come si comporta con Num("5%") per esempio. << Restrict evita questo comportamento)
Re: Sintassi accettabile incoerenteJarmo,
Grazie per i pensieri. A volte ho migliaia di colonne e decine di migliaia di righe, e la velocità può essere un problema. Il modo in cui lo faccio ora ha effettivamente ridotto il tempo a circa 1/3 di come lo facevo prima, ma possono comunque essere molti minuti. Potrei sperimentare il tuo metodo qualche volta, ma mi chiedo se pensi che creare una tabella di riepilogo ogni volta sarebbe più lento o più veloce.
Per quanto riguarda la gestione dei riferimenti alle colonne, ho provato a usare Column(dt, colname) ma non sempre funziona. Ma nel tuo script usi anche As Column(). Di norma, As Column() viene usato per le formule?
Re: Sintassi accettabile incoerente
@pcarroll1 ha scritto:
Di norma, As Column() viene utilizzato per le formule?
sì, sia come column(columnname) sia come :column(dt, columnname)[]
Re: Sintassi accettabile incoerentePer la prima "domanda": se provi a ottimizzare la velocità per casi d'uso specifici, devi solo provare quale funziona meglio, poiché ci sono molti metodi diversi e il più veloce potrebbe dipendere molto dai tuoi dati. In questo caso ho usato summary table (o Summarize) per evitare di controllare i valori replicati, ma come ho detto, dipende dai tuoi dati, dalle tue versioni JMP e forse da altre cose...
Per i riferimenti di colonna; la mia regola è: usa sempre il nome della colonna e il riferimento della tabella come punto di partenza e costruisci il riferimento finale da questi . Ciò che considero un riferimento dipende da dove e come lo sto usando. Ad esempio, se sto costruendo una formula, voglio che la mia formula faccia riferimento alle colonne come :mycol (e non AsColumn(mycol) o Column(mycol)[], che di solito vanno bene). E posso ottenere il formato :mycol usando lo stile abbastanza lungo di NameExpr(AsColumn(dt,colname)) con valutazione dell'espressione. In genere AsColumn(dt, colname) (OR dt:colname) è uguale a Column(dt, colname)[Row()] (o Column(dt, colname)[]), che otterrà il valore della riga corrente (nella mia mente non è sempre così. Ad esempio, nelle piattaforme considero :mycol come un riferimento anziché un valore specifico su una riga...).
Ad esempio, questo restituirà un valore mancante poiché mycol verrà valutato immediatamente e il valore iniziale di Row() è 0 in JMP (non è un problema all'interno di una formula poiché Row() verrà aggiornato continuamente)
Names Default To Here(1);
dt = open("$SAMPLE_DATA/Big Class.jmp");
colname = "age";
mycol = NameExpr(AsColumn(dt,colname));
show(mycol)
Questa è la differenza più difficile da capire con i riferimenti di colonna, come Column() (valore della riga corrente) vs Column() (riferimento a una colonna) in JMP. È molto difficile capire come funzionano... L'ho imparato fondamentalmente fallendo molte, molte volte. E ora ho una buona idea di quale usare inizialmente quando si costruisce un'espressione (potrei ancora sbagliarmi di tanto in tanto). Questo argomento è qualcosa su cui vorrei scrivere un post di blog a un certo punto, poiché le mie opinioni (attualmente) potrebbero differire da quelle degli altri e mi considero un bravo scripter JMP senza essere uno sviluppatore JMP (JSL).
Una cosa da notare qui : non sono uno sviluppatore JMP e non mi interessa nemmeno come gli sviluppatori pensano che funzioni JSL (a volte non sono d'accordo con gli sviluppatori). Tutte le mie risposte si basano sempre sulle mie esperienze con le versioni JMP che ho usato.
Mi dispiace molto se questo post è un po' confuso (potrei aver bevuto qualche birra questa sera):) ). Se desideri avere maggiori spiegazioni sulle mie opinioni su questi argomenti, puoi postare su questo argomento (ma taggami con @ così lo noterò più facilmente). Puoi anche inviarmi un messaggio privato se necessario.