你好,
我对 jmp(目前使用 jmp 16)相当陌生,并且对以下场景有疑问。 我有一个 csv 文件,每个缺陷都有以下列:
- 时间戳、序列号、x 位置、y 位置、缺陷大小(以毫米为单位)
我使用清洁过程,检查工具会提供当天每一个缺陷的信息。 我需要跟踪几件事:
1) 处理特定序列号的次数,并确定它是第一次被清除,还是第二次、第三次等。
2)如果序列号被多次清洗,那么我需要知道每次清洗后(x,y)位置的任何组合是否仍然在同一位置
绘制 x 和 y 位置,然后按时间过滤我可以回答 2) 但这是一个非常手动的步骤。 我希望找到一个使用 jsl 的解决方案,可以自动化我需要跟踪的信息。
x,y 位置具有什么样的分辨率? 例如,它们可能总是从 1 到 100 的整数,或者它们可能是从 0 到 100 的浮点值,四舍五入到最接近的 0.01,或者它们可能是对分辨率没有明显限制的浮点数。 x,y 对必须多近才能重复? 缺陷的大小是否与 x,y 重叠有关? 或者尺寸总是小于 x,y 网格并被忽略?
如果只有 100 个 x 和 100 个 y 值,并且大小不感兴趣,那么我会将 x 和 y 值组合成一个键,也许 x*100+y,这可能会使重复测试更容易. 对于四舍五入的浮点数可能相同,最多约 6 位有效数字。
但是如果大小很重要,或者必须测试距离,我可能会使用 KDTable。
还有,几行?不到几千可能不需要任何特定的优化。 超过一百万可能需要一个深思熟虑的算法。 (少于一千行,每一行都可以考虑其他每一行,只有 1,000,000 次比较,但大小加倍,比较四倍。)
你好@SEG
1:尝试使用Table <<汇总平台。在那里,只需将序列号、x 位置和 y 位置放入组中。
这将为三者的每个独特组合提供一个单独的行。
绘制这个新的汇总表会很有用,只需使用 N 列作为颜色或大小
2:生成一次图形后,在左上角的红色三角形下,您可以选择将脚本保存到数据表或脚本窗口中。 jmp 刚刚为您编写了脚本。
如果您使用的是 jmp 16,请单击查看>>日志,您将获得您在当前会话中所做的所有操作的脚本。
如果有帮助,请告诉我们。
朗
你好,
您能否通过分享一个示例来帮助我们更好地理解您的数据结构(模拟数据可以正常工作)? 根据您的描述,我推测每个清洁事件都可以通过时间戳和序列号的唯一组合来识别。 此外,您的数据是否包含任何复制信息(即具有相同时间戳和序列 ID 组合的行? 如果是这样,它将使过程更加苛刻。 最后,在为一个序列 ID 识别多个清洁事件后,您是否只对捕获所有事件的位置相同、任意两个事件之间相同或其他情况感兴趣?
最好的事物,
TS
你好克雷格
x 和 y 位置在小数点后 3 位。 只要它们在同一位置的 3% 以内,就会被视为重复缺陷。 大小与重叠没有任何关系。 过去所做的是,一旦将其绘制成图形,第一个循环就被指定为一种颜色的圆圈,而第二个循环则被指定为另一种颜色的“x”。 一旦它们重叠,它在视觉上很容易看出它是否在同一位置。
就行而言,它可能会达到数千,具体取决于一天的运行量。它从未超过数百万,尽管如果我们一次看很长一段时间,它是有可能的。
你好,蒂埃里,
我附上了模拟数据的截图。如您所见,每个 ID 将在同一时间戳记多次点击,因为所有缺陷都被同时写入,主要区别在于 x、y 位置和大小。 另请注意,此示例仅是第一次清洁。 第二次,相同的 ID 可能会出现更多或更少的行,这就是我想找到它是否重复并且还能够识别它的第二次、第三次等清洁循环的时候。
在对序列号进行多次清理后,我想捕获所有事件中的位置是否相同
这是一些入门代码。 您需要研究它以确保它符合您的预期。 输出在日志窗口中。 (我会附上一个文件。下面的代码可能会被弄乱,因为网站删除了一些“无效的 html”。 )
// synthesize some data
radius = 1;
dt = New Table( "Untitled",
Add Rows( 1000 ), // <<<<<<<<<<<<<< change number of rows here
New Column( "time", Numeric, "Continuous", Format( "Best", 12 ) ),
New Column( "waferID", Numeric, "Continuous", Format( "Best", 12 ) ),
New Column( "xLocation", Numeric, "Continuous", Format( "Best", 12 ) ),
New Column( "yLocation", Numeric, "Continuous", Format( "Best", 12 ) )
);
here:time = 0; // for this synth data, time just increments by 1
here:waferID = 0; // and the waferID is random 0-9
For( row = 1, row <= N Rows( dt ), row += 1,
If( Random Uniform() > .8, // determines how many points in a cleaning, before time changes and wafer re-randomizes
here:time += 1;
here:waferID = Random Integer( 0, 9 );
);
dt:time[row] = here:time;
dt:waferID[row] = here:waferID;
dt:xLocation[row] = Round( Random Uniform( -radius, radius ), 2 );
dt:yLocation[row] = Round( Random Uniform( -radius, radius ), 2 );
);
// find the unique waferIDs using summary
dtWafer = dt << Summary( Group( :waferID ), Link to original data table( 1 ) );
dtWaferTime = dt << Summary( Group( :waferID, :time ) );
dtNWafer = dtWaferTime << Summary( Group( :waferID ) ); //number of distinct times for this wafer is number of cleanings
close = .03; // <<< this determines how close makes a hit
// produce a report for each waferID that shows up in the cleaning data...
For( iWafer = 1, iWafer <= N Rows( dtWafer ), iWafer += 1,
dtWafer << clear select << selectrows( iWafer ); // dtWafer selection also selects rows in dt because linked
rows = dt << get selected rows; // all the rows for this wafer, across all times
// rows is a 1-column matrix of row numbers in dt for this wafer. the argument to KDTable
// is all the locations over all cleanings for this wafer.
kdt = KDTable( dt[rows, {xLocation, yLocation}] );
// report header for each waferID... the report might have no lines below the header, or a bunch... Not indented.
Write( Eval Insert( "\!n\!nprocessing waferID=^dtWafer:waferID[iWafer]^, ^nrows(rows)^ points during ^dtNWafer:nrows[iWafer]^ cleanings" ) );
For( irow = 1, irow <= N Rows( rows ), irow += 1, // the rows should be ascending time
row = rows[irow]; // row (singular) is an index to dt
// find rows {up to this many,up to this far} near irow
{index, distance} = kdt << kNearestRows( {dtWafer:NRows[iWafer], close}, irow );
// above, index is into the 1-column matrix of row numbers. below, it becomes index into dt
index = rows[index];
printedHeader = 0; // don't print this header unless there are duplicates to go with it
// distance[] and index[] have same number of rows; index is indexes to near neighbors and distance is to the neighbor
For( idistance = 1, idistance <= N Items( distance ), idistance += 1,
If( dt:waferID[index[idistance]] != dt:waferID[row] | dt:waferID[row] != dtWafer:waferID[iWafer],
Throw( "wafer mismatch" )
);// sanity check
If( distance[idistance] <= close, // typically there is one item with a bigger distance than we care about, must be ignored by else-break below.
If( !printedHeader, // a little indented...
Write(
Eval Insert(
"\!n matches for row ^row^ time ^dt:time[row]^ loc [^char(dt:xLocation[row],7,3)^,^char(dt:yLocation[row],7,3)^]"
)
);
printedHeader = 1;
);
// there can be more than one subsequent cleaning, indented more...
Write( Eval Insert( "\!n row ^index[idistance]^ time ^dt:time[index[idistance]]^ " ) );
Write( Eval Insert( "[^char(dt:xLocation[index[idistance]],7,3)^,^char(dt:yLocation[index[idistance]],7,3)^]) " ) );
Write( Eval Insert( "dist(^distance[idistance]^)" ) );
, //else
Break()
);
);
kdt << removerows( irow ); // never look back at this row from future rows
);
);
// a graph to examine the rows found to be close.
dt << Graph Builder(
Size( 867, 787 ),
Show Control Panel( 0 ),
Variables( X( :xLocation ), Y( :yLocation ) ),
Elements( Points( X, Y, Legend( 5 ) ) ),
Local Data Filter( Add Filter( columns( :waferID ), Modeling Type( :waferID, Nominal ), Display( :waferID, N Items( 14 ) ) ) )
);