JMP 中的 Python:資料框列到資料表列的最佳實務及對計算速度的影響背景
* 在 JMP19 腳本環境下使用 Python。
* 使用 pandas 從現有的 JMP 資料表 (dt) 建立資料框 (df)。
* 執行一個需要資料框輸入並傳回一個新的資料框結果的函數。我們稱之為 df_results。
* 將結果傳回 JMP 資料表列。
這段程式碼可以運行,但有兩點讓我懷疑我的操作有問題。首先,在本地日誌輸出中出現了警告。其次,第一次處理大量資料時耗時很長。我的程式碼計時器顯示處理 10 萬行資料需要 25 秒。如果重新運行,第二次只需要 3 秒。我設定了計時器,以便記錄每個步驟的詳細信息,從而找出是否是哪一行程式碼耗時過長。
我質疑的就是這一行程式碼。第一次填入初始值為常數(或空值-兩種情況都試過)的欄位時,處理 10 萬行資料就花了 25 秒。之後每次重試速度都快了近 10 倍。
dt[ColumnName] = df_results[ColumnName]
這是嵌入式日誌中的警告訊息
FutureWarning:Series.__getitem__ 將鍵視為位置的做法已棄用。在未來的版本中,整數鍵將始終被視為標籤(與 DataFrame 的行為一致)。若要按位置存取值,請使用 `ser.iloc[pos]`
此外,我對兩個不同資料框的總共 4 列進行了相同的操作。它們都表現出相同的行為(和警告):首次運行速度很慢,之後每次重複運行速度都很快。第 1 列、第 2 列、第 3 列和第 4 列之間的差異可以忽略不計。
這只是一個包含 130 萬行的大表的子集。所以,如果我第一次嘗試時無法找到提高速度的方法,那麼 25 秒 x 4 列就會變成 250 多秒 x 4 列。
謝謝
自動化和腳本編寫回覆:JMP 中的 Python:資料框列到資料表列的最佳實務及計算速度影響在 JMP 18 中,需要逐列地從資料幀建立資料表。 JMP 19 支援可移植資料幀協議,因此只需一行程式碼即可將資料表轉換為資料幀,同樣只需一行程式碼即可將資料幀轉換為資料表。
另請注意,首次在 JMP 中執行 Python 腳本時,需要初始化 Python 本身。首次導入包時,需要載入該包,可能包括共享庫或 .py 檔案的字節碼編譯。
此警告來自 pandas 包。要消除此警告,您需要執行其建議的 `ser.iloc[pos]`。 JMP 資料表 dt 可以使用從 0 開始的列索引或列名進行索引。使用索引速度更快,因為它避免了字串比較。
幫助 -> 腳本索引“Python”類別,jmp.from_dataframe。 “JMP 到 Pandas”範例。
import jmp
import jmputils
try:
if not jmputils.is_installed('pandas'):
jmputils.jpip('install', 'pandas', echo=False)
except Exception as e:
print(f'Install failed with exception: {e}')
import pandas as pd
dt = jmp.open(jmp.SAMPLE_DATA + "Big Class.jmp")
pandas_df = (pd.api.interchange.from_dataframe(dt))
print(pandas_df)
...
# new dt_result data table from dataframe df_result
dt_result = jmp.from_dataframe( df_result )
# or continue to do the loop on columns if you need to update the original data table from the resulting dataframe.
回覆:JMP 中的 Python:資料框列到資料表列的最佳實務及計算速度影響那麼,用 Python 程式碼向 JMP 檔案中的單一欄位填入新值的最快方法是什麼?
例如,對於一個包含 10 萬行資料的資料表?
回覆:JMP 中的 Python:資料框列到資料表列的最佳實務及計算速度影響謝謝 Paul,我會使用 jmp.from_dataframe 函數回到資料表,以避免錯誤。
我問題的另一部分是關於為什麼速度差異如此之大,為了回答這個問題,我必須確保能夠可靠地重現計算響應緩慢的情況。起初我做不到。所有運行,甚至包括打開 JMP 並加載 Python 後的第一次運行,速度都非常快。後來我意識到可能存在一個關鍵差異。如果我手動新增一個新列(尚未將其寫入程式碼),然後執行程式碼,則只有在更新該新列時才會出現速度緩慢的情況,而且這種情況僅發生在第一次更新時。
作為測試,我更新了 4 列,其中 3 列是原有列,第 4 列是新建立的。更新第 1 列的 10 萬行資料耗時 1 秒,更新第 2 列又耗時 1 秒,更新第 3 列又耗時 1 秒,而更新第 4 列則耗時 19 秒。
我創建了另一個表格,並添加了一個新的第四列。如果我儲存並關閉包含新列的資料表,然後再重新打開,最後執行程式碼…新的第四列會在大約 1 秒內更新,速度與第 1、2 和 3 列一樣快。關鍵是,如果我只保存而不關閉並重新打開包含新第四列的資料表,然後運行程式碼,更新第四列仍然需要大約 19 秒。
這說明速度問題與 JMP 的某些記憶體管理特性有關。
我不知道這個問題是否能/必須解決,也許只有在不使用 `from_dataframe` 步驟時才會出現。這確實是一個非常奇怪的現象,我很慶幸能夠重現它。現在我可以嘗試改進後的程式碼,看看問題是否仍然存在。
回覆:JMP 中的 Python:資料框列到資料表列的最佳實務及計算速度影響我的一位同事指出,JSL 中有“開始資料更新”和“結束資料更新”,所以…
jmp.run_jsl( 'dt << Begin Data Update;' )
# your python code filling new column
...
jmp.run_jsl( 'dt << End Data Update;' )
我們很可能需要用 Python 來實作這個功能。可能會使用 with 語句區塊來暫停錶格的 UI 更新,以便在填入/更新大量行時使用。
回覆:JMP 中的 Python:資料框列到資料表列的最佳實務及計算速度影響它會使用包裝在 jsl Begin Data Update 中的 dt[index] 來設定整列。
jmp.run_jsl( 'dt << Begin Data Update;' )
dt[ index ] = list_like_object # numpy array, list, dataframe column,...
jmp.run_jsl( 'dt << End Data Update;' )
或者,您可以先將資料表設為不可見,直到填滿表格後再顯示。
import jmp
dt = jmp.DataTable('Powered By Python', 40, visibility='Invisible')
...
# fill table
...
jmp.globals['jdt'] = dt # set jdt in JSL to reference dt
jmp.run_jsl('::jdt << Show Window(1);')
可選的可見性參數可能需要 JMP 19 或更高版本。 jmp.globals[ ] 是 JMP 19 的特性。
回覆:JMP 中的 Python:資料框列到資料表列的最佳實務及計算速度影響`jmp.run_jsl('dt << Begin Data Update;')` 假定 `dt` 是 JSL 中表的 JSL 變量,並且在 Python 端也是如此。例如,如果 JSL 中執行了 Python 的 `Send(dt)` 函數,則會發生這種情況;但如果表是由 Python 建立的,則不一定會出現相同的情況。
回覆:JMP 中的 Python:資料框列到資料表列的最佳實務及計算速度影響非常感謝您的解釋。
所以,程式碼如下:
dt[ index ] = list_like_object
Python中是逐步填充列的嗎?這導致了速度問題?
它看起來很像
dt[ 0, columnname] = [values ...]
在JSL中,它可以一步填入列,無需用`dt << Begin/end Data Update`包裹。
知道了。請問哪裡可以找到更多詳細資訊?
回覆:JMP 中的 Python:資料框列到資料表列的最佳實務及計算速度影響我不知道有多少用戶了解它的重要性。
dt << Begin Data Update;
這是為了編寫優秀的程式碼。但我相信很多用戶並不知道這一點。
@bswedlove提出的相關願望是針對每一行:
對於每一行(資料更新=1, ...)
將解決方案製度化是個好主意——這樣用戶就不會再遇到問題了(解決方案一直都在那裡)。
適用於 JSL 和 Python。
回覆:JMP 中的 Python:資料框列到資料表列的最佳實務及計算速度影響JMP 資料表 dt 可以透過從 0 開始的列索引或列名進行索引。使用索引速度更快,因為它避免了字串比較。
我嘗試使用索引重寫程式碼,但似乎找不到與資料框中根據名稱確定資料表列索引等效的語法。
dfColumnIndex = df.get_loc['資料框列名稱']
dtColumnIndex = ???
回覆:JMP 中的 Python:資料框列到資料表列的最佳實務及計算速度影響@jthi ,請問「加入一些程式碼來修復一個不存在的問題」這種做法叫什麼?
回覆:JMP 中的 Python:資料框列到資料表列的最佳實務及計算速度影響如果 df 是由 dt 創建的,那麼通常假設它們具有相同的索引。如果您在資料框中新增了資料列,或者兩者並非源自相同資料框,那麼使用 `dt['col_name'] =` 可能是您唯一的選擇。與使用索引相比,這種方法的開銷應該不會太大,但它本質上是字串比較,而不是直接使用數值索引。
回覆:JMP 中的 Python:資料框列到資料表列的最佳實務及計算速度影響這一切都發生在 Python 的 Protocol Buffers 的神奇之處,資料會在記憶體中被映射到支援的記憶體區塊之間。雖然可能不需要使用 `Begin | End Data Update` 進行分隔,但如果無法一次性映射整列數據,它會回退到遍歷每個值的方式,因此使用 `Data Update` 語句進行分隔是一個很好的安全措施。
回覆:JMP 中的 Python:資料框列到資料表列的最佳實務及計算速度影響我進行了大量實驗,嘗試了不同的方法。有些方法會拋出警告,有些則不會。最簡單的方法似乎是從資料框 (df) 建立一個資料轉換表 (dt),然後使用該資料轉換表來傳輸資料。這種方法從未報錯。
然而,關於速度問題…唯一產生影響的因素是,我嘗試向新建立的列寫入值,還是嘗試向資料表開啟(或建立)時已存在的列寫入值。新建立的列更新速度極慢,而已存在的列更新速度則非常快。
上次測試結果基於 10 萬行資料。執行此程式碼前,已存在 4 個參數,並新增了 5 列。總共更新了 9 列。
#這些「xfers」都使用了 dt['colname'] = dt_results['colname'] 語法。在用 DF 建立 dt_results 之後,沒有涉及任何 DataFrame。
建立步驟 df_results 耗時:0.3434 秒
步驟參數 1 傳輸時間:0.1795 秒
步驟參數 2 傳輸時間:0.1758 秒
步驟 df_results 建立時間:0.0231 秒 #這是不同 df 函數傳回的新結果集。
步驟參數 3 傳輸時間:0.1838 秒
步驟參數 4 傳輸時間:0.1890 秒
#此處的代號為:dt['NewCol1Test'] = df_results[stringnameofcolumn]
FutureWarning:Series.__getitem__ 將鍵視為位置的做法已棄用。在未來的版本中,整數鍵將始終被視為標籤(與 DataFrame 的行為一致)。若要按位置存取值,請使用 `ser.iloc[pos]`
將資料框 (df) 轉換為資料表 (dt) 並命名,耗時:15.8251 秒
#此處代號為:dt['NewCol2Test'] = df_results.iloc[:,dfColumnIndex]
FutureWarning:Series.__getitem__ 將鍵視為位置的做法已棄用。在未來的版本中,整數鍵將始終被視為標籤(與 DataFrame 的行為一致)。若要按位置存取值,請使用 `ser.iloc[pos]`
我不知道為什麼在使用iloc時仍然會拋出那個警告。
使用 iloc 傳輸將 df 轉換為 dt 耗時:16.0874 秒
#此處代號為:dt['NewCol3Test'] = dt_results[dfColumnIndex]
dt 索引到 dt 名稱的傳輸耗時:14.5659 秒
#此處代號為:dt[NewColumnIndex] = dt_results[dfColumnIndex]
dt 索引到 dt 索引的傳輸時間:14.4699 秒
此處的程式碼是:
jmp.run_jsl('dt << 開始資料更新;')
dt['NewCol5Test'] = dt_results[dfColumnIndex]
jmp.run_jsl('dt << 結束資料更新;')
#警告/錯誤結果:
"名稱未解析:在存取或評估“dt”時出現 dt,dt/*###*/
第 1 行
名稱未解析:存取或評估“dt”時出現“dt”錯誤,dt/*###*/
第 1 行”
#實際上,這則列印訊息與第二個「第 1 行」錯誤訊息在同一行。
dt 到 dt w 索引封裝在 begin/end 更新傳輸中,耗時:14.3627 秒
#10萬行資料運行時間的最終總結。
總執行時間:77.0904 秒
回覆:JMP 中的 Python:資料框列到資料表列的最佳實務及計算速度影響再次執行此程式碼 (**) 之前,請儲存您的資料 (*) - 或啟用開始/結束資料更新!
import jmp
import numpy as np
dt = jmp.open(jmp.SAMPLE_DATA + "Wafer Stacked.jmp")
jmp.globals['jdt'] = dt
jmp.run_jsl('Graph Builder( Variables( Y( :defects ) ), Elements( Bar( Y, Label( "Label by Value" ) ) ))');
# jmp.run_jsl('jdt << Begin Data Update;')
dt[ 5 ] = [np.random.randint(1000)]*177875
# jmp.run_jsl('jdt << End Data Update;')
(*)開玩笑啦,你不會丟失數據的 - 喝杯咖啡,等 JMP 完成就好。
回覆:JMP 中的 Python:資料框列到資料表列的最佳實務及計算速度影響
@hogi寫道:
第二次(**)
(**)TS - 00251136
@Dahlia_Watkins注意到,有時需要執行兩次程式碼才會遇到速度問題。
看起來像是時機「問題」。
或許:當你第一次執行程式碼時,要等到 Python 完成逐行資料更新後才會建立圖表。
編輯:
實際上,圖表並非必須作為互動元素才能使 Python 資料更新變慢。
以下文章中提供了更多不含互動元素的範例。
回覆:JMP 中的 Python:資料框列到資料表列的最佳實務及計算速度影響我已將此主題新增至「注意:Jmp 函數可能會出現意外行為」部分。
回覆:JMP 中的 Python:資料框列到資料表列的最佳實務及計算速度影響針對最初的問題:“如果存在新列,則資料存取速度會變慢”
以下是一個範例:
import jmp
dt = jmp.DataTable('dt_new', 50000)
col = dt.new_column("newCol")
# --------- cut here to make it slow - why? -------------
dt[ 0 ] = [1]*50000
分成 2 個步驟,JMP 需要幾秒鐘才能更新 50,000 個條目。
令人費解:
如果一次執行兩個程式碼區塊,JMP 不會出現問題。
回覆:JMP 中的 Python:資料框列到資料表列的最佳實務及計算速度影響除了@Paul_Nelson帖子中提到的解決方法之外,我認為還有一些其他的方法,例如:
- 使用 JSL 建立列/資料表
- 使用 JSL 向列中填入虛擬值
- 使用 JSL 儲存資料表,關閉並重新開啟它。
它們乍看之下很多都不錯——但這具有誤導性。
速度快的原因是相同的值被重複寫入列中,而 JMP 沒有觸發「資料更新警報」:
例如
jmp.globals['jdt'] = dt
jmp.run_jsl('''
nc = n cols(jdt);
Column(jdt,nc) << set each value(1)
''');
nc = dt.ncols
data = [1]*50000
dt[ nc - 1 ] =data
用Python也能實現同樣的奇蹟:
# 1st: slow
nc = dt.ncols
dt[ nc - 1 ] = range(50000)
# fast ! (same values!)
dt[ nc - 1 ] = range(50000)
# new values: slow again
dt[ nc - 1 ] = [5]*50000
回覆:JMP 中的 Python:資料框列到資料表列的最佳實務及計算速度影響您可以設定一個觸發器,以便在 JMP 認為資料更新時收到通知。
import jmp
dt = jmp.open(jmp.SAMPLE_DATA + "Big Class.jmp")
jmp.run_jsl('''
New Column( "trigger",
Formula(
If( Row() == 1,
Try( Caption( Char( ::i=::i+1 ) ), ::i = 1 )
);
:age;i
)
)
''')
dt[ 1 ] = range(40)
dt[ 1 ] = range(40) # no data update !
dt[ 1 ] = [2]*40
# for loop without begin/end data update -> finished before JMP notices the data update
jmp.run_jsl('''
for each row(:age = row());
wait(5);
''')
# for loop without begin/end data update - and some delay to give JMP the time to notice:
jmp.run_jsl('''
for each row(:age = 2*row();wait(0))
''')
所以,當原始值和新值完全相同時,確實不存在「資料更新」。
是為了解決無限循環問題而實施的嗎?太好了!
有趣的,
import jmp
dt = jmp.open(jmp.SAMPLE_DATA + "Wafer Stacked.jmp")
jmp.run_jsl('''
delete symbols(::i)
New Column( "trigger",
Formula(
If( Row() == 1,
Try( Caption( Char( ::i=::i+1 ) ), ::i = 1 )
);
:Y_Die;i
)
)
''')
nr = dt.nrows
dt[ 4 ] = range(nr)
耗時超過 1 分鐘——我數到 2。
回覆:JMP 中的 Python:資料框列到資料表列的最佳實務及計算速度影響
@Paul_Nelson wrote:
...
Most likely we will want to do support this from Python. Probably using a with block to suspend the UI updates of the table when filling / updating large number of rows.
好主意!
即使 Python 在「with」程式碼區塊中拋出錯誤,也請務必重新啟用互動功能。
另一個選擇是:
請在資料網格中新增一個指示器,以顯示互動功能何時因某些原因而停用。
開始資料更新,但沒有結束資料更新。
指示器:此資料表的互動功能已停用
回覆:JMP 中的 Python:資料框列到資料表列的最佳實務及計算速度影響前幾篇文章的研究結果—精簡如下:
import jmp
# open data table and wait for the "Interactivity Manager" to get active
jmp.run_jsl('''
dt = new table("test", add rows(100000), new column ("col"));
Python send(dt);
wait(0) // disable the delay to make it fast
'''
);
nr=dt.nrows
# Python data update: slow!
dt[ 0 ] = range(nr)
# --- run step by step: -----
# same values: fast
dt[ 0 ] = range(nr)
#new values: slow
dt[ 0 ] = [1]*nr
透過 Python 更新 JSL 列的速度非常慢。
例外情況:
- 建立/開啟資料表後立即授予寫入權限(在某種「互動管理器」啟動之前)
- 沒有任何更新(數值沒有變化)
以及@Paul_Nelson貼文中提到的變通方法:
- 私人資料表
- Python 資料更新被JSL 開始/結束資料更新()包裹

透過 JSL,即可實現快速的 Python 資料存取 
我想這些問題應該要夠嚴重,可以在下一個「.x」版本中解決…
回覆:JMP 中的 Python:資料框列到資料表列的最佳實務及計算速度影響實際上,第二種情況需要將表格設為私有,即 visibility='Private'。 「不可見」的表格即使隱藏了,仍然會顯示圖形使用者介面 (GUI),因此每次編輯儲存格時資料表格都會更新 GUI,導致 GUI 介面更新,造成卡頓。
回覆:JMP 中的 Python:資料框列到資料表列的最佳實務及計算速度影響是的,這非常重要。我們確實正在尋找解決方案。我們希望 Python 的效能能夠達到使用者願意直接使用 Python 運行程序,而不是為了提升效能而使用一些不正當的技巧方法。
我很驚訝這個問題沒有在 JMP 18 或 JMP 19 EA 測試中出現。 jmp.DataTable 物件的核心是 JSL 資料表。 Python 物件只是對底層 JMP 資料表的引用,我們呼叫的是 JMP 資料表的內部方法,這就是為什麼 `run_jsl('dt << Begin Data Update')` 會產生影響。 `jmp.run_jsl(''' code '''')` 的全部意義在於允許從 Python 呼叫 JSL 來處理我們尚未實現的功能,或者像本例中一樣,糾正列更新效能方面的疏忽。
回覆:JMP 中的 Python:資料框列到資料表列的最佳實務及計算速度影響很高興聽到問題已解決。
為什麼之前沒有發現?
我猜想很多用戶在將資料傳輸到 JMP 之前,會使用 Python 進行資料導入/資料整理——在 JMP 中,他們不會再透過 Python 直接操作資料。如果他們使用Python 的 `get(dt)`(JSL)或`from_dataframe()`(Python)函數,就不會遇到這個問題。
注意:逐列傳輸,如JMP 18 中的 Python 整合入門指南中所述。
這取決於資料表的大小。如果表格夠小,新列會在「互動管理器」啟動之前新增。
當我們發現問題時,我們可以使用 JMP19EA,並且很高興地發現我們可以透過from_dataframe()來加快表格產生速度,而且程式碼也更加簡單。
另一方面:當使用 Python更新現有 JMP 資料表中的單一欄位時,就會出現這個問題,例如在JMPyFacade:連接 JMP 和 Python 以實現無縫互動分析。作為 JSL 專家, @jthi肯定毫不猶豫地添加了必要的私有表合併資料變通方案;)
回覆:JMP 中的 Python:資料框列到資料表列的最佳實務及計算速度影響今天腳本編寫者俱樂部分享的訊息:
速度問題
dt[ index ] = list_like_object
將在 JMP 19.0.3 版本中修復 
回覆:JMP 中的 Python:資料框列到資料表列的最佳實務及計算速度影響現已推出:
