數據導出爲 .SAV文件到SPSS軟件進行分析的解決過程

我們的一個產品是做醫療數據分析統計的,核心功能是通過精細的數據過濾條件及靈活的組合方式,給予用戶使用嚴苛條件檢索出病人數據,而數據分析這塊,我們是通過 R 語言庫,編碼實現常用的統計分析功能。

有個客戶可能是習慣於使用 IBM SPSS 分析軟件,提出了篩選出來的病人數據直接進入 SPSS,然後他們就可以在 SPSS 裏面做任何分析。對我們而言就是怎麼讓數據生成好後,用戶在我們的應用裏一鍵啓動 SPSS 並且數據自動顯現在 SPSS 的數據窗口。

很顯然,如果 SPSS 不提供相關接口,我們沒辦法做到。好在 SPSS 在版本 21 裏提供了一個 javaspssplugin.jar, 有了它,調用 API 就可以在不以 page mode 方式啓動 SPSS 下使用其強大的統計分析功能,其實就是每次都要通過 statsXD.exe 打開一個 spss 後臺進程,提交數據,完了得到分析結果,最後結束進程。

這段代碼就是啓動 SPSS 進程的邏輯,StartXD.exe 就是啓動後臺進程的可執行文件。而 SPSS 的 stats.exe 是啓動 page mode 的 SPSS 進程的可執行文件,那麼我是否可以手寫一個類似的代碼,這樣就打開了 SPSS 桌面應用的窗口,並且數據傳過去了呢?

事情並沒有那麼簡單,通過 Eclipse 的反編譯插件,可以看到代碼的封裝的非常好,不管是屬性,方法,還是類,都沒有辦法繼承,或以某種方式修改關鍵屬性。乾脆,我直接反編譯代碼,新建一個 java 工程,添加一個我自己的類,然後重新打包。可是試了幾種反編譯工具,都無法得到一個沒有編譯錯誤的工程代碼。

這條路走不通,的確有點沮喪,但是忽然想到了 StartXD.exe 後面可以帶參數,那麼 stats.exe 是不是也可以呢?終於找到了 SPSS Command line options ,剩下的就簡單了,我下載一個 SPSS,然後寫個 csv 數據文件,一個 main 方法,裏面使用

Runtime.getRuntime().exec("D:/software/SPSS/stats.exe D:/data/abc.csv") 

執行SPSS命令,很快搞定。

然而又有新問題了,一般地,我們的應用是部署在客戶的服務器上的,用戶使用瀏覽器訪問這個應用,而 SPSS 桌面應用用戶會在本地安裝一個。由於數據文件是寫在服務端的,這個肯定要傳給用戶使用,只能是通過瀏覽器下載,這個不難。但是下載後,怎麼讓瀏覽器感知相關文件下載完畢,並且能像我們 main 方法裏那樣調用操作系統命令呢?

看來瀏覽器不但要下載數據文件,還得下載個 bat 文件啊,不然沒辦法執行命令啊。恩,的確 IE 瀏覽器可以通過 ActiveXObject.run() 執行 bat 文件。可是,我們的應用不管是開發還是部署到客戶那邊,都是推薦使用 chrome 的,貌似 chrome 沒有類似 API 啊,假設也有吧。那麼剩下的就是 bat 裏面怎麼寫瀏覽器所在機器 SPSS 的安裝路徑?

是不是可以在用戶下載數據文件的頁面提供一個控件,讓用戶自己提交 SPSS 的安裝目錄?顯然,有些用戶可能不知道或忘記了這個軟件裝在哪,或者直接把桌面的快捷方式地址提供了,如果提供的是快捷方式,網上倒是有相關解決方法,幫助找到真實的 SPSS 安裝路徑,但代碼稍多,不好維護。如果在 bat 裏通過 find 命令啥的找,那太費時了,不現實。這些問題其實很快就被否定了,不能往這個路上走。

最後發現,只需要把文件數據文件寫成 SPSS 的數據文件也就是 .SAV 文件(要用到 spssw-**.jar 的 CsvToSPSS.class),然後 chrome 開啓 “總是打開此類文件” 就可以做到下載完畢後,直接啓動 SPSS 桌面應用並打開數據文件。

try (BufferedInputStream input = new BufferedInputStream(new ByteArrayInputStream(matrix.getBytes()));
		BufferedOutputStream ops = new BufferedOutputStream(resp.getOutputStream())) {
	resp.setContentType("application/x-spss-sav;charset=UTF-8");
	String fName = StringUtils.isEmpty(fileName) ? UUID.randomUUID().toString().substring(0, 8) : fileName;
	resp.addHeader("Content-Disposition", "attachment;filename=" + fName + ".sav");
	resp.setCharacterEncoding("UTF-8");	
	CsvToSPSS.convert(input, ops, "UTF-8");	
	ops.flush();
}

一通下來,算是峯迴路轉吧,只是爲什麼一開始就沒想到這個最簡單的方式呢。可能只想着所有的東西全部自動化,用戶不需要做任何操作,任何設置,一切通過編程的方式執行,最關鍵的是 SPSS 可以打開 CSV文件(會彈框詢問CSV格式),這個下載完後默認的打開方式是 Excel 的,這些因素限制了思維的發散性。

=========update on 2019/11/25============

上面的方案能夠解決數據導出並自動啓動SPSS軟件,但是出現了兩個問題:

1. 所有的數據,都是變成字符串類型了,用戶需要在SPSS裏手工修改數據類型
2. 表頭單獨佔用了一行,而不是常規的 .sav 在 SPSS 軟件裏的展示,這個格式還是不夠友好,需要用戶調整

解決方案是:

1. 藉助 R 語言,我們用的是  <groupId>com.github.jbytecode</groupId> <artifactId>RCaller</artifactId> 來處理,
也就是把數據導入到 R,建立一個 data frame
2. 注意,默認情況下,data frame 裏的字符串變量都會被識別成 Factor,需要對變量數據類型進行修改,
如 df$Name <- as.character(df$Name)
3. 使用 write_sav 函數將數據框數據寫到臨時的 .sav 文件
4. 最後 java 讀取 .sav 文件,以流傳給瀏覽器

將要導出的數據落盤到臨時的 df.sav 的 R 代碼:

RCaller caller = initRCaller(new RCallerTemplate() {
	@Override
	public void addRCode(RCode code) {
		code.addStringArray("Name", names);		
		code.addDoubleArray("Score", scores);
		code.addRCode("df <- data.frame(Score, Name)");
		code.addRCode("df$Name <- as.character(df$Name)");
		code.addRCode("write_sav(df, "<temp_dir>/df.sav")");
	}
});
caller.runOnly();

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章