這篇文章是本系列文章中的第一篇,闡述了如何用其他語言(如C,C 或Java)編寫的代碼插入到Stata中。這種技術被稱為編寫插件或為Stata編寫動態鏈接庫(DLL)。
可以為任何任務編寫插件,包括數據管理、圖形分析或統計估算。根據本系列的主題,我們先討論估算命令的插件。
本文中,將討論編寫插件的利弊,并且討論一個簡單的程序,其計算將在以后的文章中用插件來替換。
什么是插件?
插件是一種或多種用另外一種語言編寫的函數,可以從Stata中調用。從技術上講,另一個程序在被調用時會鏈接到Stata,這個過程被稱為包含其他程序的庫到Stata的動態鏈接。 出于這個原因,插件也被稱為DLL。
人們用其他語言編寫插件來解決真正困難的問題。 可以為Stata編寫插件的三個最常見的原因如下。
使用另一種語言編寫的代碼不適用于Stata或Mata。
在Stata / Mata中有一個編譯系統,它不使用內置、快速、矢量化的函數,可以通過使用低級別語言(如C)進行一些計算來使其更快。
可以使用像C語言這樣的低級別語言開發方法,然后將這些方法插入到Stata和其他統計軟件包中。
插件的問題在于它們很難。用另一種語言編寫和編譯的插件要比僅僅使用Stata和Mata編寫的插件困難得多。此外,錯誤的成本很高。插件可能很危險; 可能會損壞內存或做其他事情而導致Stata退出或以其他方式“崩潰”。
在C、C 或Fortran中維護插件是很難的。對于這些插件,您希望其為工作的每個操作系統都編譯一個版本。 例如,您可能需要Windows、Mac和Linux或兩個版本的編譯庫。此外,當操作系統獲得新版本時,必須重新編譯插件,并且可能需要為操作系統的舊版本和新版本分發版本。
Java插件更容易維護,但它們通常比用C、C 或Fortran編寫的插件慢??梢苑职l單個Java庫以便在所有支持的操作系統上運行。只需在Java環境中需要更改時重新編譯即可。盡管存在這些困難,但在某些情況下,插件可以使Stata實現可用或可行。本系列文章演示了如何用不同語言編寫和編譯插件。
平均估計量
首先討論在代碼塊1中給出的mymean10.ado,它實現了命令mymean10。mymean10計算用戶指定的變量均值和估計量的方差 - 協方差(VCE)的樣本平均估計量。 mymean10允許用戶指定if限制和in范圍,處理指定變量中的缺失值,但不允許任何選項。
mymean10.ado使用Mata進行計算。隨后的文章中將用插件計算代替這些Mata計算。
請注意程序的結構。第6-7行解析用戶輸入的內容,第10行使用Mata工作函數mymean_work()進行計算,第12-18行存儲并顯示結果,第21-40行定義mymean_work()。
我們來看看這些部分。
在第6行,syntax創建本地宏varlist,其中包含用戶指定的變量。syntax還創建了本地宏if 和in,分別包含任何限制或用戶指定的范圍。第7行中,marksample使用本地宏varlist,if和in來創建樣本包含變量。該樣本包含變量對于每個包括的觀察值是1,對于每個排除的觀察值是0。樣本包含變量說明了用戶指定的if限制條件或明確排除了觀察結果的in范圍,并且在varlist中的任何變量中都缺失的值,這些值隱含地排除了觀察結果。marksample將此樣本變量的名稱放在本地宏touse中。
第8行,tempname將臨時名稱放入本地宏b,V和N中。這些名稱未被使用,當mymean10完成時,將刪除存儲在這些名稱中的對象。我們使用臨時名稱來避免覆蓋用戶創建的全局對象,如Stata矩陣和Stata標量。
第10行使用了一行調用Mata工作函數mymean_work()來計算點估計值,VCE和樣本大小。mymean_work()將點估計的矢量放入Stata矩陣中,其名稱存儲在本地宏b中。mymean_work()將VCE放入Stata矩陣中,其名稱存儲在本地宏V中。并且mymean_work()將包含的觀察數量放在Stata標量中,其名稱存儲在本地宏N中。
第12-14行將行和列名稱放在存儲點估計矢量和VCE的矩陣上。第15-17行將結果存儲在e()中,第18行產生標準的Stata輸出表。
第21-40行定義了mymean_work()。
第22-24行指定mywork_work()不向其調用者返回任何內容并接受五個字符串標量參數。第一個參數vlist包含用戶指定變量的名稱。第二個,touse,包含上面討論的樣本包含變量的名稱。 最后三個包含將存儲結果的名稱。當mymean_work()完成時,本地宏bname包含存儲點估計向量的Stata矩陣的名稱,本地宏vname包含存儲在VCE的Stata矩陣的名稱,本地宏nname包含存儲樣本觀察數量的Stata標量名稱。
現在討論mymean_work()。第26-28行聲明函數中使用的變量。第30行將Stata中的樣本包含變量為1的觀察副本放入矩陣X。
第31-34行計算結果。31行將點估計值放入Mata向量b中。第32-34行計算VCE并將其存儲在Mata矩陣V中。
在36行,st_matrix()將點估計值從b復制到Stata矩陣,其名稱存儲在bname中。在第37行中,st_matrix()將VCE從V復制到Stata矩陣,其名稱存儲在vname中。在第38行中,st_numscalar()將樣本觀察的數量從Mata標量n復制到Stata標量,名稱存儲在nname中。
當我們編寫插件時會發生什么變化?
當我們編寫插件時,所有通用結構和許多細節都保持不變。 我們稱插件為計算而不是Mata函數有什么變化。
考慮用C語言編寫代碼來復制mymean_work()執行的計算。三件事可能被改變。首先,我們不會將數據從Stata復制到我們的插件中。 插件環境為我們提供了Stata中數據的視圖。 其次,我們必須編寫一個函數來實現在第31行執行的平均值。第三,我們必須編寫一個函數來實現在第32-34行上執行的VCE計算。
為了便于介紹插件,我在Mata中進行了這些更改,如代碼塊2中的mymean11.ado所示。
第1-19行中的Stata部分保持不變,調用mymean_work()也是如此。
31和32行與mymean10.ado中的對應行不同。mymean10.ado中的第30行將用戶指定變量的觀察副本放入矩陣X中,其中樣本包含變量為1。第32行mymean11在用戶指定變量的所有觀察中獲得一個名為X的單獨視圖。 這些關于samp和X的視圖更像是插件環境提供的數據訪問功能。
在mymean11.ado的第34行中,我們使用MyAve()函數將樣本平均值放入b中,將樣本包含變量為1的觀察數量放入n中。第42-59行的MyAve()代碼類似于可寫入的代碼,例如C。最大的區別是第58行使用Mata運算符將b的每個元素除以標量。插件將包含執行這些操作的函數。
在第35行,我們使用MyV()函數將VCE放入V。 MyV()的代碼在61-95行。這段代碼也類似于用C編寫的代碼,同樣需要注意的是94行將作為一個函數來實現。
myean11.ado中的第37-39行與mymean10.ado中36-38的對應行相同。
如果我在Mata中編寫估算器,不會在myean11中使用該實現。只需將樣本包含變量為1的觀察值放入Mata視圖中,就可以大大加快速度。MyAve()和MyV()函數說明了如何在數據循環中執行計算。插件將實現這些功能的版本。