GuionBuenos días a todos,
Tengo una base de datos donde me gustaría crear una nueva columna a partir de una fórmula o script.
Aquí hay una descripción general de la base de datos:

- la columna T representa las operaciones a realizar
- la columna X_3 representa una característica
- La columna op representa objetos
El objetivo es crear una columna "evaluación" en la que agregamos "ko" y el número correspondiente a "op" cuando la suma de los defectos es diferente de una línea a otra para el mismo "obj" (aquí valor igual a AT ).
Última particularidad, la columna del balance debe completarse para el número de operaciones "3061".
Esta es una descripción general de la base de datos, la columna obj es "A", pero en la base de datos esta columna cambia de valor.
Gracias por sus respuestas !
Automatización y secuencias de comandosRe: Guión¿Por qué no se agrega la fila 6 op 180 a la última columna? 0 difiere de 1.
(Debería usar títulos de publicaciones más informativos que una sola palabra)
Re: GuiónPorque la suma es igual a 0 para la línea 6. De hecho, es más bien cuando la suma de los defectos es diferente del valor 0 entonces se informa el valor de "op" en la columna "saldo".
Re: GuiónEntonces, ¿hay alguna necesidad de verificar si los valores difieren entre dos filas? ¿O simplemente está interesado en las filas que tienen: op que no sea 0?
Re: GuiónCreo que solo nos pueden interesar las filas donde el valor de :sum default es diferente de 0. Porque cuando el valor de: default sum es 0 entonces no se obtiene nada en la columna del balance.
Re: GuiónGracias por su respuesta !
Comparando los dos métodos, me parece que el segundo es preferible para mi caso.
Lo adapte a mi base de datos, las columnas tienen los mismos formatos en ambas bases de datos y me sale este tipo de error:

Re: GuiónComo el mensaje de error está en francés, no puedo entenderlo. Podría ser que el error al final le diga que no tiene la columna bilan3. (Está diciendo que hay un subíndice no válido, pero eso se debe a que no tiene la columna bilan3. Esto se debe a que dt[input_loc, "bilan3"] devolverá [](1, 0) si no tiene la columna y luego intenta establecerle algunos valores).
Re: GuiónEsto se debe a que la variable "input_loc" está vacía. Entonces, para la asignación de nuevas celdas en la columna "balance general3" es imposible.
Re: GuiónEl valor "3061" (carácter, no número) no se encuentra en la columna "T". Además, la verificación que implementé para manejar input_loc vacío es incorrecta, debe modificarla a <1 en lugar de <0.
Re: GuiónEste script hace ambas cosas y funciona con el ejemplo proporcionado. Le sugiero encarecidamente que trate de entender lo que hace y pregunte si hay algunas partes que no entiende.
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));
);
);

Editar: instrucción if modificada Si (N Elementos (input_loc) ... de < 0 a < 1
Re: GuiónEn la base de datos, los valores de la columna :T no necesariamente están dispuestos en orden ascendente para el mismo tipo de objeto. Esta es otra columna :OPE que tiene sus elementos dispuestos en orden ascendente. Las columnas :T y :OPE están estrechamente relacionadas, son únicas para cada tipo de objeto.
Los :OPEs que sean inferiores a los valores :OPE correspondientes al valor 3061, no se debe poner nada en la columna del balance.
Cuando los valores de :OPE son mayores que los valores :OPE correspondientes al valor 3061 entonces se realiza la operación ya descrita en los primeros mensajes.
Vea el cómic a continuación como un ejemplo:

Gracias por sus respuestas !!
Re: GuiónAgregue comprobaciones adicionales para los valores :OPE. Si el valor de :OPE para la fila es menor que el valor de :OPE para "3061", ignórelo. Puede ser un poco difícil hacer un seguimiento con diferentes índices y filas, pero agregar impresiones de depuración ayuda con esto. Además, si puede haber más de un "3061" para un objeto, el cálculo se vuelve más complicado.
Re: Guión¡Sí, veo lo que quieres decir!
Me imagino algo que se parece a esto:
If( col = Loc(dt[Row(), :OPE] < Loc(dt[Row(), :T=="3061")),
continue();
,
script...
);
Por otro lado, es imposible que un mismo objeto tenga dos veces el número 3061.
Re: GuiónNo funciona, ¿sabes cómo puedo hacer un script así? @jthi (los últimos controles por hacer)?
Gracias de nuevo por tu ayuda !
Re: GuiónObtenga todos los valores :OPE para objeto_único, luego obtenga el valor :OPE para la fila 3061. Después de eso, verifique de la lista de todos los valores :OPE los índices que son más altos que el valor :OPE para 3061 (Loc() puede hacer esto).
Re: GuiónSi eso me dije, se me ocurrió algo así pero queda la dificultad para definir las variables a y 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: GuiónMétodo similar al utilizado para la variable de valores que almacena valores :SOMME DEFAUT para un objeto único específico
values = dt[object_rows, "SOMME DEFAUT"];
agregue impresiones de depuración e intente ejecutar el script una línea a la vez usando solo un objeto
Re: GuiónSí, gracias. Escribí un código como este que me permite tener la información que necesito:
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);
);Sin embargo, no puedo obtener los valores OPE correspondientes a la tarea 3061 por: obj (variable "valeurs_OPE_3061_obj").
Únicamente la variable "valeurs_OPE" recoge todos los datos OPE correspondientes a la tarea 3061.
Re: GuiónEs más fácil probar si simplemente ignora For Each (por ahora). Empezar con algo como esto
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: Guión
Continué con el siguiente guión:
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));
);
);
);Primero utilicé la función Loc() para determinar los índices de las filas, pero luego no sé cómo continuar con el script. Así que probé con un bucle "For" y el resultado aún no es correcto. ¿Puedes ayudarme a seguir adelante?
¡Gracias de antemano por todas las respuestas!
Re: GuiónNo tiene sentido volver a agregar For Each Row hasta que otras partes del script funcionen. Puede continuar con el ejemplo que proporcioné hasta que tenga el valor correcto establecido en la fila 9 (debido a que trabaja con objetos únicos [2], que es
. Para obtener índices OPE que son más altos que las filas "3061" OPE, puede usar Loc como este.
valid_ope_idx = Loc(opes > opes[input_loc]);
Tenga en cuenta que estos son índices dentro de la matriz opes NO filas de la tabla de datos. Para convertirlos en filas posteriores, puede usar esos índices con object_rows ya que object_rows es una matriz con filas para el objeto único específico
Re: GuiónSi, estoy de acuerdo con ustedes !
Los valores que encuentro con mi fórmula son correctos y encuentro lo mismo que tú.
Sin embargo, es en la construcción de la continuación del guión donde tengo más dificultades.
Porque tengo que devolver los valores "ko-..... /ko-...» cuando :OPE es mayor que :OPE correspondiente a :T = 3061 y cuando :DEFAULT SUM es diferente de 0 (como en el ejemplo).
Re: GuiónLos sigues con idea parecida
- Agregue verifique que haya más de 0 elementos en valid_ope_idx
- Obtenga todos los valores para valid_ope_idx
- Luego, a partir de esto, obtenga los índices de valores que son mayores que 0
- Y luego, con esto, finalmente puede obtener los valores de: op para estos índices específicos (recuerde convertirlos primero en filas)
- Concatene estos valores juntos usando Concat Items()
- Agregar valor a la fila correcta
Re: GuiónEn mi base de datos de prueba funciona bien con el siguiente código:
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));
);
);Sin embargo, en mi base de datos final, es posible que para el mismo obj se muestre varias veces el valor :T = 3061 (¡por otro lado, los valores de OPE son los mismos!).
Creo que hay que cambiar algo más, el valor de value_OPE_3061 debe ser único (y no por ejemplo value_OPE_3061 = [ 50 , 50 , 50 ] sino value_OPE_3061 = [ 50 ]).
¿Existe una función en JMP para eliminar duplicados de una matriz/lista?
Gracias por sus respuestas !
Re: GuiónPensé que antes dijiste que es imposible que haya múltiples valores "3061".
Una opción para eliminar duplicados es usar Associative Array() con << get keys (no funciona si tiene flotantes), otra buena opción en este caso sería usar Design(<
Re: GuiónAgregué esta parte, funciona aún mejor:
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));
);
);
);en realidad tienes razón @jthi , pero estaba hablando de que para un valor 3061 de un :obj no puede haber diferentes valores de la variable :OPE (después en un obj puede haber 2 veces por ejemplo el valor :T igual a 3061 pero el valor :OPE será el mismo).
Al final, en la salida, me gustaría que la pantalla no se repita cuando hay varias veces el valor :T igual a 3061 para el mismo :obj.
Ejemplo :

Con un guión como este:
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);
Funciona correctamente, tomo el primer índice correspondiente a :T igual a 3061.
Pero como en la captura anterior, me gustaría poder eliminar los elementos duplicados en la misma línea:
kb - 2203 / kb - 2203 --> kb - 2203
Re: GuiónSimplemente puede elegir el primer índice que tiene un valor de 3061 para el objeto.
Re: GuiónAlgo como esto es cómo habría resuelto esto. Debería funcionar con los datos de ejemplo.
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;
);