輔助 Excel 的資料計算 add-ins

嘟嘟是隻喵發表於2020-11-26

對於大多數簡單運算,Excel都提供了方便的實現手段,有時是易用的函式,有時是直觀的按鈕或選單。但我們還是會遇到的一些較複雜或特殊的運算,依靠Excel本身很難實現。Excel提供了add-in介面,可以通過這個介面執行外部程式,從而藉助外部語言或指令碼實現這些較複雜或特殊的運算,達到輔助Excel的目的。

下面,讓我們深入瞭解一些Excel的常見資料計算add-ins,並評估它們的計算能力。

Excel DNA

Excel DNA是早期出現的一款Excel add-in,它可以把程式設計師寫好的動態庫函式放到Excel裡使用,動態庫可以使用C#/F#/VB.net等語言等編寫。

具體用法上,Excel DNA和其他所有add-ins都類似,首先要編寫自定義函式。比如下面C#編寫的程式碼中(引自Excel DNA官網),MyFunction是自定義函式名。

using System;

using   System.Collections.Generic;

using System.Linq;

using System.Text;

using   System.Threading.Tasks;

using   ExcelDna.Integration;

 

namespace MyLibrary

{

        public class Class1

        {

                [ExcelFunction(Description="few   people use this way!")]

                public static string   MyFunction(string name)

                {

                        return "Bonjour" + name;

                }

        }

}

上面的程式碼須編譯成動態庫,之後才能在Excel中使用。

接下來,一般要配置自定義函式和add-in的關係。比如下面的DnaSample.dna檔案,表明本add-in的名字是"My name",對應的動態庫是Mylibrary.dll(含有多個自定義函式)。

<DnaLibrary Name="My   name" RuntimeVersion="v4.0">

    <ExternalLibrary   Path="Mylibrary.dll" />

</DnaLibary>

最後在Excel中配置該add-in,就可以在單元格中呼叫MyFunction這個函式了,如下:

應該注意到,上述過程有個編譯的動作,因為編譯過的程式可直接執行,且與Excel整合緊密,因此執行效率非常高。這便帶來了Excel DNA最大的優點:順滑無卡頓。

Excel DNA的其他優點從名字就可以看出來,換句話說,該add-in可以充分利用微軟DNA架構提供的便利,比如開發語言、開發工具、Excel整合、聯動除錯等。

還應該注意到,C#/F#/VB.net等語言的通用性很強,理論上是無所不能的,但官網的程式碼例子卻只是字串輸出,體現不出哪怕絲毫的能力,這到底是為什麼呢?

因為理論和實際是有差別的。

C#/F#/VB.net等語言缺乏結構化計算類庫,即使最基本的運算都要硬編碼實現,程式碼因此非常繁瑣,並不適合做複雜的資料計算。

除了不適合資料計算,還應注意到C#/F#/VB.net是編譯型語言,而不是解釋型語言,這就要求使用者必須維護一套編譯環境,以備修改演算法後編譯所用,而微軟的編譯環境配置較複雜,桌面資料分析師不易掌握。事實上,C#/F#/VB.net等語言本身的技術門檻就很高,這就導致Excel DNA更適合專業程式設計師作為介面使用,並不適合大多數桌面資料分析師直接使用。

除了Excel DNA,還有其他一些add-ins同樣缺乏結構化計算類庫,比如基於JAVA語言的JINX。很容易就能判斷出,JINX也不適合資料計算。事實上,Excel 自帶的VBA在語言能力上和Excel DNA/JINX相當(都不適合資料計算),但VBA免整合免編譯,比Excel DNA/JINX更有競爭優勢。

 

Excel JavaScript

顯而易見,無論什麼add-ins,至少要比VBA方便好用,才值得我們學習研究。微軟也發現了這個問題,所以2013年推出了Excel JavaScript,一種比VBA更方便的add-ins專用語言。

Excel JavaScript的用法和其他add-ins類似,這裡以及後續都不再贅述。值得強調的是,Excel JavaScript是解釋型語言,可以隨時修改並立即執行,而無需編譯,這一點和Excel DNA區別較大。既然是解釋型語言,一般就會存在卡頓問題,但Excel JavaScript是Excel內建的語言,可以在同一個程式中執行,因此實際效果非常順滑,執行效率僅次於Excel DNA。

內建於Excel還會的帶來其他好處,比如無需下載,可直接開發,這便省去了繁瑣的部署過程。再比如Excel JavaScript繼承了Excel跨平臺的能力,只需編寫一次,就可以在單機版、網頁版、Mac版上無縫遷移。另外,Excel JavaScript可直接訪問workbook、sheet、cell等Excel物件,開發效率顯著提升。

等一下,上面說的雖然都是Excel JavaScript的優勢,但好像VBA也具備同等的優勢,所以,說好的“更方便”到底體現在哪裡?

比VBA更方便,體現在Excel JavaScript的介面控制能力上。換句話說,Excel JavaScript可以用更簡單的語法訪問Excel選單欄、皮膚按鈕、彈出框,可以在JS檔案中直接定義add-ins介面,比VBA方便太多了。

唯一的問題是,介面控制並非資料計算add-ins的重點,不值得我們關注……

不錯,既然講的是資料計算add-ins,那資料計算能力才是關注重點,而不是介面控制能力。但遺憾的是,JavaScript依然沒有任何結構化計算函式,用於做Excel都難以實現的資料計算也沒啥特別的優勢,僅僅是讓Excel多了一種不同於VBA的指令碼語言而已。

 

pyxll

顯然,只有具備結構化計算類庫,才算是合格的資料計算add-ins,比如這裡要講的pyxll。Pyxll是基於Python語言的add-in,而Python擁有結構化計算類庫Pandas。

既然是合格的資料計算add-in,pyxll實現簡單演算法時自然無需硬編碼,比如對指定區域分組彙總:選中Excel中的一批員工記錄,傳給自定義函式groupEmp,由pyxll執行分組彙總演算法,並返回計算結果,只需編寫如下程式碼:

import pandas as pd

import numpy as np

from pyxll import xl_func

@xl_func("dataframe<index=False, columns=True>")

def groupEmp(df):

      df=df.groupby("deptid")['salary'].agg([len, np.sum,   np.mean])      #核心程式碼:分組彙總

    return df

上面核心程式碼只有一行,其他程式碼基本都是定式。可以看到,具備結構化庫函式的pyxll,可以用非常簡潔的程式碼實現分組彙總等簡單演算法。

當然,有時也會遇到較複雜或特殊的運算,需要用多個函式組合實現,而不是單獨使用排序、過濾之類基本函式。遺憾的是,pyxll實現較複雜或特殊的運算時不太方便。

比如規範化資料並分組彙總的例子:針對Excel中的住戶戶型明細表(A-E列),自定義函式需按STYLE和BEDROOMS分組,統計SQFEET、BATHS、PRICE的平均值,其中PRICE列原本是字串,需去掉$符號,轉為數值再計算。

處理前資料

處理後的資料在新sheet中。

實現上述演算法的自定義函式如下(只保留核心程式碼):

for i in range(1,   len(b)):

    b[i][4] = b[i][4].replace(“$”,‘ ‘)

    b[i][4] = b[i][4].replace(“,”,‘ ‘)

for i in range(1,   len(b)):

    for j in [1, 2, 3, 4]:

        b[i][j] = eval(b[i][j])

data =   pandas.DataFrame(b[1:],columns=b[0])

out =   data.groupby([‘STYLE’,‘BEDROOMS’]).mean()

return out

分組還是隻有一句,但前面的預處理卻要6行,有點麻煩。

再比如一行分多行的例子:A列儲存ID,B列儲存ID對應的列表List,List有多個成員,以空格為分隔符。自定義函式需將List按空格拆分,使每個ID對應一個成員。

處理前的資料       

處理後的資料在新sheet中:

實現上述演算法的自定義函式如下:

split_dict = df.set_index( 'ID').T.to_dict( 'list')

split_list = []

for key,value in split_dict.items():

    anomalies = value[0].split( ' ')

    key_array = np.tile(key,len(anomalies))

    split_df =   pd.DataFrame(np.array([key_array,anomalies]).T,columns=[ 'ID', 'ANOMALIES'])

    split_list.append(split_df)

df = pd.concat(split_list,ignore_index=True)

return df

可以看到,即使只保留核心運算功能,pyxll的程式碼仍然有點複雜。這就是pyxll缺點之一:不擅長實現較複雜或特殊的運算。

pyxll還有一個缺點:Excel要呼叫外部直譯器來解釋Python指令碼,因此頓挫感較強烈,會嚴重影響使用者體驗。當然,頓挫並非pyxll獨有的問題,而是所有外部解釋型指令碼共通的問題,比如XLwings、 Bert和RExcel。其中XLwings與pyxll同樣是基於Python的add-ins,優缺點基本一樣。Bert和RExcel是基於R的add-ins,R專注於科學模型演算法,其結構化計算類庫不夠專業,因此這兩款add-ins的計算能力還不如pyxll,頓挫感也會更強。

當然,解釋型語言也有優點,最大的優點是無需編譯即可執行,維護修改都很方便。

 

esProc

esProc是專業的資料計算引擎,也提供了一個Excel add-in,可以使用esProc 的SPL語言編寫計算指令碼。它與pyxll有很多相似之處,比如兩者都有豐富的結構化計算函式,因此可以輕鬆實現簡單演算法,比如對指定區域分組彙總,只需編寫指令碼groupEmp.dfx:

核心程式碼只有A2一行,非常簡潔。之後就可以在Excel單元格中引用自定義函式groupEmp,形如=dfx("groupEmp",A1:D20)。

其他基本演算法也可以輕鬆實現(只留核心程式碼):

通過了解之前的add-ins,我們已經可以得出結論:是否能方便地實現較複雜或特殊的運算,才是判斷一款add-in的資料計算能力的真正指標。

esProc能夠方便地實現較複雜或特殊的運算,這是它比pyxll更有優勢的地方。

比如規範化資料並分組彙總,前面用pyxll時顯得很麻煩,但esProc就簡單多了:

再比如一行分多行,esProc程式碼更簡單:

 

再舉個邏輯更復雜的例子:計算分期貸款明細。Excel單元格記錄著貸款資訊,包括貸款 ID,貸款總額、按月分期數、年利率,示意如下:

自定義函式的目的是計算出各期明細,包括:當期還款額、當期利息、當期本金、剩餘本金。計算結果在新sheet 中應當如下:

上面的演算法用pyxll實現會很麻煩,但esProc 就很方便:

看起來esProc的資料計算能力確實很強大,但是,非常遺憾的是,esProc也是基於外部直譯器的add-in,還需要JVM來執行,它同樣存在卡頓的問題。

 

esProc  through clipboard

有沒有辦法既能利用esProc強大的計算能力,還能順滑地操作esProc?

用剪貼簿代替自定義函式!

比如求各科前3名的學生:A列是學生姓名,B-D列分別是數學、英語、物理成績,需要求出每學科成績前3名的學生,並追加到本科成績之後。

處理前資料

選中這些單元格,先用ctrl+C複製到剪貼簿,再在esProc指令碼中執行如下程式碼:

執行上述指令碼後,只需在Excel的B11格用ctrl+V,即可將剪下板中的資料複製到B11-D13,達到和自定義函式相同的效果,如下:

類似的,大多數自定義函式都可以用剪下板簡單代替,除非遇到一些特殊情況,比如多片區域參與運算。

應該注意到,上述過程雖然可以到達順滑操作的目的,也可以利用到esProc強大的計算能力,但並沒有使用add-in協議。事實上,如果願意使用剪下板,就沒必要部署複雜的add-ins,這對資料分析師來說,難道不是一件減輕負擔的好事嗎?

還應該注意到,不僅esProc可以利用剪貼簿來解決卡頓的問題,pyxll等add-ins理論上也完全可以,只要它們在將來的版本中提供類似的函式(從剪下板獲取資料並轉換成內部的結構化資料型別)。

經過前面的比較,我們可以得出這樣的結論:流暢的add-ins計算能力差;計算能力強的add-ins存在卡頓現象。配合剪下板使用esProc可以彌補卡頓的缺點,更適合桌面分析師使用。



來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31543054/viewspace-2737328/,如需轉載,請註明出處,否則將追究法律責任。

相關文章