여보세요,
저는 jmp(현재 jmp 16 사용)를 처음 접했고 다음 시나리오에 대한 질문이 있습니다. 각 결함에 대해 다음 열이 있는 csv 파일이 있습니다.
- 타임스탬프, 시리얼 ID, x 위치, y 위치, 결함 크기(mm)
저는 청소 프로세스로 작업하고 검사 도구는 그날의 모든 결함에 대한 정보를 제공합니다. 추적해야 할 몇 가지 사항이 있습니다.
1) 특정 일련 ID가 처리된 횟수 및 첫 번째 또는 두 번째, 세 번째 등으로 치료되었는지 확인합니다.
2) 일련 ID가 두 번 이상 청소된 경우 청소할 때마다 (x,y) 위치의 조합이 여전히 동일한 지점에 있는지 알아야 합니다.
x 및 y 위치를 그래프로 표시한 다음 시간별로 필터링하면 2)에 답할 수 있지만 매우 수동적인 단계입니다. 추적해야 하는 정보를 자동화할 수 있는 jsl을 사용하는 솔루션을 찾고 싶습니다.
좋아요 Craige_Hales 솔루션이지만 과거에 유사한 작업에 직면한 적이 있기 때문에 솔루션을 제공하고 싶습니다.
내 접근 방식은 사용 가능한 데이터를 사용하여 추가 정보를 계산하고 이를 데이터 테이블에 추가한 다음 최종 결과를 보여주는 보고서(그래프 빌더, 표 작성 등)를 생성할 수 있었습니다.
귀하의 요청에서 특정 일련 ID가 처리된 횟수를 계산할 수 있기를 원합니다.또한 웨이퍼의 마지막/최종 처리에서 데이터 항목을 식별하는 표시기 열을 만드는 것이 매우 유용하다는 것을 알게 되었습니다.
"mock data.PNG"의 예제 데이터를 기반으로 다음을 수행합니다.
- 웨이퍼가 처리될 때마다 일련 번호를 계산합니다.
- 각 웨이퍼가 처리된 최종 시간 계산
- 대략적인 위치를 기반으로 결함 위치를 이름으로 식별
- 각 웨이퍼에 대해 각 위치 이름이 식별된 횟수를 계산합니다.
이 작업을 수행하기 위해 저는 tabulate를 사용하여 여러 개의 임시 데이터 테이블을 생성하고 원하는 답변을 얻기 위해 몇 가지 수식을 추가한 다음 원래 데이터 테이블에 다시 병합합니다.
JSL 코드는 다음과 같습니다.
names default to here(1);
/////////// make example data table /////////////////////
dt = New Table( "example data set",
Add Rows( 13 ),
New Column( "Time",
Numeric,
"Nominal",
Format( "Best", 12 ),
Set Values( [1, 1, 1, 1, 1, 2, 2, 2, 3, 3, 4, 4, 4] )
),
New Column( "WaferID",
Numeric,
"Nominal",
Format( "Best", 12 ),
Set Values( [1, 1, 1, 1, 1, 2, 2, 2, 3, 3, 1, 1, 1] )
),
New Column( "X location",
Numeric,
continuous,
Format( "Best", 12 ),
Set Values(
[2.745, 1.566, -28.143, -4.793, -29.251, 19.527, 6.581, 29.37, 4.907,
21.699, 2.745, -4.793, 6.581]
)
),
New Column( "Y location",
Numeric,
continuous,
Format( "Best", 12 ),
Set Values(
[44.164, 61.915, -42.954, -38.091, -46.659, 32.201, -24.715, 41.533,
60.217, -60.26, 44.164, -38.091, -24.715]
)
),
New Column( "Size",
Numeric,
"Continuous",
Format( "Best", 12 ),
Set Values(
[0.164, 0.136, 0.125, 0.122, 0.122, 0.036, 0.035, 0.035, 0.035, 0.034,
0.164, 0.122, 0.035]
)
)
);
//dt = current data table();
/////////// create temporary data table to determine Clean Count and Final Clean /////////////////
tab = dt << Tabulate(
Show Control Panel( 0 ),
Full Path Column Name( 1 ),
Add Table( Row Table( Grouping Columns( :WaferID, :Time ) ) )
);
dtx = tab << make into data table();
tab << close window();
dtx << New Column("Wafer Clean Count", Numeric, "Nominal", Formula(Col Rank(:Time, :WaferID)));
dtx << New Column("Final Wafer Clean", Numeric, "Nominal", Formula(If(:Time == Col Maximum(:Time, :WaferID), 1, 0)));
dtx << delete Column("N");
///////// move Clean Count and Final Clean information back to original data table //////////////
dt << Update(
With( dtx ),
Match Columns( :WaferID = :WaferID, :Time = :Time ),
Add Columns from Update Table( :Wafer Clean Count, :Final Wafer Clean ),
Replace Columns in Main Table( :Wafer Clean Count, :Final Wafer Clean )
);
//////// close data table //////////////////////////////////////////////////////
close(dtx, no save);
//////// modify X & Y values to allow variation in measurements////////////////
dt << new column("X_Temp", numeric, continuous, formula(round(:X location, 1)));
dt << new column("Y_Temp", numeric, continuous, formula(round(:Y location, 1)));
/////// create temporary data table to name defect locations ///////////////
tab = dt << Tabulate(
Show Control Panel( 0 ),
Include missing for grouping columns( 1 ),
Full Path Column Name( 1 ),
Add Table( Row Table( Grouping Columns( :X_Temp, :Y_Temp ) ) )
);
dtx = tab << make into data table();
tab << close window();
dtx << new column("Location", character, nominal, formula("L" || char(row())));
dtx << delete column("N");
//////// move Location information back to original data table ////////////////
dt << Update(
With( dtx ),
Match Columns( :X_Temp = :X_Temp, :Y_Temp = :Y_Temp ),
Add Columns from Update Table( :Location ),
Replace Columns in Main Table( :Location )
);
/////// clean up /////////////////////////
close(dtx, no save);
dt << delete columns(
{
"X_Temp",
"Y_Temp"
}
);
/////// calculate the number of times a defect location occured on a given wafer ////////
dt << new column("Wafer Location Count", numeric, nominal, formula(Col Rank( :Time, :Location, :WaferID )));
/////// make some reports /////////////
nw = new window("results",
h list box(
gb = dt << Graph Builder(
Show Control Panel( 0 ),
Variables(
X( :X location ),
Y( :Y location ),
Group X( :Wafer Clean Count ),
Group Y( :WaferID ),
Color( :Wafer Location Count )
),
Elements( Points( X, Y, Legend( 7 ) ) )
),
tab = dt << Tabulate(
Show Control Panel( 0 ),
Include missing for grouping columns( 1 ),
Full Path Column Name( 1 ),
Add Table(
Column Table( Statistics( Max ), Analysis Columns( :Wafer Location Count ) ),
Row Table( Grouping Columns( :WaferID, :X location, :Y location ) )
),
Local Data Filter(
Add Filter(
columns( :Final Wafer Clean ),
Where( :Final Wafer Clean == 1 )
)
)
)
)
);결함 위치의 이름을 지정할 때 x 좌표와 y 좌표를 소수점 이하 한 자리로 반올림하여 위치 측정에 약간의 변화를 허용했습니다. 이는 필요에 따라 충분할 수도 있고 충분하지 않을 수도 있지만 이 접근 방식을 사용하면 X_Temp 및 Y_Temp에 대한 공식을 수정할 수 있으며 나머지는 여전히 예상대로 작동합니다.
결과 데이터 테이블에는 다음과 같은 추가 열이 포함됩니다.
- 웨이퍼 세정 횟수
- 웨이퍼에서 수행된 세척 횟수에 대한 일련 번호를 포함합니다.
- 최종 웨이퍼 세척
- 항목이 웨이퍼가 처리된 마지막 시간이 아닌 경우 0이고 웨이퍼가 처리된 마지막 시간인 경우 1인 표시기 열
- 위치
- 웨이퍼 위치 수
- 주어진 웨이퍼에서 이 결함 위치 이름이 발생한 일련의 횟수를 포함합니다.
결과 데이터 테이블을 사용하면 질문 등을 해결할 수 있습니다. 조사할 수 있는 가능한 질문에는 다음이 포함되지만 이에 국한되지는 않습니다.
- 여러 청소 주기 후에도 어떤 결함 위치가 지속되었는지
- 첫 번째 웨이퍼 세척 주기 후 전체 불량률은 얼마입니까?
- 모든 청소 주기를 수행한 후 전체 불량률은 얼마입니까?
- 웨이퍼당 평균 세척 주기는 얼마입니까?
- 청소가 필요한 결함 영역이 지속적으로 있는 영역이 있습니까?
도움이 되길 바랍니다.
시작 코드는 다음과 같습니다. 기대한 대로 작동하는지 확인하기 위해 연구해야 합니다. 출력은 로그 창에 있습니다. (파일을 첨부하겠습니다. 웹 사이트에서 일부 "잘못된 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 ) ) ) )
);
x,y 위치는 어떤 종류의 해상도를 가집니까? 예를 들어 항상 1에서 100까지의 정수이거나 가장 가까운 .01로 반올림되는 0에서 100까지의 부동 소수점 값이거나 분해능에 명백한 제한이 없는 부동 소수점 값일 수 있습니다. 반복이 되려면 x,y 쌍이 얼마나 가까워야 합니까? 불완전함의 크기가 x,y 중첩과 관련이 있습니까? 아니면 크기가 항상 x,y 그리드보다 작고 무시됩니까?
100 x 및 100 y 값만 있고 크기가 흥미롭지 않은 경우 x 및 y 값을 단일 키(예: x*100+y)로 결합하여 반복 테스트를 더 쉽게 할 수 있습니다. . 약 6개의 유효 숫자까지 둥근 플로트에 대해서도 동일할 수 있습니다.
그러나 크기가 중요하거나 거리를 테스트해야 하는 경우 KDTable을 사용할 수 있습니다.
또한 몇 행입니까? 수천 개 미만은 특정 최적화가 필요하지 않을 것입니다. 백만 명 이상이 아마도 잘 생각한 알고리즘이 필요할 것입니다. (1,000개 미만의 행에서 모든 행은 다른 모든 행에 대해 생각할 수 있고 1,000,000개의 비교만 있지만 크기는 두 배이고 비교는 네 배입니다.)