SceneggiaturaBuongiorno a tutti,
Ho un database in cui vorrei creare una nuova colonna da una formula o uno script.
Ecco una panoramica del database:

- la colonna T rappresenta le operazioni da eseguire
- la colonna X_3 rappresenta una caratteristica
- La colonna op rappresenta gli oggetti
L'obiettivo è creare una colonna "assessment" in cui aggiungiamo "ko" e il numero corrispondente a "op" quando la somma dei difetti è diversa da una riga all'altra per lo stesso "obj" (qui valore pari a AT ).
Ultima particolarità, la colonna dello stato patrimoniale deve essere compilata per il numero di operazioni “3061”.
Questa è una panoramica del database, la colonna obj è "A" ma nel database questa colonna cambia valore.
Grazie per le tue risposte !
Automazione e scriptingRe: SceneggiaturaPerché la riga 6 op 180 non viene aggiunta all'ultima colonna? 0 differisce da 1.
(Dovresti usare titoli di post più informativi di una sola parola)
Re: SceneggiaturaPerché la somma è uguale a 0 per la riga 6. Infatti è piuttosto quando la somma dei difetti è diversa dal valore 0 allora si riporta il valore di “op” nella colonna “balance”.
Re: SceneggiaturaQuindi è necessario verificare se i valori differiscono tra due righe? O sei solo interessato alle righe che hanno :op diverso da 0?
Re: SceneggiaturaPenso che possiamo essere interessati solo alle righe in cui il valore di :sum default è diverso da 0. Perché quando il valore di: default sum è 0 allora non si ottiene nulla nella colonna del bilancio.
Re: SceneggiaturaGrazie per la risposta !
Confrontando i due metodi, mi sembra che il secondo sia preferibile per il mio caso.
L'ho adattato al mio database, le colonne hanno gli stessi formati su entrambi i database e ottengo questo tipo di errore:

Re: SceneggiaturaPoiché il messaggio di errore è in francese, non riesco a capirlo. Potrebbe essere che l'errore alla fine ti dica che non hai la colonna bilan3. (Sta dicendo che c'è un pedice non valido ma ciò è causato dalla mancanza della colonna bilan3. Questo perché dt[input_loc, "bilan3"] restituirà [](1, 0) se non si dispone della colonna e quindi si tenta di impostare alcuni valori su di essa).
Re: SceneggiaturaQuesto perché la variabile "input_loc" è vuota. Quindi per l'assegnazione di nuove celle nella colonna "balance sheet3" è impossibile.
Re: SceneggiaturaIl valore "3061" (carattere, non numero) non viene trovato dalla colonna "T". Anche il controllo che ho implementato per gestire input_loc vuoto non è corretto, dovresti modificarlo in <1 invece di <0.
Re: SceneggiaturaQuesto script fa entrambe le cose e funziona con l'esempio fornito. Ti suggerisco caldamente di provare a capire cosa fa e chiedere se ci sono alcune parti che non ti piacciono.
Names Default To Here(1);
dt = Open("$DOWNLOADS/AAA.jmp");
dt << New Column("bilan2", Character, Nominal);
dt << New Column("bilan3", Character, Nominal);
Summarize(dt, unique_objects = by(:obj));
m_obs = dt[0, "obj"];
/* comparing rows */
For Each({unique_object}, unique_objects,
object_rows = Loc(m_obs, unique_object);
values = dt[object_rows, "SOMME DEFAUT"];
values1 = Remove(values, 1);
values2 = Remove(values, N Items(values));
differences = [0] |/ values1 - values2;
ops = dt[object_rows[Loc(differences != 0)], "op"];
result_str = "ko - " || Concat Items(ops, " / ko - ");
input_loc = Loc(dt[object_rows, "T"], "3061");
If(N Items(input_loc) < 0,
continue();
,
dt[input_loc, "bilan2"] = Repeat({result_str}, N Items(input_loc));
);
);
/* zeros */
For Each({unique_object}, unique_objects,
object_rows = Loc(m_obs, unique_object);
values = dt[object_rows, "SOMME DEFAUT"];
ops = dt[object_rows[Loc(values)], "op"];
result_str = "ko - " || Concat Items(ops, " / ko - ");
input_loc = Loc(dt[object_rows, "T"], "3061");
If(N Items(input_loc) < 1,
continue();
,
dt[input_loc, "bilan3"] = Repeat({result_str}, N Items(input_loc));
);
);

Edit: Modificato if istruzione If(N elementi(input_loc) ... da < 0 a < 1
Re: SceneggiaturaNel database i valori della colonna :T non sono necessariamente disposti in ordine crescente per lo stesso tipo di oggetto. Questa è un'altra colonna :OPE che ha i suoi elementi disposti in ordine crescente. Le colonne :T e :OPE sono strettamente correlate, sono uniche per ogni tipo di oggetto.
Gli :OPE inferiori ai valori :OPE corrispondenti al valore 3061, nella colonna dello stato patrimoniale non va messo nulla.
Quando i valori di :OPE sono maggiori dei valori :OPE corrispondenti al valore 3061 allora viene eseguita l'operazione già descritta nei primi messaggi.
Guarda il fumetto qui sotto come esempio:

Grazie per le tue risposte !!
Re: SceneggiaturaAggiungi controlli aggiuntivi per i valori :OPE. Se il valore :OPE per la riga è inferiore al valore :OPE per "3061", ignoralo. Potrebbe essere un po' difficile tenere traccia di indici e righe diversi, ma l'aggiunta di stampe di debug aiuta in questo. Inoltre, se ci può essere più di un "3061" per un oggetto, il calcolo diventa più complicato.
Re: SceneggiaturaSì, capisco cosa intendi!
Immagino qualcosa che assomigli a questo:
If( col = Loc(dt[Row(), :OPE] < Loc(dt[Row(), :T=="3061")),
continue();
,
script...
);
D'altra parte, è impossibile che lo stesso oggetto abbia due volte il numero 3061.
Re: SceneggiaturaNon funziona, sai come posso fare una sceneggiatura del genere @jthi (gli ultimi controlli da fare)?
Grazie ancora per il tuo aiuto!
Re: SceneggiaturaOttieni tutti i valori :OPE per unique_object, quindi ottieni il valore :OPE per la riga 3061. Dopo quel controllo dall'elenco di tutti i valori :OPE gli indici che sono superiori al valore :OPE per 3061 (Loc() può farlo).
Re: SceneggiaturaSì è quello che mi sono detto, ho pensato a qualcosa del genere ma resta la difficoltà a definire le variabili a e b.
dt = current data table();
b = // all values :OPE for unique_object
a = // values :OPE for 3061
test = Loc(dt[Row(), b]) > Loc(dt[Row(),a]);
If( test=0 ,
continue();
,
script...;
);
Re: SceneggiaturaMetodo simile a quello utilizzato per la variabile dei valori che memorizza i valori :SOMME DEFAUT per un oggetto univoco specifico
values = dt[object_rows, "SOMME DEFAUT"];
aggiungi le stampe di debug e prova a eseguire lo script una riga alla volta utilizzando un solo oggetto
Re: SceneggiaturaSi grazie ho scritto un codice come questo che mi permette di avere le informazioni di cui ho bisogno:
dt = current data table();
For Each({unique_objects}, unique_objects,
object_rows = Loc(m_obs, unique_objects);
b = dt[object_rows, "OPE"];
show(b);
a = dt << Get Rows Where( :T == "3061");
valeurs_OPE = :OPE[a];
show(valeurs_OPE);
valeurs_OPE_3061_obj = dt[unique_objects, "valeurs_OPE"];
show(valeurs_OPE_3061_obj);
);Tuttavia, non riesco a ottenere i valori OPE corrispondenti all'attività 3061 tramite: obj (variabile "valeurs_OPE_3061_obj").
Solo la variabile "valeurs_OPE" raccoglie tutti i dati OPE corrispondenti all'attività 3061.
Re: SceneggiaturaÈ più facile testare se ignori For Each (per ora). Inizia con qualcosa del genere
Names Default To Here(1);
// dt = Open("$DOWNLOADS/AAA.jmp");
// dt = Current Data Table();
dt = New Table("AAA(1)",
Add Rows(12),
Compress File When Saved(1),
New Column("obj", Character(16), "Nominal", Set Values({"A", "A", "A", "A", "A", "A", "A", "B", "B", "B", "C", "C"})),
New Column("T", Character(4), "Ordinal", Set Values({"3052", "3061", "3082", "3940", "4012", "5010", "2890", "4060", "3061", "3081", "5010", "3061"})),
New Column("OPE", Numeric, "Continuous", Format("Best", 12), Set Values([12, 20, 32, 44, 56, 125, 210, 54, 60, 80, 45, 50])),
New Column("X__3", Character(4), "Nominal", Set Values({"rien", "rien", "incl", "ret", "ret", "rien", "incl", "incl", "ret", "incl", "ret", "rien"})),
New Column("SOMME DEFAUT", Numeric, "Continuous", Format("Best", 12), Set Values([0, 0, 1, 2, 1, 0, 2, 1, 2, 1, 1, 0])),
New Column("op", Character(12), "Nominal", Set Values({"40", "32", "55", "212", "510", "180", "2100", "2240", "2100", "500", "", ""})),
New Column("correct", Character, "Nominal", Set Values({"", "ko - 55 / ko - 212 / ko - 510 / ko - 2100", "", "", "", "", "", "", "ko - 500", "", "", "."}))
);
dt << New Column("bilan", Character, Nominal);
Summarize(dt, unique_objects = by(:obj));
m_obs = dt[0, "obj"];
unique_object = unique_objects[2];
object_rows = Loc(m_obs, unique_object);
input_loc = Loc(dt[object_rows, "T"], "3061"); // location of 3061 for unique_object
show(input_loc); // index within object_rows
show(object_rows[input_loc]); // real row number
opes = dt[object_rows, "OPE"]; // all OPE values for unique_object
valid_ope_idx = Loc(...//Re: Sceneggiatura
Ho continuato con il seguente script:
Names Default To Here(1);
dt = current data table();
dt << New Column("BILAN", Character, Nominal);
Summarize(dt, unique_objects = by(:obj));
m_obs = dt[0, "obj"];
dt = current data table();
For Each({unique_objects}, unique_objects,
object_rows = Loc(m_obs, unique_objects); // lignes pour chaque obj
b = dt[object_rows, "OPE"]; // les différentes opé pour chaque obj
show(b);
input_loc = Loc(dt[object_rows, "T"], "3061"); // localiser 3061 pour chaque objet
z = object_rows[input_loc]; // localiser les lignes OPE 3061 pour chaque obj
value_OPE_3061 = :OPE[z]; // valeur de l'ope 3061 pour chaque obj
show(value_OPE_3061);
//valid_ope_idx = Loc(b > value_OPE_3061);<BR /> //show(valid_ope_idx);
For( i = 1, i <= N Items(b), i++,
If(N Items(input_loc) < 1 & b[i] <= value_OPE_3061,
continue();
,
values = dt[object_rows, "SOMME DEFAUT"];
ops = dt[object_rows[Loc(values)], "op"];
result_str = "ko - " || Concat Items(ops, " / ko - ");
dt[object_rows[input_loc], "BILAN"] = Repeat({result_str}, N Items(input_loc));
);
);
);Per prima cosa ho usato la funzione Loc() per determinare gli indici di riga ma poi non so come continuare lo script. Quindi ho provato con un ciclo "For" e il risultato non è ancora corretto. Puoi aiutarmi ad andare avanti?
Grazie in anticipo per tutte le risposte!
Re: SceneggiaturaNon ha senso aggiungere nuovamente For Each Row fino a quando non si ottengono altre parti dello script funzionanti. Puoi continuare con l'esempio che ho fornito finché non hai impostato il valore corretto sulla riga 9 (a causa del lavoro con unique_objects[2] che è
. Per ottenere indici OPE superiori alle righe "3061" OPE è possibile utilizzare Loc in questo modo.
valid_ope_idx = Loc(opes > opes[input_loc]);
Si noti che questi sono indici all'interno della matrice opes NON righe dalla tabella dei dati. Per convertirli in righe è possibile utilizzare quegli indici con object_rows poiché object_rows è una matrice con righe per l'oggetto univoco specifico
Re: SceneggiaturaSi sono d'accordo con te !
I valori che trovo con la mia formula sono corretti e trovo uguali a te.
Tuttavia è nella costruzione della continuazione della sceneggiatura che ho più difficoltà!
Perché devo restituire i valori "ko-..... /ko-...» quando :OPE è maggiore di :OPE corrispondente a :T = 3061 e quando :DEFAULT SUM è diverso da 0 (come nell'esempio).
Re: SceneggiaturaLi continui con un'idea simile
- Aggiungi verifica che ci siano più di 0 elementi in valid_ope_idx
- Ottieni tutti i valori per valid_ope_idx
- Quindi da questo ottieni gli indici dei valori che sono maggiori di 0
- E poi con questo puoi finalmente ottenere i valori da :op per questi indici specifici (ricorda di convertirli prima in righe)
- Concatena questi valori insieme usando Concat Items()
- Aggiungi valore alla riga corretta
Re: SceneggiaturaNel mio database di test funziona bene con il seguente codice:
Names Default To Here(1);
dt = current data table();
dt << New Column("BILAN", Character, Nominal);
Summarize(dt, unique_objects = by(:obj));
m_obs = dt[0, "obj"];
somme_col = Filter Each( {col_name}, dt << Get Column Names( "String", Continuous ),
Starts With( col_name, "SOMME" )
);
dt = current data table();
For Each({unique_objects}, unique_objects,
object_rows = Loc(m_obs, unique_objects); // lignes pour chaque obj
b = dt[object_rows, "OPE"]; // les différentes opé pour chaque obj
show(b);
input_loc = Loc(dt[object_rows, "T"], "3061"); // localiser 3061 pour chaque objet
z = object_rows[input_loc]; // localiser les lignes OPE 3061 pour chaque obj
value_OPE_3061 = :OPE[z]; // valeur de l'ope 3061 pour chaque obj
show(value_OPE_3061);
valid_ope_idx = Loc(b > value_OPE_3061 & dt[object_rows,somme_col] > 0) ;
show(valid_ope_idx);
if( N items(valid_ope_idx) < 1,
continue();
,
ops = dt[object_rows[valid_ope_idx], "op"];
result_str = "ko - " || Concat Items(ops, " / ko - ");
dt[object_rows[input_loc], "BILAN"] = Repeat({result_str}, N Items(input_loc));
);
);Tuttavia, nel mio database finale, è possibile che per lo stesso obj venga visualizzato più volte il valore :T = 3061 (d'altra parte i valori di OPE sono gli stessi!).
Penso che qualcos'altro debba essere cambiato, il valore di value_OPE_3061 deve essere unico (e non per esempio value_OPE_3061 = [ 50 , 50 , 50 ] ma piuttosto value_OPE_3061 = [ 50 ]).
Esiste una funzione su JMP per rimuovere i duplicati da un array/elenco?
Grazie per le tue risposte !
Re: SceneggiaturaPrima pensavo che avessi detto che è impossibile che ci siano più valori "3061"?
Un'opzione per rimuovere i duplicati è utilizzare Associative Array() con << get keys (non funziona se si dispone di float), un altro vantaggio in questo caso sarebbe utilizzare Design(<
Re: SceneggiaturaHo aggiunto questa parte funziona ancora meglio:
Names Default To Here(1);
dt = current data table();
dt << New Column("BILAN", Character, Nominal);
Summarize(dt, unique_objects = by(:obj));
m_obs = dt[0, "obj"];
somme_col = Filter Each( {col_name}, dt << Get Column Names( "String", Continuous ),
Starts With( col_name, "SOMME" )
);
dt = current data table();
For Each({unique_objects}, unique_objects,
object_rows = Loc(m_obs, unique_objects); // lignes pour chaque obj
b = dt[object_rows, "OPE"]; // les différentes opé pour chaque obj
show(b);
input_loc = Loc(dt[object_rows, "T"], "3061"); // localiser 3061 pour chaque objet
if( N items(input_loc) < 1,
continue();
,
z = object_rows[input_loc]; // localiser les lignes OPE 3061 pour chaque obj
value_OPE_3061 = :OPE[z]; // valeur de l'ope 3061 pour chaque obj
liste={}; // transformer en liste pour supprimer les doublons
for (i = 1, i <= NRows(value_OPE_3061), i++,
for (j = 1, j <= NCols(value_OPE_3061), j++,
InsertInto(liste, value_OPE_3061[i, j]);
);
);
// supprimer les doublons
if( N items(liste) <= 1,
a = liste;
,
a = Left(liste,1);
);
// repasser en matrice pour faire les comparaisons
Mat_3061 = Matrix(a);
show(Mat_3061);
valid_ope_idx = Loc(b > Mat_3061 & dt[object_rows,somme_col] > 0) ;
show(valid_ope_idx);
if( N items(valid_ope_idx) < 1,
continue();
,
ops = dt[object_rows[valid_ope_idx], "op"];
result_str = "ko - " || Concat Items(ops, " / ko - ");
dt[object_rows[input_loc], "BILAN"] = Repeat({result_str}, N Items(input_loc));
);
);
);In effetti hai ragione @jthi , ma dicevo che per un valore 3061 di un :obj non possono esserci valori diversi della variabile :OPE (poi in un obj possono esserci 2 volte ad esempio il valore :T uguale a 3061 ma il valore :OPE sarà lo stesso).
Alla fine, in output, vorrei che il display non si ripetesse quando ci sono più volte il valore :T pari a 3061 per lo stesso :obj.
Esempio :

Con uno script come questo:
input_loc = Loc(dt[object_rows, "T"], "3061");// localiser 3061 pour chaque objet
liste2={}; // transformer en liste pour supprimer les doublons
for (i = 1, i <= NRows(input_loc), i++,
for (j = 1, j <= NCols(input_loc), j++,
InsertInto(liste2, input_loc[i, j]);
);
);
// supprimer les doublons
if( N items(liste2) <= 1,
c = liste2;
,
c = Left(liste2,1);
);
// repasser en matrice pour faire les comparaisons
loc_3061 = Matrix(c);
Funziona correttamente, prendo il primo indice corrispondente a :T pari a 3061.
Ma come nell'acquisizione sopra, vorrei poter rimuovere gli elementi duplicati sulla stessa riga:
kb - 2203 / kb - 2203 --> kb - 2203
Re: SceneggiaturaPotresti semplicemente scegliere il primo indice che ha un valore 3061 per l'oggetto.
Re: SceneggiaturaQualcosa del genere è come avrei risolto questo problema. Dovrebbe funzionare con i dati di esempio
Names Default To Here(1);
// dt = Open("$DOWNLOADS/AAA.jmp");
// dt = Current Data Table();
dt = New Table("AAA(1)",
Add Rows(12),
Compress File When Saved(1),
New Column("obj", Character(16), "Nominal", Set Values({"A", "A", "A", "A", "A", "A", "A", "B", "B", "B", "B", "C", "C"})),
New Column("T", Character(4), "Ordinal", Set Values({"3052", "3061", "3082", "3940", "4012", "5010", "2890", "4060", "3061", "3061", "3081", "5010", "3061"})),
New Column("OPE", Numeric, "Continuous", Format("Best", 12), Set Values([12, 20, 32, 44, 56, 125, 210, 54, 60, 60, 80, 45, 50])),
New Column("X__3", Character(4), "Nominal", Set Values({"rien", "rien", "incl", "ret", "ret", "rien", "incl", "incl", "ret", "ret", "incl", "ret", "rien"})),
New Column("SOMME DEFAUT", Numeric, "Continuous", Format("Best", 12), Set Values([0, 0, 1, 2, 1, 0, 2, 1, 2, 2, 1, 1, 0])),
New Column("op", Character(12), "Nominal", Set Values({"40", "32", "55", "212", "510", "180", "2100", "2240", "2100", "2100", "500", "", ""}))
);
dt << New Column("bilan", Character, Nominal);
Summarize(dt, unique_objects = by(:obj));
m_obs = dt[0, "obj"];
For Each({unique_object}, unique_objects,
object_rows = Loc(m_obs, unique_object);
input_loc = Loc(dt[object_rows, "T"], "3061");
If(N Items(input_loc) == 0,
continue();
//Throw("3061 not found");
, N Items(input_loc) > 0,
input_loc = input_loc[1];
);
opes = dt[object_rows, "OPE"];
valid_ope_idx = Loc(opes > opes[input_loc]);
If(N Items(valid_ope_idx) < 1,
result_str = ".";
,
values = dt[object_rows[valid_ope_idx], "SOMME DEFAUT"];
ops = dt[object_rows[valid_ope_idx[Loc(values)]], "op"];
result_str = "ko - " || Concat Items(ops, " / ko - ");
);
dt[object_rows[input_loc], "bilan"] = result_str;
);