
스케일링된 응답 오버레이 그래프가 있는 다중 Y축
잠깐! 대본만 필요하시다면, 게시물 하단에 첨부해 두었습니다.
편집: 중지! - 이 기능은 이제 v19의 Graph Builder에 구현되었습니다. 
이것은 모든 y축 변수를 축척하고 이를 오버레이한 다음 변수의 단위 및 범위에 y축 축척을 제공하는 특수 그래프입니다. 이는 모두 다른 범위에 있는 3개 또는 4개의 변수를 오버레이하려는 경우에 유용합니다. 위 그림에서 유량은 0-40 범위에 있고 전력은 200-1000 및 100-500 범위에 있습니다. 이러한 유형의 그래프는 여러 센서가 동시에 데이터를 기록하지만 각 센서의 범위가 상당히 다른 과학 및 엔지니어링 응용 프로그램에서 자주 사용됩니다. 예를 들어 발효 예에서 포도당은 1-5g/dl, pH는 6.8-7.8, 젖산은 10-50mmol, 세포 수 또는 탁도는 훨씬 더 큽니다. 시간에 따른 모든 변수 간의 관계를 그래프로 나타내는 간단한 방법은 프로세스의 반응 속도(또는 비정상적인 패턴 감지)를 이해하는 데 매우 유용합니다.
이 특정 그래프는 꽤 오랫동안 제 골칫거리였습니다. JMP나 PowerPoint에서 창의적인 복사 및 붙여넣기를 해야 제법 괜찮은 결과물을 얻을 수 있습니다. 그런데 이 그래프는 왜 이렇게 어려울까요? 어떻게든 데이터를 스케일링하고 하나의 그래프로 합쳐야 하고, 스케일링되지 않은 모든 y축 구성 요소를 스케일링된 데이터 그래프와 결합해야 합니다. 제가 보고 시도해 본 모든 방법은 너무 많은 복사 및 붙여넣기 단계를 포함하고 있으며, PowerPoint에서 그래프 요소의 그룹을 해제하고 서식을 다시 지정하는 방법은 모든 요소를 슬라이드에 정렬하는 지루함 때문에 때로는 좋지 않은 결과를 초래할 수 있습니다. 지저분하고, 수동적이며, 지루하고, 반복적인 프로세스는 JSL 자동화를 절실히 필요로 합니다.
솔루션 스크립팅 전략을 생각할 때 고려해야 할 몇 가지 단계가 있습니다.
1. 각 변수/열에 대해 y축 상자를 만듭니다.
2. 각 열의 값을 동일한 범위로 조정합니다.
3. 오버레이 플롯에서 작동하도록 데이터를 재구성합니다.
4. y 역할과 x 역할에 대한 열을 선택하기 위한 시작 대화 상자가 있으면 좋을 것 같습니다. (아니면 행 번호만 사용하면 될 수도 있겠죠.)
5. 마지막으로, 그래프에는 원래 척도의 각 변수가 포함된 축 상자가 포함되어야 합니다.
JSL 연필을 깎고 이 도형을 자동으로 만드는 작업을 시도해 봤습니다. 여기서 제가 한 일을 설명하려고 하고, 더 기능적으로 만드는 방법에 대해 더 논의해 주시면 감사하겠습니다. 현재 스크립트는 한 가지 유형의 고정 도형만 생성할 수 있고, 중간 테이블의 잔해를 정리하는 데는 신경 쓰지 않습니다. (전체 스크립트는 게시글에 첨부하겠습니다.)
첫 번째 단계: 시작 대화 상자
cd = Column Dialog(
ylist = ColList( "Y", Min Col( 2 ), Max Col( 8 ), Data Type( "Numeric" ), Modeling Type( {"Continuous"} ) ),
xlist = ColList( "X", Max Col( 1 ), Modeling Type( {"Continuous", "Multiple Response"} ) )
);
Eval( Eval Expr( cd[1] ) ); //eval expr, returns what is in cd[1], and the eval makes it execute
Eval( Eval Expr( cd[2] ) ); //separately run just cd[1], then run evalexpr)cd[1], this is how I get the ylist and xlist variables
시작 대화상자를 작성할 때마다 저는 스크립팅 인덱스에서 뻔뻔하게 복사해서 필요 없는 건 삭제하고 필요에 따라 더 추가합니다. 목표는 Y와 X를 포착하는 것입니다. 여기에는 몇 가지 규칙이 있는데, 열은 숫자로 연속적이어야 하고, Y는 최소 2개 이상 있어야 합니다. (이유는 나중에 더 명확해지겠지만, Y가 하나뿐이라면 왜 이렇게 해야 할까요?) Y 응답은 8개로 제한되어 있습니다. 자유롭게 수정하세요. 8개가 넘으면 너무 복잡해 보입니다.
eval(eval expr())을 사용한 마지막 두 줄은 열 대화 상자에서 "cd"에 반환된 내용으로부터 ylist와 xlist 변수를 생성하는 방법입니다.
데이터 테이블 1: 표준화된 데이터 테이블
dt1 = New Table( "multiple y-axis table" ); //this is the table where the standardized values go
If( N Items( xlist ) == 1, //note that if there is a column selected for xlist, then it doesn't get scaled
Insert Into( ylist, xlist, 1 ), //call me crazy, but the first value in ylist is the "X", if it exists
dt1 << New Column( "Row", formula( Row() ) )
);
DT1은 표준화된 후 데이터를 포함할 테이블입니다. 대화 상자에서 X를 선택하지 않으면 첫 번째 열이 "행"이 되고, 그래프의 x축으로 사용할 행 번호가 지정됩니다. X가 있으면 ylist의 첫 번째 위치에 추가됩니다. 네, 맞습니다. ylist 변수에는 X와 Y가 모두 있습니다.
dt1 채우기 및 "축 상자" 만들기
For(
<FONT color="#FF6600"> If( N Items( xlist ) == 1, //yep, that's an "if" argument inside a "for" loop
i = 2; //if an "x" is specified, then start with the second thing in the list
ycol = As Column( dt, ylist[1] ) << get values;
dt1 << New Column( Munger( ylist[1], 1, ":", "" ), set values( ycol ) );//this is soooo ugly
,i = 1), i <= N Items( ylist ), i++, //end of setup for the For loop</FONT>
<FONT color="#008000"> ycol = As Column( dt, ylist[i] ) << get values;
yycol = ((ycol - Min( ycol )) / (Max( ycol ) - Min( ycol )));
dt1 << New Column( Munger( ylist[i], 1, ":", "" ), set values( yycol ) ); //this is soooo ugly, very sorry</FONT>
<FONT color="#0000FF"> amin = Min( ycol ); //this second part of this script is getting values to make the y-axis boxes
amax = Max( ycol ); //I need the min and max values from each of the unscaled columns, along with their names
ymin = amin - ((amax - amin) * .1); //The y-axis box axis range is 10% higher and lower than the min and max.
ymax = amax + ((amax - amin) * .1); //It looks better and is more closely aligned with the scaled data in gb
obj1 = Graph Box( Framesize( 0, 230 ), yName( Char( ylist[i] ) ), Y Scale( ymin, ymax ) ); </FONT><BR /><FONT color="#0000FF"> //its not a true axis box, just a squished graph box. the axis box was free
Insert Into( objhlb1, Name Expr( obj1 ) ); </FONT><BR /><FONT color="#0000FF"> //there is a lot of inserting of name expr() here. its part of building expressions from the inside out.</FONT>
);
다음 부분은 Y에 선택된 열 목록을 반복합니다. X가 지정된 경우 목록의 두 번째 항목부터 시작합니다.
첫 번째 부분에서는 루프를 설정하고 결과에 대한 표를 만드는 몇 가지 논리를 사용합니다. 표의 변형은 x축 변수의 지정 여부에 따라 달라집니다.
스크립트 중간 부분은 짧지만 정말 중요합니다. 원본 테이블(dt)의 각 열에서 값을 가져와 크기 조정된 값을 dt1에 넣습니다. 이 부분은 정말 마음에 들지 않습니다. dt1 << 새로운 칼럼 ( 멍거 ( ylist[i], 1 , ":" , "" ), set values( yycol ) ); 그런데 문제는, New column에 "colname" 또는 colname을 입력해야 하는데, :colname을 입력하면 이 단계가 종료되기 때문에 콜론을 삭제했습니다. (댓글에 더 나은 방법을 알려주세요.)
마지막 부분에서는 각 열 값 집합을 살펴보고 이를 사용하여 그래프 상자의 크기를 조정했습니다. 그래프 상자는 보기 좋은 축 상자를 만들며, 그래프 상자의 너비를 0으로 설정하면 y축 상자만 표시됩니다. 이 단계는 그래프 서식을 지정하는 데 중요합니다. 마지막 줄에서는 그래프 상자의 표현식을 표시 상자에 삽입하고, 이 표현식은 마지막에 평가됩니다. expr이라는 이름을 사용하여 obj1(그래프 상자)을 참조함으로써 스크립트를 생성하고 스크립트의 모든 변수를 평가하지만, 스크립트 자체는 평가되지 않습니다. 각 그래프 상자는 가로 목록 상자(objhlb1)에 삽입됩니다. 나중에 이 상자는 다른 상자에 삽입됩니다.
그래프를 만들기 위한 표 만들기:
For(
If( N Items( xlist ) == 1,
i = 2,
i = 1
), i <= N Items( ylist ), i++,
Insert Into( dt2stackcols, ylist[i] )
);
Insert Into( dt2stack, Name Expr( dt2stackcols ), 1 );
Eval(
Substitute( Expr( dt2 = dt1 << ping ),
Expr( ping ), Eval Expr( dt2stack )
)
); //This is tricky!
dt2 << set name( "multiple y-axis stack" );
위에서 제약 조건을 언급했습니다. 최소 두 개의 y 변수를 선택해야 한다는 것이었습니다. 그런데 변수는 이미 쌓여 있기 때문에 하나만 쌓을 수 없고, 변수가 하나만 있으면 스크립트가 충돌합니다. 여기서 흥미로운 점은 X 값을 지정할지 여부를 처리하는 로직입니다. X 값이 있다면 이미 크기를 조정하고 싶지 않았고, 이제는 X 값을 레이블로 지정하고 싶어서 스택에 포함하지 않습니다. X 값이 지정되지 않으면 ylist에 있는 모든 변수를 사용하고 싶습니다.
빨간색으로 표시된 마지막 부분은 설명하기 어렵습니다. 한 줄로 스택을 생성하는 스크립트가 빌드되고 실행/평가됩니다. "Ping"은 대체되는 패턴인데, 이 표현들을 어떻게 바꿔야 할지 고민할 때 가장 먼저 떠오른 단어였습니다.
dt2stackcols 표현식은 어디에 있나요? 명확성을 위해, 그리고 이 작업을 할 때 제 정신 건강을 위해 정의된 모든 표현식을 스크립트 시작 부분으로 옮겼습니다. 스크립트 순서를 변경하면서 표현식이 필요하기도 전에 이미 존재했기 때문에 이 방법이 유용했습니다. 스크립트에 표현식을 분산시켜 두면, 스크립트 후반부에 정의된 표현식에 표현식을 삽입하려고 할 때가 종종 있었습니다. 첨부된 전체 스크립트를 확인하시면 어떻게 구성되어 있는지 확인하실 수 있습니다. 최선의 방법은 아니지만, 저는 적대적인 그래프를 위해 모든 노력을 다하고 있습니다.
모든 부품 조립
Insert Into( objout, Name Expr( objhlb1 ) );
Insert Into( objvlb, Name Expr( objout ) );
Insert Into( objhlb2, Name Expr( objvlb ) );
Insert Into( objhlb2, Name Expr( obj ) );
Insert Into( objwin, Name Expr( objhlb2 ) );
Eval( Eval Expr( objwin ) ); //Evaluating all the assembled parts after all the expressions are evaluated
gb << size( 600, 285 ); //if you want to format the graph, send messages to gb
JSL의 가장 좋은 점 중 하나는 함수 기반 언어라는 것입니다.이는 두 가지 중요한 일을 달성합니다.첫째, 바퀴를 다시 발명하지 않고도 특정 작업을 쉽게 수행할 수 있게 해주고 둘째, 복잡한 구조를 만드는 방법에 대해 생각하기 쉽게 해줍니다.(안에서부터 쌓아 올리는 양파와 같다고 생각한다면 말입니다.) 예를 들어, 일반적인 사고방식은 다음과 같습니다.먼저 땅을 많이 사고, 그 땅에 건물을 짓고, 건물에 방을 넣고, 그 방에 사무실 의자 같은 것을 넣고, 그 의자에 앉아서 마지막으로 일을 시작합니다.함수적 사고는 다음과 같이 작동합니다.먼저 의자가 있었고, 저는 그 안에 바이런을 넣었습니다.그런 다음 그 의자(아직도 바이런이 있습니다)를 사무실에 넣고, 그 건물을 건물에 넣고, 그 건물을 부지에 넣고, 그 부지를 사고, 마지막으로 바이런이 작동합니다.
첫 번째 단계는 그래프 상자를 objhlb1에 넣는 것이었고, 그 다음에 개요 상자(objout)와 objhlb1이 삽입되는 수직 목록 상자(objvlb)가 있습니다. 이것은 그래프의 왼쪽입니다. 왼쪽이므로 ovjvlb가 먼저 두 번째 수평 목록 상자(objhlb2)에 삽입되고 그 다음에 그래프 빌더 차트(obj)가 삽입됩니다. 마지막으로 objhlb2(이제 모든 것이 포함됨)가 새 창 상자에 삽입되어 표시될 수 있습니다. 그런 다음 모든 것이 작동하도록 평가됩니다. Eval만 사용했다면.. 무슨 일이 일어나는지 잘 모르겠지만 작동하지 않으므로 먼저 Eval Expr을 실행하여 모든 것의 전체 평가된 스크립트가 표시되도록 했습니다(스크립트에서 Eval Expr(objwin)을 실행하여 무슨 뜻인지 확인해 보세요). 그런 다음 Eval을 사용하여 해당 부분을 실행했습니다.
obj 그래프 빌더 플롯에 약간의 서식이 있습니다. 제목은 꺼져 있고, 프레임 크기는 재조정되었으며, y축은 꺼져 있습니다. 이는 모두 y축 상자의 크기와 모양에 맞게 조정하기 위한 것입니다. x축은 여전히 편집 가능하고, 범례는 이동할 수 있으며, 모든 GB 옵션을 사용하여 추가 서식을 지정할 수 있으므로 그래프 빌더 플롯을 사용하는 것이 편리합니다.
현재 종료 지점
스크립트는 7번째 주요 개정을 거치면서 그다지 깔끔하지 않고, 여러 표가 엉켜 있습니다. 이 문제를 비롯한 여러 가지 문제가 수정되어야 합니다. 추가 아이디어가 있으시면 알려주세요. 그래프의 데이터가 원본 데이터 표에 동적으로 연결되지 않고, Y축 상자가 Scaled Response Graph 윤곽선 상자에 연결되지 않는 등 일부 기능이 제대로 작동하지 않을 수 있습니다. 각 상자의 크기를 개별적으로 조정해야 합니다. 이 기능이 제대로 작동하면 좋겠습니다. 마지막으로, 스크립트가 제대로 작동하지 않거나 예상치 못한 결과를 반환하는 경우가 있을 것이라고 거의 100% 확신합니다. 특수 열 이름, 극단적인 이상치 또는 누락된 데이터가 많은지 주의하세요.
이 스크립트를 더 잘 작동하게 하는 방법에 대한 의견을 듣고 싶습니다. (작동에 관해 말씀드리자면, 저는 PC와 Mac에서 v13과 v14로 테스트했고, 게시하기에 적합했습니다.)
저자의 마지막 말:
이 글은 아주 비공식적인 어조로 쓰여졌는데, 마치 우리가 이 문제에 대해 이야기하는 것처럼 말입니다.
제 생각에는 코딩 과정에서 형식적인 전문 용어를 사용하면 의사소통이 어려워지기 때문에 의도적으로 그렇게 했습니다. 대신, 모호하게 표현하는 것은 지양합니다.