
具有縮放響應疊加圖的多個 Y 軸
停止!如果您只想要腳本,它附在帖子的底部。
編輯:停止! - 現在已在 v19 的 Graph Builder 中實現此功能。 :)
這是一個專門的圖表,它縮放所有 y 軸變數並疊加它們,然後以變數的單位和範圍提供 y 軸比例。當您想要覆蓋 3 或 4 個處於不同範圍的變數時,這很有用。在上圖中,流速在 0-40 範圍內,而功率在 200-1000 和 100 到 500 範圍內。這種類型的圖表通常用於科學和工程應用中,其中多個感測器同時記錄數據,但每個感測器的範圍差異很大。例如,在發酵範例中,葡萄糖為 1-5 g/dl,pH 值為 6.8 - 7.8,乳酸為 10-50 mmol,細胞計數或濁度要大一個數量級。繪製所有變數隨時間變化的關係的簡單方法對於理解過程的動力學(或檢測異常模式)非常有幫助。
這張特定的圖表已經成為我的敵人很長一段時間了。通常需要在 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。這裡有一些規則,例如列必須是數字連續的,並且必須至少有 2 個 Y(這樣做的原因稍後會更清楚,但是,如果你只有一個 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 ) );但事情是這樣的,新列需要“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中,然後有一個垂直列錶框(objvlb),其中插入一個輪廓框(objout)和objhlb1。這是圖表的左側。由於它位於左側,因此首先將 ovjvlb 插入第二個水平列錶框(objhlb2)中,然後插入圖形產生器圖表(obj)。最後,將 objhlb2(現在包含所有內容)插入到新的視窗框中以便顯示。然後,為了使一切正常運轉,需要對其進行評估。如果我只是使用 Eval,那麼......我不確定會發生什麼,但它不起作用,所以我首先執行了 Eval Expr,以便顯示所有內容的整個評估腳本(嘗試在腳本中運行 Eval Expr(objwin)來了解我的意思)然後我使用 Eval 來運行該部分。
對 obj 和圖形產生器繪圖進行了少量格式化。標題關閉,框架大小重新調整,y 軸關閉。這一切都是為了使其與 y 軸框的比例和形狀相匹配。使用圖形產生器繪圖很方便,因為 x 軸仍然可以編輯,圖例可以移動,並且所有 GB 選項均可用於進一步格式化。
目前終點
就腳本而言,在第 7 次重大修訂中,它不是很整潔,並且留下了一堆懸而未決的表格。這個問題和其他一些問題都應該解決。因此,如果您有補充的想法,請發送過來。有些事情就是不會發生,例如圖表中的資料不會動態連結到原始資料表,而且 Y 軸框不會連結到縮放響應圖輪廓框,您必須單獨調整每個框的大小。如果這個方法可行的話就太好了。最後,我幾乎 100% 確定有方法可以使腳本中斷或返回不幸的結果,因此請注意特殊的列名和極端異常值或大量缺失的資料。
我很樂意聽到有關如何讓這個腳本更好地運行的評論。 (說到工作,我在 PC 和 Mac 上的 v13 和 v14 中對此進行了測試,並且運行良好。)
作者最後補充:
這是用最非正式的語氣寫的,就像我們在談論這件事一樣。
這是故意的,因為在我看來,編碼中的正式術語會阻礙溝通。相反,我避免混淆。